From 4e9f69111105eabd8b7ef4d09b0aa41e912461b7 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 5 May 2020 18:46:12 -0700 Subject: [PATCH 01/14] Migrate engine to use multi-window API The goal here is to convert from the window singleton to an API that has the concept of multiple windows. I'm not attempting to actually enable creating multiple windows here, just migrate to an API that has a concept of multiple windows/screens. --- ci/licenses_golden/licenses_flutter | 13 + lib/ui/BUILD.gn | 6 + lib/ui/compositing.dart | 10 +- lib/ui/compositing/scene.cc | 13 +- lib/ui/dart_ui.cc | 4 +- lib/ui/dart_ui.gni | 2 + lib/ui/hooks.dart | 321 ++++-- lib/ui/natives.dart | 4 +- lib/ui/painting/canvas.cc | 9 +- lib/ui/platform_dispatcher.dart | 1023 +++++++++++++++++ lib/ui/screen.dart | 25 + lib/ui/semantics.dart | 10 +- lib/ui/text.dart | 2 +- lib/ui/text/font_collection.cc | 8 +- lib/ui/text/paragraph_builder.cc | 8 +- lib/ui/ui.dart | 2 + lib/ui/ui_dart_state.cc | 18 +- lib/ui/ui_dart_state.h | 11 +- lib/ui/window.dart | 733 ++++++------ lib/ui/window/platform_configuration.cc | 451 ++++++++ lib/ui/window/platform_configuration.h | 118 ++ .../window/platform_message_response_dart.cc | 1 + lib/ui/window/screen.cc | 54 + lib/ui/window/screen.h | 39 + lib/ui/window/screen_metrics.cc | 101 ++ lib/ui/window/screen_metrics.h | 80 ++ lib/ui/window/viewport_metrics.cc | 22 +- lib/ui/window/viewport_metrics.h | 11 +- lib/ui/window/window.cc | 398 +------ lib/ui/window/window.h | 80 +- lib/web_ui/lib/src/engine.dart | 35 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 8 +- lib/web_ui/lib/src/engine/canvas_pool.dart | 12 +- .../src/engine/compositor/embedded_views.dart | 2 +- .../src/engine/compositor/screen_metrics.dart | 18 + .../lib/src/engine/compositor/util.dart | 2 +- .../engine/compositor/viewport_metrics.dart | 2 - lib/web_ui/lib/src/engine/dom_renderer.dart | 8 +- lib/web_ui/lib/src/engine/history.dart | 8 +- lib/web_ui/lib/src/engine/keyboard.dart | 6 +- .../lib/src/engine/platform_dispatcher.dart | 978 ++++++++++++++++ .../lib/src/engine/pointer_binding.dart | 4 +- lib/web_ui/lib/src/engine/screen.dart | 26 + .../src/engine/semantics/incrementable.dart | 4 +- .../lib/src/engine/semantics/scrollable.dart | 8 +- .../lib/src/engine/semantics/semantics.dart | 4 +- .../lib/src/engine/semantics/tappable.dart | 2 +- .../lib/src/engine/semantics/text_field.dart | 4 +- lib/web_ui/lib/src/engine/surface/path.dart | 2 +- .../src/engine/surface/render_vertices.dart | 4 +- .../lib/src/engine/surface/surface_stats.dart | 8 +- .../src/engine/text_editing/text_editing.dart | 18 +- lib/web_ui/lib/src/engine/util.dart | 4 +- lib/web_ui/lib/src/engine/window.dart | 674 +---------- lib/web_ui/lib/src/ui/compositing.dart | 10 +- lib/web_ui/lib/src/ui/initialization.dart | 2 +- .../lib/src/ui/platform_dispatcher.dart | 310 +++++ lib/web_ui/lib/src/ui/screen.dart | 12 + lib/web_ui/lib/src/ui/semantics.dart | 10 +- lib/web_ui/lib/src/ui/text.dart | 1 - lib/web_ui/lib/src/ui/window.dart | 593 ++-------- lib/web_ui/lib/ui.dart | 2 + lib/web_ui/pubspec.yaml | 1 + lib/web_ui/test/canvaskit/util_test.dart | 2 +- lib/web_ui/test/engine/history_test.dart | 2 +- lib/web_ui/test/engine/navigation_test.dart | 4 +- lib/web_ui/test/engine/window_test.dart | 28 +- .../engine/canvas_golden_test.dart | 4 +- .../engine/compositing_golden_test.dart | 4 +- lib/web_ui/test/window_test.dart | 24 +- runtime/dart_isolate.cc | 7 +- runtime/dart_isolate.h | 4 +- runtime/runtime_controller.cc | 108 +- runtime/runtime_controller.h | 18 +- runtime/runtime_delegate.h | 2 +- runtime/window_data.h | 6 +- shell/common/engine.cc | 16 +- shell/common/engine.h | 34 +- shell/common/fixtures/shell_test.dart | 40 +- shell/common/platform_view.cc | 4 + shell/common/platform_view.h | 22 + shell/common/shell.cc | 23 + shell/common/shell.h | 3 + shell/common/shell_test.cc | 17 +- shell/common/shell_test.h | 1 + shell/common/shell_unittests.cc | 11 +- .../ios/framework/Source/FlutterEngine.mm | 13 +- .../framework/Source/FlutterEngine_Internal.h | 2 + .../framework/Source/FlutterViewController.mm | 38 +- .../Source/accessibility_bridge_test.mm | 1 + .../macos/framework/Source/FlutterEngine.mm | 19 +- shell/platform/embedder/embedder.cc | 23 +- shell/platform/embedder/embedder.h | 16 +- shell/platform/embedder/embedder_engine.cc | 13 + shell/platform/embedder/embedder_engine.h | 2 + shell/platform/embedder/fixtures/main.dart | 180 +-- .../embedder/tests/embedder_unittests.cc | 132 ++- .../fuchsia/flutter/compilation_trace.txt | 4 +- .../platform/fuchsia/flutter/platform_view.cc | 50 +- .../fuchsia/flutter/platform_view_unittest.cc | 2 + shell/platform/glfw/flutter_glfw.cc | 20 +- shell/platform/linux/fl_engine.cc | 18 +- shell/platform/linux/fl_engine_private.h | 14 +- shell/platform/linux/fl_view.cc | 5 +- .../platform/windows/win32_flutter_window.cc | 27 +- shell/platform/windows/win32_flutter_window.h | 6 +- shell/testing/tester_main.cc | 15 +- .../dart/window_hooks_integration_test.dart | 166 ++- .../lib/src/animated_color_square.dart | 8 +- .../scenario_app/lib/src/channel_util.dart | 2 +- .../lib/src/locale_initialization.dart | 6 +- .../scenario_app/lib/src/platform_view.dart | 134 +-- .../scenario_app/lib/src/poppable_screen.dart | 8 +- testing/scenario_app/lib/src/scenario.dart | 16 +- testing/scenario_app/lib/src/scenarios.dart | 46 +- .../lib/src/send_text_focus_semantics.dart | 2 +- .../lib/src/touches_scenario.dart | 2 +- .../.dart_tool/package_config.json | 8 +- tools/const_finder/.packages | 2 +- 119 files changed, 5132 insertions(+), 2611 deletions(-) create mode 100644 lib/ui/platform_dispatcher.dart create mode 100644 lib/ui/screen.dart create mode 100644 lib/ui/window/platform_configuration.cc create mode 100644 lib/ui/window/platform_configuration.h create mode 100644 lib/ui/window/screen.cc create mode 100644 lib/ui/window/screen.h create mode 100644 lib/ui/window/screen_metrics.cc create mode 100644 lib/ui/window/screen_metrics.h create mode 100644 lib/web_ui/lib/src/engine/compositor/screen_metrics.dart create mode 100644 lib/web_ui/lib/src/engine/platform_dispatcher.dart create mode 100644 lib/web_ui/lib/src/engine/screen.dart create mode 100644 lib/web_ui/lib/src/ui/platform_dispatcher.dart create mode 100644 lib/web_ui/lib/src/ui/screen.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2ec3ecf983dd6..c0b6860574acc 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -362,10 +362,12 @@ FILE: ../../../flutter/lib/ui/painting/single_frame_codec.h FILE: ../../../flutter/lib/ui/painting/vertices.cc FILE: ../../../flutter/lib/ui/painting/vertices.h FILE: ../../../flutter/lib/ui/painting/vertices_unittests.cc +FILE: ../../../flutter/lib/ui/platform_dispatcher.dart FILE: ../../../flutter/lib/ui/plugins.dart FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc FILE: ../../../flutter/lib/ui/plugins/callback_cache.h FILE: ../../../flutter/lib/ui/pointer.dart +FILE: ../../../flutter/lib/ui/screen.dart FILE: ../../../flutter/lib/ui/semantics.dart FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.cc FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.h @@ -392,6 +394,8 @@ FILE: ../../../flutter/lib/ui/ui_benchmarks.cc FILE: ../../../flutter/lib/ui/ui_dart_state.cc FILE: ../../../flutter/lib/ui/ui_dart_state.h FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/platform_configuration.cc +FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_message.cc FILE: ../../../flutter/lib/ui/window/platform_message.h FILE: ../../../flutter/lib/ui/window/platform_message_response.cc @@ -405,6 +409,10 @@ FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter_unittests.cc +FILE: ../../../flutter/lib/ui/window/screen.cc +FILE: ../../../flutter/lib/ui/window/screen.h +FILE: ../../../flutter/lib/ui/window/screen_metrics.cc +FILE: ../../../flutter/lib/ui/window/screen_metrics.h FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc FILE: ../../../flutter/lib/ui/window/viewport_metrics.h FILE: ../../../flutter/lib/ui/window/window.cc @@ -439,6 +447,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/picture_recorder.dar FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/platform_message.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/raster_cache.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/rasterizer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/text.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/util.dart @@ -457,12 +466,14 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/onscreen_logging.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/path_to_svg.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_converter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/profiler.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/screen.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/image.dart @@ -529,7 +540,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/path.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/path_metrics.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/pointer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/screen.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/semantics.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/test_embedding.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/text.dart diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index a122a35eaa9c5..751d871acc5b0 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -89,6 +89,8 @@ source_set("ui") { "text/text_box.h", "ui_dart_state.cc", "ui_dart_state.h", + "window/platform_configuration.cc", + "window/platform_configuration.h", "window/platform_message.cc", "window/platform_message.h", "window/platform_message_response.cc", @@ -101,6 +103,10 @@ source_set("ui") { "window/pointer_data_packet.h", "window/pointer_data_packet_converter.cc", "window/pointer_data_packet_converter.h", + "window/screen.cc", + "window/screen.h", + "window/screen_metrics.cc", + "window/screen_metrics.h", "window/viewport_metrics.cc", "window/viewport_metrics.h", "window/window.cc", diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index ea3c0b714465a..a62c2918151a8 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -11,7 +11,7 @@ part of dart.ui; /// To create a Scene object, use a [SceneBuilder]. /// /// Scene objects can be displayed on the screen using the -/// [Window.render] method. +/// [FlutterWindow.render] method. @pragma('vm:entry-point') class Scene extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated @@ -179,7 +179,7 @@ class PhysicalShapeEngineLayer extends _EngineLayerWrapper { /// Builds a [Scene] containing the given visuals. /// -/// A [Scene] can then be rendered using [Window.render]. +/// A [Scene] can then be rendered using [FlutterWindow.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add @@ -650,8 +650,8 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call - /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "raster + /// [FlutterWindow.render]). The UI thread frame time is the total time + /// spent executing the [FlutterWindow.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. @@ -795,7 +795,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [Window.render]. + /// screen with [FlutterWindow.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index f5403ecae9a61..3e8a2177fdd5c 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -8,6 +8,7 @@ #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/picture.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/window.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" @@ -41,13 +42,21 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - auto viewport_metrics = UIDartState::Current()->window()->viewport_metrics(); + // Currently only support a single window on a single screen. + auto viewport_metrics = UIDartState::Current() + ->platform_configuration() + ->get_window(0) + ->viewport_metrics(); + auto screen_metrics = UIDartState::Current() + ->platform_configuration() + ->get_screen(0) + ->screen_metrics(); layer_tree_ = std::make_unique( SkISize::Make(viewport_metrics.physical_width, viewport_metrics.physical_height), static_cast(viewport_metrics.physical_depth), - static_cast(viewport_metrics.device_pixel_ratio)); + static_cast(screen_metrics.device_pixel_ratio)); layer_tree_->set_root_layer(std::move(rootLayer)); layer_tree_->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); layer_tree_->set_checkerboard_raster_cache_images( diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 1c82fff84c7ce..8374c85b1fc99 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -28,7 +28,7 @@ #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/text/paragraph.h" #include "flutter/lib/ui/text/paragraph_builder.h" -#include "flutter/lib/ui/window/window.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/logging/dart_error.h" @@ -81,7 +81,7 @@ void DartUI::InitForGlobal() { SemanticsUpdate::RegisterNatives(g_natives); SemanticsUpdateBuilder::RegisterNatives(g_natives); Vertices::RegisterNatives(g_natives); - Window::RegisterNatives(g_natives); + PlatformConfiguration::RegisterNatives(g_natives); #if defined(OS_FUCHSIA) SceneHost::RegisterNatives(g_natives); #endif diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 28cb82e9cd125..8e03d47b25e80 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -12,9 +12,11 @@ dart_ui_files = [ "//flutter/lib/ui/isolate_name_server.dart", "//flutter/lib/ui/lerp.dart", "//flutter/lib/ui/natives.dart", + "//flutter/lib/ui/platform_dispatcher.dart", "//flutter/lib/ui/painting.dart", "//flutter/lib/ui/plugins.dart", "//flutter/lib/ui/pointer.dart", + "//flutter/lib/ui/screen.dart", "//flutter/lib/ui/semantics.dart", "//flutter/lib/ui/text.dart", "//flutter/lib/ui/ui.dart", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 645e54e3879dd..601214dd8f167 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -11,7 +11,10 @@ part of dart.ui; @pragma('vm:entry-point') // ignore: unused_element void _updateWindowMetrics( - double devicePixelRatio, + Object id, + Object screenId, + double left, + double top, double width, double height, double depth, @@ -28,40 +31,116 @@ void _updateWindowMetrics( double systemGestureInsetBottom, double systemGestureInsetLeft, ) { - window - .._devicePixelRatio = devicePixelRatio - .._physicalSize = Size(width, height) - .._physicalDepth = depth - .._viewPadding = WindowPadding._( - top: viewPaddingTop, - right: viewPaddingRight, - bottom: viewPaddingBottom, - left: viewPaddingLeft) - .._viewInsets = WindowPadding._( - top: viewInsetTop, - right: viewInsetRight, - bottom: viewInsetBottom, - left: viewInsetLeft) - .._padding = WindowPadding._( - top: math.max(0.0, viewPaddingTop - viewInsetTop), - right: math.max(0.0, viewPaddingRight - viewInsetRight), - bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), - left: math.max(0.0, viewPaddingLeft - viewInsetLeft)) - .._systemGestureInsets = WindowPadding._( - top: math.max(0.0, systemGestureInsetTop), - right: math.max(0.0, systemGestureInsetRight), - bottom: math.max(0.0, systemGestureInsetBottom), - left: math.max(0.0, systemGestureInsetLeft)); - _invoke(window.onMetricsChanged, window._onMetricsChangedZone); + final ViewConfiguration previousConfiguration = + PlatformDispatcher.instance._viewConfigurations[id] ?? const ViewConfiguration(); + PlatformDispatcher.instance._viewConfigurations[id] = previousConfiguration.copyWith( + screen: PlatformDispatcher.instance._screens[screenId], + geometry: Rect.fromLTWH(left, top, width, height), + depth: depth, + viewPadding: WindowPadding._( + top: viewPaddingTop, + right: viewPaddingRight, + bottom: viewPaddingBottom, + left: viewPaddingLeft, + ), + viewInsets: WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft, + ), + padding: WindowPadding._( + top: math.max(0.0, viewPaddingTop - viewInsetTop), + right: math.max(0.0, viewPaddingRight - viewInsetRight), + bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), + left: math.max(0.0, viewPaddingLeft - viewInsetLeft), + ), + systemGestureInsets: WindowPadding._( + top: math.max(0.0, systemGestureInsetTop), + right: math.max(0.0, systemGestureInsetRight), + bottom: math.max(0.0, systemGestureInsetBottom), + left: math.max(0.0, systemGestureInsetLeft), + ), + ); + if (!PlatformDispatcher.instance._views.containsKey(id)) { + PlatformDispatcher.instance._views[id] = FlutterWindow._(windowId: id, platformDispatcher: PlatformDispatcher.instance); + } + _invoke( + PlatformDispatcher.instance.onMetricsChanged, + PlatformDispatcher.instance._onMetricsChangedZone, + ); +} + +@pragma('vm:entry-point') +// ignore: unused_element +void _updateScreenMetrics( + Object/*!*/ id, + String/*!*/ displayName, + double/*!*/ left, + double/*!*/ top, + double/*!*/ width, + double/*!*/ height, + double/*!*/ devicePixelRatio, + double/*!*/ viewPaddingTop, + double/*!*/ viewPaddingRight, + double/*!*/ viewPaddingBottom, + double/*!*/ viewPaddingLeft, + double/*!*/ viewInsetTop, + double/*!*/ viewInsetRight, + double/*!*/ viewInsetBottom, + double/*!*/ viewInsetLeft, + double/*!*/ systemGestureInsetTop, + double/*!*/ systemGestureInsetRight, + double/*!*/ systemGestureInsetBottom, + double/*!*/ systemGestureInsetLeft, +) { + final ScreenConfiguration previousConfiguration = + PlatformDispatcher.instance._screenConfigurations[id] ?? const ScreenConfiguration(); + PlatformDispatcher.instance._screenConfigurations[id] = previousConfiguration.copyWith( + screenName: displayName, + geometry: Rect.fromLTWH(left, top, width, height), + devicePixelRatio: devicePixelRatio, + viewPadding: WindowPadding._( + top: viewPaddingTop, + right: viewPaddingRight, + bottom: viewPaddingBottom, + left: viewPaddingLeft, + ), + viewInsets: WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft, + ), + padding: WindowPadding._( + top: math.max(0.0, viewPaddingTop - viewInsetTop), + right: math.max(0.0, viewPaddingRight - viewInsetRight), + bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), + left: math.max(0.0, viewPaddingLeft - viewInsetLeft), + ), + systemGestureInsets: WindowPadding._( + top: math.max(0.0, systemGestureInsetTop), + right: math.max(0.0, systemGestureInsetRight), + bottom: math.max(0.0, systemGestureInsetBottom), + left: math.max(0.0, systemGestureInsetLeft), + ), + ); + if (!PlatformDispatcher.instance._screens.containsKey(id)) { + PlatformDispatcher.instance._screens[id] = Screen._(screenId: id, platformDispatcher: PlatformDispatcher.instance); + } + _invoke( + PlatformDispatcher.instance.onMetricsChanged, + PlatformDispatcher.instance._onMetricsChangedZone, + ); } typedef _LocaleClosure = String? Function(); String? _localeClosure() { - if (window.locale == null) { + if (PlatformDispatcher.instance.configuration.platformResolvedLocale == null) { return null; } - return window.locale.toString(); + return PlatformDispatcher.instance.configuration.platformResolvedLocale.toString(); } @pragma('vm:entry-point') @@ -73,7 +152,9 @@ _LocaleClosure? _getLocaleClosure() => _localeClosure; void _updateLocales(List locales) { const int stringsPerLocale = 4; final int numLocales = locales.length ~/ stringsPerLocale; + final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; final List newLocales = []; + bool localesDiffer = numLocales != previousConfiguration.locales.length; for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { final String countryCode = locales[localeIndex * stringsPerLocale + 1]; final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; @@ -83,9 +164,24 @@ void _updateLocales(List locales) { countryCode: countryCode.isEmpty ? null : countryCode, scriptCode: scriptCode.isEmpty ? null : scriptCode, )); + if (!localesDiffer && newLocales.last != previousConfiguration.locales[localeIndex]) { + localesDiffer = true; + } + } + if (!localesDiffer) { + return; } - window._locales = newLocales; - _invoke(window.onLocaleChanged, window._onLocaleChangedZone); + PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( + locales: newLocales, + ); + _invoke( + PlatformDispatcher.instance.onPlatformConfigurationChanged, + PlatformDispatcher.instance._onPlatformConfigurationChangedZone, + ); + _invoke( + PlatformDispatcher.instance.onLocaleChanged, + PlatformDispatcher.instance._onLocaleChangedZone, + ); } @pragma('vm:entry-point') @@ -95,50 +191,99 @@ void _updateUserSettingsData(String jsonData) { if (data.isEmpty) { return; } - _updateTextScaleFactor((data['textScaleFactor'] as num).toDouble()); - _updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat'] as bool); - _updatePlatformBrightness(data['platformBrightness'] as String); + + final double textScaleFactor = (data['textScaleFactor'] as num).toDouble(); + final bool alwaysUse24HourFormat = data['alwaysUse24HourFormat'] as bool; + final Brightness platformBrightness = + data['platformBrightness'] as String == 'dark' ? Brightness.dark : Brightness.light; + final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; + final bool platformBrightnessChanged = + previousConfiguration.platformBrightness != platformBrightness; + final bool textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; + final bool alwaysUse24HourFormatChanged = + previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; + if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged) { + return; + } + PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( + textScaleFactor: textScaleFactor, + alwaysUse24HourFormat: alwaysUse24HourFormat, + platformBrightness: platformBrightness, + ); + _invoke( + PlatformDispatcher.instance.onPlatformConfigurationChanged, + PlatformDispatcher.instance._onPlatformConfigurationChangedZone, + ); + if (textScaleFactorChanged) { + _invoke( + PlatformDispatcher.instance.onTextScaleFactorChanged, + PlatformDispatcher.instance._onTextScaleFactorChangedZone, + ); + } + if (platformBrightnessChanged) { + _invoke( + PlatformDispatcher.instance.onPlatformBrightnessChanged, + PlatformDispatcher.instance._onPlatformBrightnessChangedZone, + ); + } } @pragma('vm:entry-point') // ignore: unused_element -void _updateLifecycleState(String state) { - // We do not update the state if the state has already been used to initialize - // the lifecycleState. - if (!window._initialLifecycleStateAccessed) - window._initialLifecycleState = state; +void _resetWindowConfiguration(Object id) { + PlatformDispatcher.instance._viewConfigurations[id] = const ViewConfiguration(); } - -void _updateTextScaleFactor(double textScaleFactor) { - window._textScaleFactor = textScaleFactor; - _invoke(window.onTextScaleFactorChanged, window._onTextScaleFactorChangedZone); -} - -void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) { - window._alwaysUse24HourFormat = alwaysUse24HourFormat; +@pragma('vm:entry-point') +// ignore: unused_element +void _resetScreenConfiguration(Object id) { + PlatformDispatcher.instance._screenConfigurations[id] = const ScreenConfiguration(); } -void _updatePlatformBrightness(String brightnessName) { - window._platformBrightness = brightnessName == 'dark' ? Brightness.dark : Brightness.light; - _invoke(window.onPlatformBrightnessChanged, window._onPlatformBrightnessChangedZone); +@pragma('vm:entry-point') +// ignore: unused_element +void _updateLifecycleState(String state) { + // We do not update the state if the state has already been used to initialize + // the lifecycleState. + if (!PlatformDispatcher.instance._initialLifecycleStateAccessed) + PlatformDispatcher.instance._initialLifecycleState = state; } @pragma('vm:entry-point') // ignore: unused_element void _updateSemanticsEnabled(bool enabled) { - window._semanticsEnabled = enabled; - _invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone); + final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; + if (previousConfiguration.semanticsEnabled == enabled) { + return; + } + PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( + semanticsEnabled: enabled, + ); + _invoke(PlatformDispatcher.instance.onPlatformConfigurationChanged, + PlatformDispatcher.instance._onPlatformConfigurationChangedZone); + _invoke(PlatformDispatcher.instance.onSemanticsEnabledChanged, + PlatformDispatcher.instance._onSemanticsEnabledChangedZone); } @pragma('vm:entry-point') // ignore: unused_element void _updateAccessibilityFeatures(int values) { final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values); - if (newFeatures == window._accessibilityFeatures) + final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; + if (newFeatures == previousConfiguration.accessibilityFeatures) { return; - window._accessibilityFeatures = newFeatures; - _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFeaturesChangedZone); + } + PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( + accessibilityFeatures: newFeatures, + ); + _invoke( + PlatformDispatcher.instance.onPlatformConfigurationChanged, + PlatformDispatcher.instance._onPlatformConfigurationChangedZone, + ); + _invoke( + PlatformDispatcher.instance.onAccessibilityFeaturesChanged, + PlatformDispatcher.instance._onAccessibilityFeaturesChangedZone, + ); } @pragma('vm:entry-point') @@ -150,21 +295,21 @@ void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { } catch (ex) { _printDebug('Message to "$name" caused exception $ex'); } finally { - window._respondToPlatformMessage(responseId, null); + PlatformDispatcher.instance._respondToPlatformMessage(responseId, null); } - } else if (window.onPlatformMessage != null) { + } else if (PlatformDispatcher.instance.onPlatformMessage != null) { _invoke3( - window.onPlatformMessage, - window._onPlatformMessageZone, + PlatformDispatcher.instance.onPlatformMessage, + PlatformDispatcher.instance._onPlatformMessageZone, name, data, (ByteData? responseData) { - window._respondToPlatformMessage(responseId, responseData); + PlatformDispatcher.instance._respondToPlatformMessage(responseId, responseData); }, ); } else { channelBuffers.push(name, data, (ByteData? responseData) { - window._respondToPlatformMessage(responseId, responseData); + PlatformDispatcher.instance._respondToPlatformMessage(responseId, responseData); }); } } @@ -172,16 +317,20 @@ void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { @pragma('vm:entry-point') // ignore: unused_element void _dispatchPointerDataPacket(ByteData packet) { - if (window.onPointerDataPacket != null) - _invoke1(window.onPointerDataPacket, window._onPointerDataPacketZone, _unpackPointerDataPacket(packet)); + if (PlatformDispatcher.instance.onPointerDataPacket != null) + _invoke1( + PlatformDispatcher.instance.onPointerDataPacket, + PlatformDispatcher.instance._onPointerDataPacketZone, + _unpackPointerDataPacket(packet), + ); } @pragma('vm:entry-point') // ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData? args) { _invoke3( - window.onSemanticsAction, - window._onSemanticsActionZone, + PlatformDispatcher.instance.onSemanticsAction, + PlatformDispatcher.instance._onSemanticsActionZone, id, SemanticsAction.values[action]!, args, @@ -191,7 +340,11 @@ void _dispatchSemanticsAction(int id, int action, ByteData? args) { @pragma('vm:entry-point') // ignore: unused_element void _beginFrame(int microseconds) { - _invoke1(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds)); + _invoke1( + PlatformDispatcher.instance.onBeginFrame, + PlatformDispatcher.instance._onBeginFrameZone, + Duration(microseconds: microseconds), + ); } @pragma('vm:entry-point') @@ -202,13 +355,20 @@ void _reportTimings(List timings) { for (int i = 0; i < timings.length; i += FramePhase.values.length) { frameTimings.add(FrameTiming(timings.sublist(i, i + FramePhase.values.length))); } - _invoke1(window.onReportTimings, window._onReportTimingsZone, frameTimings); + _invoke1( + PlatformDispatcher.instance.onReportTimings, + PlatformDispatcher.instance._onReportTimingsZone, + frameTimings, + ); } @pragma('vm:entry-point') // ignore: unused_element void _drawFrame() { - _invoke(window.onDrawFrame, window._onDrawFrameZone); + _invoke( + PlatformDispatcher.instance.onDrawFrame, + PlatformDispatcher.instance._onDrawFrameZone, + ); } // ignore: always_declare_return_types, prefer_generic_function_type_aliases @@ -218,10 +378,12 @@ typedef _BinaryFunction(Null args, Null message); @pragma('vm:entry-point') // ignore: unused_element -void _runMainZoned(Function startMainIsolateFunction, - Function userMainFunction, - List args) { - startMainIsolateFunction((){ +void _runMainZoned( + Function startMainIsolateFunction, + Function userMainFunction, + List args, +) { + startMainIsolateFunction(() { runZonedGuarded(() { if (userMainFunction is _BinaryFunction) { // This seems to be undocumented but supported by the command line VM. @@ -238,12 +400,14 @@ void _runMainZoned(Function startMainIsolateFunction, }, null); } -void _reportUnhandledException(String error, String stackTrace) native 'Window_reportUnhandledException'; +void _reportUnhandledException(String error, String stackTrace) + native 'PlatformConfiguration_reportUnhandledException'; /// Invokes [callback] inside the given [zone]. void _invoke(void callback()?, Zone zone) { - if (callback == null) + if (callback == null) { return; + } assert(zone != null); // ignore: unnecessary_null_comparison @@ -256,8 +420,9 @@ void _invoke(void callback()?, Zone zone) { /// Invokes [callback] inside the given [zone] passing it [arg]. void _invoke1(void callback(A a)?, Zone zone, A arg) { - if (callback == null) + if (callback == null) { return; + } assert(zone != null); // ignore: unnecessary_null_comparison @@ -269,9 +434,11 @@ void _invoke1(void callback(A a)?, Zone zone, A arg) { } /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3(void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg1, A2 arg2, A3 arg3) { - if (callback == null) +void _invoke3( + void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) { return; + } assert(zone != null); // ignore: unnecessary_null_comparison @@ -327,7 +494,7 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) { tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian) + scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), )); assert(offset == (i + 1) * _kPointerDataFieldCount); } diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index ce29fe15cf83e..b20382c7166ed 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -34,7 +34,7 @@ Future _scheduleFrame( Map parameters ) async { // Schedule the frame. - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); // Always succeed. return developer.ServiceExtensionResponse.result(json.encode({ 'type': 'Success', @@ -45,7 +45,7 @@ Future _scheduleFrame( void _setupHooks() { // ignore: unused_element assert(() { // In debug mode, register the schedule frame extension. - developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); + developer.registerExtension('ext.ui.PlatformDispatcher.instance.scheduleFrame', _scheduleFrame); return true; }()); } diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index d3d0845a0e112..72025e05e7ba1 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -11,6 +11,7 @@ #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/matrix.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/window.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -425,8 +426,12 @@ void Canvas::drawShadow(const CanvasPath* path, if (!path) Dart_ThrowException( ToDart("Canvas.drawShader called with non-genuine Path.")); - SkScalar dpr = - UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio; + // Currently only supports a single screen. + SkScalar dpr = UIDartState::Current() + ->platform_configuration() + ->get_screen(0) + ->screen_metrics() + .device_pixel_ratio; external_allocation_size_ += path->path().approximateBytesUsed(); flutter::PhysicalShapeLayer::DrawShadow(canvas_, path->path(), color, elevation, transparentOccluder, dpr); diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart new file mode 100644 index 0000000000000..43c85b43d977c --- /dev/null +++ b/lib/ui/platform_dispatcher.dart @@ -0,0 +1,1023 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of dart.ui; + +// Callback types for events. + +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); + +typedef ViewCreatedCallback = void Function(FlutterView view); +typedef ViewDisposedCallback = void Function(FlutterView view); + +/// Platform event dispatcher singleton. +/// +/// The most basic interface to the host operating system's interface. +/// +/// This is the central entry point for platform messages and configuration +/// events from the platform. +/// +/// It exposes the size of the screen(s), the core scheduler API, the input +/// event callback, the graphics drawing API, and other such core services. +/// +/// It manages the list of the application's [views] and the [screens] attached +/// to the device, as well as the [configuration] of various platform +/// attributes. +/// +/// Please try to avoid statically referencing this singleton though +/// [PlatformDispatcher.instance] and instead use a binding for dependency +/// resolution such as `WidgetsBinding.instance.platformDispatcher`. See +/// [PlatformDispatcher.instance] for more information about why this is +/// preferred. +class PlatformDispatcher { + /// Private constructor, since only dart:ui is supposed to create one of + /// these. + PlatformDispatcher._() { + _setNeedsReportTimings = _nativeSetNeedsReportTimings; + } + + /// The [PlatformDispatcher] singleton. + /// + /// Please try to avoid statically referencing this and instead use a binding + /// for dependency resolution such as + /// `WidgetsBinding.instance.platformDispatcher`. + /// + /// Static access of this object means that Flutter has few, if any options to + /// fake or mock the given object in tests. Even in cases where Dart offers + /// special language constructs to forcefully shadow such properties, those + /// mechanisms would only be reasonable for tests and they would not be + /// reasonable for a future of Flutter where we legitimately want to select an + /// appropriate implementation at runtime. + /// + /// The only place that `WidgetsBinding.instance.platformDispatcher` is + /// inappropriate is if access to these APIs is required before invoking + /// `runApp()`. In that case, it is acceptable (though unfortunate) to use the + /// [PlatformDispatcher.instance] object statically. + static PlatformDispatcher get instance => _instance; + static final PlatformDispatcher _instance = PlatformDispatcher._(); + + /// The current platform configuration. + /// + /// If values in this configuration change, [onMetricsChanged] will be called. + PlatformConfiguration get configuration => _configuration; + PlatformConfiguration _configuration = const PlatformConfiguration(); + + /// Called when the platform configuration changes. + VoidCallback get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + VoidCallback _onPlatformConfigurationChanged; + Zone _onPlatformConfigurationChangedZone; + set onPlatformConfigurationChanged(VoidCallback callback) { + _onPlatformConfigurationChanged = callback; + _onPlatformConfigurationChangedZone = Zone.current; + } + + /// The current list of available screens on the device. + /// + /// If the list of screens or their configuration changes, [onMetricsChanged] + /// will be called. + Iterable get screens => _screens.values; + Map _screens = {}; + + // A map of opaque platform screen identifiers to screen configurations. + Map _screenConfigurations = {}; + + /// The current list of views, including top level platform windows used by + /// the application. + /// + /// If the list of views changes, [onViewCreated] or [onViewDisposed] will be + /// called. If their configurations change, [onMetricsChanged] will be called. + Iterable get views => _views.values; + Map _views = {}; + + // A map of opaque platform view identifiers to view configurations. + Map _viewConfigurations = {}; + + /// Is called after [createView] is called and returns with a new view. + /// + /// Passes the newly created [FlutterView]. + ViewCreatedCallback get onViewCreated => _onViewCreated; + ViewCreatedCallback _onViewCreated; + Zone _onViewCreatedZone; // ignore: unused_field + set onViewCreated(ViewCreatedCallback callback) { + _onViewCreated = callback; + _onViewCreatedZone = Zone.current; + } + + /// The callback called when a view disposal is requested by the platform. + /// + /// If the application wishes to allow this disposal, it should call + /// [FlutterView.dispose] on the given [FlutterView]. + /// + /// If the disposal is to be ignored, just do nothing. + ViewDisposedCallback get onViewDisposed => _onViewDisposed; + ViewDisposedCallback _onViewDisposed; + Zone _onViewDisposedZone; // ignore: unused_field + set onViewDisposed(ViewDisposedCallback callback) { + _onViewDisposed = callback; + _onViewDisposedZone = Zone.current; + } + + /// A callback that is invoked whenever any [ViewConfiguration] field in the + /// [views] or [ScreenConfiguration] field in the [screens] changes, or when a + /// view or screen is added or removed. + /// + /// For example when the device is rotated or when the application is resized + /// (e.g. when showing applications side-by-side on Android). + /// + /// The engine invokes this callback in the same zone in which the callback + /// was set. + /// + /// The framework registers with this callback and updates the layout + /// appropriately. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// register for notifications when this is called. + /// * [MediaQuery.of], a simpler mechanism for the same. + VoidCallback get onMetricsChanged => _onMetricsChanged; + VoidCallback _onMetricsChanged; + Zone _onMetricsChangedZone; // ignore: unused_field + set onMetricsChanged(VoidCallback callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// A callback invoked when any view begins a frame. + /// + /// {@template flutter.foundation.PlatformDispatcher.onBeginFrame} + /// A callback that is invoked to notify the application that it is an + /// appropriate time to provide a scene using the [SceneBuilder] API and the + /// [PlatformDispatcher.render] method. + /// + /// When possible, this is driven by the hardware VSync signal of the attached + /// screen with the highest VSync rate. This is only called if + /// [PlatformDispatcher.scheduleFrame] has been called since the last time + /// this callback was invoked. + /// {@endtemplate} + FrameCallback get onBeginFrame => _onBeginFrame; + FrameCallback _onBeginFrame; + Zone _onBeginFrameZone; + set onBeginFrame(FrameCallback callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// {@template flutter.foundation.PlatformDispatcher.onDrawFrame} + /// A callback that is invoked for each frame after [onBeginFrame] has + /// completed and after the microtask queue has been drained. + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. + /// {@endtemplate} + VoidCallback get onDrawFrame => _onDrawFrame; + VoidCallback _onDrawFrame; + Zone _onDrawFrameZone; + set onDrawFrame(VoidCallback callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; + PointerDataPacketCallback _onPointerDataPacket; + Zone _onPointerDataPacketZone; + set onPointerDataPacket(PointerDataPacketCallback callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// A callback that is invoked to report the [FrameTiming] of recently + /// rasterized frames. + /// + /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use + /// [onReportTimings] directly because [SchedulerBinding.addTimingsCallback] + /// allows multiple callbacks. + /// + /// This can be used to see if the application has missed frames (through + /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high + /// latencies (through [FrameTiming.totalSpan]). + /// + /// Unlike [Timeline], the timing information here is available in the release + /// mode (additional to the profile and the debug mode). Hence this can be + /// used to monitor the application's performance in the wild. + /// + /// {@macro dart.ui.TimingsCallback.list} + /// + /// If this is null, no additional work will be done. If this is not null, + /// Flutter spends less than 0.1ms every 1 second to report the timings + /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for + /// 60fps), or 0.01% CPU usage per second. + TimingsCallback get onReportTimings => _onReportTimings; + TimingsCallback _onReportTimings; + Zone _onReportTimingsZone; + set onReportTimings(TimingsCallback callback) { + if ((callback == null) != (_onReportTimings == null)) { + _setNeedsReportTimings(callback != null); + } + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + _SetNeedsReportTimingsFunc _setNeedsReportTimings; + void _nativeSetNeedsReportTimings(bool value) + native 'PlatformConfiguration_setNeedsReportTimings'; + + /// Creates a new view and returns the view created. + /// + /// The configuration obtained and the one requested may not match, depending + /// on what the platform was able to accommodate. + /// + /// The future returns when the view has been created, and the view has been + /// added to [views]. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + Future createView(ViewConfigurationRequest request) async { + throw UnimplementedError(); + // Awaits the platform view creation response, and calls onViewCreated + // before returning. + } + + /// Reconfigures an existing view. + /// + /// This can be used to resize, show, hide, or change the order of the given + /// view, according to what is in the [ViewConfigurationRequest]. + /// + /// The configuration obtained and the one requested may not match, depending + /// on what the platform was able to accommodate. + /// + /// The Future returns when the view has been reconfigured. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + Future configureView( + FlutterView view, + ViewConfigurationRequest configuration, + ) async { + throw UnimplementedError(); + } + + /// Requests permanently closing a view. + /// + /// The Future completes when the view has been disposed and has been removed + /// from [views]. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + Future disposeView(FlutterView view) async { + throw UnimplementedError(); + } + + /// Sends a message to a platform-specific plugin. + /// + /// The `name` parameter determines which plugin receives the message. The + /// `data` parameter contains the message payload and is typically UTF-8 + /// encoded JSON but can be arbitrary data. If the plugin replies to the + /// message, `callback` will be called with the response. + /// + /// The framework invokes [callback] in the same zone in which this method was + /// called. + void sendPlatformMessage(String/*!*/ name, ByteData/*?*/ data, PlatformMessageResponseCallback/*?*/ callback) { + final String error = + _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); + if (error != null) { + throw Exception(error); + } + } + + String _sendPlatformMessage(String name, PlatformMessageResponseCallback callback, ByteData data) + native 'PlatformConfiguration_sendPlatformMessage'; + + /// Called whenever this platform dispatcher receives a message from a + /// platform-specific plugin. + /// + /// The `name` parameter determines which plugin sent the message. The `data` + /// parameter is the payload and is typically UTF-8 encoded JSON but can be + /// arbitrary data. + /// + /// Message handlers must call the function given in the `callback` parameter. + /// If the handler does not need to respond, the handler should pass null to + /// the callback. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; + PlatformMessageCallback _onPlatformMessage; + Zone _onPlatformMessageZone; + set onPlatformMessage(PlatformMessageCallback callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Called by [_dispatchPlatformMessage]. + void _respondToPlatformMessage(int/*!*/ responseId, ByteData/*?*/ data) + native 'PlatformConfiguration_respondToPlatformMessage'; + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static PlatformMessageResponseCallback/*?*/ _zonedPlatformMessageResponseCallback( + PlatformMessageResponseCallback/*?*/ callback) { + if (callback == null) { + return null; + } + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + /// Set the debug name associated with this platform dispatcher's root + /// isolate. + /// + /// Normally debug names are automatically generated from the Dart port, entry + /// point, and source file. For example: `main.dart$main-1234`. + /// + /// This can be combined with flutter tools `--isolate-filter` flag to debug + /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. + /// Note that this does not rename any child isolates of the root. + void setIsolateDebugName(String/*!*/ name) native 'PlatformConfiguration_setIsolateDebugName'; + + /// The embedder can specify data that the isolate can request synchronously + /// on launch. This accessor fetches that data. + /// + /// This data is persistent for the duration of the Flutter application and is + /// available even after isolate restarts. Because of this lifecycle, the size + /// of this data must be kept to a minimum. + /// + /// For asynchronous communication between the embedder and isolate, a + /// platform channel may be used. + ByteData/*?*/ getPersistentIsolateData() native 'PlatformConfiguration_getPersistentIsolateData'; + + /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and + /// [onDrawFrame] callbacks be invoked. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; + + /// Updates the application's rendering on the GPU with the newly provided + /// [Scene]. This function must be called within the scope of the + /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. + /// + /// If given, draws the scene into the given `view`. If no `view` is given, + /// then the scene is drawn into the default [window]. + /// + /// If this function is called a second time during a single + /// [onBeginFrame]/[onDrawFrame] callback sequence or called outside the scope + /// of those callbacks, the call will be ignored. + /// + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. + /// + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + void render(Scene scene, [FlutterView view]) native 'PlatformConfiguration_render'; + + /// Additional accessibility features that may be enabled by the platform. + AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; + + /// A callback that is invoked when the value of [accessibilityFeatures] + /// changes. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + VoidCallback _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone; + set onAccessibilityFeaturesChanged(VoidCallback callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + /// Change the retained semantics data about this platform dispatcher. + /// + /// If [semanticsEnabled] is true, the user has requested that this function + /// be called whenever the semantic content of this platform dispatcher + /// changes. + /// + /// In either case, this function disposes the given update, which means the + /// semantics update cannot be used further. + void updateSemantics(SemanticsUpdate update) native 'PlatformConfiguration_updateSemantics'; + + /// The system-reported default locale of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// This is the first locale selected by the user and is the user's primary + /// locale (the locale the device UI is displayed in) + /// + /// This is equivalent to `locales.first` and will provide an empty non-null + /// locale if the [locales] list has not been set or is empty. + Locale get locale { + if (configuration?.locales != null && configuration.locales.isNotEmpty) { + return locales.first; + } + return null; + } + + /// The full system-reported supported locales of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// The list is ordered in order of priority, with lower-indexed locales being + /// preferred over higher-indexed ones. The first element is the primary + /// [locale]. + /// + /// The [onLocaleChanged] callback is called whenever this value changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + List get locales => configuration.locales; + + /// The locale that the platform's native locale resolution system resolves + /// to. + /// + /// This value may differ between platforms and is meant to allow Flutter's + /// locale resolution algorithms access to a locale that is consistent with + /// other apps on the device. Using this property is optional. + /// + /// This value may be used in a custom [localeListResolutionCallback] or used + /// directly in order to arrive at the most appropriate locale for the app. + /// + /// See [locales], which is the list of locales the user/device prefers. + Locale get platformResolvedLocale => configuration.platformResolvedLocale; + + /// A callback that is invoked whenever [locale] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback get onLocaleChanged => _onLocaleChanged; + VoidCallback _onLocaleChanged; + Zone _onLocaleChangedZone; // ignore: unused_field + set onLocaleChanged(VoidCallback callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// The lifecycle state immediately after dart isolate initialization. + /// + /// This property will not be updated as the lifecycle changes. + /// + /// It is used to initialize [SchedulerBinding.lifecycleState] at startup with + /// any buffered lifecycle state events. + String get initialLifecycleState { + _initialLifecycleStateAccessed = true; + return _initialLifecycleState; + } + + String _initialLifecycleState; + + /// Tracks if the initial state has been accessed. Once accessed, we will stop + /// updating the [initialLifecycleState], as it is not the preferred way to + /// access the state. + bool _initialLifecycleStateAccessed = false; + + /// The system-reported text scale. + /// + /// This establishes the text scaling factor to use when rendering text, + /// according to the user's platform preferences. + /// + /// The [onTextScaleFactorChanged] callback is called whenever this value + /// changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + double get textScaleFactor => configuration.textScaleFactor; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + /// + /// This option is used by [showTimePicker]. + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + /// A callback that is invoked whenever [textScaleFactor] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; + VoidCallback _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone; + set onTextScaleFactorChanged(VoidCallback callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + Brightness get platformBrightness => configuration.platformBrightness; + + /// A callback that is invoked whenever [platformBrightness] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + VoidCallback _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone; + set onPlatformBrightnessChanged(VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// Whether the user has requested that [updateSemantics] be called when the + /// semantic contents of a view changes. + /// + /// The [onSemanticsEnabledChanged] callback is called whenever this value + /// changes. + bool get semanticsEnabled => configuration.semanticsEnabled; + + /// A callback that is invoked when the value of [semanticsEnabled] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + VoidCallback _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone; + set onSemanticsEnabledChanged(VoidCallback callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// A callback that is invoked whenever the user requests an action to be + /// performed. + /// + /// This callback is used when the user expresses the action they wish to + /// perform based on the semantics supplied by [updateSemantics]. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; + SemanticsActionCallback _onSemanticsAction; + Zone _onSemanticsActionZone; + set onSemanticsAction(SemanticsActionCallback callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// The route or path that the embedder requested when the application was + /// launched. + /// + /// This will be the string "`/`" if no particular route was requested. + /// + /// ## Android + /// + /// On Android, calling + /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `createFlutterView` method in your `FlutterActivity` + /// subclass is a suitable time to set the value. The application's + /// `AndroidManifest.xml` file must also be updated to have a suitable + /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). + /// + /// ## iOS + /// + /// On iOS, calling + /// [`FlutterViewController.setInitialRoute`](/objcdoc/Classes/FlutterViewController.html#/c:objc%28cs%29FlutterViewController%28im%29setInitialRoute:) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `application:didFinishLaunchingWithOptions:` method is a + /// suitable time to set this value. + /// + /// See also: + /// + /// * [Navigator], a widget that handles routing. + /// * [SystemChannels.navigation], which handles subsequent navigation + /// requests from the embedder. + String get initialRouteName => _initialRouteName(); + String _initialRouteName() native 'PlatformConfiguration_initialRouteName'; +} + +/// Configuration of the platform. +/// +/// Immutable class (but can't use @immutable in dart:ui) +class PlatformConfiguration { + /// Const constructor for [PlatformConfiguration]. + const PlatformConfiguration({ + this.accessibilityFeatures = const AccessibilityFeatures._(0), + this.alwaysUse24HourFormat = false, + this.semanticsEnabled = false, + this.platformBrightness = Brightness.light, + this.textScaleFactor = 1.0, + this.locales = const [], + this.platformResolvedLocale, + this.initialRouteName, + }) : assert(alwaysUse24HourFormat != null), + assert(semanticsEnabled != null), + assert(platformBrightness != null), + assert(textScaleFactor != null); + + /// Copy a [PlatformConfiguration] with some fields replaced. + PlatformConfiguration copyWith({ + AccessibilityFeatures accessibilityFeatures, + bool alwaysUse24HourFormat, + bool semanticsEnabled, + Brightness platformBrightness, + double textScaleFactor, + List locales, + Locale platformResolvedLocale, + String initialRouteName, + }) { + return PlatformConfiguration( + accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, + alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, + semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, + platformBrightness: platformBrightness ?? this.platformBrightness, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + locales: locales ?? this.locales, + platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, + initialRouteName: initialRouteName ?? this.initialRouteName, + ); + } + + /// Additional accessibility features that may be enabled by the platform. + final AccessibilityFeatures accessibilityFeatures; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + final bool alwaysUse24HourFormat; + + /// Whether the user has requested that [updateSemantics] be called when the + /// semantic contents of a view changes. + final bool semanticsEnabled; + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + final Brightness platformBrightness; + + /// The system-reported text scale. + final double textScaleFactor; + + /// The full system-reported supported locales of the device. + final List locales; + + /// The system-reported default locale of the device. + final Locale platformResolvedLocale; + + /// The route or path that the embedder requested when the application was + /// launched. + final String initialRouteName; +} + +/// Immutable configuration information for a screen. +class ScreenConfiguration { + /// Const constructor for [ScreenConfiguration] information. + const ScreenConfiguration({ + this.screenName = '', + this.geometry = Rect.zero, + this.devicePixelRatio = 1.0, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + }) : assert(screenName != null), + assert(geometry != null), + assert(devicePixelRatio != null), + assert(viewInsets != null), + assert(systemGestureInsets != null), + assert(padding != null); + + /// Makes a new copy of this [ViewConfigurationRequest] with some attributes + /// replaced. + ScreenConfiguration copyWith({ + String screenName, + Rect geometry, + double devicePixelRatio, + WindowPadding viewInsets, + WindowPadding viewPadding, + WindowPadding systemGestureInsets, + WindowPadding padding, + }) { + return ScreenConfiguration( + screenName: screenName ?? this.screenName, + geometry: geometry ?? this.geometry, + devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + ); + } + + /// Platform-provided name for screen. + final String screenName; + + /// Screen rect in Flutter logical pixels + final Rect geometry; + + /// Device pixel ratio in device pixels to logical pixels. + final double devicePixelRatio; + + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but over which the operating + /// system will likely place system UI, such as the keyboard or system menus, + /// that fully obscures any content. + final WindowPadding viewInsets; + + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding viewPadding; + + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but where the operating system + /// will consume input gestures for the sake of system navigation. + final WindowPadding systemGestureInsets; + + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding padding; + + @override + String toString() { + return '$runtimeType[screenName: $screenName, geometry: $geometry, devicePixelRatio: $devicePixelRatio]'; + } +} + +/// Class that holds the information needed for a view configuration request. +/// +/// Used to request a different configuration of a [FlutterView], so that +/// multiple view parameters can be configured simultaneously. +/// +/// Parameters that shouldn't change with this request may be null. At least one +/// parameter must be set. +class ViewConfigurationRequest { + /// Const constructor for a [ViewConfigurationRequest]. + const ViewConfigurationRequest({ + this.screen, + this.geometry, + this.visible, + this.order, + this.orderView, + }) : assert(orderView != null || (order != ViewOrder.aboveOther && order != ViewOrder.belowOther)), + assert(orderView == null || (order != ViewOrder.top && order != ViewOrder.bottom)), + assert(screen != null || geometry != null || order != null || visible != null, 'At least one parameter must be non-null'); + + /// Makes a new copy of this [ViewConfigurationRequest] with some attributes + /// replaced. + ViewConfigurationRequest copyWith({ + Screen screen, + Rect geometry, + bool visible, + ViewOrder order, + FlutterView orderView, + }) { + return ViewConfigurationRequest( + screen: screen ?? this.screen, + geometry: geometry ?? this.geometry, + visible: visible ?? this.visible, + order: order ?? this.order, + orderView: orderView ?? this.orderView, + ); + } + + /// The screen that this view should appear on. + /// + /// If the platform supports spanning multiple screens, this is the screen + /// that the upper left corner of the view appears on. + final Screen screen; + + /// The geometry requested for the view on the [screen], in logical pixels. + /// + /// This uses the device pixel ratio of the screen with the upper left corner + /// of this view on it. + final Rect geometry; + + /// Whether or not the view should be visible. + /// + /// If this request is given to [PlatformDispatcher.createView], then setting + /// this to true means that the view will be made visible as soon as it is + /// created. + final bool visible; + + /// The depth ordering of this view relative to other views. + final ViewOrder order; + + /// The opaque ID of the view to place this view on a layer relative to, + /// according to [order]. + /// + /// Only used (and required) if [order] is [ViewOrder.aboveOther] or + /// [ViewOrder.belowOther]. + /// + /// This ID corresponds to the view that this one should be above or below. + final FlutterView orderView; + + @override + String toString() { + return '$runtimeType[screen: $screen, geometry: $geometry, order: $order]'; + } +} + +/// An enum describing how to layer this view in a [ViewConfigurationRequest]. +enum ViewOrder { + /// Place this view immediately above the + /// [ViewConfigurationRequest.orderView]. + aboveOther, + + /// Place this view immediately above the + /// [ViewConfigurationRequest.orderView]. + belowOther, + + /// Place this view on top of all other views. + top, + + /// Place this view below all other views. + bottom, +} + +/// An immutable view configuration. +/// +/// See also: +/// * [ViewConfigurationRequest], a class used to request a change to the +/// view configuration using [PlatformDispatcher.configureView]. +class ViewConfiguration { + /// A const constructor for an immutable [ViewConfiguration]. + const ViewConfiguration({ + this.screen, + this.window, + this.geometry = Rect.zero, + this.depth = double.maxFinite, + this.visible = false, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + }) : assert(geometry != null), + assert(depth != null), + assert(visible != null), + assert(viewInsets != null), + assert(viewPadding != null), + assert(systemGestureInsets != null), + assert(padding != null); + + /// Copy this configuration with some fields replaced. + ViewConfiguration copyWith({ + Screen screen, + FlutterWindow window, + Rect geometry, + double depth, + bool visible, + WindowPadding viewInsets, + WindowPadding viewPadding, + WindowPadding systemGestureInsets, + WindowPadding padding, + }) { + return ViewConfiguration( + screen: screen ?? this.screen, + window: window ?? this.window, + geometry: geometry ?? this.geometry, + depth: depth ?? this.depth, + visible: visible ?? this.visible, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + ); + } + + /// The screen that this view should appear on. + /// + /// This is the screen that the upper left corner of the view appears on. + final Screen screen; + + /// The top level view into which the view is placed and its geometry is + /// relative to. + /// + /// If null, then this configuration represents a top level view itself. + final FlutterWindow window; + + /// The geometry requested for the view on the [screen] or within its parent + /// window, in logical pixels. + /// + /// This uses the device pixel ratio of the [screen]. + final Rect geometry; + + /// The depth that is the maximum elevation that the view allows. + /// + /// Physical layers drawn at or above this elevation will have their elevation + /// clamped to this value. This can happen if the physical layer itself has an + /// elevation larger than available depth, or if some ancestor of the layer + /// causes it to have a cumulative elevation that is larger than the available + /// depth. + /// + /// The default value is [double.maxFinite], which is used for platforms that + /// do not specify a maximum elevation. This property is currently only + /// expected to be set to a non-default value on Fuchsia. + final double depth; + + /// Whether or not the view is currently visible on the screen. + final bool visible; + + /// The view insets, as it intersects with [Screen.viewInsets] for the screen + /// it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.viewInsets] area, [viewInsets] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this view rectangle into + /// which the application can draw, but over which the operating system will + /// likely place system UI, such as the keyboard or system menus, that fully + /// obscures any content. + final WindowPadding viewInsets; + + /// The view insets, as it intersects with [ScreenConfiguration.viewPadding] + /// for the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.viewPadding] area, [viewPadding] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding viewPadding; + + /// The view insets, as it intersects with + /// [ScreenConfiguration.systemGestureInsets] for the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.systemGestureInsets] area, [systemGestureInsets] will + /// be [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but where the operating system + /// will consume input gestures for the sake of system navigation. + final WindowPadding systemGestureInsets; + + /// The view insets, as it intersects with [ScreenConfiguration.padding] for + /// the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.padding] area, [padding] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding padding; + + @override + String toString() { + return '$runtimeType[screen: $screen, window: $window, geometry: $geometry, depth: $depth]'; + } +} diff --git a/lib/ui/screen.dart b/lib/ui/screen.dart new file mode 100644 index 0000000000000..7074d673cb2a9 --- /dev/null +++ b/lib/ui/screen.dart @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of dart.ui; + +/// A class representing the screen that application windows are displayed on. +class Screen { + Screen._({Object screenId, PlatformDispatcher platformDispatcher}) + : _screenId = screenId, + _platformDispatcher = platformDispatcher; + + /// The opaque ID for this screen. + final Object _screenId; + + /// The platform dispatcher that this screen is registered with. + final PlatformDispatcher _platformDispatcher; + + /// The configuration of this screen. + ScreenConfiguration get configuration { + assert(_platformDispatcher._screens.containsKey(_screenId)); + return _platformDispatcher._screenConfigurations[_screenId]; + } +} \ No newline at end of file diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index e73f8335251f7..0f8eca5d50b00 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -609,7 +609,7 @@ class SemanticsFlag { /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [Window.updateSemantics] to update the semantics conveyed to the user. +/// [FlutterWindow.updateSemantics] to update the semantics conveyed to the user. @pragma('vm:entry-point') class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates an empty [SemanticsUpdateBuilder] object. @@ -637,9 +637,9 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [Window.onSemanticsAction] will be called with `id` and one of + /// node, the [FlutterWindow.onSemanticsAction] will be called with `id` and one of /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [Window.onSemanticsAction] callback might be called + /// asynchronously, the [FlutterWindow.onSemanticsAction] callback might be called /// with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property @@ -816,7 +816,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [Window.updateSemantics] to actually + /// The returned object can be passed to [FlutterWindow.updateSemantics] to actually /// update the semantics retained by the system. SemanticsUpdate build() { final SemanticsUpdate semanticsUpdate = SemanticsUpdate._(); @@ -831,7 +831,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree -/// using the [Window.updateSemantics] method. +/// using the [FlutterWindow.updateSemantics] method. @pragma('vm:entry-point') class SemanticsUpdate extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 00691fb1e4c2d..a576dd56dd1c4 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -2272,7 +2272,7 @@ final ByteData _fontChangeMessage = utf8.encoder.convert( ).buffer.asByteData(); FutureOr _sendFontChangeMessage() async { - window.onPlatformMessage?.call( + PlatformDispatcher.instance.onPlatformMessage?.call( 'flutter/system', _fontChangeMessage, (_) {}, diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index c59dac720875d..a133fa8d72286 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -8,7 +8,7 @@ #include "flutter/lib/ui/text/asset_manager_font_provider.h" #include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/runtime/test_font_data.h" #include "rapidjson/document.h" #include "rapidjson/rapidjson.h" @@ -30,8 +30,10 @@ namespace { void LoadFontFromList(tonic::Uint8List& font_data, Dart_Handle callback, std::string family_name) { - FontCollection& font_collection = - UIDartState::Current()->window()->client()->GetFontCollection(); + FontCollection& font_collection = UIDartState::Current() + ->platform_configuration() + ->client() + ->GetFontCollection(); font_collection.LoadFontFromList(font_data.data(), font_data.num_elements(), family_name); font_data.Release(); diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index aeeb8a0b4ad8e..69c2f663af492 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -10,7 +10,7 @@ #include "flutter/fml/task_runner.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/third_party/txt/src/txt/font_style.h" #include "flutter/third_party/txt/src/txt/font_weight.h" #include "flutter/third_party/txt/src/txt/paragraph_style.h" @@ -288,8 +288,10 @@ ParagraphBuilder::ParagraphBuilder( style.locale = locale; } - FontCollection& font_collection = - UIDartState::Current()->window()->client()->GetFontCollection(); + FontCollection& font_collection = UIDartState::Current() + ->platform_configuration() + ->client() + ->GetFontCollection(); #if FLUTTER_ENABLE_SKSHAPER #define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index 7612cf50976bb..718855ef164c3 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -33,8 +33,10 @@ part 'isolate_name_server.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; +part 'platform_dispatcher.dart'; part 'plugins.dart'; part 'pointer.dart'; +part 'screen.dart'; part 'semantics.dart'; part 'text.dart'; part 'window.dart'; diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index 4d04a30d404c2..ca215745bf831 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -5,6 +5,7 @@ #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/fml/message_loop.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_message_handler.h" @@ -73,18 +74,23 @@ void UIDartState::ThrowIfUIOperationsProhibited() { void UIDartState::SetDebugName(const std::string debug_name) { debug_name_ = debug_name; - if (window_) - window_->client()->UpdateIsolateDescription(debug_name_, main_port_); + if (platform_configuration_) { + platform_configuration_->client()->UpdateIsolateDescription(debug_name_, + main_port_); + } } UIDartState* UIDartState::Current() { return static_cast(DartState::Current()); } -void UIDartState::SetWindow(std::unique_ptr window) { - window_ = std::move(window); - if (window_) - window_->client()->UpdateIsolateDescription(debug_name_, main_port_); +void UIDartState::SetPlatformConfiguration( + std::unique_ptr platform_configuration) { + platform_configuration_ = std::move(platform_configuration); + if (platform_configuration_) { + platform_configuration_->client()->UpdateIsolateDescription(debug_name_, + main_port_); + } } const TaskRunners& UIDartState::GetTaskRunners() const { diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index 2fdedc8094963..816fbb9840d48 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -27,7 +27,7 @@ namespace flutter { class FontSelector; -class Window; +class PlatformConfiguration; class UIDartState : public tonic::DartState { public: @@ -44,7 +44,9 @@ class UIDartState : public tonic::DartState { const std::string& logger_prefix() const { return logger_prefix_; } - Window* window() const { return window_.get(); } + PlatformConfiguration* platform_configuration() const { + return platform_configuration_.get(); + } const TaskRunners& GetTaskRunners() const; @@ -97,7 +99,8 @@ class UIDartState : public tonic::DartState { ~UIDartState() override; - void SetWindow(std::unique_ptr window); + void SetPlatformConfiguration( + std::unique_ptr platform_configuration); const std::string& GetAdvisoryScriptURI() const; @@ -119,7 +122,7 @@ class UIDartState : public tonic::DartState { Dart_Port main_port_ = ILLEGAL_PORT; const bool is_root_isolate_; std::string debug_name_; - std::unique_ptr window_; + std::unique_ptr platform_configuration_; tonic::DartMicrotaskQueue microtask_queue_; UnhandledExceptionCallback unhandled_exception_callback_; const std::shared_ptr isolate_name_server_; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index d9edf7c793d38..6fda0a564fcf6 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -8,10 +8,10 @@ part of dart.ui; /// Signature of callbacks that have no arguments and return no data. typedef VoidCallback = void Function(); -/// Signature for [Window.onBeginFrame]. +/// Signature for [FlutterWindow.onBeginFrame]. typedef FrameCallback = void Function(Duration duration); -/// Signature for [Window.onReportTimings]. +/// Signature for [FlutterWindow.onReportTimings]. /// /// {@template dart.ui.TimingsCallback.list} /// The callback takes a list of [FrameTiming] because it may not be @@ -25,19 +25,19 @@ typedef FrameCallback = void Function(Duration duration); /// {@endtemplate} typedef TimingsCallback = void Function(List timings); -/// Signature for [Window.onPointerDataPacket]. +/// Signature for [FlutterWindow.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); -/// Signature for [Window.onSemanticsAction]. +/// Signature for [FlutterWindow.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); /// Signature for responses to platform messages. /// -/// Used as a parameter to [Window.sendPlatformMessage] and -/// [Window.onPlatformMessage]. +/// Used as a parameter to [FlutterWindow.sendPlatformMessage] and +/// [FlutterWindow.onPlatformMessage]. typedef PlatformMessageResponseCallback = void Function(ByteData? data); -/// Signature for [Window.onPlatformMessage]. +/// Signature for [FlutterWindow.onPlatformMessage]. typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); // Signature for _setNeedsReportTimings. @@ -72,10 +72,10 @@ enum FramePhase { /// /// If you're using the whole Flutter framework, please use /// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using -/// [Window.onReportTimings] directly because +/// [FlutterWindow.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If -/// [SchedulerBinding] is unavailable, then see [Window.onReportTimings] for how -/// to get this. +/// [SchedulerBinding] is unavailable, then see [FlutterWindow.onReportTimings] +/// for how to get this. /// /// The metrics in debug mode (`flutter run` without any flags) may be very /// different from those in profile and release modes due to the debug overhead. @@ -88,9 +88,10 @@ class FrameTiming { /// [FramePhase.values]. /// /// This constructor is usually only called by the Flutter engine, or a test. - /// To get the [FrameTiming] of your app, see [Window.onReportTimings]. + /// To get the [FrameTiming] of your app, see [FlutterWindow.onReportTimings]. FrameTiming(List timestamps) - : assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps; + : assert(timestamps.length == FramePhase.values.length), + _timestamps = timestamps; /// This is a raw timestamp in microseconds from some epoch. The epoch in all /// [FrameTiming] is the same, but it may not match [DateTime]'s epoch. @@ -100,11 +101,11 @@ class FrameTiming { /// The duration to build the frame on the UI thread. /// - /// The build starts approximately when [Window.onBeginFrame] is called. The - /// [Duration] in the [Window.onBeginFrame] callback is exactly the + /// The build starts approximately when [FlutterWindow.onBeginFrame] is called. The + /// [Duration] in the [FlutterWindow.onBeginFrame] callback is exactly the /// `Duration(microseconds: timestampInMicroseconds(FramePhase.buildStart))`. /// - /// The build finishes when [Window.render] is called. + /// The build finishes when [FlutterWindow.render] is called. /// /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} /// To ensure smooth animations of X fps, this should not exceed 1000/X @@ -176,7 +177,7 @@ enum AppLifecycleState { /// user input, and running in the background. /// /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// [FlutterWindow.onBeginFrame] and [FlutterWindow.onDrawFrame] callbacks. paused, /// The application is still hosted on a flutter engine but is detached from @@ -191,8 +192,8 @@ enum AppLifecycleState { /// A representation of distances for each of the four edges of a rectangle, /// used to encode the view insets and padding that applications should place -/// around their user interface, as exposed by [Window.viewInsets] and -/// [Window.padding]. View insets and padding are preferably read via +/// around their user interface, as exposed by [FlutterWindow.viewInsets] and +/// [FlutterWindow.padding]. View insets and padding are preferably read via /// [MediaQuery.of]. /// /// For a generic class that represents distances around a rectangle, see the @@ -244,7 +245,7 @@ class WindowPadding { /// /// See also: /// -/// * [Window.locale], which specifies the system's currently selected +/// * [FlutterWindow.locale], which specifies the system's currently selected /// [Locale]. class Locale { /// Creates a new Locale object. The first argument is the @@ -495,7 +496,7 @@ class Locale { /// /// This identifier happens to be a valid Unicode Locale Identifier using /// underscores as separator, however it is intended to be used for debugging - /// purposes only. For parseable results, use [toLanguageTag] instead. + /// purposes only. For parsable results, use [toLanguageTag] instead. @keepToString @override String toString() { @@ -523,67 +524,75 @@ class Locale { } } -/// The most basic interface to the host operating system's user interface. +/// A view into which a Flutter [Scene] is drawn. /// -/// It exposes the size of the display, the core scheduler API, the input event -/// callback, the graphics drawing API, and other such core services. +/// Each [FlutterView] has its own layer tree that is rendered into an area +/// inside of the [FlutterWindow] whenever [render] is called with a [Scene]. /// -/// There is a single Window instance in the system, which you can -/// obtain from `WidgetsBinding.instance.window`. -/// -/// There is also a [window] singleton object in `dart:ui` if `WidgetsBinding` -/// is unavailable. But we strongly advise to avoid statically referencing it. -/// See the document of [window] for more details of why it should be avoided. +/// New views can be created by the [PlatformDispatcher], using +/// [PlatformDispatcher.createView]. /// /// ## Insets and Padding /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/window_padding.mp4} /// -/// In this diagram, the black areas represent system UI that the app cannot -/// draw over. The red area represents view padding that the application may not +/// In this illustration, the black areas represent system UI that the app +/// cannot draw over. The red area represents view padding that the view may not /// be able to detect gestures in and may not want to draw in. The grey area -/// represents the system keyboard, which can cover over the bottom view -/// padding when visible. +/// represents the system keyboard, which can cover over the bottom view padding +/// when visible. /// -/// The [Window.viewInsets] are the physical pixels which the operating +/// The [FlutterWindow.viewInsets] are the physical pixels which the operating /// system reserves for system UI, such as the keyboard, which would fully /// obscure any content drawn in that area. /// -/// The [Window.viewPadding] are the physical pixels on each side of the display -/// that may be partially obscured by system UI or by physical intrusions into -/// the display, such as an overscan region on a television or a "notch" on a -/// phone. Unlike the insets, these areas may have portions that show the user -/// application painted pixels without being obscured, such as a notch at the -/// top of a phone that covers only a subset of the area. Insets, on the other -/// hand, either partially or fully obscure the window, such as an opaque -/// keyboard or a partially transluscent statusbar, which cover an area without -/// gaps. +/// The [FlutterWindow.viewPadding] are the physical pixels on each side of the +/// display that may be partially obscured by system UI or by physical +/// intrusions into the display, such as an overscan region on a television or a +/// "notch" on a phone. Unlike the insets, these areas may have portions that +/// show the user view-painted pixels without being obscured, such as a +/// notch at the top of a phone that covers only a subset of the area. Insets, +/// on the other hand, either partially or fully obscure the window, such as an +/// opaque keyboard or a partially translucent status bar, which cover an area +/// without gaps. /// -/// The [Window.padding] property is computed from both [Window.viewInsets] and -/// [Window.viewPadding]. It will allow a view inset to consume view padding -/// where appropriate, such as when a phone's keyboard is covering the bottom -/// view padding and so "absorbs" it. +/// The [FlutterWindow.padding] property is computed from both +/// [FlutterWindow.viewInsets] and [FlutterWindow.viewPadding]. It will allow a +/// view inset to consume view padding where appropriate, such as when a phone's +/// keyboard is covering the bottom view padding and so "absorbs" it. /// /// Clients that want to position elements relative to the view padding -/// regardless of the view insets should use the [Window.viewPadding] property, -/// e.g. if you wish to draw a widget at the center of the screen with respect -/// to the iPhone "safe area" regardless of whether the keyboard is showing. +/// regardless of the view insets should use the [FlutterWindow.viewPadding] +/// property, e.g. if you wish to draw a widget at the center of the screen with +/// respect to the iPhone "safe area" regardless of whether the keyboard is +/// showing. /// -/// [Window.padding] is useful for clients that want to know how much padding -/// should be accounted for without concern for the current inset(s) state, e.g. -/// determining whether a gesture should be considered for scrolling purposes. -/// This value varies based on the current state of the insets. For example, a -/// visible keyboard will consume all gestures in the bottom part of the -/// [Window.viewPadding] anyway, so there is no need to account for that in the -/// [Window.padding], which is always safe to use for such calculations. -class Window { - Window._() { - _setNeedsReportTimings = _nativeSetNeedsReportTimings; - } +/// [FlutterWindow.padding] is useful for clients that want to know how much +/// padding should be accounted for without concern for the current inset(s) +/// state, e.g. determining whether a gesture should be considered for scrolling +/// purposes. This value varies based on the current state of the insets. For +/// example, a visible keyboard will consume all gestures in the bottom part of +/// the [FlutterWindow.viewPadding] anyway, so there is no need to account for +/// that in the [FlutterWindow.padding], which is always safe to use for such +/// calculations. +/// +/// See also: +/// +/// * [FlutterWindow], a special case of a [FlutterView] that is represented on +/// the platform as a separate window which can host other [FlutterView]s. +abstract class FlutterView { + /// The platform dispatcher that this view is registered with, and gets its + /// information from. + PlatformDispatcher get platformDispatcher; - /// The number of device pixels for each logical pixel. This number might not - /// be a power of two. Indeed, it might not even be an integer. For example, - /// the Nexus 6 has a device pixel ratio of 3.5. + /// The configuration of this view. + ViewConfiguration get viewConfiguration; + + /// The number of device pixels for each logical pixel for the screen this + /// view is displayed on. + /// + /// This number might not be a power of two. Indeed, it might not even be an + /// integer. For example, the Nexus 6 has a device pixel ratio of 3.5. /// /// Device pixels are also referred to as physical pixels. Logical pixels are /// also referred to as device-independent or resolution-independent pixels. @@ -604,30 +613,51 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get devicePixelRatio => _devicePixelRatio; - double _devicePixelRatio = 1.0; + double get devicePixelRatio => viewConfiguration.screen.configuration.devicePixelRatio; - /// The dimensions of the rectangle into which the application will be drawn, - /// in physical pixels. + /// The dimensions and location of the rectangle into which the scene rendered + /// in this view will be drawn on the screen, in physical pixels. /// /// When this changes, [onMetricsChanged] is called. /// - /// At startup, the size of the application window may not be known before Dart + /// At startup, the size and location of the view may not be known before Dart /// code runs. If this value is observed early in the application lifecycle, - /// it may report [Size.zero]. + /// it may report [Rect.zero]. /// /// This value does not take into account any on-screen keyboards or other /// system UI. The [padding] and [viewInsets] properties provide a view into - /// how much of each side of the application may be obscured by system UI. + /// how much of each side of the view may be obscured by system UI. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + Rect get physicalGeometry => viewConfiguration.geometry; + + /// The dimensions of the rectangle into which the scene rendered in this view + /// will be drawn on the screen, in physical pixels. + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// At startup, the size of the view may not be known before Dart code runs. + /// If this value is observed early in the application lifecycle, it may + /// report [Size.zero]. + /// + /// This value does not take into account any on-screen keyboards or other + /// system UI. The [padding] and [viewInsets] properties provide information + /// about how much of each side of the view may be obscured by system UI. + /// + /// This value is the same as [physicalGeometry.size]. /// /// See also: /// + /// * [physicalGeometry], which reports the location of the view as well as + /// its size. /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - Size get physicalSize => _physicalSize; - Size _physicalSize = Size.zero; + Size get physicalSize => viewConfiguration.geometry.size; - /// The physical depth is the maximum elevation that the Window allows. + /// The physical depth is the maximum elevation that the `FlutterView` allows. /// /// Physical layers drawn at or above this elevation will have their elevation /// clamped to this value. This can happen if the physical layer itself has @@ -636,21 +666,19 @@ class Window { /// depth. /// /// The default value is [double.maxFinite], which is used for platforms that - /// do not specify a maximum elevation. This property is currently on expected - /// to be set to a non-default value on Fuchsia. - double get physicalDepth => _physicalDepth; - double _physicalDepth = double.maxFinite; + /// do not specify a maximum elevation. This property is currently only + /// expected to be set to a non-default value on the Fuchsia platform. + double get physicalDepth => viewConfiguration.depth; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but over which the operating system - /// will likely place system UI, such as the keyboard, that fully obscures - /// any content. + /// which the view can render, but over which the operating system will likely + /// place system UI, such as the keyboard, that fully obscures any content. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [FlutterWindow.viewInsets], + /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in + /// more detail in the documentation for [FlutterWindow]. /// /// See also: /// @@ -659,25 +687,24 @@ class Window { /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the view insets in material /// design applications. - WindowPadding get viewInsets => _viewInsets; - WindowPadding _viewInsets = WindowPadding.zero; + WindowPadding get viewInsets => viewConfiguration.viewInsets; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but which may be partially obscured by - /// system UI (such as the system notification area), or or physical - /// intrusions in the display (e.g. overscan regions on television screens or - /// phone sensor housings). + /// which the view can render, but which may be partially obscured by system + /// UI (such as the system notification area), or or physical intrusions in + /// the display (e.g. overscan regions on television screens or phone sensor + /// housings). /// - /// Unlike [Window.padding], this value does not change relative to - /// [Window.viewInsets]. For example, on an iPhone X, it will not change in - /// response to the soft keyboard being visible or hidden, whereas - /// [Window.padding] will. + /// Unlike [FlutterWindow.padding], this value does not change relative to + /// [FlutterWindow.viewInsets]. For example, on an iPhone X, it will not + /// change in response to the soft keyboard being visible or hidden, whereas + /// [FlutterWindow.padding] will. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [FlutterWindow.viewInsets], + /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in + /// more detail in the documentation for [FlutterWindow]. /// /// See also: /// @@ -686,12 +713,11 @@ class Window { /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the padding in material design /// applications. - WindowPadding get viewPadding => _viewPadding; - WindowPadding _viewPadding = WindowPadding.zero; + WindowPadding get viewPadding => viewConfiguration.viewPadding; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but where the operating system will - /// consume input gestures for the sake of system navigation. + /// which the view can render, but where the operating system will consume + /// input gestures for the sake of system navigation. /// /// For example, an operating system might use the vertical edges of the /// screen, where swiping inwards from the edges takes users backward @@ -704,28 +730,27 @@ class Window { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. - WindowPadding get systemGestureInsets => _systemGestureInsets; - WindowPadding _systemGestureInsets = WindowPadding.zero; + WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but which may be partially obscured by - /// system UI (such as the system notification area), or or physical - /// intrusions in the display (e.g. overscan regions on television screens or - /// phone sensor housings). - /// - /// This value is calculated by taking - /// `max(0.0, Window.viewPadding - Window.viewInsets)`. This will treat a - /// system IME that increases the bottom inset as consuming that much of the - /// bottom padding. For example, on an iPhone X, [Window.padding.bottom] is - /// the same as [Window.viewPadding.bottom] when the soft keyboard is not - /// drawn (to account for the bottom soft button area), but will be `0.0` when - /// the soft keyboard is visible. + /// which the view can render, but which may be partially obscured by system + /// UI (such as the system notification area), or or physical intrusions in + /// the display (e.g. overscan regions on television screens or phone sensor + /// housings). + /// + /// This value is calculated by taking `max(0.0, FlutterView.viewPadding - + /// FlutterView.viewInsets)`. This will treat a system IME that increases the + /// bottom inset as consuming that much of the bottom padding. For example, on + /// an iPhone X, [FlutterView.padding.bottom] is the same as + /// [FlutterView.viewPadding.bottom] when the soft keyboard is not drawn (to + /// account for the bottom soft button area), but will be `0.0` when the soft + /// keyboard is visible. /// /// When this changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [FlutterWindow.viewInsets], + /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in + /// more detail in the documentation for [FlutterWindow]. /// /// See also: /// @@ -734,54 +759,167 @@ class Window { /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the padding in material design /// applications. - WindowPadding get padding => _padding; - WindowPadding _padding = WindowPadding.zero; + WindowPadding get padding => viewConfiguration.padding; - /// A callback that is invoked whenever the [devicePixelRatio], - /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] - /// values change, for example when the device is rotated or when the - /// application is resized (e.g. when showing applications side-by-side - /// on Android). + /// Updates the view's rendering on the GPU with the newly provided + /// [Scene]. /// - /// The engine invokes this callback in the same zone in which the callback - /// was set. + /// This function must be called within the scope of the + /// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame] + /// callbacks being invoked. If this function is called a second time during a + /// single [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame] + /// callback sequence or called outside the scope of those callbacks, the call + /// will be ignored. + /// + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. /// - /// The framework registers with this callback and updates the layout - /// appropriately. + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. /// /// See also: /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// register for notifications when this is called. - /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback? get onMetricsChanged => _onMetricsChanged; - VoidCallback? _onMetricsChanged; - Zone _onMetricsChangedZone = Zone.root; + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + void render(Scene scene) => platformDispatcher.render(scene, this); + + /// Dispose of this view, closing it permanently. + /// + /// This function should be called in response to a call to + /// [PlatformDispatcher.onViewDisposed], or to permanently dispose of a view + /// that the application would like to close. + void dispose() => platformDispatcher.disposeView(this); +} + +/// A type of [FlutterView] that can be hosted inside of a [FlutterWindow], into +/// which a Flutter [Scene] is drawn. +/// +/// A [FlutterWindowView] has its own layer tree that is rendered into an area +/// inside of a [FlutterWindow] when [render] is called with a [Scene]. +/// +/// A [FlutterWindow] is a subclass of a [FlutterView] that is a separate window +/// which can host other [FlutterView]s. +/// +/// A `FlutterWindowView` can only be created by [PlatformDispatcher.createView] +/// where the requested configuration includes a parent [FlutterWindow] set as +/// the [ViewConfiguration.window]. +class FlutterWindowView extends FlutterView { + FlutterWindowView._({Object viewId, this.platformDispatcher}) + : _viewId = viewId; + + /// The opaque ID for this view. + final Object _viewId; + + @override + final PlatformDispatcher platformDispatcher; + + @override + ViewConfiguration get viewConfiguration { + assert(platformDispatcher._viewConfigurations.containsKey(_viewId)); + return platformDispatcher._viewConfigurations[_viewId]; + } +} + +/// A top-level platform window displaying a Flutter layer tree drawn from a +/// [Scene]. +/// +/// The current list of all Flutter views for the application is available from +/// `WidgetsBinding.instance.platformDispatcher.views`. Only views that are of +/// type [FlutterWindow] are top level platform windows. +/// +/// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` +/// if `WidgetsBinding` is unavailable, but we strongly advise avoiding a static +/// reference to it. See the documentation for [PlatformDispatcher.instance] for +/// more details about why it should be avoided. +/// +/// See also: +/// +/// * [PlatformDispatcher], which manages the current list of [FlutterView] +/// (and thus [FlutterWindow]) instances. +class FlutterWindow extends FlutterView { + FlutterWindow._({Object windowId, this.platformDispatcher}) + : _windowId = windowId; + + /// The opaque ID for this view. + final Object _windowId; + + @override + final PlatformDispatcher platformDispatcher; + + /// The configuration of this view. + @override + ViewConfiguration get viewConfiguration { + assert(platformDispatcher._viewConfigurations.containsKey(_windowId)); + return platformDispatcher._viewConfigurations[_windowId]; + } +} + +/// A [FlutterWindow] that includes access to setting callbacks and retrieving +/// properties that reside on the [PlatformDispatcher]. +/// +/// It is the type of the legacy global [window] singleton, and the +/// `WidgetsBinding.instance.window` singleton. +/// +/// This class provides backward compatibility with code that was written before +/// Flutter supported multiple top level windows. New code should refer to the +/// [WidgetsBinding.instance.platformDispatcher] or [FlutterWindow] class +/// directly. +/// +/// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` +/// if `WidgetsBinding` is unavailable. But we strongly advise avoiding a static +/// reference to it. See the documentation for [PlatformDispatcher.instance] for +/// more details about why it should be avoided. +class SingletonFlutterWindow extends FlutterWindow { + SingletonFlutterWindow._({Object windowId, PlatformDispatcher platformDispatcher}) + : super._(windowId: windowId, platformDispatcher: platformDispatcher); + + /// A callback that is invoked whenever the [devicePixelRatio], + /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] + /// values change. + /// + /// {@template flutter.lib.ui.window.forwardWarning} + /// + /// See [PlatformDispatcher.onMetricsChanged] for more information. + VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; set onMetricsChanged(VoidCallback? callback) { - _onMetricsChanged = callback; - _onMetricsChangedZone = Zone.current; + platformDispatcher.onMetricsChanged = callback; } /// The system-reported default locale of the device. /// - /// This establishes the language and formatting conventions that application + /// {@template flutter.lib.ui.window.accessorForwardWarning} + /// Accessing this value returns the value contained in the + /// [PlatformDispatcher] singleton, so instead of getting it from here, you + /// should consider getting it from + /// `WidgetsBinding.instance.platformDispatcher` instead (or, as a last resort + /// when `WidgetsBinding` isn't available, from + /// [PlatformDispatcher.instance]). The reason this value forwards to the + /// [PlatformDispatcher] is to avoid breaking code that was written before + /// Flutter supported multiple windows. + /// {@endtemplate} + /// + /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// /// This is the first locale selected by the user and is the user's /// primary locale (the locale the device UI is displayed in) /// - /// This is equivalent to `locales.first` and will provide an empty non-null locale - /// if the [locales] list has not been set or is empty. - Locale? get locale { - if (_locales != null && _locales!.isNotEmpty) { - return _locales!.first; - } - return null; - } + /// This is equivalent to `locales.first` and will provide an empty non-null + /// locale if the [locales] list has not been set or is empty. + Locale? get locale => platformDispatcher.locale; /// The full system-reported supported locales of the device. /// - /// This establishes the language and formatting conventions that application + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// + /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// /// The list is ordered in order of priority, with lower-indexed locales being @@ -793,8 +931,7 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - List? get locales => _locales; - List? _locales; + List? get locales => platformDispatcher.locales; /// Performs the platform-native locale resolution. /// @@ -816,9 +953,9 @@ class Window { if (result.isNotEmpty) { return Locale.fromSubtags( - languageCode: result[0], - countryCode: result[1] == '' ? null : result[1], - scriptCode: result[2] == '' ? null : result[2]); + languageCode: result[0], + countryCode: result[1] == '' ? null : result[1], + scriptCode: result[2] == '' ? null : result[2]); } return null; } @@ -826,6 +963,8 @@ class Window { /// A callback that is invoked whenever [locale] changes value. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -833,32 +972,25 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onLocaleChanged => _onLocaleChanged; - VoidCallback? _onLocaleChanged; - Zone _onLocaleChangedZone = Zone.root; + VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; set onLocaleChanged(VoidCallback? callback) { - _onLocaleChanged = callback; - _onLocaleChangedZone = Zone.current; + platformDispatcher.onLocaleChanged = callback; } /// The lifecycle state immediately after dart isolate initialization. /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// /// This property will not be updated as the lifecycle changes. /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. - String get initialLifecycleState { - _initialLifecycleStateAccessed = true; - return _initialLifecycleState; - } - late String _initialLifecycleState; - /// Tracks if the initial state has been accessed. Once accessed, we - /// will stop updating the [initialLifecycleState], as it is not the - /// preferred way to access the state. - bool _initialLifecycleStateAccessed = false; + String get initialLifecycleState => platformDispatcher.initialLifecycleState; /// The system-reported text scale. /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// /// This establishes the text scaling factor to use when rendering text, /// according to the user's platform preferences. /// @@ -869,18 +1001,20 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get textScaleFactor => _textScaleFactor; - double _textScaleFactor = 1.0; + double get textScaleFactor => platformDispatcher.textScaleFactor; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// /// This option is used by [showTimePicker]. - bool get alwaysUse24HourFormat => _alwaysUse24HourFormat; - bool _alwaysUse24HourFormat = false; + bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -888,21 +1022,23 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback? _onTextScaleFactorChanged; - Zone _onTextScaleFactorChangedZone = Zone.root; + VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; set onTextScaleFactorChanged(VoidCallback? callback) { - _onTextScaleFactorChanged = callback; - _onTextScaleFactorChangedZone = Zone.current; + platformDispatcher.onTextScaleFactorChanged = callback; } /// The setting indicating the current brightness mode of the host platform. - /// If the platform has no preference, [platformBrightness] defaults to [Brightness.light]. - Brightness get platformBrightness => _platformBrightness; - Brightness _platformBrightness = Brightness.light; + /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + Brightness get platformBrightness => platformDispatcher.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -910,19 +1046,20 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback? _onPlatformBrightnessChanged; - Zone _onPlatformBrightnessChangedZone = Zone.root; + VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; set onPlatformBrightnessChanged(VoidCallback? callback) { - _onPlatformBrightnessChanged = callback; - _onPlatformBrightnessChangedZone = Zone.current; + platformDispatcher.onPlatformBrightnessChanged = callback; } - /// A callback that is invoked to notify the application that it is an - /// appropriate time to provide a scene using the [SceneBuilder] API and the - /// [render] method. When possible, this is driven by the hardware VSync - /// signal. This is only called if [scheduleFrame] has been called since the - /// last time this callback was invoked. + /// A callback that is invoked to notify the window that it is an appropriate + /// time to provide a scene using the [SceneBuilder] API and the [render] + /// method. + /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// + /// When possible, this is driven by the hardware VSync signal. This is only + /// called if [scheduleFrame] has been called since the last time this + /// callback was invoked. /// /// The [onDrawFrame] callback is invoked immediately after [onBeginFrame], /// after draining any microtasks (e.g. completions of any [Future]s) queued @@ -937,18 +1074,18 @@ class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - FrameCallback? get onBeginFrame => _onBeginFrame; - FrameCallback? _onBeginFrame; - Zone _onBeginFrameZone = Zone.root; + FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; set onBeginFrame(FrameCallback? callback) { - _onBeginFrame = callback; - _onBeginFrameZone = Zone.current; + platformDispatcher.onBeginFrame = callback; } /// A callback that is invoked for each frame after [onBeginFrame] has - /// completed and after the microtask queue has been drained. This can be - /// used to implement a second phase of frame rendering that happens - /// after any deferred work queued by the [onBeginFrame] phase. + /// completed and after the microtask queue has been drained. + /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -959,22 +1096,21 @@ class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - VoidCallback? get onDrawFrame => _onDrawFrame; - VoidCallback? _onDrawFrame; - Zone _onDrawFrameZone = Zone.root; + VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; set onDrawFrame(VoidCallback? callback) { - _onDrawFrame = callback; - _onDrawFrameZone = Zone.current; + platformDispatcher.onDrawFrame = callback; } /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use - /// [Window.onReportTimings] directly because + /// [FlutterWindow.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. /// - /// This can be used to see if the application has missed frames (through + /// This can be used to see if the window has missed frames (through /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high /// latencies (through [FrameTiming.totalSpan]). /// @@ -988,22 +1124,15 @@ class Window { /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. - TimingsCallback? get onReportTimings => _onReportTimings; - TimingsCallback? _onReportTimings; - Zone _onReportTimingsZone = Zone.root; + TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; set onReportTimings(TimingsCallback? callback) { - if ((callback == null) != (_onReportTimings == null)) { - _setNeedsReportTimings(callback != null); - } - _onReportTimings = callback; - _onReportTimingsZone = Zone.current; + platformDispatcher.onReportTimings = callback; } - late _SetNeedsReportTimingsFunc _setNeedsReportTimings; - void _nativeSetNeedsReportTimings(bool value) native 'Window_setNeedsReportTimings'; - /// A callback that is invoked when pointer data is available. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -1011,17 +1140,16 @@ class Window { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback? _onPointerDataPacket; - Zone _onPointerDataPacketZone = Zone.root; + PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback) { - _onPointerDataPacket = callback; - _onPointerDataPacketZone = Zone.current; + platformDispatcher.onPointerDataPacket = callback; } /// The route or path that the embedder requested when the application was /// launched. /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// /// This will be the string "`/`" if no particular route was requested. /// /// ## Android @@ -1049,118 +1177,91 @@ class Window { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String get defaultRouteName => _defaultRouteName(); - String _defaultRouteName() native 'Window_defaultRouteName'; + String get initialRouteName => platformDispatcher.initialRouteName; /// Requests that, at the next appropriate opportunity, the [onBeginFrame] /// and [onDrawFrame] callbacks be invoked. /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - void scheduleFrame() native 'Window_scheduleFrame'; - - /// Updates the application's rendering on the GPU with the newly provided - /// [Scene]. This function must be called within the scope of the - /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function - /// is called a second time during a single [onBeginFrame]/[onDrawFrame] - /// callback sequence or called outside the scope of those callbacks, the call - /// will be ignored. - /// - /// To record graphical operations, first create a [PictureRecorder], then - /// construct a [Canvas], passing that [PictureRecorder] to its constructor. - /// After issuing all the graphical operations, call the - /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain - /// the final [Picture] that represents the issued graphical operations. - /// - /// Next, create a [SceneBuilder], and add the [Picture] to it using - /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can - /// then obtain a [Scene] object, which you can display to the user via this - /// [render] function. + /// {@template flutter.lib.ui.window.functionForwardWarning} + /// Calling this function calls the same function on the [PlatformDispatcher] + /// singleton, so instead of calling it here, you should consider calling it + /// on `WidgetsBinding.instance.platformDispatcher` instead (or, as a last + /// resort when `WidgetsBinding` isn't available, on + /// [PlatformDispatcher.instance]). The reason this function forwards to the + /// [PlatformDispatcher] is to avoid breaking code that was written before + /// Flutter supported multiple windows. + /// {@endtemplate} /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. - void render(Scene scene) native 'Window_render'; + void scheduleFrame() => platformDispatcher.scheduleFrame(); /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. /// + /// {@macro flutter.lib.ui.window.accessorForwardWarning} + /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. - bool get semanticsEnabled => _semanticsEnabled; - bool _semanticsEnabled = false; + bool get semanticsEnabled => platformDispatcher.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback? _onSemanticsEnabledChanged; - Zone _onSemanticsEnabledChangedZone = Zone.root; + VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; set onSemanticsEnabledChanged(VoidCallback? callback) { - _onSemanticsEnabledChanged = callback; - _onSemanticsEnabledChangedZone = Zone.current; + platformDispatcher.onSemanticsEnabledChanged = callback; } /// A callback that is invoked whenever the user requests an action to be /// performed. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// This callback is used when the user expresses the action they wish to /// perform based on the semantics supplied by [updateSemantics]. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback? _onSemanticsAction; - Zone _onSemanticsActionZone = Zone.root; + SemanticsActionCallback? get onSemanticsAction => platformDispatcher.onSemanticsAction; set onSemanticsAction(SemanticsActionCallback? callback) { - _onSemanticsAction = callback; - _onSemanticsActionZone = Zone.current; + platformDispatcher.onSemanticsAction = callback; } /// Additional accessibility features that may be enabled by the platform. - AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; - // The zero value matches the default value in `window_data.h`. - AccessibilityFeatures _accessibilityFeatures = const AccessibilityFeatures._(0); + AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; - VoidCallback? _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFeaturesChangedZone = Zone.root; + VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback) { - _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFeaturesChangedZone = Zone.current; + platformDispatcher.onAccessibilityFeaturesChanged = callback; } /// Change the retained semantics data about this window. /// + /// {@macro flutter.lib.ui.window.functionForwardWarning} + /// /// If [semanticsEnabled] is true, the user has requested that this function /// be called whenever the semantic content of this window changes. /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. - void updateSemantics(SemanticsUpdate update) native 'Window_updateSemantics'; - - /// Set the debug name associated with this window's root isolate. - /// - /// Normally debug names are automatically generated from the Dart port, entry - /// point, and source file. For example: `main.dart$main-1234`. - /// - /// This can be combined with flutter tools `--isolate-filter` flag to debug - /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. - /// Note that this does not rename any child isolates of the root. - void setIsolateDebugName(String name) native 'Window_setIsolateDebugName'; + void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); /// Sends a message to a platform-specific plugin. /// + /// {@macro flutter.lib.ui.window.functionForwardWarning} + /// /// The `name` parameter determines which plugin receives the message. The /// `data` parameter contains the message payload and is typically UTF-8 /// encoded JSON but can be arbitrary data. If the plugin replies to the @@ -1169,20 +1270,16 @@ class Window { /// The framework invokes [callback] in the same zone in which this method /// was called. void sendPlatformMessage(String name, - ByteData? data, - PlatformMessageResponseCallback? callback) { - final String? error = - _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); - if (error != null) - throw Exception(error); + ByteData? data, + PlatformMessageResponseCallback? callback) { + platformDispatcher.sendPlatformMessage(name, data, callback); } - String? _sendPlatformMessage(String name, - PlatformMessageResponseCallback? callback, - ByteData? data) native 'Window_sendPlatformMessage'; /// Called whenever this window receives a message from a platform-specific /// plugin. /// + /// {@macro flutter.lib.ui.window.forwardWarning} + /// /// The `name` parameter determines which plugin sent the message. The `data` /// parameter is the payload and is typically UTF-8 encoded JSON but can be /// arbitrary data. @@ -1193,43 +1290,10 @@ class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback? _onPlatformMessage; - Zone _onPlatformMessageZone = Zone.root; + PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback) { - _onPlatformMessage = callback; - _onPlatformMessageZone = Zone.current; - } - - /// Called by [_dispatchPlatformMessage]. - void _respondToPlatformMessage(int responseId, ByteData? data) - native 'Window_respondToPlatformMessage'; - - /// Wraps the given [callback] in another callback that ensures that the - /// original callback is called in the zone it was registered in. - static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback? callback) { - if (callback == null) - return null; - - // Store the zone in which the callback is being registered. - final Zone registrationZone = Zone.current; - - return (ByteData? data) { - registrationZone.runUnaryGuarded(callback, data); - }; + platformDispatcher.onPlatformMessage = callback; } - - - /// The embedder can specify data that the isolate can request synchronously - /// on launch. This accessor fetches that data. - /// - /// This data is persistent for the duration of the Flutter application and is - /// available even after isolate restarts. Because of this lifecycle, the size - /// of this data must be kept to a minimum. - /// - /// For asynchronous communication between the embedder and isolate, a - /// platform channel may be used. - ByteData? getPersistentIsolateData() native 'Window_getPersistentIsolateData'; } /// Additional accessibility features that may be enabled by the platform. @@ -1326,7 +1390,16 @@ enum Brightness { light, } -/// The [Window] singleton. +/// The [SingletonFlutterWindow] representing the "only" window on platforms +/// where there is only one window, such as single-display mobile devices. +/// +/// This is a legacy object for code that was written before Flutter supported +/// multiple windows. New code should use the API on +/// `WidgetsBinding.instance.platformDispatcher`, or, where that is not +/// available, on [PlatformDispatcher.instance]. +/// +/// On platforms with more than one window, this window typically represents the +/// first window created, but it may not be visible. /// /// Please try to avoid statically referencing this and instead use a /// binding for dependency resolution such as `WidgetsBinding.instance.window`. @@ -1338,7 +1411,13 @@ enum Brightness { /// reasonable for a future of Flutter where we legitimately want to select an /// appropriate implementation at runtime. /// -/// The only place that `WidgetsBinding.instance.window` is inappropriate is if -/// a `Window` is required before invoking `runApp()`. In that case, it is -/// acceptable (though unfortunate) to use this object statically. -final Window window = Window._(); +/// The only place that using `WidgetsBinding.instance.platformDispatcher` is +/// inappropriate is if access to these APIs is required before invoking +/// `runApp()`. In that case, it is acceptable (though unfortunate) to use the +/// [PlatformDispatcher.instance] object statically. +/// +/// See also: +/// +/// * [PlatformDispatcher.views], contains the current list of Flutter windows +/// belonging to the application. +final SingletonFlutterWindow window = SingletonFlutterWindow._(windowId: 0, platformDispatcher: PlatformDispatcher.instance); diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc new file mode 100644 index 0000000000000..8dd2cdb98f433 --- /dev/null +++ b/lib/ui/window/platform_configuration.cc @@ -0,0 +1,451 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/platform_configuration.h" + +#include "flutter/lib/ui/compositing/scene.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_message_response_dart.h" +#include "flutter/lib/ui/window/screen.h" +#include "flutter/lib/ui/window/window.h" +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/logging/dart_invoke.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { + +namespace { + +void InitialRouteName(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + std::string routeName = UIDartState::Current() + ->platform_configuration() + ->client() + ->InitialRouteName(); + Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); +} + +void ScheduleFrame(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + UIDartState::Current()->platform_configuration()->client()->ScheduleFrame(); +} + +void Render(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + Dart_Handle exception = nullptr; + Scene* scene = + tonic::DartConverter::FromArguments(args, 1, exception); + // The view argument is currently ignored. + if (exception) { + Dart_ThrowException(exception); + return; + } + UIDartState::Current()->platform_configuration()->client()->Render(scene); +} + +void UpdateSemantics(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + Dart_Handle exception = nullptr; + SemanticsUpdate* update = + tonic::DartConverter::FromArguments(args, 1, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + UIDartState::Current()->platform_configuration()->client()->UpdateSemantics( + update); +} + +void SetIsolateDebugName(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + Dart_Handle exception = nullptr; + const std::string name = + tonic::DartConverter::FromArguments(args, 1, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + UIDartState::Current()->SetDebugName(name); +} + +void SetNeedsReportTimings(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + Dart_Handle exception = nullptr; + bool value = tonic::DartConverter::FromArguments(args, 1, exception); + UIDartState::Current() + ->platform_configuration() + ->client() + ->SetNeedsReportTimings(value); +} + +void ReportUnhandledException(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + + Dart_Handle exception = nullptr; + + auto error_name = + tonic::DartConverter::FromArguments(args, 0, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + + auto stack_trace = + tonic::DartConverter::FromArguments(args, 1, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + + UIDartState::Current()->ReportUnhandledException(std::move(error_name), + std::move(stack_trace)); +} + +Dart_Handle SendPlatformMessage(Dart_Handle window, + const std::string& name, + Dart_Handle callback, + Dart_Handle data_handle) { + UIDartState* dart_state = UIDartState::Current(); + + if (!dart_state->platform_configuration()) { + return tonic::ToDart( + "Platform messages can only be sent from the main isolate"); + } + + fml::RefPtr response; + if (!Dart_IsNull(callback)) { + response = fml::MakeRefCounted( + tonic::DartPersistentValue(dart_state, callback), + dart_state->GetTaskRunners().GetUITaskRunner()); + } + if (Dart_IsNull(data_handle)) { + dart_state->platform_configuration()->client()->HandlePlatformMessage( + fml::MakeRefCounted(name, response)); + } else { + tonic::DartByteData data(data_handle); + const uint8_t* buffer = static_cast(data.data()); + dart_state->platform_configuration()->client()->HandlePlatformMessage( + fml::MakeRefCounted( + name, std::vector(buffer, buffer + data.length_in_bytes()), + response)); + } + + return Dart_Null(); +} + +void _SendPlatformMessage(Dart_NativeArguments args) { + tonic::DartCallStatic(&SendPlatformMessage, args); +} + +void RespondToPlatformMessage(Dart_Handle window, + int response_id, + const tonic::DartByteData& data) { + if (Dart_IsNull(data.dart_handle())) { + UIDartState::Current() + ->platform_configuration() + ->CompletePlatformMessageEmptyResponse(response_id); + } else { + // TODO(engine): Avoid this copy. + const uint8_t* buffer = static_cast(data.data()); + UIDartState::Current() + ->platform_configuration() + ->CompletePlatformMessageResponse( + response_id, + std::vector(buffer, buffer + data.length_in_bytes())); + } +} + +void _RespondToPlatformMessage(Dart_NativeArguments args) { + tonic::DartCallStatic(&RespondToPlatformMessage, args); +} + +void GetPersistentIsolateData(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + + auto persistent_isolate_data = UIDartState::Current() + ->platform_configuration() + ->client() + ->GetPersistentIsolateData(); + + if (!persistent_isolate_data) { + Dart_SetReturnValue(args, Dart_Null()); + return; + } + + Dart_SetReturnValue( + args, tonic::DartByteData::Create(persistent_isolate_data->GetMapping(), + persistent_isolate_data->GetSize())); +} + +} // namespace + +Dart_Handle ToByteData(const std::vector& buffer) { + Dart_Handle data_handle = + Dart_NewTypedData(Dart_TypedData_kByteData, buffer.size()); + if (Dart_IsError(data_handle)) + return data_handle; + + Dart_TypedData_Type type; + void* data = nullptr; + intptr_t num_bytes = 0; + FML_CHECK(!Dart_IsError( + Dart_TypedDataAcquireData(data_handle, &type, &data, &num_bytes))); + + memcpy(data, buffer.data(), num_bytes); + Dart_TypedDataReleaseData(data_handle); + return data_handle; +} + +WindowClient::~WindowClient() {} + +PlatformConfiguration::PlatformConfiguration(WindowClient* client) + : client_(client) { + // For now, there's only one window and one screen. + screens_.insert(std::make_pair(0, std::unique_ptr(new Screen(0)))); + windows_.insert(std::make_pair(0, std::unique_ptr(new Window(0, 0)))); +} + +PlatformConfiguration::~PlatformConfiguration() {} + +void PlatformConfiguration::DidCreateIsolate() { + library_.Set(tonic::DartState::Current(), + Dart_LookupLibrary(tonic::ToDart("dart:ui"))); + for (auto const& window : windows_) { + window.second->DidCreateIsolate(); + } + for (auto const& screen : screens_) { + screen.second->DidCreateIsolate(); + } +} + +void PlatformConfiguration::UpdateLocales( + const std::vector& locales) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updateLocales", + { + tonic::ToDart>(locales), + })); +} + +void PlatformConfiguration::UpdatePlatformResolvedLocale( + const std::vector& locale) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updatePlatformResolvedLocale", + { + tonic::ToDart>(locale), + })); +} + +void PlatformConfiguration::UpdateUserSettingsData(const std::string& data) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateUserSettingsData", + { + tonic::StdStringToDart(data), + })); +} + +void PlatformConfiguration::UpdateLifecycleState(const std::string& data) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateLifecycleState", + { + tonic::StdStringToDart(data), + })); +} + +void PlatformConfiguration::UpdateSemanticsEnabled(bool enabled) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + UIDartState::ThrowIfUIOperationsProhibited(); + + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updateSemanticsEnabled", {tonic::ToDart(enabled)})); +} + +void PlatformConfiguration::UpdateAccessibilityFeatures(int32_t values) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateAccessibilityFeatures", + {tonic::ToDart(values)})); +} + +void PlatformConfiguration::DispatchPlatformMessage( + fml::RefPtr message) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) { + FML_DLOG(WARNING) + << "Dropping platform message for lack of DartState on channel: " + << message->channel(); + return; + } + tonic::DartState::Scope scope(dart_state); + Dart_Handle data_handle = + (message->hasData()) ? ToByteData(message->data()) : Dart_Null(); + if (Dart_IsError(data_handle)) { + FML_DLOG(WARNING) + << "Dropping platform message because of a Dart error on channel: " + << message->channel(); + return; + } + + int response_id = 0; + if (auto response = message->response()) { + response_id = next_response_id_++; + pending_responses_[response_id] = response; + } + + tonic::LogIfError( + tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage", + {tonic::ToDart(message->channel()), data_handle, + tonic::ToDart(response_id)})); +} + +void PlatformConfiguration::DispatchPointerDataPacket( + const PointerDataPacket& packet) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + Dart_Handle data_handle = ToByteData(packet.data()); + if (Dart_IsError(data_handle)) + return; + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_dispatchPointerDataPacket", {data_handle})); +} + +void PlatformConfiguration::DispatchSemanticsAction(int32_t id, + SemanticsAction action, + std::vector args) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + Dart_Handle args_handle = (args.empty()) ? Dart_Null() : ToByteData(args); + + if (Dart_IsError(args_handle)) + return; + + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_dispatchSemanticsAction", + {tonic::ToDart(id), tonic::ToDart(static_cast(action)), + args_handle})); +} + +void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); + + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame", + { + Dart_NewInteger(microseconds), + })); + + UIDartState::Current()->FlushMicrotasksNow(); + + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {})); +} + +void PlatformConfiguration::ReportTimings(std::vector timings) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + Dart_Handle data_handle = + Dart_NewTypedData(Dart_TypedData_kInt64, timings.size()); + + Dart_TypedData_Type type; + void* data = nullptr; + intptr_t num_acquired = 0; + FML_CHECK(!Dart_IsError( + Dart_TypedDataAcquireData(data_handle, &type, &data, &num_acquired))); + FML_DCHECK(num_acquired == static_cast(timings.size())); + + memcpy(data, timings.data(), sizeof(int64_t) * timings.size()); + FML_CHECK(Dart_TypedDataReleaseData(data_handle)); + + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_reportTimings", + { + data_handle, + })); +} + +void PlatformConfiguration::CompletePlatformMessageEmptyResponse( + int response_id) { + if (!response_id) + return; + auto it = pending_responses_.find(response_id); + if (it == pending_responses_.end()) + return; + auto response = std::move(it->second); + pending_responses_.erase(it); + response->CompleteEmpty(); +} + +void PlatformConfiguration::CompletePlatformMessageResponse( + int response_id, + std::vector data) { + if (!response_id) + return; + auto it = pending_responses_.find(response_id); + if (it == pending_responses_.end()) + return; + auto response = std::move(it->second); + pending_responses_.erase(it); + response->Complete(std::make_unique(std::move(data))); +} + +void PlatformConfiguration::RegisterNatives( + tonic::DartLibraryNatives* natives) { + natives->Register({ + {"PlatformConfiguration_initialRouteName", InitialRouteName, 1, true}, + {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true}, + {"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4, + true}, + {"PlatformConfiguration_respondToPlatformMessage", + _RespondToPlatformMessage, 3, true}, + {"PlatformConfiguration_render", Render, 3, true}, + {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, + {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, + true}, + {"PlatformConfiguration_reportUnhandledException", + ReportUnhandledException, 2, true}, + {"PlatformConfiguration_setNeedsReportTimings", SetNeedsReportTimings, 2, + true}, + {"PlatformConfiguration_getPersistentIsolateData", + GetPersistentIsolateData, 1, true}, + }); +} + +} // namespace flutter diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h new file mode 100644 index 0000000000000..d738003d4a239 --- /dev/null +++ b/lib/ui/window/platform_configuration.h @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ +#define FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ + +#include +#include +#include +#include + +#include "flutter/fml/time/time_point.h" +#include "flutter/lib/ui/semantics/semantics_update.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/lib/ui/window/screen.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/lib/ui/window/window.h" +#include "third_party/tonic/dart_persistent_value.h" + +namespace tonic { +class DartLibraryNatives; + +// So tonic::ToDart> returns List instead of +// List. +template <> +struct DartListFactory { + static Dart_Handle NewList(intptr_t length) { + return Dart_NewListOf(Dart_CoreType_Int, length); + } +}; + +} // namespace tonic + +namespace flutter { +class FontCollection; +class PlatformMessage; +class Scene; + +Dart_Handle ToByteData(const std::vector& buffer); + +// Must match the AccessibilityFeatureFlag enum in framework. +enum class AccessibilityFeatureFlag : int32_t { + kAccessibleNavigation = 1 << 0, + kInvertColors = 1 << 1, + kDisableAnimations = 1 << 2, + kBoldText = 1 << 3, + kReduceMotion = 1 << 4, + kHighContrast = 1 << 5, +}; + +Dart_Handle ToByteData(const std::vector& buffer); + +class WindowClient { + public: + virtual std::string InitialRouteName() = 0; + virtual void ScheduleFrame() = 0; + virtual void Render(Scene* scene) = 0; + virtual void UpdateSemantics(SemanticsUpdate* update) = 0; + virtual void HandlePlatformMessage(fml::RefPtr message) = 0; + virtual FontCollection& GetFontCollection() = 0; + virtual void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) = 0; + virtual void SetNeedsReportTimings(bool value) = 0; + virtual std::shared_ptr GetPersistentIsolateData() = 0; + + protected: + virtual ~WindowClient(); +}; + +class PlatformConfiguration final { + public: + explicit PlatformConfiguration(WindowClient* client); + + ~PlatformConfiguration(); + + WindowClient* client() const { return client_; } + + void DidCreateIsolate(); + void UpdateLocales(const std::vector& locales); + void UpdatePlatformResolvedLocale(const std::vector& locale); + void UpdateUserSettingsData(const std::string& data); + void UpdateLifecycleState(const std::string& data); + void UpdateSemanticsEnabled(bool enabled); + void UpdateAccessibilityFeatures(int32_t flags); + void DispatchPlatformMessage(fml::RefPtr message); + void DispatchPointerDataPacket(const PointerDataPacket& packet); + void DispatchSemanticsAction(int32_t id, + SemanticsAction action, + std::vector args); + void BeginFrame(fml::TimePoint frameTime); + void ReportTimings(std::vector timings); + + void CompletePlatformMessageResponse(int response_id, + std::vector data); + void CompletePlatformMessageEmptyResponse(int response_id); + + static void RegisterNatives(tonic::DartLibraryNatives* natives); + + Window* get_window(int window_id) { return windows_[window_id].get(); } + Screen* get_screen(int screen_id) { return screens_[screen_id].get(); } + + private: + tonic::DartPersistentValue library_; + ViewportMetrics viewport_metrics_; + WindowClient* client_; + std::unordered_map> windows_; + std::unordered_map> screens_; + + // We use id 0 to mean that no response is expected. + int next_response_id_ = 1; + std::unordered_map> + pending_responses_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ diff --git a/lib/ui/window/platform_message_response_dart.cc b/lib/ui/window/platform_message_response_dart.cc index 91681d074bce0..1deb332f19230 100644 --- a/lib/ui/window/platform_message_response_dart.cc +++ b/lib/ui/window/platform_message_response_dart.cc @@ -8,6 +8,7 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/make_copyable.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "third_party/tonic/dart_state.h" #include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/typed_data/dart_byte_data.h" diff --git a/lib/ui/window/screen.cc b/lib/ui/window/screen.cc new file mode 100644 index 0000000000000..d1ba9097bf52a --- /dev/null +++ b/lib/ui/window/screen.cc @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/screen.h" + +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/logging/dart_invoke.h" + +namespace flutter { +Screen::Screen(int screen_id) : screen_id_(screen_id) {} + +Screen::~Screen() {} + +void Screen::DidCreateIsolate() { + library_.Set(tonic::DartState::Current(), + Dart_LookupLibrary(tonic::ToDart("dart:ui"))); + UpdateScreenMetrics(screen_metrics_); +} + +void Screen::UpdateScreenMetrics(const ScreenMetrics& metrics) { + screen_metrics_ = metrics; + + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updateScreenMetrics", + { + tonic::ToDart(screen_id_), + tonic::ToDart(metrics.display_name), + tonic::ToDart(metrics.physical_left), + tonic::ToDart(metrics.physical_top), + tonic::ToDart(metrics.physical_width), + tonic::ToDart(metrics.physical_height), + tonic::ToDart(metrics.device_pixel_ratio), + tonic::ToDart(metrics.physical_padding_top), + tonic::ToDart(metrics.physical_padding_right), + tonic::ToDart(metrics.physical_padding_bottom), + tonic::ToDart(metrics.physical_padding_left), + tonic::ToDart(metrics.physical_view_inset_top), + tonic::ToDart(metrics.physical_view_inset_right), + tonic::ToDart(metrics.physical_view_inset_bottom), + tonic::ToDart(metrics.physical_view_inset_left), + tonic::ToDart(metrics.physical_system_gesture_inset_top), + tonic::ToDart(metrics.physical_system_gesture_inset_right), + tonic::ToDart(metrics.physical_system_gesture_inset_bottom), + tonic::ToDart(metrics.physical_system_gesture_inset_left), + })); +} + +} // namespace flutter diff --git a/lib/ui/window/screen.h b/lib/ui/window/screen.h new file mode 100644 index 0000000000000..da7f1cae7261a --- /dev/null +++ b/lib/ui/window/screen.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_SCREEN_H_ +#define FLUTTER_LIB_UI_WINDOW_SCREEN_H_ + +#include +#include +#include + +#include "flutter/lib/ui/window/screen_metrics.h" +#include "third_party/tonic/dart_persistent_value.h" + +namespace flutter { + +class Screen final { + public: + explicit Screen(int id); + + ~Screen(); + + void DidCreateIsolate(); + + const ScreenMetrics& screen_metrics() { return screen_metrics_; } + + int screen_id() { return screen_id_; } + + void UpdateScreenMetrics(const ScreenMetrics& metrics); + + private: + tonic::DartPersistentValue library_; + int screen_id_; + ScreenMetrics screen_metrics_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_SCREEN_H_ diff --git a/lib/ui/window/screen_metrics.cc b/lib/ui/window/screen_metrics.cc new file mode 100644 index 0000000000000..b8a2dfb2a0830 --- /dev/null +++ b/lib/ui/window/screen_metrics.cc @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/screen_metrics.h" + +#include "flutter/fml/logging.h" + +namespace flutter { + +ScreenMetrics::ScreenMetrics(std::string p_display_name, + double p_device_pixel_ratio, + double p_physical_left, + double p_physical_top, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left, + double p_physical_system_gesture_inset_top, + double p_physical_system_gesture_inset_right, + double p_physical_system_gesture_inset_bottom, + double p_physical_system_gesture_inset_left) + : display_name(p_display_name), + device_pixel_ratio(p_device_pixel_ratio), + physical_left(p_physical_left), + physical_top(p_physical_top), + physical_width(p_physical_width), + physical_height(p_physical_height), + physical_padding_top(p_physical_padding_top), + physical_padding_right(p_physical_padding_right), + physical_padding_bottom(p_physical_padding_bottom), + physical_padding_left(p_physical_padding_left), + physical_view_inset_top(p_physical_view_inset_top), + physical_view_inset_right(p_physical_view_inset_right), + physical_view_inset_bottom(p_physical_view_inset_bottom), + physical_view_inset_left(p_physical_view_inset_left), + physical_system_gesture_inset_top(p_physical_system_gesture_inset_top), + physical_system_gesture_inset_right( + p_physical_system_gesture_inset_right), + physical_system_gesture_inset_bottom( + p_physical_system_gesture_inset_bottom), + physical_system_gesture_inset_left(p_physical_system_gesture_inset_left) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(device_pixel_ratio > 0); + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); +} + +ScreenMetrics::ScreenMetrics(std::string p_display_name, + double p_device_pixel_ratio, + double p_physical_left, + double p_physical_top, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left) + : display_name(p_display_name), + device_pixel_ratio(p_device_pixel_ratio), + physical_left(p_physical_left), + physical_top(p_physical_top), + physical_width(p_physical_width), + physical_height(p_physical_height), + physical_padding_top(p_physical_padding_top), + physical_padding_right(p_physical_padding_right), + physical_padding_bottom(p_physical_padding_bottom), + physical_padding_left(p_physical_padding_left), + physical_view_inset_top(p_physical_view_inset_top), + physical_view_inset_right(p_physical_view_inset_right), + physical_view_inset_bottom(p_physical_view_inset_bottom), + physical_view_inset_left(p_physical_view_inset_left) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(device_pixel_ratio > 0); + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); +} + +ScreenMetrics::ScreenMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height) + : device_pixel_ratio(p_device_pixel_ratio), + physical_width(p_physical_width), + physical_height(p_physical_height) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(device_pixel_ratio > 0); + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); +} + +} // namespace flutter diff --git a/lib/ui/window/screen_metrics.h b/lib/ui/window/screen_metrics.h new file mode 100644 index 0000000000000..63263e0b874a4 --- /dev/null +++ b/lib/ui/window/screen_metrics.h @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_SCREEN_METRICS_H_ +#define FLUTTER_LIB_UI_WINDOW_SCREEN_METRICS_H_ + +#include + +#include + +namespace flutter { + +struct ScreenMetrics { + ScreenMetrics() = default; + ScreenMetrics(const ScreenMetrics& other) = default; + + // Create a ScreenMetrics instance. + ScreenMetrics(std::string p_display_name, + double p_device_pixel_ratio, + double p_physical_left, + double p_physical_top, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left, + double p_physical_system_gesture_inset_top, + double p_physical_system_gesture_inset_right, + double p_physical_system_gesture_inset_bottom, + double p_physical_system_gesture_inset_left); + + // Create a ScreenMetrics instance without system gesture insets. + ScreenMetrics(std::string p_display_name, + double p_device_pixel_ratio, + double p_physical_left, + double p_physical_top, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left); + + ScreenMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height); + + std::string display_name = ""; + double device_pixel_ratio = 1.0; + double physical_left = 0; + double physical_top = 0; + double physical_width = 0; + double physical_height = 0; + double physical_padding_top = 0; + double physical_padding_right = 0; + double physical_padding_bottom = 0; + double physical_padding_left = 0; + double physical_view_inset_top = 0; + double physical_view_inset_right = 0; + double physical_view_inset_bottom = 0; + double physical_view_inset_left = 0; + double physical_system_gesture_inset_top = 0; + double physical_system_gesture_inset_right = 0; + double physical_system_gesture_inset_bottom = 0; + double physical_system_gesture_inset_left = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_SCREEN_METRICS_H_ diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index 0b6dab6d4c1e0..aa1c9059b4f87 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -8,7 +8,8 @@ namespace flutter { -ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, +ViewportMetrics::ViewportMetrics(double p_physical_left, + double p_physical_top, double p_physical_width, double p_physical_height, double p_physical_padding_top, @@ -23,7 +24,8 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, double p_physical_system_gesture_inset_left) - : device_pixel_ratio(p_device_pixel_ratio), + : physical_left(p_physical_left), + physical_top(p_physical_top), physical_width(p_physical_width), physical_height(p_physical_height), physical_padding_top(p_physical_padding_top), @@ -43,10 +45,10 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, // Ensure we don't have nonsensical dimensions. FML_DCHECK(physical_width >= 0); FML_DCHECK(physical_height >= 0); - FML_DCHECK(device_pixel_ratio > 0); } -ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, +ViewportMetrics::ViewportMetrics(double p_physical_left, + double p_physical_top, double p_physical_width, double p_physical_height, double p_physical_depth, @@ -60,7 +62,8 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_view_inset_right, double p_physical_view_inset_bottom, double p_physical_view_inset_left) - : device_pixel_ratio(p_device_pixel_ratio), + : physical_left(p_physical_left), + physical_top(p_physical_top), physical_width(p_physical_width), physical_height(p_physical_height), physical_depth(p_physical_depth), @@ -77,7 +80,14 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, // Ensure we don't have nonsensical dimensions. FML_DCHECK(physical_width >= 0); FML_DCHECK(physical_height >= 0); - FML_DCHECK(device_pixel_ratio > 0); +} + +ViewportMetrics::ViewportMetrics(double p_physical_width, + double p_physical_height) + : physical_width(p_physical_width), physical_height(p_physical_height) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); } } // namespace flutter diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index f60adbfcee110..7cef610160326 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -20,7 +20,8 @@ struct ViewportMetrics { ViewportMetrics(const ViewportMetrics& other) = default; // Create a 2D ViewportMetrics instance. - ViewportMetrics(double p_device_pixel_ratio, + ViewportMetrics(double p_physical_left, + double p_physical_top, double p_physical_width, double p_physical_height, double p_physical_padding_top, @@ -37,7 +38,8 @@ struct ViewportMetrics { double p_physical_system_gesture_inset_left); // Create a ViewportMetrics instance that contains z information. - ViewportMetrics(double p_device_pixel_ratio, + ViewportMetrics(double p_physical_left, + double p_physical_top, double p_physical_width, double p_physical_height, double p_physical_depth, @@ -52,7 +54,10 @@ struct ViewportMetrics { double p_physical_view_inset_bottom, double p_physical_view_inset_left); - double device_pixel_ratio = 1.0; + ViewportMetrics(double p_physical_width, double p_physical_height); + + double physical_left = 0; + double physical_top = 0; double physical_width = 0; double physical_height = 0; double physical_depth = kUnsetDepth; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 7c1c0fa2ce421..fc9532112deff 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -4,183 +4,21 @@ #include "flutter/lib/ui/window/window.h" -#include "flutter/lib/ui/compositing/scene.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/platform_message_response_dart.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" -#include "third_party/tonic/dart_library_natives.h" -#include "third_party/tonic/dart_microtask_queue.h" #include "third_party/tonic/logging/dart_invoke.h" -#include "third_party/tonic/typed_data/dart_byte_data.h" namespace flutter { -namespace { -void DefaultRouteName(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - std::string routeName = - UIDartState::Current()->window()->client()->DefaultRouteName(); - Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); -} - -void ScheduleFrame(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - UIDartState::Current()->window()->client()->ScheduleFrame(); -} - -void Render(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - Dart_Handle exception = nullptr; - Scene* scene = - tonic::DartConverter::FromArguments(args, 1, exception); - if (exception) { - Dart_ThrowException(exception); - return; - } - UIDartState::Current()->window()->client()->Render(scene); -} - -void UpdateSemantics(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - Dart_Handle exception = nullptr; - SemanticsUpdate* update = - tonic::DartConverter::FromArguments(args, 1, exception); - if (exception) { - Dart_ThrowException(exception); - return; - } - UIDartState::Current()->window()->client()->UpdateSemantics(update); -} - -void SetIsolateDebugName(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - Dart_Handle exception = nullptr; - const std::string name = - tonic::DartConverter::FromArguments(args, 1, exception); - if (exception) { - Dart_ThrowException(exception); - return; - } - UIDartState::Current()->SetDebugName(name); -} - -void SetNeedsReportTimings(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - Dart_Handle exception = nullptr; - bool value = tonic::DartConverter::FromArguments(args, 1, exception); - UIDartState::Current()->window()->client()->SetNeedsReportTimings(value); -} - -void ReportUnhandledException(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - - Dart_Handle exception = nullptr; - - auto error_name = - tonic::DartConverter::FromArguments(args, 0, exception); - if (exception) { - Dart_ThrowException(exception); - return; - } - - auto stack_trace = - tonic::DartConverter::FromArguments(args, 1, exception); - if (exception) { - Dart_ThrowException(exception); - return; - } - - UIDartState::Current()->ReportUnhandledException(std::move(error_name), - std::move(stack_trace)); -} - -Dart_Handle SendPlatformMessage(Dart_Handle window, - const std::string& name, - Dart_Handle callback, - Dart_Handle data_handle) { - UIDartState* dart_state = UIDartState::Current(); - - if (!dart_state->window()) { - return tonic::ToDart( - "Platform messages can only be sent from the main isolate"); - } - - fml::RefPtr response; - if (!Dart_IsNull(callback)) { - response = fml::MakeRefCounted( - tonic::DartPersistentValue(dart_state, callback), - dart_state->GetTaskRunners().GetUITaskRunner()); - } - if (Dart_IsNull(data_handle)) { - dart_state->window()->client()->HandlePlatformMessage( - fml::MakeRefCounted(name, response)); - } else { - tonic::DartByteData data(data_handle); - const uint8_t* buffer = static_cast(data.data()); - dart_state->window()->client()->HandlePlatformMessage( - fml::MakeRefCounted( - name, std::vector(buffer, buffer + data.length_in_bytes()), - response)); - } - - return Dart_Null(); -} - -void _SendPlatformMessage(Dart_NativeArguments args) { - tonic::DartCallStatic(&SendPlatformMessage, args); -} - -void RespondToPlatformMessage(Dart_Handle window, - int response_id, - const tonic::DartByteData& data) { - if (Dart_IsNull(data.dart_handle())) { - UIDartState::Current()->window()->CompletePlatformMessageEmptyResponse( - response_id); - } else { - // TODO(engine): Avoid this copy. - const uint8_t* buffer = static_cast(data.data()); - UIDartState::Current()->window()->CompletePlatformMessageResponse( - response_id, - std::vector(buffer, buffer + data.length_in_bytes())); - } -} - -void _RespondToPlatformMessage(Dart_NativeArguments args) { - tonic::DartCallStatic(&RespondToPlatformMessage, args); -} - -void GetPersistentIsolateData(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - - auto persistent_isolate_data = - UIDartState::Current()->window()->client()->GetPersistentIsolateData(); - - if (!persistent_isolate_data) { - Dart_SetReturnValue(args, Dart_Null()); - return; - } - - Dart_SetReturnValue( - args, tonic::DartByteData::Create(persistent_isolate_data->GetMapping(), - persistent_isolate_data->GetSize())); -} - -Dart_Handle ToByteData(const std::vector& buffer) { - return tonic::DartByteData::Create(buffer.data(), buffer.size()); -} - -} // namespace - -WindowClient::~WindowClient() {} - -Window::Window(WindowClient* client) : client_(client) {} +Window::Window(int window_id, int screen_id) + : window_id_(window_id), screen_id_(screen_id) {} Window::~Window() {} void Window::DidCreateIsolate() { library_.Set(tonic::DartState::Current(), Dart_LookupLibrary(tonic::ToDart("dart:ui"))); + UpdateWindowMetrics(viewport_metrics_); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { @@ -193,7 +31,10 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateWindowMetrics", { - tonic::ToDart(metrics.device_pixel_ratio), + tonic::ToDart(window_id_), + tonic::ToDart(screen_id_), + tonic::ToDart(metrics.physical_top), + tonic::ToDart(metrics.physical_left), tonic::ToDart(metrics.physical_width), tonic::ToDart(metrics.physical_height), tonic::ToDart(metrics.physical_depth), @@ -212,229 +53,4 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { })); } -void Window::UpdateLocales(const std::vector& locales) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_updateLocales", - { - tonic::ToDart>(locales), - })); -} - -void Window::UpdateUserSettingsData(const std::string& data) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateUserSettingsData", - { - tonic::StdStringToDart(data), - })); -} - -void Window::UpdateLifecycleState(const std::string& data) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateLifecycleState", - { - tonic::StdStringToDart(data), - })); -} - -void Window::UpdateSemanticsEnabled(bool enabled) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - UIDartState::ThrowIfUIOperationsProhibited(); - - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_updateSemanticsEnabled", {tonic::ToDart(enabled)})); -} - -void Window::UpdateAccessibilityFeatures(int32_t values) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateAccessibilityFeatures", - {tonic::ToDart(values)})); -} - -void Window::DispatchPlatformMessage(fml::RefPtr message) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) { - FML_DLOG(WARNING) - << "Dropping platform message for lack of DartState on channel: " - << message->channel(); - return; - } - tonic::DartState::Scope scope(dart_state); - Dart_Handle data_handle = - (message->hasData()) ? ToByteData(message->data()) : Dart_Null(); - if (Dart_IsError(data_handle)) { - FML_DLOG(WARNING) - << "Dropping platform message because of a Dart error on channel: " - << message->channel(); - return; - } - - int response_id = 0; - if (auto response = message->response()) { - response_id = next_response_id_++; - pending_responses_[response_id] = response; - } - - tonic::LogIfError( - tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage", - {tonic::ToDart(message->channel()), data_handle, - tonic::ToDart(response_id)})); -} - -void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - Dart_Handle data_handle = ToByteData(packet.data()); - if (Dart_IsError(data_handle)) - return; - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_dispatchPointerDataPacket", {data_handle})); -} - -void Window::DispatchSemanticsAction(int32_t id, - SemanticsAction action, - std::vector args) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - Dart_Handle args_handle = (args.empty()) ? Dart_Null() : ToByteData(args); - - if (Dart_IsError(args_handle)) - return; - - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_dispatchSemanticsAction", - {tonic::ToDart(id), tonic::ToDart(static_cast(action)), - args_handle})); -} - -void Window::BeginFrame(fml::TimePoint frameTime) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); - - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame", - { - Dart_NewInteger(microseconds), - })); - - UIDartState::Current()->FlushMicrotasksNow(); - - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {})); -} - -void Window::ReportTimings(std::vector timings) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - - Dart_Handle data_handle = - Dart_NewTypedData(Dart_TypedData_kInt64, timings.size()); - - Dart_TypedData_Type type; - void* data = nullptr; - intptr_t num_acquired = 0; - FML_CHECK(!Dart_IsError( - Dart_TypedDataAcquireData(data_handle, &type, &data, &num_acquired))); - FML_DCHECK(num_acquired == static_cast(timings.size())); - - memcpy(data, timings.data(), sizeof(int64_t) * timings.size()); - FML_CHECK(Dart_TypedDataReleaseData(data_handle)); - - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_reportTimings", - { - data_handle, - })); -} - -void Window::CompletePlatformMessageEmptyResponse(int response_id) { - if (!response_id) - return; - auto it = pending_responses_.find(response_id); - if (it == pending_responses_.end()) - return; - auto response = std::move(it->second); - pending_responses_.erase(it); - response->CompleteEmpty(); -} - -void Window::CompletePlatformMessageResponse(int response_id, - std::vector data) { - if (!response_id) - return; - auto it = pending_responses_.find(response_id); - if (it == pending_responses_.end()) - return; - auto response = std::move(it->second); - pending_responses_.erase(it); - response->Complete(std::make_unique(std::move(data))); -} - -Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { - std::vector supportedLocales = - tonic::DartConverter>::FromDart( - supportedLocalesHandle); - - std::vector results = - *UIDartState::Current() - ->window() - ->client() - ->ComputePlatformResolvedLocale(supportedLocales); - - return tonic::DartConverter>::ToDart(results); -} - -static void _ComputePlatformResolvedLocale(Dart_NativeArguments args) { - UIDartState::ThrowIfUIOperationsProhibited(); - Dart_Handle result = - ComputePlatformResolvedLocale(Dart_GetNativeArgument(args, 1)); - Dart_SetReturnValue(args, result); -} - -void Window::RegisterNatives(tonic::DartLibraryNatives* natives) { - natives->Register({ - {"Window_defaultRouteName", DefaultRouteName, 1, true}, - {"Window_scheduleFrame", ScheduleFrame, 1, true}, - {"Window_sendPlatformMessage", _SendPlatformMessage, 4, true}, - {"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, - {"Window_render", Render, 2, true}, - {"Window_updateSemantics", UpdateSemantics, 2, true}, - {"Window_setIsolateDebugName", SetIsolateDebugName, 2, true}, - {"Window_reportUnhandledException", ReportUnhandledException, 2, true}, - {"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true}, - {"Window_getPersistentIsolateData", GetPersistentIsolateData, 1, true}, - {"Window_computePlatformResolvedLocale", _ComputePlatformResolvedLocale, - 2, true}, - }); -} - } // namespace flutter diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 95055c70beddb..7c76b73bf862a 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -9,102 +9,32 @@ #include #include -#include "flutter/fml/time/time_point.h" -#include "flutter/lib/ui/semantics/semantics_update.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/tonic/dart_persistent_value.h" -namespace tonic { -class DartLibraryNatives; - -// So tonice::ToDart> returns List instead of -// List. -template <> -struct DartListFactory { - static Dart_Handle NewList(intptr_t length) { - return Dart_NewListOf(Dart_CoreType_Int, length); - } -}; - -} // namespace tonic - namespace flutter { -class FontCollection; -class Scene; - -// Must match the AccessibilityFeatureFlag enum in window.dart. -enum class AccessibilityFeatureFlag : int32_t { - kAccessibleNavigation = 1 << 0, - kInvertColors = 1 << 1, - kDisableAnimations = 1 << 2, - kBoldText = 1 << 3, - kReduceMotion = 1 << 4, - kHighContrast = 1 << 5, -}; - -class WindowClient { - public: - virtual std::string DefaultRouteName() = 0; - virtual void ScheduleFrame() = 0; - virtual void Render(Scene* scene) = 0; - virtual void UpdateSemantics(SemanticsUpdate* update) = 0; - virtual void HandlePlatformMessage(fml::RefPtr message) = 0; - virtual FontCollection& GetFontCollection() = 0; - virtual void UpdateIsolateDescription(const std::string isolate_name, - int64_t isolate_port) = 0; - virtual void SetNeedsReportTimings(bool value) = 0; - virtual std::shared_ptr GetPersistentIsolateData() = 0; - virtual std::unique_ptr> - ComputePlatformResolvedLocale( - const std::vector& supported_locale_data) = 0; - - protected: - virtual ~WindowClient(); -}; - class Window final { public: - explicit Window(WindowClient* client); + explicit Window(int window_id, int screen_id); ~Window(); - WindowClient* client() const { return client_; } + int screen() const { return screen_id_; } const ViewportMetrics& viewport_metrics() { return viewport_metrics_; } - void DidCreateIsolate(); void UpdateWindowMetrics(const ViewportMetrics& metrics); - void UpdateLocales(const std::vector& locales); - void UpdateUserSettingsData(const std::string& data); - void UpdateLifecycleState(const std::string& data); - void UpdateSemanticsEnabled(bool enabled); - void UpdateAccessibilityFeatures(int32_t flags); - void DispatchPlatformMessage(fml::RefPtr message); - void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchSemanticsAction(int32_t id, - SemanticsAction action, - std::vector args); - void BeginFrame(fml::TimePoint frameTime); - void ReportTimings(std::vector timings); - void CompletePlatformMessageResponse(int response_id, - std::vector data); - void CompletePlatformMessageEmptyResponse(int response_id); - - static void RegisterNatives(tonic::DartLibraryNatives* natives); + void DidCreateIsolate(); private: - WindowClient* client_; tonic::DartPersistentValue library_; + int window_id_; + int screen_id_; ViewportMetrics viewport_metrics_; - - // We use id 0 to mean that no response is expected. - int next_response_id_ = 1; - std::unordered_map> - pending_responses_; }; } // namespace flutter diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 857d6acd1b76e..f6cc89521ce7a 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -27,26 +27,27 @@ part 'engine/browser_location.dart'; part 'engine/canvas_pool.dart'; part 'engine/clipboard.dart'; part 'engine/color_filter.dart'; -part 'engine/compositor/canvas.dart'; part 'engine/compositor/canvas_kit_canvas.dart'; +part 'engine/compositor/canvas.dart'; part 'engine/compositor/color_filter.dart'; part 'engine/compositor/embedded_views.dart'; part 'engine/compositor/fonts.dart'; -part 'engine/compositor/image.dart'; part 'engine/compositor/image_filter.dart'; +part 'engine/compositor/image.dart'; part 'engine/compositor/initialization.dart'; -part 'engine/compositor/layer.dart'; part 'engine/compositor/layer_scene_builder.dart'; part 'engine/compositor/layer_tree.dart'; +part 'engine/compositor/layer.dart'; part 'engine/compositor/n_way_canvas.dart'; -part 'engine/compositor/path.dart'; part 'engine/compositor/painting.dart'; part 'engine/compositor/path_metrics.dart'; -part 'engine/compositor/picture.dart'; +part 'engine/compositor/path.dart'; part 'engine/compositor/picture_recorder.dart'; +part 'engine/compositor/picture.dart'; part 'engine/compositor/platform_message.dart'; part 'engine/compositor/raster_cache.dart'; part 'engine/compositor/rasterizer.dart'; +part 'engine/compositor/screen_metrics.dart'; part 'engine/compositor/surface.dart'; part 'engine/compositor/text.dart'; part 'engine/compositor/util.dart'; @@ -65,12 +66,14 @@ part 'engine/mouse_cursor.dart'; part 'engine/onscreen_logging.dart'; part 'engine/path_to_svg.dart'; part 'engine/picture.dart'; +part 'engine/platform_dispatcher.dart'; part 'engine/platform_views.dart'; part 'engine/plugins.dart'; part 'engine/pointer_binding.dart'; part 'engine/pointer_converter.dart'; part 'engine/profiler.dart'; part 'engine/rrect_renderer.dart'; +part 'engine/screen.dart'; part 'engine/semantics/accessibility.dart'; part 'engine/semantics/checkable.dart'; part 'engine/semantics/image.dart'; @@ -78,8 +81,8 @@ part 'engine/semantics/incrementable.dart'; part 'engine/semantics/label_and_value.dart'; part 'engine/semantics/live_region.dart'; part 'engine/semantics/scrollable.dart'; -part 'engine/semantics/semantics.dart'; part 'engine/semantics/semantics_helper.dart'; +part 'engine/semantics/semantics.dart'; part 'engine/semantics/tappable.dart'; part 'engine/semantics/text_field.dart'; part 'engine/services/buffers.dart'; @@ -97,17 +100,20 @@ part 'engine/surface/offset.dart'; part 'engine/surface/opacity.dart'; part 'engine/surface/painting.dart'; part 'engine/surface/path_metrics.dart'; +part 'engine/surface/path.dart'; part 'engine/surface/picture.dart'; part 'engine/surface/platform_view.dart'; part 'engine/surface/recording_canvas.dart'; part 'engine/surface/render_vertices.dart'; -part 'engine/surface/scene.dart'; part 'engine/surface/scene_builder.dart'; -part 'engine/surface/surface.dart'; -part 'engine/surface/path.dart'; +part 'engine/surface/scene.dart'; part 'engine/surface/surface_stats.dart'; +part 'engine/surface/surface.dart'; part 'engine/surface/transform.dart'; part 'engine/test_embedding.dart'; +part 'engine/text_editing/autofill_hint.dart'; +part 'engine/text_editing/input_type.dart'; +part 'engine/text_editing/text_editing.dart'; part 'engine/text/font_collection.dart'; part 'engine/text/line_break_properties.dart'; part 'engine/text/line_breaker.dart'; @@ -117,9 +123,6 @@ part 'engine/text/ruler.dart'; part 'engine/text/unicode_range.dart'; part 'engine/text/word_break_properties.dart'; part 'engine/text/word_breaker.dart'; -part 'engine/text_editing/autofill_hint.dart'; -part 'engine/text_editing/input_type.dart'; -part 'engine/text_editing/text_editing.dart'; part 'engine/util.dart'; part 'engine/validators.dart'; part 'engine/vector_math.dart'; @@ -201,17 +204,17 @@ void initializeEngine() { // microsecond precision, and only then convert to `int`. final int highResTimeMicroseconds = (1000 * highResTime).toInt(); - if (window._onBeginFrame != null) { - window.invokeOnBeginFrame( + if (EnginePlatformDispatcher.instance._onBeginFrame != null) { + EnginePlatformDispatcher.instance.invokeOnBeginFrame( Duration(microseconds: highResTimeMicroseconds)); } - if (window._onDrawFrame != null) { + if (EnginePlatformDispatcher.instance._onDrawFrame != null) { // TODO(yjbanov): technically Flutter flushes microtasks between // onBeginFrame and onDrawFrame. We don't, which hasn't // been an issue yet, but eventually we'll have to // implement it properly. - window.invokeOnDrawFrame(); + EnginePlatformDispatcher.instance.invokeOnDrawFrame(); } }); } diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 1e200df3dd16c..a1ae274ebff81 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -71,7 +71,7 @@ class BitmapCanvas extends EngineCanvas { /// Keeps track of what device pixel ratio was used when this [BitmapCanvas] /// was created. - final double _devicePixelRatio = EngineWindow.browserDevicePixelRatio; + final double _devicePixelRatio = EnginePlatformDispatcher.browserDevicePixelRatio; // Compensation for [_initializeViewport] snapping canvas position to 1 pixel. int _canvasPositionX, _canvasPositionY; @@ -150,13 +150,13 @@ class BitmapCanvas extends EngineCanvas { static int _widthToPhysical(double width) { final double boundsWidth = width + 1; - return (boundsWidth * EngineWindow.browserDevicePixelRatio).ceil() + + return (boundsWidth * EnginePlatformDispatcher.browserDevicePixelRatio).ceil() + 2 * kPaddingPixels; } static int _heightToPhysical(double height) { final double boundsHeight = height + 1; - return (boundsHeight * EngineWindow.browserDevicePixelRatio).ceil() + + return (boundsHeight * EnginePlatformDispatcher.browserDevicePixelRatio).ceil() + 2 * kPaddingPixels; } @@ -198,7 +198,7 @@ class BitmapCanvas extends EngineCanvas { /// * [PersistedStandardPicture._recycleCanvas] which also uses this method /// for the same reason. bool isReusable() { - return _devicePixelRatio == EngineWindow.browserDevicePixelRatio; + return _devicePixelRatio == EnginePlatformDispatcher.browserDevicePixelRatio; } /// Returns a data URI containing a representation of the image in this diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index 0be145972fdb7..52a911dd7cbc6 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -96,9 +96,9 @@ class _CanvasPool extends _SaveStackTracking { // * To make sure that when we scale the canvas by devicePixelRatio (see // _initializeViewport below) the pixels line up. final double cssWidth = - _widthInBitmapPixels / EngineWindow.browserDevicePixelRatio; + _widthInBitmapPixels / EnginePlatformDispatcher.browserDevicePixelRatio; final double cssHeight = - _heightInBitmapPixels / EngineWindow.browserDevicePixelRatio; + _heightInBitmapPixels / EnginePlatformDispatcher.browserDevicePixelRatio; _canvas = html.CanvasElement( width: _widthInBitmapPixels, height: _heightInBitmapPixels, @@ -184,7 +184,7 @@ class _CanvasPool extends _SaveStackTracking { clipTimeTransform[5] != prevTransform[5] || clipTimeTransform[12] != prevTransform[12] || clipTimeTransform[13] != prevTransform[13]) { - final double ratio = EngineWindow.browserDevicePixelRatio; + final double ratio = EnginePlatformDispatcher.browserDevicePixelRatio; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); ctx.transform( clipTimeTransform[0], @@ -213,7 +213,7 @@ class _CanvasPool extends _SaveStackTracking { transform[5] != prevTransform[5] || transform[12] != prevTransform[12] || transform[13] != prevTransform[13]) { - final double ratio = EngineWindow.browserDevicePixelRatio; + final double ratio = EnginePlatformDispatcher.browserDevicePixelRatio; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); ctx.transform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); @@ -297,8 +297,8 @@ class _CanvasPool extends _SaveStackTracking { // This scale makes sure that 1 CSS pixel is translated to the correct // number of bitmap pixels. - ctx.scale(EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio); + ctx.scale(EnginePlatformDispatcher.browserDevicePixelRatio, + EnginePlatformDispatcher.browserDevicePixelRatio); } void resetTransform() { diff --git a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart index d5a76e55a3c36..9c970162be361 100644 --- a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart @@ -290,7 +290,7 @@ class HtmlViewEmbedder { // // HTML elements use logical (CSS) pixels, but we have been using physical // pixels, so scale down the head element to match the logical resolution. - final double scale = EngineWindow.browserDevicePixelRatio; + final double scale = EnginePlatformDispatcher.browserDevicePixelRatio; final double inverseScale = 1 / scale; final Matrix4 scaleMatrix = Matrix4.diagonal3Values(inverseScale, inverseScale, 1); diff --git a/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart b/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart new file mode 100644 index 0000000000000..4f4029750d820 --- /dev/null +++ b/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of engine; + +class ScreenMetrics { + final double devicePixelRatio; + final double physicalWidth; + final double physicalHeight; + + const ScreenMetrics( + this.devicePixelRatio, + this.physicalWidth, + this.physicalHeight, + ); +} diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index 008291cc2a849..a043d80102d3b 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -66,7 +66,7 @@ class SkiaObjects { // beyond a single frame. @visibleForTesting static final List managedObjects = () { - window.rasterizer.addPostFrameCallback(postFrameCleanUp); + EnginePlatformDispatcher.instance.rasterizer.addPostFrameCallback(postFrameCleanUp); return []; }(); diff --git a/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart b/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart index 13cd698db89a9..3696ee3969099 100644 --- a/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart +++ b/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart @@ -6,12 +6,10 @@ part of engine; class ViewportMetrics { - final double devicePixelRatio; final double physicalWidth; final double physicalHeight; const ViewportMetrics( - this.devicePixelRatio, this.physicalWidth, this.physicalHeight, ); diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index a2d03c018b176..77523c88eb471 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -472,7 +472,7 @@ flt-glass-pane * { } _localeSubscription = languageChangeEvent.forTarget(html.window) .listen(_languageDidChange); - window._updateLocales(); + EnginePlatformDispatcher.instance._updateLocales(); } /// Called immediately after browser window metrics change. @@ -487,18 +487,18 @@ flt-glass-pane * { void _metricsDidChange(html.Event event) { if(isMobile && !window.isRotation() && textEditing.isEditing) { window.computeOnScreenKeyboardInsets(); - window.invokeOnMetricsChanged(); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); } else { window._computePhysicalSize(); // When physical size changes this value has to be recalculated. window.computeOnScreenKeyboardInsets(); - window.invokeOnMetricsChanged(); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); } } /// Called immediately after browser window language change. void _languageDidChange(html.Event event) { - window._updateLocales(); + EnginePlatformDispatcher.instance._updateLocales(); if (ui.window.onLocaleChanged != null) { ui.window.onLocaleChanged(); } diff --git a/lib/web_ui/lib/src/engine/history.dart b/lib/web_ui/lib/src/engine/history.dart index 7a30d62d8f345..dd89f324d3245 100644 --- a/lib/web_ui/lib/src/engine/history.dart +++ b/lib/web_ui/lib/src/engine/history.dart @@ -98,8 +98,8 @@ class BrowserHistory { _setupFlutterEntry(_locationStrategy); // 2. Send a 'popRoute' platform message so the app can handle it accordingly. - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), (_) {}, @@ -117,8 +117,8 @@ class BrowserHistory { _userProvidedRouteName = null; // Send a 'pushRoute' platform message so the app handles it accordingly. - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall( MethodCall('pushRoute', newRouteName), diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 2569a05a3ab9f..066a89fd11f0a 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -81,7 +81,7 @@ class Keyboard { final html.KeyboardEvent keyboardEvent = event as html.KeyboardEvent; - if (window._onPlatformMessage == null) { + if (EnginePlatformDispatcher.instance._onPlatformMessage == null) { return; } @@ -122,7 +122,7 @@ class Keyboard { 'metaState': _lastMetaState, }; - window.invokeOnPlatformMessage('flutter/keyevent', + EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', _messageCodec.encodeMessage(eventData), _noopCallback); } @@ -145,7 +145,7 @@ class Keyboard { 'metaState': _lastMetaState, }; - window.invokeOnPlatformMessage('flutter/keyevent', + EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', _messageCodec.encodeMessage(eventData), _noopCallback); } } diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart new file mode 100644 index 0000000000000..ae1abb7829549 --- /dev/null +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -0,0 +1,978 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of engine; + +/// Platform event dispatcher. +/// +/// This is the central entry point for platform messages and configuration +/// events from the platform. +class EnginePlatformDispatcher extends ui.PlatformDispatcher { + /// Private constructor, since only dart:ui is supposed to create one of + /// these. + EnginePlatformDispatcher._() { + _addBrightnessMediaQueryListener(); + } + + /// The [EnginePlatformDispatcher] singleton. + static EnginePlatformDispatcher get instance => _instance; + static final EnginePlatformDispatcher _instance = EnginePlatformDispatcher._(); + + /// The current platform configuration. + @override + ui.PlatformConfiguration get configuration => _configuration; + ui.PlatformConfiguration _configuration = ui.PlatformConfiguration(locales: parseBrowserLanguages()); + + /// Receives all events related to platform configuration changes. + @override + ui.VoidCallback get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + ui.VoidCallback _onPlatformConfigurationChanged; + Zone _onPlatformConfigurationChangedZone; + @override + set onPlatformConfigurationChanged(ui.VoidCallback callback) { + _onPlatformConfigurationChanged = callback; + _onPlatformConfigurationChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformConfigurationChanged() { + _invoke(_onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + } + + /// The current list of screens. + Iterable get screens => _screens.values; + Map _screens = {}; + + /// A map of opaque platform screen identifiers to screen configurations. + /// + /// This should be considered a protected member, only to be used by + /// [PlatformDispatcher] subclasses. + Map _screenConfigurations = {}; + + /// The current list of windows, + Iterable get views => _windows.values; + Map _windows = {}; + + /// A map of opaque platform window identifiers to window configurations. + /// + /// This should be considered a protected member, only to be used by + /// [PlatformDispatcher] subclasses. + Map _windowConfigurations = {}; + + /// Opens a new window and returns the window created. + /// + /// The configuration obtained and the one requested may not match, depending + /// on what the platform was able to accommodate. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + @override + Future createView(ui.ViewConfigurationRequest configuration) async { + throw UnimplementedError(); + // Awaits the platform window creation response, and calls onWindowOpened before returning. + } + + /// Reconfigures an existing window. + /// + /// The configuration obtained and the one requested may not match, depending + /// on what the platform was able to accommodate. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + @override + Future configureView( + ui.FlutterView view, ui.ViewConfigurationRequest configuration) async { + throw UnimplementedError(); + } + + /// Requests closing a window. + /// + /// Future completes when the window has been closed. + /// + /// This function is currently not implemented, but is part of a planned + /// feature. + @override + Future disposeView(ui.FlutterView view) async { + throw UnimplementedError(); + } + + /// Is called when [createView] is called. + /// + /// Sends the newly opened window. + @override + ui.ViewCreatedCallback get onViewCreated => _onWindowOpened; + ui.ViewCreatedCallback _onWindowOpened; + Zone _onWindowOpenedZone; // ignore: unused_field + @override + set onViewCreated(ui.ViewCreatedCallback callback) { + _onWindowOpened = callback; + _onWindowOpenedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnWindowCreated(Object id) { + _invoke1(_onWindowOpened, _onWindowOpenedZone, id); + } + + /// Is called when a window closure is requested by the platform. + /// + /// Sends the window to be closed. + @override + ui.ViewDisposedCallback get onViewDisposed => _onWindowClosed; + ui.ViewDisposedCallback _onWindowClosed; + Zone _onWindowClosedZone; // ignore: unused_field + @override + set onViewDisposed(ui.ViewDisposedCallback callback) { + _onWindowClosed = callback; + _onWindowClosedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnWindowDisposed(Object id) { + _invoke1(_onWindowClosed, _onWindowClosedZone, id); + } + + /// A callback that is invoked whenever the platform's [devicePixelRatio], + /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] + /// values change, for example when the device is rotated or when the + /// application is resized (e.g. when showing applications side-by-side + /// on Android). + /// + /// The engine invokes this callback in the same zone in which the callback + /// was set. + /// + /// The framework registers with this callback and updates the layout + /// appropriately. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// register for notifications when this is called. + /// * [MediaQuery.of], a simpler mechanism for the same. + @override + ui.VoidCallback get onMetricsChanged => _onMetricsChanged; + ui.VoidCallback _onMetricsChanged; + Zone _onMetricsChangedZone; // ignore: unused_field + @override + set onMetricsChanged(ui.VoidCallback callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnMetricsChanged() { + _invoke(_onMetricsChanged, _onMetricsChangedZone); + } + + /// Returns device pixel ratio returned by browser. + static double get browserDevicePixelRatio { + double ratio = html.window.devicePixelRatio; + // Guard against WebOS returning 0. + return (ratio == null || ratio == 0.0) ? 1.0 : ratio; + } + + /// A callback invoked when any window begins a frame. + /// + /// {@template flutter.foundation.PlatformDispatcher.onBeginFrame} + /// A callback that is invoked to notify the application that it is an + /// appropriate time to provide a scene using the [SceneBuilder] API and the + /// [PlatformWindow.render] method. + /// When possible, this is driven by the hardware VSync signal of the attached + /// screen with the highest VSync rate. This is only called if + /// [PlatformWindow.scheduleFrame] has been called since the last time this + /// callback was invoked. + /// {@endtemplate} + @override + ui.FrameCallback get onBeginFrame => _onBeginFrame; + ui.FrameCallback _onBeginFrame; + Zone _onBeginFrameZone; + @override + set onBeginFrame(ui.FrameCallback callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnBeginFrame(Duration duration) { + _invoke1(_onBeginFrame, _onBeginFrameZone, duration); + } + + /// {@template flutter.foundation.PlatformDispatcher.onDrawFrame} + /// A callback that is invoked for each frame after [onBeginFrame] has + /// completed and after the microtask queue has been drained. + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. + /// {@endtemplate} + @override + ui.VoidCallback get onDrawFrame => _onDrawFrame; + ui.VoidCallback _onDrawFrame; + Zone _onDrawFrameZone; + @override + set onDrawFrame(ui.VoidCallback callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnDrawFrame() { + _invoke(_onDrawFrame, _onDrawFrameZone); + } + + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + @override + ui.PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; + ui.PointerDataPacketCallback _onPointerDataPacket; + Zone _onPointerDataPacketZone; + @override + set onPointerDataPacket(ui.PointerDataPacketCallback callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPointerDataPacket(ui.PointerDataPacket dataPacket) { + _invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); + } + + /// A callback that is invoked to report the [FrameTiming] of recently + /// rasterized frames. + /// + /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use + /// [Window.onReportTimings] directly because + /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. + /// + /// This can be used to see if the application has missed frames (through + /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high + /// latencies (through [FrameTiming.totalSpan]). + /// + /// Unlike [Timeline], the timing information here is available in the release + /// mode (additional to the profile and the debug mode). Hence this can be + /// used to monitor the application's performance in the wild. + /// + /// {@macro dart.ui.TimingsCallback.list} + /// + /// If this is null, no additional work will be done. If this is not null, + /// Flutter spends less than 0.1ms every 1 second to report the timings + /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for + /// 60fps), or 0.01% CPU usage per second. + @override + ui.TimingsCallback get onReportTimings => _onReportTimings; + ui.TimingsCallback _onReportTimings; + Zone _onReportTimingsZone; + @override + set onReportTimings(ui.TimingsCallback callback) { + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnReportTimings(List timings) { + _invoke1>(_onReportTimings, _onReportTimingsZone, timings); + } + + @override + void sendPlatformMessage( + String/*!*/ name, + ByteData/*?*/ data, + ui.PlatformMessageResponseCallback/*?*/ callback, + ) { + _sendPlatformMessage(name, data, _zonedPlatformMessageResponseCallback(callback)); + } + + @override + ui.PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; + ui.PlatformMessageCallback _onPlatformMessage; + Zone _onPlatformMessageZone; + @override + set onPlatformMessage(ui.PlatformMessageCallback callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformMessage(String name, ByteData data, ui.PlatformMessageResponseCallback callback) { + _invoke3( + _onPlatformMessage, + _onPlatformMessageZone, + name, + data, + callback, + ); + } + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback callback) { + if (callback == null) + return null; + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + void _sendPlatformMessage( + String/*?*/ name, + ByteData/*?*/ data, + ui.PlatformMessageResponseCallback/*?*/ callback, + ) { + // In widget tests we want to bypass processing of platform messages. + if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { + return; + } + + if (_debugPrintPlatformMessages) { + print('Sent platform message on channel: "$name"'); + } + + if (assertionsEnabled && name == 'flutter/debug-echo') { + // Echoes back the data unchanged. Used for testing purposes. + _replyToPlatformMessage(callback, data); + return; + } + + switch (name) { + case 'flutter/assets': + assert(ui.webOnlyAssetManager != null); + final String url = utf8.decode(data.buffer.asUint8List()); + ui.webOnlyAssetManager.load(url).then((ByteData assetData) { + _replyToPlatformMessage(callback, assetData); + }, onError: (dynamic error) { + html.window.console + .warn('Error while trying to load an asset: $error'); + _replyToPlatformMessage(callback, null); + }); + return; + + case 'flutter/platform': + const MethodCodec codec = JSONMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + switch (decoded.method) { + case 'SystemNavigator.pop': + _browserHistory.exit().then((_) { + _replyToPlatformMessage( + callback, codec.encodeSuccessEnvelope(true)); + }); + return; + case 'HapticFeedback.vibrate': + final String type = decoded.arguments; + domRenderer.vibrate(_getHapticFeedbackDuration(type)); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'SystemChrome.setApplicationSwitcherDescription': + final Map arguments = decoded.arguments; + domRenderer.setTitle(arguments['label']); + domRenderer.setThemeColor(ui.Color(arguments['primaryColor'])); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'SystemChrome.setPreferredOrientations': + final List arguments = decoded.arguments; + domRenderer.setPreferredOrientation(arguments).then((bool success) { + _replyToPlatformMessage(callback, + codec.encodeSuccessEnvelope(success)); + }); + return; + case 'SystemSound.play': + // There are no default system sounds on web. + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'Clipboard.setData': + ClipboardMessageHandler().setDataMethodCall(decoded, callback); + return; + case 'Clipboard.getData': + ClipboardMessageHandler().getDataMethodCall(callback); + return; + } + break; + + case 'flutter/textinput': + textEditing.channel.handleTextInput(data, callback); + return; + + case 'flutter/mousecursor': + const MethodCodec codec = StandardMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + final Map arguments = decoded.arguments; + switch (decoded.method) { + case 'activateSystemCursor': + MouseCursor.instance.activateSystemCursor(arguments['kind']); + } + return; + + case 'flutter/web_test_e2e': + const MethodCodec codec = JSONMethodCodec(); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope( + _handleWebTestEnd2EndMessage(codec, data) + )); + return; + + case 'flutter/platform_views': + if (experimentalUseSkia) { + rasterizer.viewEmbedder.handlePlatformViewCall(data, callback); + } else { + handlePlatformViewCall(data, callback); + } + return; + + case 'flutter/accessibility': + // In widget tests we want to bypass processing of platform messages. + final StandardMessageCodec codec = StandardMessageCodec(); + accessibilityAnnouncements.handleMessage(codec, data); + _replyToPlatformMessage(callback, codec.encodeMessage(true)); + return; + + case 'flutter/navigation': + const MethodCodec codec = JSONMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + final Map message = decoded.arguments; + switch (decoded.method) { + case 'routeUpdated': + case 'routePushed': + case 'routeReplaced': + _browserHistory.setRouteName(message['routeName']); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + break; + case 'routePopped': + _browserHistory.setRouteName(message['previousRouteName']); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + break; + } + // As soon as Flutter starts taking control of the app navigation, we + // should reset [_initialRouteName] to "/" so it doesn't have any + // further effect after this point. + _initialRouteName = '/'; + return; + } + + if (pluginMessageCallHandler != null) { + pluginMessageCallHandler(name, data, callback); + return; + } + + // TODO(flutter_web): Some Flutter widgets send platform messages that we + // don't handle on web. So for now, let's just ignore them. In the future, + // we should consider uncommenting the following "callback(null)" line. + + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + // callback(null); + } + + int _getHapticFeedbackDuration(String type) { + switch (type) { + case 'HapticFeedbackType.lightImpact': + return DomRenderer.vibrateLightImpact; + case 'HapticFeedbackType.mediumImpact': + return DomRenderer.vibrateMediumImpact; + case 'HapticFeedbackType.heavyImpact': + return DomRenderer.vibrateHeavyImpact; + case 'HapticFeedbackType.selectionClick': + return DomRenderer.vibrateSelectionClick; + default: + return DomRenderer.vibrateLongPress; + } + } + + /// Requests that, at the next appropriate opportunity, the [onBeginFrame] + /// and [onDrawFrame] callbacks be invoked. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + void scheduleFrame() { + if (ui.webOnlyScheduleFrameCallback == null) { + throw new Exception( + 'webOnlyScheduleFrameCallback must be initialized first.'); + } + ui.webOnlyScheduleFrameCallback(); + } + + /// Updates the application's rendering on the GPU with the newly provided + /// [Scene]. This function must be called within the scope of the + /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function + /// is called a second time during a single [onBeginFrame]/[onDrawFrame] + /// callback sequence or called outside the scope of those callbacks, the call + /// will be ignored. + /// + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. + /// + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + @override + void render(ui.Scene/*!*/ scene, [ui.FlutterView view]) { + if (experimentalUseSkia) { + final LayerScene layerScene = scene; + rasterizer.draw(layerScene.layerTree); + } else { + final SurfaceScene surfaceScene = scene; + domRenderer.renderScene(surfaceScene.webOnlyRootElement); + } + } + + /// Additional accessibility features that may be enabled by the platform. + ui.AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; + + /// A callback that is invoked when the value of [accessibilityFeatures] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + ui.VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + ui.VoidCallback _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone; + set onAccessibilityFeaturesChanged(ui.VoidCallback callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnAccessibilityFeaturesChanged() { + _invoke(_onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + } + + /// Change the retained semantics data about this window. + /// + /// If [semanticsEnabled] is true, the user has requested that this function + /// be called whenever the semantic content of this window changes. + /// + /// In either case, this function disposes the given update, which means the + /// semantics update cannot be used further. + void updateSemantics(ui.SemanticsUpdate update) { + EngineSemanticsOwner.instance.updateSemantics(update); + } + + /// We use the first locale in the [locales] list instead of the browser's + /// built-in `navigator.language` because browsers do not agree on the + /// implementation. + /// + /// See also: + /// + /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, + /// which explains browser quirks in the implementation notes. + ui.Locale get locale => locales.first; + + /// The full system-reported supported locales of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// The list is ordered in order of priority, with lower-indexed locales being + /// preferred over higher-indexed ones. The first element is the primary [locale]. + /// + /// The [onLocaleChanged] callback is called whenever this value changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + List get locales => configuration.locales; + + /// The locale that the platform's native locale resolution system resolves to. + /// + /// This value may differ between platforms and is meant to allow Flutter's locale + /// resolution algorithms access to a locale that is consistent with other apps + /// on the device. Using this property is optional. + /// + /// This value may be used in a custom [localeListResolutionCallback] or used directly + /// in order to arrive at the most appropriate locale for the app. + /// + /// See [locales], which is the list of locales the user/device prefers. + ui.Locale get platformResolvedLocale => configuration.platformResolvedLocale; + + /// A callback that is invoked whenever [locale] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + ui.VoidCallback get onLocaleChanged => _onLocaleChanged; + ui.VoidCallback _onLocaleChanged; + Zone _onLocaleChangedZone; // ignore: unused_field + set onLocaleChanged(ui.VoidCallback callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// The locale used when we fail to get the list from the browser. + static const _defaultLocale = const ui.Locale('en', 'US'); + + /// Sets locales to an empty list. + /// + /// The empty list is not a valid value for locales. This is only used for + /// testing locale update logic. + void debugResetLocales() { + _configuration = _configuration.copyWith(locales: const []); + } + + // Called by DomRenderer when browser languages change. + void _updateLocales() { + _configuration = _configuration.copyWith(locales: parseBrowserLanguages()); + } + + static List parseBrowserLanguages() { + // TODO(yjbanov): find a solution for IE + final bool languagesFeatureMissing = !js_util.hasProperty(html.window.navigator, 'languages'); + if (languagesFeatureMissing || html.window.navigator.languages.isEmpty) { + // To make it easier for the app code, let's not leave the locales list + // empty. This way there's fewer corner cases for apps to handle. + return const [_defaultLocale]; + } + + final List locales = []; + for (final String language in html.window.navigator.languages) { + final List parts = language.split('-'); + if (parts.length > 1) { + locales.add(ui.Locale(parts.first, parts.last)); + } else { + locales.add(ui.Locale(language)); + } + } + + assert(locales.isNotEmpty); + return locales; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnLocaleChanged() { + _invoke(_onLocaleChanged, _onLocaleChangedZone); + } + + /// The lifecycle state immediately after dart isolate initialization. + /// + /// This property will not be updated as the lifecycle changes. + /// + /// It is used to initialize [SchedulerBinding.lifecycleState] at startup + /// with any buffered lifecycle state events. + String get initialLifecycleState => _initialLifecycleState; + String _initialLifecycleState; + + /// The system-reported text scale. + /// + /// This establishes the text scaling factor to use when rendering text, + /// according to the user's platform preferences. + /// + /// The [onTextScaleFactorChanged] callback is called whenever this value + /// changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + double get textScaleFactor => configuration.textScaleFactor; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + /// + /// This option is used by [showTimePicker]. + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + /// A callback that is invoked whenever [textScaleFactor] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + ui.VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; + ui.VoidCallback _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone; + set onTextScaleFactorChanged(ui.VoidCallback callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnTextScaleFactorChanged() { + _invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to [Brightness.light]. + ui.Brightness get platformBrightness => configuration.platformBrightness; + + /// Updates [_platformBrightness] and invokes [onPlatformBrightnessChanged] + /// callback if [_platformBrightness] changed. + void _updatePlatformBrightness(ui.Brightness value) { + if (configuration.platformBrightness != value && + onPlatformBrightnessChanged != null) { + _configuration = configuration.copyWith(platformBrightness: value); + invokeOnPlatformConfigurationChanged(); + invokeOnPlatformBrightnessChanged(); + } + } + + /// Reference to css media query that indicates the user theme preference on the web. + final html.MediaQueryList _brightnessMediaQuery = + html.window.matchMedia('(prefers-color-scheme: dark)'); + + /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. + /// + /// Updates the [_platformBrightness] with the new user preference. + html.EventListener _brightnessMediaQueryListener; + + /// Set the callback function for listening changes in [_brightnessMediaQuery] value. + void _addBrightnessMediaQueryListener() { + _updatePlatformBrightness(_brightnessMediaQuery.matches + ? ui.Brightness.dark + : ui.Brightness.light); + + _brightnessMediaQueryListener = (html.Event event) { + final html.MediaQueryListEvent mqEvent = event; + _updatePlatformBrightness( + mqEvent.matches ? ui.Brightness.dark : ui.Brightness.light); + }; + _brightnessMediaQuery.addListener(_brightnessMediaQueryListener); + registerHotRestartListener(() { + _removeBrightnessMediaQueryListener(); + }); + } + + /// Remove the callback function for listening changes in [_brightnessMediaQuery] value. + void _removeBrightnessMediaQueryListener() { + _brightnessMediaQuery.removeListener(_brightnessMediaQueryListener); + _brightnessMediaQueryListener = null; + } + + /// A callback that is invoked whenever [platformBrightness] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + ui.VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + ui.VoidCallback _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone; + set onPlatformBrightnessChanged(ui.VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformBrightnessChanged() { + _invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + + /// Whether the user has requested that [updateSemantics] be called when + /// the semantic contents of window changes. + /// + /// The [onSemanticsEnabledChanged] callback is called whenever this value + /// changes. + bool get semanticsEnabled => configuration.semanticsEnabled; + + /// A callback that is invoked when the value of [semanticsEnabled] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + ui.VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + ui.VoidCallback _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone; + set onSemanticsEnabledChanged(ui.VoidCallback callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsEnabledChanged() { + _invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + + /// A callback that is invoked whenever the user requests an action to be + /// performed. + /// + /// This callback is used when the user expresses the action they wish to + /// perform based on the semantics supplied by [updateSemantics]. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + ui.SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; + ui.SemanticsActionCallback _onSemanticsAction; + Zone _onSemanticsActionZone; + set onSemanticsAction(ui.SemanticsActionCallback callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsAction(int id, ui.SemanticsAction action, ByteData args) { + _invoke3(_onSemanticsAction, _onSemanticsActionZone, id, action, args); + } + + /// The route or path that the embedder requested when the application was + /// launched. + /// + /// This will be the string "`/`" if no particular route was requested. + /// + /// ## Android + /// + /// On Android, calling + /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `createFlutterView` method in your `FlutterActivity` + /// subclass is a suitable time to set the value. The application's + /// `AndroidManifest.xml` file must also be updated to have a suitable + /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). + /// + /// ## iOS + /// + /// On iOS, calling + /// [`FlutterViewController.setInitialRoute`](/objcdoc/Classes/FlutterViewController.html#/c:objc%28cs%29FlutterViewController%28im%29setInitialRoute:) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `application:didFinishLaunchingWithOptions:` method is a + /// suitable time to set this value. + /// + /// See also: + /// + /// * [Navigator], a widget that handles routing. + /// * [SystemChannels.navigation], which handles subsequent navigation + /// requests from the embedder. + @override + String get initialRouteName => _initialRouteName ??= _browserHistory.currentPath; + + /// Lazily initialized when the `initialRouteName` getter is invoked. + /// + /// The reason for the lazy initialization is to give enough time for the app + /// to set [locationStrategy] in `lib/src/ui/initialization.dart`. + String _initialRouteName; + + /// Handles the browser history integration to allow users to use the back + /// button, etc. + final BrowserHistory _browserHistory = BrowserHistory(); + + /// Simulates clicking the browser's back button. + Future webOnlyBack() => _browserHistory.back(); + + /// Change the strategy to use for handling browser history location. + /// Setting this member will automatically update [_browserHistory]. + /// + /// By setting this to null, the browser history will be disabled. + set locationStrategy(LocationStrategy strategy) { + _browserHistory.locationStrategy = strategy; + } + + @visibleForTesting + Rasterizer rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; + + /// In Flutter, platform messages are exchanged between threads so the + /// messages and responses have to be exchanged asynchronously. We simulate + /// that by adding a zero-length delay to the reply. + void _replyToPlatformMessage( + ui.PlatformMessageResponseCallback callback, + ByteData data, + ) { + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(data); + } + }); + } +} + +bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { + final MethodCall decoded = codec.decodeMethodCall(data); + double ratio = double.parse(decoded.arguments); + switch(decoded.method) { + case 'setDevicePixelRatio': + window.debugOverrideDevicePixelRatio(ratio); + window.onMetricsChanged(); + return true; + } + return false; +} + +/// Invokes [callback] inside the given [zone]. +void _invoke(void callback(), Zone zone) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(); + } else { + zone.runGuarded(callback); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg]. +void _invoke1(void callback(A a), Zone zone, A arg) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg); + } else { + zone.runUnaryGuarded(callback, arg); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. +void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2, arg3); + } else { + zone.runGuarded(() { + callback(arg1, arg2, arg3); + }); + } +} + diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index 48e43dc72db45..687f5d2493736 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -125,9 +125,7 @@ class PointerBinding { void _onPointerData(Iterable data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); - if (window._onPointerDataPacket != null) { - window.invokeOnPointerDataPacket(packet); - } + EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(packet); } } diff --git a/lib/web_ui/lib/src/engine/screen.dart b/lib/web_ui/lib/src/engine/screen.dart new file mode 100644 index 0000000000000..af12843ed1e42 --- /dev/null +++ b/lib/web_ui/lib/src/engine/screen.dart @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of engine; + +/// A class representing the screen that application windows are displayed on. +class EngineScreen extends ui.Screen { + EngineScreen({Object screenId, ui.PlatformDispatcher platformDispatcher}) + : _screenId = screenId, + _platformDispatcher = platformDispatcher; + + /// The opaque ID for this screen. + final Object _screenId; + + /// The platform dispatcher that this screen is registered with. + final ui.PlatformDispatcher _platformDispatcher; + + /// The configuration of this screen. + ui.ScreenConfiguration get configuration { + final EnginePlatformDispatcher engineDispatcher = _platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher._screenConfigurations.containsKey(_screenId)); + return engineDispatcher._screenConfigurations[_screenId]; + } +} diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index aec945a769b65..328c220b4f41d 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -53,11 +53,11 @@ class Incrementable extends RoleManager { final int newInputValue = int.parse(_element.value); if (newInputValue > _currentSurrogateValue) { _currentSurrogateValue += 1; - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.increase, null); } else if (newInputValue < _currentSurrogateValue) { _currentSurrogateValue -= 1; - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.decrease, null); } }); diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 0c818314a26f1..1f1607ff2e925 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -53,20 +53,20 @@ class Scrollable extends RoleManager { final int semanticsId = semanticsObject.id; if (doScrollForward) { if (semanticsObject.isVerticalScrollContainer) { - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollUp, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollLeft, null); } } else { if (semanticsObject.isVerticalScrollContainer) { - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollDown, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollRight, null); } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index d1808ce2c223a..789bcd1aaa294 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -1236,8 +1236,8 @@ class EngineSemanticsOwner { _gestureModeClock?.datetime = null; } - if (window._onSemanticsEnabledChanged != null) { - window.invokeOnSemanticsEnabledChanged(); + if (EnginePlatformDispatcher.instance._onSemanticsEnabledChanged != null) { + EnginePlatformDispatcher.instance.invokeOnSemanticsEnabledChanged(); } } diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index e60c9881918e5..dcf5e1891bf4e 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -39,7 +39,7 @@ class Tappable extends RoleManager { GestureMode.browserGestures) { return; } - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); }; element.addEventListener('click', _clickListener); diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 5018d8d2d5aa2..d3e542507af4c 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -149,7 +149,7 @@ class TextField extends RoleManager { } textEditing.useCustomEditableElement(textEditingElement); - window + EnginePlatformDispatcher.instance .invokeOnSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null); }); } @@ -187,7 +187,7 @@ class TextField extends RoleManager { if (offsetX * offsetX + offsetY * offsetY < kTouchSlop) { // Recognize it as a tap that requires a keyboard. - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); } } else { diff --git a/lib/web_ui/lib/src/engine/surface/path.dart b/lib/web_ui/lib/src/engine/surface/path.dart index de9b90a24d1f1..ff3cf726fc528 100644 --- a/lib/web_ui/lib/src/engine/surface/path.dart +++ b/lib/web_ui/lib/src/engine/surface/path.dart @@ -669,7 +669,7 @@ class SurfacePath implements ui.Path { // If device pixel ratio has changed we can't reuse prior raw recorder. if (_rawRecorder != null && _rawRecorder._devicePixelRatio != - EngineWindow.browserDevicePixelRatio) { + EnginePlatformDispatcher.browserDevicePixelRatio) { _rawRecorder = null; } final double dpr = window.devicePixelRatio; diff --git a/lib/web_ui/lib/src/engine/surface/render_vertices.dart b/lib/web_ui/lib/src/engine/surface/render_vertices.dart index 97ae71ec78f63..7e612e8b44550 100644 --- a/lib/web_ui/lib/src/engine/surface/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/surface/render_vertices.dart @@ -661,8 +661,8 @@ class _OffscreenCanvas { height: heightInPixels, ); _glCanvas.className = 'gl-canvas'; - final double cssWidth = widthInPixels / EngineWindow.browserDevicePixelRatio; - final double cssHeight = heightInPixels / EngineWindow.browserDevicePixelRatio; + final double cssWidth = widthInPixels / EnginePlatformDispatcher.browserDevicePixelRatio; + final double cssHeight = heightInPixels / EnginePlatformDispatcher.browserDevicePixelRatio; _glCanvas.style ..position = 'absolute' ..width = '${cssWidth}px' diff --git a/lib/web_ui/lib/src/engine/surface/surface_stats.dart b/lib/web_ui/lib/src/engine/surface/surface_stats.dart index 9bb6cab18a3b4..bc0a9ac767ecc 100644 --- a/lib/web_ui/lib/src/engine/surface/surface_stats.dart +++ b/lib/web_ui/lib/src/engine/surface/surface_stats.dart @@ -125,9 +125,9 @@ void _debugRepaintSurfaceStatsOverlay(PersistedScene scene) { ..fill(); final double physicalScreenWidth = - html.window.innerWidth * EngineWindow.browserDevicePixelRatio; + html.window.innerWidth * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicalScreenHeight = - html.window.innerHeight * EngineWindow.browserDevicePixelRatio; + html.window.innerHeight * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; @@ -296,9 +296,9 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { return pixels; }).fold(0, (int total, int pixels) => total + pixels); final double physicalScreenWidth = - html.window.innerWidth * EngineWindow.browserDevicePixelRatio; + html.window.innerWidth * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicalScreenHeight = - html.window.innerHeight * EngineWindow.browserDevicePixelRatio; + html.window.innerHeight * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; final double screenPixelRatio = pixelCount / physicsScreenPixelCount; diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 89b236db170cc..b8c44402819d6 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -183,8 +183,8 @@ class EngineAutofillForm { /// Sends the 'TextInputClient.updateEditingStateWithTag' message to the framework. void _sendAutofillEditingState(String tag, EditingState editingState) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( @@ -1048,13 +1048,13 @@ class TextEditingChannel { throw StateError( 'Unsupported method call on the flutter/textinput channel: ${call.method}'); } - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + EnginePlatformDispatcher.instance._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); } /// Sends the 'TextInputClient.updateEditingState' message to the framework. void updateEditingState(int clientId, EditingState editingState) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall('TextInputClient.updateEditingState', [ @@ -1069,8 +1069,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.performAction' message to the framework. void performAction(int clientId, String inputAction) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( @@ -1085,8 +1085,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.onConnectionClosed' message to the framework. void onConnectionClosed(int clientId) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 1f4e5c386aab5..a680a439d068e 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -485,13 +485,13 @@ final ByteData _fontChangeMessage = JSONMessageCodec().encodeMessage( sendFontChangeMessage() async { - if (window._onPlatformMessage != null) + if (EnginePlatformDispatcher.instance._onPlatformMessage != null) if (!_fontChangeScheduled) { _fontChangeScheduled = true; // Batch updates into next animationframe. html.window.requestAnimationFrame((num _) { _fontChangeScheduled = false; - window.invokeOnPlatformMessage( + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/system', _fontChangeMessage, (_) {}, diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index dc764de1e76ee..b376f7ee54ab5 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -13,9 +13,10 @@ const bool _debugPrintPlatformMessages = false; /// This may be overridden in tests, for example, to pump fake frames. ui.VoidCallback scheduleFrameCallback; -/// The Web implementation of [ui.Window]. -class EngineWindow extends ui.Window { - EngineWindow() { +/// The Web implementation of [ui.FlutterWindow]. +class EngineFlutterWindow extends ui.FlutterWindow { + EngineFlutterWindow({Object windowId, this.platformDispatcher}) + : _windowId = windowId { _addBrightnessMediaQueryListener(); js.context['_flutter_web_set_location_strategy'] = (LocationStrategy strategy) { locationStrategy = strategy; @@ -25,18 +26,45 @@ class EngineWindow extends ui.Window { }); } + final Object _windowId; + final ui.PlatformDispatcher platformDispatcher; + @override - double get devicePixelRatio => _debugDevicePixelRatio != null - ? _debugDevicePixelRatio - : browserDevicePixelRatio; + ui.ViewConfiguration get viewConfiguration { + final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher._windowConfigurations.containsKey(_windowId)); + return engineDispatcher._windowConfigurations[_windowId]; + } +} + +/// The Web implementation of [ui.SingletonFlutterWindow]. +class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { + EngineSingletonFlutterWindow({Object windowId, this.platformDispatcher}) + : _windowId = windowId { + final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; + final ui.Screen newScreen = EngineScreen(screenId: 0, platformDispatcher: platformDispatcher); + engineDispatcher._screens[0] = newScreen; + engineDispatcher._screenConfigurations[0] = ui.ScreenConfiguration(); + engineDispatcher._windows[windowId] = this; + engineDispatcher._windowConfigurations[windowId] = ui.ViewConfiguration(screen: newScreen); + } + + final Object _windowId; + + final ui.PlatformDispatcher platformDispatcher; - /// Returns device pixel ratio returned by browser. - static double get browserDevicePixelRatio { - double ratio = html.window.devicePixelRatio; - // Guard against WebOS returning 0. - return (ratio == null || ratio == 0.0) ? 1.0 : ratio; + @override + ui.ViewConfiguration get viewConfiguration { + final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher._windowConfigurations.containsKey(_windowId)); + return engineDispatcher._windowConfigurations[_windowId]; } + @override + double get devicePixelRatio => _debugDevicePixelRatio != null + ? _debugDevicePixelRatio + : EnginePlatformDispatcher.browserDevicePixelRatio; + /// Overrides the default device pixel ratio. /// /// This is useful in tests to emulate screens of different dimensions. @@ -147,25 +175,18 @@ class EngineWindow extends ui.Window { /// Overrides the value of [physicalSize] in tests. ui.Size webOnlyDebugPhysicalSizeOverride; +} - @override - double get physicalDepth => double.maxFinite; - - /// Handles the browser history integration to allow users to use the back - /// button, etc. - final BrowserHistory _browserHistory = BrowserHistory(); - - /// Simulates clicking the browser's back button. - Future webOnlyBack() => _browserHistory.back(); - - /// Lazily initialized when the `defaultRouteName` getter is invoked. - /// - /// The reason for the lazy initialization is to give enough time for the app to set [locationStrategy] - /// in `lib/src/ui/initialization.dart`. - String _defaultRouteName; +/// A type of [FlutterView] that can be hosted inside of a [FlutterWindow]. +/// +/// A window view can only be created by [PlatformDispatcher.createView] where +/// the requested configuration includes a parent [FlutterWindow] set as the +/// [ViewConfiguration.window]. +class EngineFlutterWindowView extends ui.FlutterWindowView { + EngineFlutterWindowView._({Object viewId, this.platformDispatcher}) + : _viewId = viewId; - @override - String/*!*/ get defaultRouteName => _defaultRouteName ??= _browserHistory.currentPath; + final Object _viewId; @override void scheduleFrame() { @@ -184,600 +205,17 @@ class EngineWindow extends ui.Window { _browserHistory.locationStrategy = strategy; } + final ui.PlatformDispatcher platformDispatcher; + /// Returns the currently active location strategy. @visibleForTesting LocationStrategy get locationStrategy => _browserHistory.locationStrategy; @override - ui.VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; - ui.VoidCallback _onTextScaleFactorChanged; - Zone _onTextScaleFactorChangedZone; - @override - set onTextScaleFactorChanged(ui.VoidCallback callback) { - _onTextScaleFactorChanged = callback; - _onTextScaleFactorChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnTextScaleFactorChanged() { - _invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); - } - - @override - ui.VoidCallback get onPlatformBrightnessChanged => - _onPlatformBrightnessChanged; - ui.VoidCallback _onPlatformBrightnessChanged; - Zone _onPlatformBrightnessChangedZone; - @override - set onPlatformBrightnessChanged(ui.VoidCallback callback) { - _onPlatformBrightnessChanged = callback; - _onPlatformBrightnessChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPlatformBrightnessChanged() { - _invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); - } - - @override - ui.VoidCallback/*?*/ get onMetricsChanged => _onMetricsChanged; - ui.VoidCallback/*?*/ _onMetricsChanged; - Zone/*!*/ _onMetricsChangedZone = Zone.root; - @override - set onMetricsChanged(ui.VoidCallback callback) { - _onMetricsChanged = callback; - _onMetricsChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnMetricsChanged() { - if (window._onMetricsChanged != null) { - _invoke(_onMetricsChanged, _onMetricsChangedZone); - } - } - - @override - ui.VoidCallback get onLocaleChanged => _onLocaleChanged; - ui.VoidCallback _onLocaleChanged; - Zone _onLocaleChangedZone; - @override - set onLocaleChanged(ui.VoidCallback callback) { - _onLocaleChanged = callback; - _onLocaleChangedZone = Zone.current; - } - - /// The locale used when we fail to get the list from the browser. - static const _defaultLocale = const ui.Locale('en', 'US'); - - /// We use the first locale in the [locales] list instead of the browser's - /// built-in `navigator.language` because browsers do not agree on the - /// implementation. - /// - /// See also: - /// - /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, - /// which explains browser quirks in the implementation notes. - @override - ui.Locale get locale => _locales.first; - - @override - List get locales => _locales; - List _locales = parseBrowserLanguages(); - - /// Sets locales to `null`. - /// - /// `null` is not a valid value for locales. This is only used for testing - /// locale update logic. - void debugResetLocales() { - _locales = null; - } - - // Called by DomRenderer when browser languages change. - void _updateLocales() { - _locales = parseBrowserLanguages(); - } - - static List parseBrowserLanguages() { - // TODO(yjbanov): find a solution for IE - final bool languagesFeatureMissing = !js_util.hasProperty(html.window.navigator, 'languages'); - if (languagesFeatureMissing || html.window.navigator.languages.isEmpty) { - // To make it easier for the app code, let's not leave the locales list - // empty. This way there's fewer corner cases for apps to handle. - return const [_defaultLocale]; - } - - final List locales = []; - for (final String language in html.window.navigator.languages) { - final List parts = language.split('-'); - if (parts.length > 1) { - locales.add(ui.Locale(parts.first, parts.last)); - } else { - locales.add(ui.Locale(language)); - } - } - - assert(locales.isNotEmpty); - return locales; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnLocaleChanged() { - _invoke(_onLocaleChanged, _onLocaleChangedZone); - } - - @override - ui.FrameCallback get onBeginFrame => _onBeginFrame; - ui.FrameCallback _onBeginFrame; - Zone _onBeginFrameZone; - @override - set onBeginFrame(ui.FrameCallback callback) { - _onBeginFrame = callback; - _onBeginFrameZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnBeginFrame(Duration duration) { - _invoke1(_onBeginFrame, _onBeginFrameZone, duration); - } - - @override - ui.TimingsCallback get onReportTimings => _onReportTimings; - ui.TimingsCallback _onReportTimings; - Zone _onReportTimingsZone; - @override - set onReportTimings(ui.TimingsCallback callback) { - _onReportTimings = callback; - _onReportTimingsZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnReportTimings(List timings) { - _invoke1>( - _onReportTimings, _onReportTimingsZone, timings); - } - - @override - ui.VoidCallback get onDrawFrame => _onDrawFrame; - ui.VoidCallback _onDrawFrame; - Zone _onDrawFrameZone; - @override - set onDrawFrame(ui.VoidCallback callback) { - _onDrawFrame = callback; - _onDrawFrameZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnDrawFrame() { - _invoke(_onDrawFrame, _onDrawFrameZone); - } - - @override - ui.PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; - ui.PointerDataPacketCallback _onPointerDataPacket; - Zone _onPointerDataPacketZone; - @override - set onPointerDataPacket(ui.PointerDataPacketCallback callback) { - _onPointerDataPacket = callback; - _onPointerDataPacketZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPointerDataPacket(ui.PointerDataPacket packet) { - _invoke1( - _onPointerDataPacket, _onPointerDataPacketZone, packet); - } - - @override - ui.VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - ui.VoidCallback _onSemanticsEnabledChanged; - Zone _onSemanticsEnabledChangedZone; - @override - set onSemanticsEnabledChanged(ui.VoidCallback callback) { - _onSemanticsEnabledChanged = callback; - _onSemanticsEnabledChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnSemanticsEnabledChanged() { - _invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); - } - - @override - ui.SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; - ui.SemanticsActionCallback _onSemanticsAction; - Zone _onSemanticsActionZone; - @override - set onSemanticsAction(ui.SemanticsActionCallback callback) { - _onSemanticsAction = callback; - _onSemanticsActionZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnSemanticsAction( - int id, ui.SemanticsAction action, ByteData args) { - _invoke3( - _onSemanticsAction, _onSemanticsActionZone, id, action, args); - } - - @override - ui.VoidCallback get onAccessibilityFeaturesChanged => - _onAccessibilityFeaturesChanged; - ui.VoidCallback _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFeaturesChangedZone; - @override - set onAccessibilityFeaturesChanged(ui.VoidCallback callback) { - _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFeaturesChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnAccessibilityFeaturesChanged() { - _invoke( - _onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); - } - - @override - ui.PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; - ui.PlatformMessageCallback _onPlatformMessage; - Zone _onPlatformMessageZone; - @override - set onPlatformMessage(ui.PlatformMessageCallback callback) { - _onPlatformMessage = callback; - _onPlatformMessageZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPlatformMessage( - String name, ByteData data, ui.PlatformMessageResponseCallback callback) { - _invoke3( - _onPlatformMessage, - _onPlatformMessageZone, - name, - data, - callback, - ); - } - - @override - void sendPlatformMessage( - String/*!*/ name, - ByteData/*?*/ data, - ui.PlatformMessageResponseCallback/*?*/ callback, - ) { - _sendPlatformMessage( - name, data, _zonedPlatformMessageResponseCallback(callback)); - } - - /// Wraps the given [callback] in another callback that ensures that the - /// original callback is called in the zone it was registered in. - static ui.PlatformMessageResponseCallback/*?*/ _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback/*?*/ callback) { - if (callback == null) { - return null; - } - - // Store the zone in which the callback is being registered. - final Zone registrationZone = Zone.current; - - return (ByteData data) { - registrationZone.runUnaryGuarded(callback, data); - }; - } - - void _sendPlatformMessage( - String/*!*/ name, - ByteData/*?*/ data, - ui.PlatformMessageResponseCallback/*?*/ callback, - ) { - // In widget tests we want to bypass processing of platform messages. - if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { - return; - } - - if (_debugPrintPlatformMessages) { - print('Sent platform message on channel: "$name"'); - } - - if (assertionsEnabled && name == 'flutter/debug-echo') { - // Echoes back the data unchanged. Used for testing purpopses. - _replyToPlatformMessage(callback, data); - return; - } - - switch (name) { - case 'flutter/assets': - assert(ui.webOnlyAssetManager != null); - final String url = utf8.decode(data.buffer.asUint8List()); - ui.webOnlyAssetManager.load(url).then((ByteData assetData) { - _replyToPlatformMessage(callback, assetData); - }, onError: (dynamic error) { - html.window.console - .warn('Error while trying to load an asset: $error'); - _replyToPlatformMessage(callback, null); - }); - return; - - case 'flutter/platform': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - switch (decoded.method) { - case 'SystemNavigator.pop': - _browserHistory.exit().then((_) { - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - }); - return; - case 'HapticFeedback.vibrate': - final String type = decoded.arguments; - domRenderer.vibrate(_getHapticFeedbackDuration(type)); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'SystemChrome.setApplicationSwitcherDescription': - final Map arguments = decoded.arguments; - domRenderer.setTitle(arguments['label']); - domRenderer.setThemeColor(ui.Color(arguments['primaryColor'])); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'SystemChrome.setPreferredOrientations': - final List arguments = decoded.arguments; - domRenderer.setPreferredOrientation(arguments).then((bool success) { - _replyToPlatformMessage(callback, - codec.encodeSuccessEnvelope(success)); - }); - return; - case 'SystemSound.play': - // There are no default system sounds on web. - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'Clipboard.setData': - ClipboardMessageHandler().setDataMethodCall(decoded, callback); - return; - case 'Clipboard.getData': - ClipboardMessageHandler().getDataMethodCall(callback); - return; - } - break; - - case 'flutter/textinput': - textEditing.channel.handleTextInput(data, callback); - return; - - case 'flutter/mousecursor': - const MethodCodec codec = StandardMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - final Map arguments = decoded.arguments; - switch (decoded.method) { - case 'activateSystemCursor': - MouseCursor.instance.activateSystemCursor(arguments['kind']); - } - return; - - case 'flutter/web_test_e2e': - const MethodCodec codec = JSONMethodCodec(); - _replyToPlatformMessage( - callback, - codec.encodeSuccessEnvelope( - _handleWebTestEnd2EndMessage(codec, data))); - return; - - case 'flutter/platform_views': - if (experimentalUseSkia) { - rasterizer.viewEmbedder.handlePlatformViewCall(data, callback); - } else { - ui.handlePlatformViewCall(data, callback); - } - return; - - case 'flutter/accessibility': - // In widget tests we want to bypass processing of platform messages. - final StandardMessageCodec codec = StandardMessageCodec(); - accessibilityAnnouncements.handleMessage(codec, data); - _replyToPlatformMessage(callback, codec.encodeMessage(true)); - return; - - case 'flutter/navigation': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - final Map message = decoded.arguments; - switch (decoded.method) { - case 'routeUpdated': - case 'routePushed': - case 'routeReplaced': - _browserHistory.setRouteName(message['routeName']); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - break; - case 'routePopped': - _browserHistory.setRouteName(message['previousRouteName']); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - break; - } - // As soon as Flutter starts taking control of the app navigation, we - // should reset [_defaultRouteName] to "/" so it doesn't have any - // further effect after this point. - _defaultRouteName = '/'; - return; - } - - if (pluginMessageCallHandler != null) { - pluginMessageCallHandler(name, data, callback); - return; - } - - // TODO(flutter_web): Some Flutter widgets send platform messages that we - // don't handle on web. So for now, let's just ignore them. In the future, - // we should consider uncommenting the following "callback(null)" line. - - // Passing [null] to [callback] indicates that the platform message isn't - // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is - // handled. - // callback(null); - } - - int _getHapticFeedbackDuration(String type) { - switch (type) { - case 'HapticFeedbackType.lightImpact': - return DomRenderer.vibrateLightImpact; - case 'HapticFeedbackType.mediumImpact': - return DomRenderer.vibrateMediumImpact; - case 'HapticFeedbackType.heavyImpact': - return DomRenderer.vibrateHeavyImpact; - case 'HapticFeedbackType.selectionClick': - return DomRenderer.vibrateSelectionClick; - default: - return DomRenderer.vibrateLongPress; - } - } - - /// In Flutter, platform messages are exchanged between threads so the - /// messages and responses have to be exchanged asynchronously. We simulate - /// that by adding a zero-length delay to the reply. - void _replyToPlatformMessage( - ui.PlatformMessageResponseCallback callback, - ByteData data, - ) { - Future.delayed(Duration.zero).then((_) { - if (callback != null) { - callback(data); - } - }); - } - - @override - ui.Brightness get platformBrightness => _platformBrightness; - ui.Brightness _platformBrightness = ui.Brightness.light; - - /// Updates [_platformBrightness] and invokes [onPlatformBrightnessChanged] - /// callback if [_platformBrightness] changed. - void _updatePlatformBrightness(ui.Brightness newPlatformBrightness) { - ui.Brightness previousPlatformBrightness = _platformBrightness; - _platformBrightness = newPlatformBrightness; - - if (previousPlatformBrightness != _platformBrightness && - onPlatformBrightnessChanged != null) { - invokeOnPlatformBrightnessChanged(); - } - } - - /// Reference to css media query that indicates the user theme preference on the web. - final html.MediaQueryList _brightnessMediaQuery = - html.window.matchMedia('(prefers-color-scheme: dark)'); - - /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. - /// - /// Updates the [_platformBrightness] with the new user preference. - html.EventListener _brightnessMediaQueryListener; - - /// Set the callback function for listening changes in [_brightnessMediaQuery] value. - void _addBrightnessMediaQueryListener() { - _updatePlatformBrightness(_brightnessMediaQuery.matches - ? ui.Brightness.dark - : ui.Brightness.light); - - _brightnessMediaQueryListener = (html.Event event) { - final html.MediaQueryListEvent mqEvent = event; - _updatePlatformBrightness( - mqEvent.matches ? ui.Brightness.dark : ui.Brightness.light); - }; - _brightnessMediaQuery.addListener(_brightnessMediaQueryListener); - registerHotRestartListener(() { - _removeBrightnessMediaQueryListener(); - }); - } - - /// Remove the callback function for listening changes in [_brightnessMediaQuery] value. - void _removeBrightnessMediaQueryListener() { - _brightnessMediaQuery.removeListener(_brightnessMediaQueryListener); - _brightnessMediaQueryListener = null; - } - - @override - void render(ui.Scene/*!*/ scene) { - if (experimentalUseSkia) { - final LayerScene layerScene = scene; - rasterizer.draw(layerScene.layerTree); - } else { - final SurfaceScene surfaceScene = scene; - domRenderer.renderScene(surfaceScene.webOnlyRootElement); - } - } - - @visibleForTesting - Rasterizer rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; -} - -bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { - final MethodCall decoded = codec.decodeMethodCall(data); - double ratio = double.parse(decoded.arguments); - switch (decoded.method) { - case 'setDevicePixelRatio': - window.debugOverrideDevicePixelRatio(ratio); - window.onMetricsChanged(); - return true; - } - return false; -} - -/// Invokes [callback] inside the given [zone]. -void _invoke(void callback(), Zone zone) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(); - } else { - zone.runGuarded(callback); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg]. -void _invoke1(void callback(A a), Zone zone, A arg) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg); - } else { - zone.runUnaryGuarded(callback, arg); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3( - void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg1, arg2, arg3); - } else { - zone.runGuarded(() { - callback(arg1, arg2, arg3); - }); + ui.ViewConfiguration get viewConfiguration { + final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher._windowConfigurations.containsKey(_viewId)); + return engineDispatcher._windowConfigurations[_viewId]; } } @@ -786,7 +224,7 @@ void _invoke3( /// `dart:ui` window delegates to this value. However, this value has a wider /// API surface, providing Web-specific functionality that the standard /// `dart:ui` version does not. -final EngineWindow/*!*/ window = EngineWindow(); +final EngineSingletonFlutterWindow/*!*/ window = EngineSingletonFlutterWindow(windowId: 0, platformDispatcher: EnginePlatformDispatcher.instance); /// The Web implementation of [ui.WindowPadding]. class WindowPadding implements ui.WindowPadding { diff --git a/lib/web_ui/lib/src/ui/compositing.dart b/lib/web_ui/lib/src/ui/compositing.dart index 635dd72615864..8ab00f38cd413 100644 --- a/lib/web_ui/lib/src/ui/compositing.dart +++ b/lib/web_ui/lib/src/ui/compositing.dart @@ -10,7 +10,7 @@ part of ui; /// To create a Scene object, use a [SceneBuilder]. /// /// Scene objects can be displayed on the screen using the -/// [Window.render] method. +/// [FlutterWindow.render] method. abstract class Scene { /// Creates a raster image representation of the current state of the scene. /// This is a slow operation that is performed on a background thread. @@ -105,7 +105,7 @@ abstract class PhysicalShapeEngineLayer implements EngineLayer {} /// Builds a [Scene] containing the given visuals. /// -/// A [Scene] can then be rendered using [Window.render]. +/// A [Scene] can then be rendered using [FlutterWindow.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add @@ -297,8 +297,8 @@ abstract class SceneBuilder { /// /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call - /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "raster + /// [FlutterWindow.render]). The UI thread frame time is the total time + /// spent executing the [FlutterWindow.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. @@ -400,7 +400,7 @@ abstract class SceneBuilder { /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [Window.render]. + /// screen with [FlutterWindow.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. diff --git a/lib/web_ui/lib/src/ui/initialization.dart b/lib/web_ui/lib/src/ui/initialization.dart index 9fa6810f25e8e..9959b18a8b4de 100644 --- a/lib/web_ui/lib/src/ui/initialization.dart +++ b/lib/web_ui/lib/src/ui/initialization.dart @@ -23,7 +23,7 @@ Future _initializePlatform({ engine.AssetManager? assetManager, }) async { if (!debugEmulateFlutterTesterEnvironment) { - engine.window.locationStrategy = const engine.HashLocationStrategy(); + engine.EnginePlatformDispatcher.instance.locationStrategy = const engine.HashLocationStrategy(); } engine.initializeEngine(); diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart new file mode 100644 index 0000000000000..6fc5018f8677a --- /dev/null +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -0,0 +1,310 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of ui; + +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); +typedef ViewCreatedCallback = void Function(FlutterView view); +typedef ViewDisposedCallback = void Function(FlutterView view); + +abstract class PlatformDispatcher { + static PlatformDispatcher get instance => engine.EnginePlatformDispatcher.instance; + + PlatformConfiguration get configuration; + VoidCallback get onPlatformConfigurationChanged; + set onPlatformConfigurationChanged(VoidCallback callback); + + Iterable get screens; + + Iterable get views; + ViewCreatedCallback get onViewCreated; + set onViewCreated(ViewCreatedCallback callback); + ViewDisposedCallback get onViewDisposed; + set onViewDisposed(ViewDisposedCallback callback); + Future createView(ViewConfigurationRequest request); + Future configureView(FlutterView view, ViewConfigurationRequest configuration); + Future disposeView(FlutterView view); + + VoidCallback get onMetricsChanged; + set onMetricsChanged(VoidCallback callback); + + FrameCallback get onBeginFrame; + set onBeginFrame(FrameCallback callback); + + VoidCallback get onDrawFrame; + set onDrawFrame(VoidCallback callback); + + PointerDataPacketCallback get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback callback); + + TimingsCallback get onReportTimings; + set onReportTimings(TimingsCallback callback); + + void sendPlatformMessage( + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ); + + PlatformMessageCallback get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback callback); + + void scheduleFrame() { + if (webOnlyScheduleFrameCallback == null) { + throw new Exception('webOnlyScheduleFrameCallback must be initialized first.'); + } + webOnlyScheduleFrameCallback(); + } + + void render(Scene scene, [FlutterView view]); + + AccessibilityFeatures get accessibilityFeatures; + + VoidCallback get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback callback); + + void updateSemantics(SemanticsUpdate update); + + Locale get locale; + + List get locales => configuration.locales; + + Locale get platformResolvedLocale => configuration.platformResolvedLocale; + + VoidCallback get onLocaleChanged; + set onLocaleChanged(VoidCallback callback); + + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + double get textScaleFactor => configuration.textScaleFactor; + + VoidCallback get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback callback); + + Brightness get platformBrightness => configuration.platformBrightness; + + VoidCallback get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback callback); + + bool get semanticsEnabled => configuration.semanticsEnabled; + + VoidCallback get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback callback); + + SemanticsActionCallback get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback callback); + + String get initialRouteName; + + void setIsolateDebugName(String name) {} + + ByteData getPersistentIsolateData() => null; + + String get initialLifecycleState; +} + +VoidCallback webOnlyScheduleFrameCallback; + +class PlatformConfiguration { + const PlatformConfiguration({ + this.accessibilityFeatures = const AccessibilityFeatures._(0), + this.alwaysUse24HourFormat = false, + this.semanticsEnabled = false, + this.platformBrightness = Brightness.light, + this.textScaleFactor = 1.0, + this.locales = const [], + this.platformResolvedLocale, + this.initialRouteName, + }) : assert(alwaysUse24HourFormat != null), + assert(semanticsEnabled != null), + assert(platformBrightness != null), + assert(textScaleFactor != null); + + PlatformConfiguration copyWith({ + AccessibilityFeatures accessibilityFeatures, + bool alwaysUse24HourFormat, + bool semanticsEnabled, + Brightness platformBrightness, + double textScaleFactor, + List locales, + Locale platformResolvedLocale, + String initialRouteName, + }) { + return PlatformConfiguration( + accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, + alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, + semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, + platformBrightness: platformBrightness ?? this.platformBrightness, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + locales: locales ?? this.locales, + platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, + initialRouteName: initialRouteName ?? this.initialRouteName, + ); + } + + final AccessibilityFeatures accessibilityFeatures; + final bool alwaysUse24HourFormat; + final bool semanticsEnabled; + final Brightness platformBrightness; + final double textScaleFactor; + final List locales; + final Locale platformResolvedLocale; + final String initialRouteName; +} + +class ScreenConfiguration { + const ScreenConfiguration({ + this.screenName = '', + this.geometry = Rect.zero, + this.devicePixelRatio = 1.0, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + }) : assert(screenName != null), + assert(geometry != null), + assert(devicePixelRatio != null), + assert(viewInsets != null), + assert(systemGestureInsets != null), + assert(padding != null); + + ScreenConfiguration copyWith({ + String screenName, + Rect geometry, + double devicePixelRatio, + WindowPadding viewInsets, + WindowPadding viewPadding, + WindowPadding systemGestureInsets, + WindowPadding padding, + }) { + return ScreenConfiguration( + screenName: screenName ?? this.screenName, + geometry: geometry ?? this.geometry, + devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + ); + } + + final String screenName; + final Rect geometry; + final double devicePixelRatio; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; +} + + +class ViewConfigurationRequest { + const ViewConfigurationRequest({ + this.screen, + this.geometry, + this.visible, + this.order, + this.orderView, + }) : assert(orderView != null || (order != ViewOrder.aboveOther && order != ViewOrder.belowOther)), + assert(orderView == null || (order != ViewOrder.top && order != ViewOrder.bottom)), + assert(screen != null || geometry != null || order != null || visible != null, 'At least one parameter must be non-null'); + + ViewConfigurationRequest copyWith({ + Screen screen, + Rect geometry, + bool visible, + ViewOrder order, + FlutterView orderView, + }) { + return ViewConfigurationRequest( + screen: screen ?? this.screen, + geometry: geometry ?? this.geometry, + visible: visible ?? this.visible, + order: order ?? this.order, + orderView: orderView ?? this.orderView, + ); + } + + final Screen screen; + final Rect geometry; + final bool visible; + final ViewOrder order; + final FlutterView orderView; + + @override + String toString() { + return '$runtimeType[screen: $screen, geometry: $geometry, order: $order]'; + } +} + +enum ViewOrder { + aboveOther, + + belowOther, + + top, + + bottom, +} + +class ViewConfiguration { + const ViewConfiguration({ + this.screen, + this.window, + this.geometry = Rect.zero, + this.depth = double.maxFinite, + this.visible = false, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + }) : assert(screen != null), + assert(geometry != null), + assert(depth != null), + assert(visible != null), + assert(viewInsets != null), + assert(viewPadding != null), + assert(systemGestureInsets != null), + assert(padding != null); + + ViewConfiguration copyWith({ + Screen screen, + FlutterWindow window, + Rect geometry, + double depth, + bool visible, + WindowPadding viewInsets, + WindowPadding viewPadding, + WindowPadding systemGestureInsets, + WindowPadding padding, + }) { + return ViewConfiguration( + screen: screen ?? this.screen, + window: window ?? this.window, + geometry: geometry ?? this.geometry, + depth: depth ?? this.depth, + visible: visible ?? this.visible, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + ); + } + + final Screen screen; + final FlutterWindow window; + final Rect geometry; + final double depth; + final bool visible; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; + + @override + String toString() { + return '$runtimeType[screen: $screen, window: $window, geometry: $geometry, depth: $depth]'; + } +} diff --git a/lib/web_ui/lib/src/ui/screen.dart b/lib/web_ui/lib/src/ui/screen.dart new file mode 100644 index 0000000000000..125d89665ad9f --- /dev/null +++ b/lib/web_ui/lib/src/ui/screen.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of ui; + +/// A class representing the screen that application windows are displayed on. +abstract class Screen { + /// The configuration of this screen. + ScreenConfiguration get configuration; +} diff --git a/lib/web_ui/lib/src/ui/semantics.dart b/lib/web_ui/lib/src/ui/semantics.dart index c6dffe2fc1986..141fea1748e5a 100644 --- a/lib/web_ui/lib/src/ui/semantics.dart +++ b/lib/web_ui/lib/src/ui/semantics.dart @@ -610,7 +610,7 @@ class SemanticsFlag { /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [Window.updateSemantics] to update the semantics conveyed to the user. +/// [FlutterWindow.updateSemantics] to update the semantics conveyed to the user. class SemanticsUpdateBuilder { /// Creates an empty [SemanticsUpdateBuilder] object. SemanticsUpdateBuilder(); @@ -638,9 +638,9 @@ class SemanticsUpdateBuilder { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [Window.onSemanticsAction] will be called with `id` and one of + /// node, the [FlutterWindow.onSemanticsAction] will be called with `id` and one of /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [Window.onSemanticsAction] callback might be called + /// asynchronously, the [FlutterWindow.onSemanticsAction] callback might be called /// with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property @@ -734,7 +734,7 @@ class SemanticsUpdateBuilder { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [Window.updateSemantics] to actually + /// The returned object can be passed to [FlutterWindow.updateSemantics] to actually /// update the semantics retained by the system. SemanticsUpdate build() { return SemanticsUpdate._( @@ -748,7 +748,7 @@ class SemanticsUpdateBuilder { /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree -/// using the [Window.updateSemantics] method. +/// using the [FlutterWindow.updateSemantics] method. abstract class SemanticsUpdate { /// This class is created by the engine, and should not be instantiated /// or extended directly. diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 9fe24e78323ee..86464bba6e185 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1,7 +1,6 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Synced 2019-05-30T14:20:57.833907. // @dart = 2.9 part of ui; diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index aab9bdc6a1349..7c7ef1ee19235 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -1,106 +1,26 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Synced 2019-05-30T14:20:57.841444. // @dart = 2.9 part of ui; -/// Signature of callbacks that have no arguments and return no data. typedef VoidCallback = void Function(); - -/// Signature for frame-related callbacks from the scheduler. -/// -/// The `timeStamp` is the number of milliseconds since the beginning of the -/// scheduler's epoch. Use timeStamp to determine how far to advance animation -/// timelines so that all the animations in the system are synchronized to a -/// common time base. typedef FrameCallback = void Function(Duration duration); - -/// Signature for [Window.onReportTimings]. typedef TimingsCallback = void Function(List timings); - -/// Signature for [Window.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); - -/// Signature for [Window.onSemanticsAction]. -typedef SemanticsActionCallback = void Function( - int id, SemanticsAction action, ByteData? args); - -/// Signature for responses to platform messages. -/// -/// Used as a parameter to [Window.sendPlatformMessage] and -/// [Window.onPlatformMessage]. +typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); typedef PlatformMessageResponseCallback = void Function(ByteData? data); - -/// Signature for [Window.onPlatformMessage]. typedef PlatformMessageCallback = void Function( String name, ByteData? data, PlatformMessageResponseCallback? callback); -/// States that an application can be in. -/// -/// The values below describe notifications from the operating system. -/// Applications should not expect to always receive all possible -/// notifications. For example, if the users pulls out the battery from the -/// device, no notification will be sent before the application is suddenly -/// terminated, along with the rest of the operating system. -/// -/// See also: -/// -/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state -/// from the widgets layer. enum AppLifecycleState { - /// The application is visible and responding to user input. resumed, - - /// The application is in an inactive state and is not receiving user input. - /// - /// On iOS, this state corresponds to an app or the Flutter host view running - /// in the foreground inactive state. Apps transition to this state when in - /// a phone call, responding to a TouchID request, when entering the app - /// switcher or the control center, or when the UIViewController hosting the - /// Flutter app is transitioning. - /// - /// On Android, this corresponds to an app or the Flutter host view running - /// in the foreground inactive state. Apps transition to this state when - /// another activity is focused, such as a split-screen app, a phone call, - /// a picture-in-picture app, a system dialog, or another window. - /// - /// Apps in this state should assume that they may be [paused] at any time. inactive, - - /// The application is not currently visible to the user, not responding to - /// user input, and running in the background. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. paused, - - /// The application is detached from view. - /// - /// When the application is in this state, the engine is running without - /// a platform UI. detached, } -/// A representation of distances for each of the four edges of a rectangle, -/// used to encode the view insets and padding that applications should place -/// around their user interface, as exposed by [Window.viewInsets] and -/// [Window.padding]. View insets and padding are preferably read via -/// [MediaQuery.of]. -/// -/// For the engine implementation of this class see the [engine.WindowPadding]. -/// -/// For a generic class that represents distances around a rectangle, see the -/// [EdgeInsets] class. -/// -/// See also: -/// -/// * [WidgetsBindingObserver], for a widgets layer mechanism to receive -/// notifications when the padding changes. -/// * [MediaQuery.of], for the preferred mechanism for accessing these values. -/// * [Scaffold], which automatically applies the padding in material design -/// applications. abstract class WindowPadding { const factory WindowPadding._( {required double left, @@ -108,25 +28,11 @@ abstract class WindowPadding { required double right, required double bottom}) = engine.WindowPadding; - /// The distance from the left edge to the first unpadded pixel, in physical - /// pixels. double get left; - - /// The distance from the top edge to the first unpadded pixel, in physical - /// pixels. double get top; - - /// The distance from the right edge to the first unpadded pixel, in physical - /// pixels. double get right; - - /// The distance from the bottom edge to the first unpadded pixel, in physical - /// pixels. double get bottom; - - /// A window padding that has zeros for each edge. - static const WindowPadding zero = - WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); + static const WindowPadding zero = WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); @override String toString() { @@ -134,56 +40,7 @@ abstract class WindowPadding { } } -/// An identifier used to select a user's language and formatting preferences. -/// -/// This represents a [Unicode Language -/// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) -/// (i.e. without Locale extensions), except variants are not supported. -/// -/// Locales are canonicalized according to the "preferred value" entries in the -/// [IANA Language Subtag -/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). -/// For example, `const Locale('he')` and `const Locale('iw')` are equal and -/// both have the [languageCode] `he`, because `iw` is a deprecated language -/// subtag that was replaced by the subtag `he`. -/// -/// See also: -/// -/// * [Window.locale], which specifies the system's currently selected -/// [Locale]. class Locale { - /// Creates a new Locale object. The first argument is the - /// primary language subtag, the second is the region (also - /// referred to as 'country') subtag. - /// - /// For example: - /// - /// ```dart - /// const Locale swissFrench = const Locale('fr', 'CH'); - /// const Locale canadianFrench = const Locale('fr', 'CA'); - /// ``` - /// - /// The primary language subtag must not be null. The region subtag is - /// optional. When there is no region/country subtag, the parameter should - /// be omitted or passed `null` instead of an empty-string. - /// - /// The subtag values are _case sensitive_ and must be one of the valid - /// subtags according to CLDR supplemental data: - /// [language](http://unicode.org/cldr/latest/common/validity/language.xml), - /// [region](http://unicode.org/cldr/latest/common/validity/region.xml). The - /// primary language subtag must be at least two and at most eight lowercase - /// letters, but not four letters. The region region subtag must be two - /// uppercase letters or three digits. See the [Unicode Language - /// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) - /// specification. - /// - /// Validity is not checked by default, but some methods may throw away - /// invalid data. - /// - /// See also: - /// - /// * [new Locale.fromSubtags], which also allows a [scriptCode] to be - /// specified. const Locale( this._languageCode, [ this._countryCode, @@ -191,22 +48,6 @@ class Locale { assert(_languageCode != ''), scriptCode = null; - /// Creates a new Locale object. - /// - /// The keyword arguments specify the subtags of the Locale. - /// - /// The subtag values are _case sensitive_ and must be valid subtags according - /// to CLDR supplemental data: - /// [language](http://unicode.org/cldr/latest/common/validity/language.xml), - /// [script](http://unicode.org/cldr/latest/common/validity/script.xml) and - /// [region](http://unicode.org/cldr/latest/common/validity/region.xml) for - /// each of languageCode, scriptCode and countryCode respectively. - /// - /// The [countryCode] subtag is optional. When there is no country subtag, - /// the parameter should be omitted or passed `null` instead of an empty-string. - /// - /// Validity is not checked by default, but some methods may throw away - /// invalid data. const Locale.fromSubtags({ String languageCode = 'und', this.scriptCode, @@ -218,29 +59,6 @@ class Locale { assert(countryCode != ''), _countryCode = countryCode; - /// The primary language subtag for the locale. - /// - /// This must not be null. It may be 'und', representing 'undefined'. - /// - /// This is expected to be string registered in the [IANA Language Subtag - /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) - /// with the type "language". The string specified must match the case of the - /// string in the registry. - /// - /// Language subtags that are deprecated in the registry and have a preferred - /// code are changed to their preferred code. For example, `const - /// Locale('he')` and `const Locale('iw')` are equal, and both have the - /// [languageCode] `he`, because `iw` is a deprecated language subtag that was - /// replaced by the subtag `he`. - /// - /// This must be a valid Unicode Language subtag as listed in [Unicode CLDR - /// supplemental - /// data](http://unicode.org/cldr/latest/common/validity/language.xml). - /// - /// See also: - /// - /// * [new Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; final String _languageCode; @@ -327,39 +145,8 @@ class Locale { 'yuu': 'yug', // Yugh; deprecated 2014-02-28 }; - /// The script subtag for the locale. - /// - /// This may be null, indicating that there is no specified script subtag. - /// - /// This must be a valid Unicode Language Identifier script subtag as listed - /// in [Unicode CLDR supplemental - /// data](http://unicode.org/cldr/latest/common/validity/script.xml). - /// - /// See also: - /// - /// * [new Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. final String? scriptCode; - /// The region subtag for the locale. - /// - /// This may be null, indicating that there is no specified region subtag. - /// - /// This is expected to be string registered in the [IANA Language Subtag - /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) - /// with the type "region". The string specified must match the case of the - /// string in the registry. - /// - /// Region subtags that are deprecated in the registry and have a preferred - /// code are changed to their preferred code. For example, `const Locale('de', - /// 'DE')` and `const Locale('de', 'DD')` are equal, and both have the - /// [countryCode] `DE`, because `DD` is a deprecated language subtag that was - /// replaced by the subtag `DE`. - /// - /// See also: - /// - /// * [new Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; final String? _countryCode; @@ -409,234 +196,63 @@ class Locale { } } -/// The most basic interface to the host operating system's user interface. -/// -/// There is a single Window instance in the system, which you can -/// obtain from the [window] property. -abstract class Window { - /// The number of device pixels for each logical pixel. This number might not - /// be a power of two. Indeed, it might not even be an integer. For example, - /// the Nexus 6 has a device pixel ratio of 3.5. - /// - /// Device pixels are also referred to as physical pixels. Logical pixels are - /// also referred to as device-independent or resolution-independent pixels. - /// - /// By definition, there are roughly 38 logical pixels per centimeter, or - /// about 96 logical pixels per inch, of the physical display. The value - /// returned by [devicePixelRatio] is ultimately obtained either from the - /// hardware itself, the device drivers, or a hard-coded value stored in the - /// operating system or firmware, and may be inaccurate, sometimes by a - /// significant margin. - /// - /// The Flutter framework operates in logical pixels, so it is rarely - /// necessary to directly deal with this property. - /// - /// When this changes, [onMetricsChanged] is called. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - double get devicePixelRatio; - - /// The dimensions of the rectangle into which the application will be drawn, - /// in physical pixels. - /// - /// When this changes, [onMetricsChanged] is called. - /// - /// At startup, the size of the application window may not be known before - /// Dart code runs. If this value is observed early in the application - /// lifecycle, it may report [Size.zero]. - /// - /// This value does not take into account any on-screen keyboards or other - /// system UI. The [padding] and [viewInsets] properties provide a view into - /// how much of each side of the application may be obscured by system UI. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - Size get physicalSize; +abstract class FlutterView { + PlatformDispatcher get platformDispatcher; + ViewConfiguration get viewConfiguration; + double get devicePixelRatio => viewConfiguration.screen.configuration.devicePixelRatio; + Rect get physicalGeometry => viewConfiguration.geometry; + Size get physicalSize => viewConfiguration.geometry.size; + double get physicalDepth => viewConfiguration.depth; + WindowPadding get viewInsets => viewConfiguration.viewInsets; + WindowPadding get viewPadding => viewConfiguration.viewPadding; + WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets; + WindowPadding get padding => viewConfiguration.padding; + + void render(Scene scene) => platformDispatcher.render(scene, this); + void dispose() => platformDispatcher.disposeView(this); +} - /// The physical depth is the maximum elevation that the Window allows. - /// - /// Physical layers drawn at or above this elevation will have their elevation - /// clamped to this value. This can happen if the physical layer itself has - /// an elevation larger than available depth, or if some ancestor of the layer - /// causes it to have a cumulative elevation that is larger than the available - /// depth. - /// - /// The default value is [double.maxFinite], which is used for platforms that - /// do not specify a maximum elevation. This property is currently on expected - /// to be set to a non-default value on Fuchsia. - double get physicalDepth; - - /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but over which the operating system - /// will likely place system UI, such as the keyboard, that fully obscures - /// any content. - /// - /// When this changes, [onMetricsChanged] is called. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - /// * [MediaQuery.of], a simpler mechanism for the same. - /// * [Scaffold], which automatically applies the view insets in material - /// design applications. - WindowPadding get viewInsets => WindowPadding.zero; - - WindowPadding get viewPadding => WindowPadding.zero; - - WindowPadding get systemGestureInsets => WindowPadding.zero; - - /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but which may be partially obscured by - /// system UI (such as the system notification area), or or physical - /// intrusions in the display (e.g. overscan regions on television screens or - /// phone sensor housings). - /// - /// When this changes, [onMetricsChanged] is called. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - /// * [MediaQuery.of], a simpler mechanism for the same. - /// * [Scaffold], which automatically applies the padding in material design - /// applications. - WindowPadding get padding => WindowPadding.zero; - - /// The system-reported text scale. - /// - /// This establishes the text scaling factor to use when rendering text, - /// according to the user's platform preferences. - /// - /// The [onTextScaleFactorChanged] callback is called whenever this value - /// changes. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - double get textScaleFactor => _textScaleFactor; - double _textScaleFactor = 1.0; +abstract class FlutterWindowView extends FlutterView { + @override + ViewConfiguration get viewConfiguration; +} - /// The setting indicating whether time should always be shown in the 24-hour - /// format. - /// - /// This option is used by [showTimePicker]. - bool get alwaysUse24HourFormat => _alwaysUse24HourFormat; - bool _alwaysUse24HourFormat = false; +abstract class FlutterWindow extends FlutterView { + @override + ViewConfiguration get viewConfiguration; +} - /// A callback that is invoked whenever [textScaleFactor] changes value. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this callback is invoked. - VoidCallback? get onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback? callback); +abstract class SingletonFlutterWindow extends FlutterWindow { + VoidCallback get onMetricsChanged => platformDispatcher.onMetricsChanged; + set onMetricsChanged(VoidCallback callback) { + platformDispatcher.onMetricsChanged = callback; + } - /// The setting indicating the current brightness mode of the host platform. - Brightness get platformBrightness; + Locale get locale => platformDispatcher.locale; + List get locales => platformDispatcher.locales; - /// A callback that is invoked whenever [platformBrightness] changes value. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this callback is invoked. - VoidCallback? get onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback? callback); - - /// A callback that is invoked whenever the [devicePixelRatio], - /// [physicalSize], [padding], or [viewInsets] values change, for example - /// when the device is rotated or when the application is resized (e.g. when - /// showing applications side-by-side on Android). - /// - /// The engine invokes this callback in the same zone in which the callback - /// was set. - /// - /// The framework registers with this callback and updates the layout - /// appropriately. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// register for notifications when this is called. - /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback? get onMetricsChanged; - set onMetricsChanged(VoidCallback? callback); + Locale? get platformResolvedLocale => platformDispatcher.platformResolvedLocale; - /// The system-reported default locale of the device. - /// - /// This establishes the language and formatting conventions that application - /// should, if possible, use to render their user interface. - /// - /// This is the first locale selected by the user and is the user's - /// primary locale (the locale the device UI is displayed in) - /// - /// This is equivalent to `locales.first` and will provide an empty non-null locale - /// if the [locales] list has not been set or is empty. - Locale? get locale; + VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; + set onLocaleChanged(VoidCallback? callback) { + platformDispatcher.onLocaleChanged = callback; + } - /// The full system-reported supported locales of the device. - /// - /// This establishes the language and formatting conventions that application - /// should, if possible, use to render their user interface. - /// - /// The list is ordered in order of priority, with lower-indexed locales being - /// preferred over higher-indexed ones. The first element is the primary [locale]. - /// - /// The [onLocaleChanged] callback is called whenever this value changes. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - List? get locales; + String get initialLifecycleState => platformDispatcher.initialLifecycleState; + double get textScaleFactor => platformDispatcher.textScaleFactor; + bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; - /// Performs the platform-native locale resolution. - /// - /// Each platform may return different results. - /// - /// If the platform fails to resolve a locale, then this will return null. - /// - /// This method returns synchronously and is a direct call to - /// platform specific APIs without invoking method channels. - Locale? computePlatformResolvedLocale(List supportedLocales) { - // TODO(garyq): Implement on web. - return null; + VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback? callback) { + platformDispatcher.onTextScaleFactorChanged = callback; } - /// A callback that is invoked whenever [locale] changes value. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this callback is invoked. - VoidCallback? get onLocaleChanged; - set onLocaleChanged(VoidCallback? callback); + Brightness get platformBrightness => platformDispatcher.platformBrightness; - /// Requests that, at the next appropriate opportunity, the [onBeginFrame] - /// and [onDrawFrame] callbacks be invoked. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - void scheduleFrame(); + VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback? callback) { + platformDispatcher.onPlatformBrightnessChanged = callback; + } /// A callback that is invoked to notify the application that it is an /// appropriate time to provide a scene using the [SceneBuilder] API and the @@ -803,15 +419,8 @@ abstract class Window { engine.EngineSemanticsOwner.instance.updateSemantics(update); } - /// Sends a message to a platform-specific plugin. - /// - /// The `name` parameter determines which plugin receives the message. The - /// `data` parameter contains the message payload and is typically UTF-8 - /// encoded JSON but can be arbitrary data. If the plugin replies to the - /// message, `callback` will be called with the response. - /// - /// The framework invokes [callback] in the same zone in which this method - /// was called. + void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); + void sendPlatformMessage( String name, ByteData? data, @@ -855,11 +464,6 @@ abstract class Window { ByteData? getPersistentIsolateData() => null; } -/// Additional accessibility features that may be enabled by the platform. -/// -/// It is not possible to enable these settings from Flutter, instead they are -/// used by the platform to indicate that additional accessibility features are -/// enabled. class AccessibilityFeatures { const AccessibilityFeatures._(this._index); @@ -938,18 +542,8 @@ class AccessibilityFeatures { int get hashCode => _index.hashCode; } -/// Describes the contrast of a theme or color palette. enum Brightness { - /// The color is dark and will require a light text color to achieve readable - /// contrast. - /// - /// For example, the color might be dark grey, requiring white text. dark, - - /// The color is light and will require a dark text color to achieve readable - /// contrast. - /// - /// For example, the color might be bright white, requiring black text. light, } @@ -985,7 +579,7 @@ class PluginUtilities { } } -// TODO(flutter_web): probably dont implement this one. +// TODO(flutter_web): probably don't implement this one. class IsolateNameServer { // This class is only a namespace, and should not be instantiated or // extended directly. @@ -1004,39 +598,13 @@ class IsolateNameServer { } } -/// Various important time points in the lifetime of a frame. -/// -/// [FrameTiming] records a timestamp of each phase for performance analysis. enum FramePhase { - /// When the UI thread starts building a frame. - /// - /// See also [FrameTiming.buildDuration]. buildStart, - - /// When the UI thread finishes building a frame. - /// - /// See also [FrameTiming.buildDuration]. buildFinish, - - /// When the raster thread starts rasterizing a frame. - /// - /// See also [FrameTiming.rasterDuration]. rasterStart, - - /// When the raster thread finishes rasterizing a frame. - /// - /// See also [FrameTiming.rasterDuration]. rasterFinish, } -/// Time-related performance metrics of a frame. -/// -/// See [Window.onReportTimings] for how to get this. -/// -/// The metrics in debug mode (`flutter run` without any flags) may be very -/// different from those in profile and release modes due to the debug overhead. -/// Therefore it's recommended to only monitor and analyze performance metrics -/// in profile and release modes. class FrameTiming { /// Construct [FrameTiming] with raw timestamps in microseconds. /// @@ -1108,3 +676,62 @@ class FrameTiming { /// core scheduler API, the input event callback, the graphics drawing API, and /// other such core services. Window get window => engine.window; + FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; + set onBeginFrame(FrameCallback? callback) { + platformDispatcher.onBeginFrame = callback; + } + VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; + set onDrawFrame(VoidCallback? callback) { + platformDispatcher.onDrawFrame = callback; + } + TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; + set onReportTimings(TimingsCallback? callback) { + platformDispatcher.onReportTimings = callback; + } + PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback? callback) { + platformDispatcher.onPointerDataPacket = callback; + } + String get initialRouteName => platformDispatcher.initialRouteName; + void scheduleFrame() => platformDispatcher.scheduleFrame(); + void render(Scene scene) => platformDispatcher.render(scene, this); + bool get semanticsEnabled => platformDispatcher.semanticsEnabled; + VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback? callback) { + platformDispatcher.onSemanticsEnabledChanged = callback; + } + SemanticsActionCallback? get onSemanticsAction => platformDispatcher.onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback? callback) { + platformDispatcher.onSemanticsAction = callback; + } + AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; + VoidCallback? get onAccessibilityFeaturesChanged => + platformDispatcher.onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback? callback) { + platformDispatcher.onAccessibilityFeaturesChanged = callback; + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { + platformDispatcher.sendPlatformMessage(name, data, callback); + } + PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback? callback) { + platformDispatcher.onPlatformMessage = callback; + } + bool get accessibleNavigation => _kAccessibleNavigation & _index != 0; + bool get invertColors => _kInvertColorsIndex & _index != 0; + bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; + bool get boldText => _kBoldTextIndex & _index != 0; + bool get reduceMotion => _kReduceMotionIndex & _index != 0; + bool get highContrast => _kHighContrastIndex & _index != 0; + FrameTiming(List timestamps) + int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; + Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); + Duration get buildDuration => + _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); + Duration get rasterDuration => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); + Duration get totalSpan => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.buildStart); +SingletonFlutterWindow get window => engine.window; diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index bfedc844f9ae4..233bc62b366d0 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -29,7 +29,9 @@ part 'src/ui/natives.dart'; part 'src/ui/painting.dart'; part 'src/ui/path.dart'; part 'src/ui/path_metrics.dart'; +part 'src/ui/platform_dispatcher.dart'; part 'src/ui/pointer.dart'; +part 'src/ui/screen.dart'; part 'src/ui/semantics.dart'; part 'src/ui/test_embedding.dart'; part 'src/ui/text.dart'; diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index 8dabd46ba0a52..84efd60d9143d 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -20,6 +20,7 @@ dev_dependencies: build_web_compilers: 2.11.0 yaml: 2.2.1 watcher: 0.9.7+15 + http_multi_server: 2.2.0 web_engine_tester: path: ../../web_sdk/web_engine_tester simulators: diff --git a/lib/web_ui/test/canvaskit/util_test.dart b/lib/web_ui/test/canvaskit/util_test.dart index e9f1f442995e5..bd8d1b5ce0ba1 100644 --- a/lib/web_ui/test/canvaskit/util_test.dart +++ b/lib/web_ui/test/canvaskit/util_test.dart @@ -19,7 +19,7 @@ void main() { when(mockRasterizer.addPostFrameCallback(any)).thenAnswer((_) { addPostFrameCallbackCount++; }); - window.rasterizer = mockRasterizer; + EnginePlatformDispatcher.instance.rasterizer = mockRasterizer; // Trigger first create final TestSkiaObject testObject = TestSkiaObject(); diff --git a/lib/web_ui/test/engine/history_test.dart b/lib/web_ui/test/engine/history_test.dart index 58d536aff4785..a04218a40e1d1 100644 --- a/lib/web_ui/test/engine/history_test.dart +++ b/lib/web_ui/test/engine/history_test.dart @@ -18,7 +18,7 @@ import '../spy.dart'; TestLocationStrategy _strategy; TestLocationStrategy get strategy => _strategy; set strategy(TestLocationStrategy newStrategy) { - window.locationStrategy = _strategy = newStrategy; + EnginePlatformDispatcher.instance.locationStrategy = _strategy = newStrategy; } const Map originState = {'origin': true}; diff --git a/lib/web_ui/test/engine/navigation_test.dart b/lib/web_ui/test/engine/navigation_test.dart index 8266fd7ec9904..15660b7efd6b5 100644 --- a/lib/web_ui/test/engine/navigation_test.dart +++ b/lib/web_ui/test/engine/navigation_test.dart @@ -16,11 +16,11 @@ void emptyCallback(ByteData date) {} void main() { setUp(() { - engine.window.locationStrategy = _strategy = engine.TestLocationStrategy(); + engine.EnginePlatformDispatcher.instance.locationStrategy = _strategy = engine.TestLocationStrategy(); }); tearDown(() { - engine.window.locationStrategy = _strategy = null; + engine.EnginePlatformDispatcher.instance.locationStrategy = _strategy = null; }); test('Tracks pushed, replaced and popped routes', () { diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart index d73fcfdf94e94..35343fce3cd63 100644 --- a/lib/web_ui/test/engine/window_test.dart +++ b/lib/web_ui/test/engine/window_test.dart @@ -25,7 +25,7 @@ void main() { expect(window.onTextScaleFactorChanged, same(callback)); }); - window.invokeOnTextScaleFactorChanged(); + EnginePlatformDispatcher.instance.invokeOnTextScaleFactorChanged(); }); test('onPlatformBrightnessChanged preserves the zone', () { @@ -41,7 +41,7 @@ void main() { expect(window.onPlatformBrightnessChanged, same(callback)); }); - window.invokeOnPlatformBrightnessChanged(); + EnginePlatformDispatcher.instance.invokeOnPlatformBrightnessChanged(); }); test('onMetricsChanged preserves the zone', () { @@ -57,7 +57,7 @@ void main() { expect(window.onMetricsChanged, same(callback)); }); - window.invokeOnMetricsChanged(); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); }); test('onLocaleChanged preserves the zone', () { @@ -73,7 +73,7 @@ void main() { expect(window.onLocaleChanged, same(callback)); }); - window.invokeOnLocaleChanged(); + EnginePlatformDispatcher.instance.invokeOnLocaleChanged(); }); test('onBeginFrame preserves the zone', () { @@ -89,7 +89,7 @@ void main() { expect(window.onBeginFrame, same(callback)); }); - window.invokeOnBeginFrame(null); + EnginePlatformDispatcher.instance.invokeOnBeginFrame(null); }); test('onReportTimings preserves the zone', () { @@ -105,7 +105,7 @@ void main() { expect(window.onReportTimings, same(callback)); }); - window.invokeOnReportTimings(null); + EnginePlatformDispatcher.instance.invokeOnReportTimings(null); }); test('onDrawFrame preserves the zone', () { @@ -121,7 +121,7 @@ void main() { expect(window.onDrawFrame, same(callback)); }); - window.invokeOnDrawFrame(); + EnginePlatformDispatcher.instance.invokeOnDrawFrame(); }); test('onPointerDataPacket preserves the zone', () { @@ -137,7 +137,7 @@ void main() { expect(window.onPointerDataPacket, same(callback)); }); - window.invokeOnPointerDataPacket(null); + EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(null); }); test('onSemanticsEnabledChanged preserves the zone', () { @@ -153,7 +153,7 @@ void main() { expect(window.onSemanticsEnabledChanged, same(callback)); }); - window.invokeOnSemanticsEnabledChanged(); + EnginePlatformDispatcher.instance.invokeOnSemanticsEnabledChanged(); }); test('onSemanticsAction preserves the zone', () { @@ -169,7 +169,7 @@ void main() { expect(window.onSemanticsAction, same(callback)); }); - window.invokeOnSemanticsAction(null, null, null); + EnginePlatformDispatcher.instance.invokeOnSemanticsAction(null, null, null); }); test('onAccessibilityFeaturesChanged preserves the zone', () { @@ -185,7 +185,7 @@ void main() { expect(window.onAccessibilityFeaturesChanged, same(callback)); }); - window.invokeOnAccessibilityFeaturesChanged(); + EnginePlatformDispatcher.instance.invokeOnAccessibilityFeaturesChanged(); }); test('onPlatformMessage preserves the zone', () { @@ -201,7 +201,7 @@ void main() { expect(window.onPlatformMessage, same(callback)); }); - window.invokeOnPlatformMessage(null, null, null); + EnginePlatformDispatcher.instance.invokeOnPlatformMessage(null, null, null); }); test('sendPlatformMessage preserves the zone', () async { @@ -243,8 +243,8 @@ void main() { // Trigger a change notification (reset locales because the notification // doesn't actually change the list of languages; the test only observes // that the list is populated again). - window.debugResetLocales(); - expect(window.locales, null); + EnginePlatformDispatcher.instance.debugResetLocales(); + expect(window.locales, isEmpty); expect(localeChangedCount, 0); html.window.dispatchEvent(html.Event('languagechange')); expect(window.locales, isNotEmpty); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart index ce55498eba86a..76546378a7819 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart @@ -222,8 +222,8 @@ void main() async { ); final SceneBuilder sb = SceneBuilder(); - sb.pushTransform(Matrix4.diagonal3Values(EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio, 1.0).toFloat64()); + sb.pushTransform(Matrix4.diagonal3Values(EnginePlatformDispatcher.browserDevicePixelRatio, + EnginePlatformDispatcher.browserDevicePixelRatio, 1.0).toFloat64()); sb.pushTransform(Matrix4.rotationZ(math.pi / 2).toFloat64()); sb.pushOffset(0, -500); sb.pushClipRect(canvasSize); diff --git a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart index 2c270a0a0f938..7328e555e883b 100644 --- a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart @@ -407,8 +407,8 @@ void _testCullRectComputation() { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushTransform(Matrix4.diagonal3Values( - EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio, 1.0).toFloat64()); + EnginePlatformDispatcher.browserDevicePixelRatio, + EnginePlatformDispatcher.browserDevicePixelRatio, 1.0).toFloat64()); // TODO(yjbanov): see the TODO below. // final double screenWidth = html.window.innerWidth.toDouble(); diff --git a/lib/web_ui/test/window_test.dart b/lib/web_ui/test/window_test.dart index 8924a06798d5f..7a71bd99b701e 100644 --- a/lib/web_ui/test/window_test.dart +++ b/lib/web_ui/test/window_test.dart @@ -15,21 +15,27 @@ const MethodCodec codec = JSONMethodCodec(); void emptyCallback(ByteData date) {} +TestLocationStrategy _strategy; +TestLocationStrategy get strategy => _strategy; +set strategy(TestLocationStrategy newStrategy) { + EnginePlatformDispatcher.instance.locationStrategy = _strategy = newStrategy; +} + void main() { - test('window.defaultRouteName should not change', () { + test('window.initialRouteName should not change', () { window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); - expect(window.defaultRouteName, '/initial'); + expect(window.initialRouteName, '/initial'); - // Changing the URL in the address bar later shouldn't affect [window.defaultRouteName]. + // Changing the URL in the address bar later shouldn't affect [window.initialRouteName]. window.locationStrategy.replaceState(null, null, '/newpath'); - expect(window.defaultRouteName, '/initial'); + expect(window.initialRouteName, '/initial'); }); - test('window.defaultRouteName should reset after navigation platform message', () { + test('window.initialRouteName should reset after navigation platform message', () { window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); // Reading it multiple times should return the same value. - expect(window.defaultRouteName, '/initial'); - expect(window.defaultRouteName, '/initial'); + expect(window.initialRouteName, '/initial'); + expect(window.initialRouteName, '/initial'); window.sendPlatformMessage( 'flutter/navigation', @@ -39,9 +45,9 @@ void main() { )), emptyCallback, ); - // After a navigation platform message, [window.defaultRouteName] should + // After a navigation platform message, [window.initialRouteName] should // reset to "/". - expect(window.defaultRouteName, '/'); + expect(window.initialRouteName, '/'); }); test('can disable location strategy', () async { diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index d23b38e752b3e..26b062ff491df 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -56,7 +56,7 @@ std::weak_ptr DartIsolate::CreateRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, TaskRunners task_runners, - std::unique_ptr window, + std::unique_ptr platform_configuration, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, @@ -111,7 +111,8 @@ std::weak_ptr DartIsolate::CreateRootIsolate( std::shared_ptr* root_isolate_data = static_cast*>(Dart_IsolateData(vm_isolate)); - (*root_isolate_data)->SetWindow(std::move(window)); + (*root_isolate_data) + ->SetPlatformConfiguration(std::move(platform_configuration)); return (*root_isolate_data)->GetWeakIsolatePtr(); } @@ -597,7 +598,7 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( vm_data->GetSettings(), // settings vm_data->GetIsolateSnapshot(), // isolate snapshot null_task_runners, // task runners - nullptr, // window + nullptr, // platform_configuration {}, // snapshot delegate {}, // IO Manager {}, // Skia unref queue diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index 7f59aa7dc28d0..770a99094b8ea 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -16,7 +16,7 @@ #include "flutter/lib/ui/io_manager.h" #include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/runtime/dart_snapshot.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/tonic/dart_state.h" @@ -192,7 +192,7 @@ class DartIsolate : public UIDartState { const Settings& settings, fml::RefPtr isolate_snapshot, TaskRunners task_runners, - std::unique_ptr window, + std::unique_ptr platform_configuration, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr skia_unref_queue, diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index aa557d2abf716..12aa04fa6b630 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -8,6 +8,7 @@ #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/window.h" #include "flutter/runtime/runtime_delegate.h" #include "third_party/tonic/dart_message_handler.h" @@ -49,20 +50,21 @@ RuntimeController::RuntimeController( // It will be run at a later point when the engine provides a run // configuration and then runs the isolate. auto strong_root_isolate = - DartIsolate::CreateRootIsolate(vm_->GetVMData()->GetSettings(), // - isolate_snapshot_, // - task_runners_, // - std::make_unique(this), // - snapshot_delegate_, // - io_manager_, // - unref_queue_, // - image_decoder_, // - p_advisory_script_uri, // - p_advisory_script_entrypoint, // - nullptr, // - isolate_create_callback_, // - isolate_shutdown_callback_ // - ) + DartIsolate::CreateRootIsolate( + vm_->GetVMData()->GetSettings(), // + isolate_snapshot_, // + task_runners_, // + std::make_unique(this), // + snapshot_delegate_, // + io_manager_, // + unref_queue_, // + image_decoder_, // + p_advisory_script_uri, // + p_advisory_script_entrypoint, // + nullptr, // + isolate_create_callback_, // + isolate_shutdown_callback_ // + ) .lock(); FML_CHECK(strong_root_isolate) << "Could not create root isolate."; @@ -74,9 +76,9 @@ RuntimeController::RuntimeController( root_isolate_return_code_ = {true, code}; }); - if (auto* window = GetWindowIfAvailable()) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { tonic::DartState::Scope scope(strong_root_isolate); - window->DidCreateIsolate(); + platform_configuration->DidCreateIsolate(); if (!FlushRuntimeStateToIsolate()) { FML_DLOG(ERROR) << "Could not setup initial isolate state."; } @@ -129,7 +131,8 @@ std::unique_ptr RuntimeController::Clone() const { } bool RuntimeController::FlushRuntimeStateToIsolate() { - return SetViewportMetrics(window_data_.viewport_metrics) && + return SetScreenMetrics(window_data_.screen_metrics) && + SetViewportMetrics(window_data_.viewport_metrics) && SetLocales(window_data_.locale_data) && SetSemanticsEnabled(window_data_.semantics_enabled) && SetAccessibilityFeatures(window_data_.accessibility_feature_flags_) && @@ -147,12 +150,23 @@ bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { return false; } +bool RuntimeController::SetScreenMetrics(const ScreenMetrics& metrics) { + window_data_.screen_metrics = metrics; + + if (auto* platformConfiguration = GetPlatformConfigurationIfAvailable()) { + // Currently only supports a single screen. + platformConfiguration->get_screen(0)->UpdateScreenMetrics(metrics); + return true; + } + return false; +} + bool RuntimeController::SetLocales( const std::vector& locale_data) { window_data_.locale_data = locale_data; - if (auto* window = GetWindowIfAvailable()) { - window->UpdateLocales(locale_data); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateLocales(locale_data); return true; } @@ -162,8 +176,9 @@ bool RuntimeController::SetLocales( bool RuntimeController::SetUserSettingsData(const std::string& data) { window_data_.user_settings_data = data; - if (auto* window = GetWindowIfAvailable()) { - window->UpdateUserSettingsData(window_data_.user_settings_data); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateUserSettingsData( + window_data_.user_settings_data); return true; } @@ -173,8 +188,8 @@ bool RuntimeController::SetUserSettingsData(const std::string& data) { bool RuntimeController::SetLifecycleState(const std::string& data) { window_data_.lifecycle_state = data; - if (auto* window = GetWindowIfAvailable()) { - window->UpdateLifecycleState(window_data_.lifecycle_state); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateLifecycleState(window_data_.lifecycle_state); return true; } @@ -184,8 +199,9 @@ bool RuntimeController::SetLifecycleState(const std::string& data) { bool RuntimeController::SetSemanticsEnabled(bool enabled) { window_data_.semantics_enabled = enabled; - if (auto* window = GetWindowIfAvailable()) { - window->UpdateSemanticsEnabled(window_data_.semantics_enabled); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateSemanticsEnabled( + window_data_.semantics_enabled); return true; } @@ -194,8 +210,8 @@ bool RuntimeController::SetSemanticsEnabled(bool enabled) { bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { window_data_.accessibility_feature_flags_ = flags; - if (auto* window = GetWindowIfAvailable()) { - window->UpdateAccessibilityFeatures( + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateAccessibilityFeatures( window_data_.accessibility_feature_flags_); return true; } @@ -204,16 +220,16 @@ bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { } bool RuntimeController::BeginFrame(fml::TimePoint frame_time) { - if (auto* window = GetWindowIfAvailable()) { - window->BeginFrame(frame_time); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->BeginFrame(frame_time); return true; } return false; } bool RuntimeController::ReportTimings(std::vector timings) { - if (auto* window = GetWindowIfAvailable()) { - window->ReportTimings(std::move(timings)); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->ReportTimings(std::move(timings)); return true; } return false; @@ -239,10 +255,10 @@ bool RuntimeController::NotifyIdle(int64_t deadline) { bool RuntimeController::DispatchPlatformMessage( fml::RefPtr message) { - if (auto* window = GetWindowIfAvailable()) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", "mode", "basic"); - window->DispatchPlatformMessage(std::move(message)); + platform_configuration->DispatchPlatformMessage(std::move(message)); return true; } return false; @@ -250,10 +266,10 @@ bool RuntimeController::DispatchPlatformMessage( bool RuntimeController::DispatchPointerDataPacket( const PointerDataPacket& packet) { - if (auto* window = GetWindowIfAvailable()) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", "mode", "basic"); - window->DispatchPointerDataPacket(packet); + platform_configuration->DispatchPointerDataPacket(packet); return true; } return false; @@ -264,21 +280,31 @@ bool RuntimeController::DispatchSemanticsAction(int32_t id, std::vector args) { TRACE_EVENT1("flutter", "RuntimeController::DispatchSemanticsAction", "mode", "basic"); - if (auto* window = GetWindowIfAvailable()) { - window->DispatchSemanticsAction(id, action, std::move(args)); + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->DispatchSemanticsAction(id, action, + std::move(args)); return true; } return false; } -Window* RuntimeController::GetWindowIfAvailable() { +PlatformConfiguration* +RuntimeController::GetPlatformConfigurationIfAvailable() { std::shared_ptr root_isolate = root_isolate_.lock(); - return root_isolate ? root_isolate->window() : nullptr; + return root_isolate ? root_isolate->platform_configuration() : nullptr; +} + +Window* RuntimeController::GetWindowIfAvailable() { + // This only handles the one window for now. + PlatformConfiguration* platform_configuration = + GetPlatformConfigurationIfAvailable(); + return platform_configuration ? platform_configuration->get_window(0) + : nullptr; } // |WindowClient| -std::string RuntimeController::DefaultRouteName() { - return client_.DefaultRouteName(); +std::string RuntimeController::InitialRouteName() { + return client_.InitialRouteName(); } // |WindowClient| diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index ad89cbeae064b..2728aed467c68 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -14,8 +14,8 @@ #include "flutter/lib/ui/io_manager.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/pointer_data_packet.h" -#include "flutter/lib/ui/window/window.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/window_data.h" #include "rapidjson/document.h" @@ -140,12 +140,23 @@ class RuntimeController final : public WindowClient { /// If the isolate is not running, these metrics will be saved and /// flushed to the isolate when it starts. /// - /// @param[in] metrics The metrics. + /// @param[in] metrics The window's viewport metrics. /// /// @return If the window metrics were forwarded to the running isolate. /// bool SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Forward the specified screen metrics to the running isolate. + /// If the isolate is not running, these metrics will be saved and + /// flushed to the isolate when it starts. + /// + /// @param[in] metrics The screen metrics. + /// + /// @return If the screen metrics were forwarded to the running isolate. + /// + bool SetScreenMetrics(const ScreenMetrics& metrics); + //---------------------------------------------------------------------------- /// @brief Forward the specified locale data to the running isolate. If /// the isolate is not running, this data will be saved and @@ -474,11 +485,12 @@ class RuntimeController final : public WindowClient { std::shared_ptr persistent_isolate_data_; Window* GetWindowIfAvailable(); + PlatformConfiguration* GetPlatformConfigurationIfAvailable(); bool FlushRuntimeStateToIsolate(); // |WindowClient| - std::string DefaultRouteName() override; + std::string InitialRouteName() override; // |WindowClient| void ScheduleFrame() override; diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 20059827b8150..edf4d16cc4b36 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -19,7 +19,7 @@ namespace flutter { class RuntimeDelegate { public: - virtual std::string DefaultRouteName() = 0; + virtual std::string InitialRouteName() = 0; virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; diff --git a/runtime/window_data.h b/runtime/window_data.h index e234d2f558162..fc907fc4a0839 100644 --- a/runtime/window_data.h +++ b/runtime/window_data.h @@ -5,12 +5,13 @@ #ifndef FLUTTER_RUNTIME_WINDOW_DATA_H_ #define FLUTTER_RUNTIME_WINDOW_DATA_H_ -#include "flutter/lib/ui/window/viewport_metrics.h" - #include #include #include +#include "flutter/lib/ui/window/screen_metrics.h" +#include "flutter/lib/ui/window/viewport_metrics.h" + namespace flutter { //------------------------------------------------------------------------------ @@ -31,6 +32,7 @@ struct WindowData { ~WindowData(); ViewportMetrics viewport_metrics; + ScreenMetrics screen_metrics; std::string language_code; std::string country_code; std::string script_code; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 7428cbf49828e..c540177c4dca8 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -287,6 +287,20 @@ void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { } } +void Engine::SetScreenMetrics(const ScreenMetrics& metrics) { + bool dimensions_changed = + screen_metrics_.physical_height != metrics.physical_height || + screen_metrics_.physical_width != metrics.physical_width; + screen_metrics_ = metrics; + runtime_controller_->SetScreenMetrics(screen_metrics_); + if (animator_) { + if (dimensions_changed) + animator_->SetDimensionChangePending(); + if (have_surface_) + ScheduleFrame(); + } +} + void Engine::DispatchPlatformMessage(fml::RefPtr message) { if (message->channel() == kLifecycleChannel) { if (HandleLifecyclePlatformMessage(message.get())) @@ -432,7 +446,7 @@ void Engine::StartAnimatorIfPossible() { animator_->Start(); } -std::string Engine::DefaultRouteName() { +std::string Engine::InitialRouteName() { if (!initial_route_.empty()) { return initial_route_; } diff --git a/shell/common/engine.h b/shell/common/engine.h index e92a91a955742..9c1affd35c077 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -189,13 +189,14 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// @brief Notifies the shell of the name of the root isolate and its /// port when that isolate is launched, restarted (in the /// cold-restart scenario) or the application itself updates the - /// name of the root isolate (via `Window.setIsolateDebugName` - /// in `window.dart`). The name of the isolate is meaningless to - /// the engine but is used in instrumentation and tooling. - /// Currently, this information is to update the service - /// protocol list of available root isolates running in the VM - /// and their names so that the appropriate isolate can be - /// selected in the tools for debugging and instrumentation. + /// name of the root isolate (via + /// `PlatformDispatcher.setIsolateDebugName` in + /// `platform_dispatcher.dart`). The name of the isolate is + /// meaningless to the engine but is used in instrumentation and + /// tooling. Currently, this information is to update the + /// service protocol list of available root isolates running in + /// the VM and their names so that the appropriate isolate can + /// be selected in the tools for debugging and instrumentation. /// /// @param[in] isolate_name The isolate name /// @param[in] isolate_port The isolate port @@ -546,8 +547,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// "main.dart", the entrypoint is "main" and the port name /// "1234". Once launched, the isolate may re-christen itself /// using a name it selects via `setIsolateDebugName` in - /// `window.dart`. This name is purely advisory and only used by - /// instrumentation and reporting purposes. + /// `platform_dispatcher.dart`. This name is purely advisory and + /// only used by instrumentation and reporting purposes. /// /// @return The debug name of the root isolate. /// @@ -653,6 +654,18 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// void SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Updates the screen metrics for the currently running Flutter + /// application. The screen metrics detail the size of the + /// screen that the rendering viewport is in in texels as well + /// as edge insets if present. + /// + /// @see `ScreenMetrics`, + /// + /// @param[in] metrics The metrics + /// + void SetScreenMetrics(const ScreenMetrics& metrics); + //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder has sent it a message. /// This call originates in the platform view and has been @@ -766,6 +779,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::string last_entry_point_library_; std::string initial_route_; ViewportMetrics viewport_metrics_; + ScreenMetrics screen_metrics_; std::shared_ptr asset_manager_; bool activity_running_; bool have_surface_; @@ -775,7 +789,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { fml::WeakPtrFactory weak_factory_; // |RuntimeDelegate| - std::string DefaultRouteName() override; + std::string InitialRouteName() override; // |RuntimeDelegate| void Render(std::unique_ptr layer_tree) override; diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index ceeee77a4e4f7..530996d1e2d64 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -15,7 +15,7 @@ void nativeOnPointerDataPacket(List sequences) native 'NativeOnPointerDataP @pragma('vm:entry-point') void reportTimingsMain() { - window.onReportTimings = (List timings) { + PlatformDispatcher.instance.onReportTimings = (List timings) { List timestamps = []; for (FrameTiming t in timings) { for (FramePhase phase in FramePhase.values) { @@ -28,15 +28,15 @@ void reportTimingsMain() { @pragma('vm:entry-point') void onBeginFrameMain() { - window.onBeginFrame = (Duration beginTime) { + PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) { nativeOnBeginFrame(beginTime.inMicroseconds); }; } @pragma('vm:entry-point') void onPointerDataPacketMain() { - window.onPointerDataPacket = (PointerDataPacket packet) { - List sequence= []; + PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) { + List sequence = []; for (PointerData data in packet.data) { sequence.add(PointerChange.values.indexOf(data.change)); } @@ -49,7 +49,7 @@ void emptyMain() {} @pragma('vm:entry-point') void dummyReportTimingsMain() { - window.onReportTimings = (List timings) {}; + PlatformDispatcher.instance.onReportTimings = (List timings) {}; } @pragma('vm:entry-point') @@ -89,7 +89,7 @@ void testSkiaResourceCacheSendsResponse() { "method": "Skia.setResourceCacheMaxBytes", "args": 10000 }'''; - window.sendPlatformMessage( + PlatformDispatcher.instance.sendPlatformMessage( 'flutter/skia', Uint8List.fromList(utf8.encode(jsonRequest)).buffer.asByteData(), callback, @@ -107,17 +107,24 @@ void canCreateImageFromDecompressedData() { (int i) => i % 4 < 2 ? 0x00 : 0xFF, )); - decodeImageFromPixels( - pixels, imageWidth, imageHeight, PixelFormat.rgba8888, - (Image image) { - notifyWidthHeight(image.width, image.height); - }); + pixels, + imageWidth, + imageHeight, + PixelFormat.rgba8888, + (Image image) { + notifyWidthHeight(image.width, image.height); + }, + ); } @pragma('vm:entry-point') void canAccessIsolateLaunchData() { - notifyMessage(utf8.decode(window.getPersistentIsolateData().buffer.asUint8List())); + notifyMessage( + utf8.decode( + PlatformDispatcher.instance.getPersistentIsolateData().buffer.asUint8List(), + ), + ); } void notifyMessage(String string) native 'NotifyMessage'; @@ -132,9 +139,12 @@ void sendFixtureMapping(List list) native 'SendFixtureMapping'; @pragma('vm:entry-point') void canDecompressImageFromAsset() { - decodeImageFromList(Uint8List.fromList(getFixtureImage()), (Image result) { - notifyWidthHeight(result.width, result.height); - }); + decodeImageFromList( + Uint8List.fromList(getFixtureImage()), + (Image result) { + notifyWidthHeight(result.width, result.height); + }, + ); } List getFixtureImage() native 'GetFixtureImage'; diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 146874933e739..4a5df67bf0953 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -60,6 +60,10 @@ void PlatformView::SetViewportMetrics(const ViewportMetrics& metrics) { delegate_.OnPlatformViewSetViewportMetrics(metrics); } +void PlatformView::SetScreenMetrics(const ScreenMetrics& metrics) { + delegate_.OnPlatformViewSetScreenMetrics(metrics); +} + void PlatformView::NotifyCreated() { std::unique_ptr surface; diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 5596a01e59230..68a41ebe4bcd6 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -17,6 +17,7 @@ #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" +#include "flutter/lib/ui/window/screen_metrics.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/shell/common/pointer_data_dispatcher.h" #include "flutter/shell/common/vsync_waiter.h" @@ -99,6 +100,15 @@ class PlatformView { virtual void OnPlatformViewSetViewportMetrics( const ViewportMetrics& metrics) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate the screen metrics of the platform + /// screen have been updated. + /// + /// @param[in] metrics The updated screen metrics. + /// + virtual void OnPlatformViewSetScreenMetrics( + const ScreenMetrics& metrics) = 0; + //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform has dispatched a /// platform message from the embedder to the Flutter @@ -381,6 +391,18 @@ class PlatformView { /// void SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to specify the updated screen metrics. In + /// response to this call, on the raster thread, the rasterizer + /// may need to be reconfigured to the updated viewport dimensions + /// if they are affected by the change in screen metrics. On the + /// UI thread, the framework may need to start generating a new + /// frame for the updated viewport metrics as well. + /// + /// @param[in] metrics The updated screen metrics. + /// + void SetScreenMetrics(const ScreenMetrics& metrics); + //---------------------------------------------------------------------------- /// @brief Used by embedders to notify the shell that a platform view /// has been created. This notification is used to create a diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 4acf8af36799c..7feaa6a5a8cee 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -787,6 +787,29 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { }); } +// |PlatformView::Delegate| +void Shell::OnPlatformViewSetScreenMetrics(const ScreenMetrics& metrics) { + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + // This is the formula Android uses. + // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41 + size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; + task_runners_.GetRasterTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] { + if (rasterizer) { + rasterizer->SetResourceCacheMaxBytes(max_bytes, false); + } + }); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), metrics]() { + if (engine) { + engine->SetScreenMetrics(metrics); + } + }); +} + // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchPlatformMessage( fml::RefPtr message) { diff --git a/shell/common/shell.h b/shell/common/shell.h index fd1a30d3f4e35..f557fa2656eb6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -449,6 +449,9 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewSetViewportMetrics( const ViewportMetrics& metrics) override; + // |PlatformView::Delegate| + void OnPlatformViewSetScreenMetrics(const ScreenMetrics& metrics) override; + // |PlatformView::Delegate| void OnPlatformViewDispatchPlatformMessage( fml::RefPtr message) override; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 8bd7d8b14b24e..320c7f075c9c0 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -5,7 +5,6 @@ #define FML_USED_ON_EMBEDDER #include "flutter/shell/common/shell_test.h" -#include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/transform_layer.h" @@ -13,6 +12,7 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/vsync_waiter_fallback.h" #include "flutter/testing/testing.h" @@ -126,21 +126,22 @@ void ShellTest::PumpOneFrame(Shell* shell, double width, double height, LayerTreeBuilder builder) { - PumpOneFrame(shell, - flutter::ViewportMetrics{1, width, height, flutter::kUnsetDepth, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - std::move(builder)); + PumpOneFrame(shell, flutter::ViewportMetrics(width, height), + flutter::ScreenMetrics(1.0, width, height), std::move(builder)); } void ShellTest::PumpOneFrame(Shell* shell, flutter::ViewportMetrics viewport_metrics, + flutter::ScreenMetrics screen_metrics, LayerTreeBuilder builder) { // Set viewport to nonempty, and call Animator::BeginFrame to make the layer // tree pipeline nonempty. Without either of this, the layer tree below // won't be rasterized. fml::AutoResetWaitableEvent latch; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, engine = shell->weak_engine_, viewport_metrics]() { + [&latch, engine = shell->weak_engine_, viewport_metrics, + screen_metrics]() { + engine->SetScreenMetrics(std::move(screen_metrics)); engine->SetViewportMetrics(std::move(viewport_metrics)); const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = @@ -154,12 +155,12 @@ void ShellTest::PumpOneFrame(Shell* shell, // Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized| fml::WeakPtr runtime_delegate = shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, runtime_delegate, &builder, viewport_metrics]() { + [&latch, runtime_delegate, &builder, viewport_metrics, screen_metrics]() { auto layer_tree = std::make_unique( SkISize::Make(viewport_metrics.physical_width, viewport_metrics.physical_height), static_cast(viewport_metrics.physical_depth), - static_cast(viewport_metrics.device_pixel_ratio)); + static_cast(screen_metrics.device_pixel_ratio)); SkMatrix identity; identity.setIdentity(); auto root_layer = std::make_shared(identity); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index caf055120f0a0..63a259054764a 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -69,6 +69,7 @@ class ShellTest : public ThreadTest { LayerTreeBuilder = {}); static void PumpOneFrame(Shell* shell, flutter::ViewportMetrics viewport_metrics, + flutter::ScreenMetrics screen_metrics, LayerTreeBuilder = {}); static void DispatchFakePointerData(Shell* shell); static void DispatchPointerData(Shell* shell, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 9b3da6833f60d..07c9742725b00 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -677,7 +677,8 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + PumpOneFrame(shell.get(), flutter::ViewportMetrics(0, 0), + flutter::ScreenMetrics(1.0, 0, 0)); fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); ASSERT_FALSE(result.ok()); @@ -796,7 +797,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + flutter::ViewportMetrics(400, 200)); }); PumpOneFrame(shell.get()); @@ -816,7 +817,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, 800, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + flutter::ViewportMetrics(800, 400)); }); PumpOneFrame(shell.get()); @@ -835,7 +836,7 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + flutter::ViewportMetrics(400, 200)); }); PumpOneFrame(shell.get()); @@ -864,7 +865,7 @@ TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + flutter::ViewportMetrics(400, 200)); }); PumpOneFrame(shell.get()); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index a6ec2a64822f4..34d48d482a80d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -11,12 +11,16 @@ #include "flutter/fml/message_loop.h" #include "flutter/fml/platform/darwin/platform_version.h" #include "flutter/fml/trace_event.h" +#include "flutter/lib/ui/window/screen_metrics.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/darwin/common/command_line.h" +#include "flutter/shell/platform/darwin/ios/rendering_api_selection.h" +#include "flutter/shell/profiling/sampling_profiler.h" + #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h" @@ -27,8 +31,6 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" -#include "flutter/shell/platform/darwin/ios/rendering_api_selection.h" -#include "flutter/shell/profiling/sampling_profiler.h" NSString* const FlutterDefaultDartEntrypoint = nil; static constexpr int kNumProfilerSamplesPerSec = 5; @@ -183,6 +185,13 @@ - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics { self.platformView->SetViewportMetrics(std::move(viewportMetrics)); } +- (void)updateScreenMetrics:(flutter::ScreenMetrics)screenMetrics { + if (!self.platformView) { + return; + } + self.platformView->SetScreenMetrics(std::move(screenMetrics)); +} + - (void)dispatchPointerDataPacket:(std::unique_ptr)packet { if (!self.platformView) { return; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index e995b6f77f9c9..706b04b57ceb2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -21,6 +21,7 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" +#include "lib/ui/window/screen_metrics.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" @@ -29,6 +30,7 @@ - (flutter::Shell&)shell; - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics; +- (void)updateScreenMetrics:(flutter::ScreenMetrics)screenMetrics; - (void)dispatchPointerDataPacket:(std::unique_ptr)packet; - (fml::RefPtr)platformTaskRunner; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index bc8f4c457e512..2cf42bf4be200 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -68,6 +68,7 @@ @implementation FlutterViewController { UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; flutter::ViewportMetrics _viewportMetrics; + flutter::ScreenMetrics _screenMetrics; BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; @@ -822,6 +823,10 @@ - (void)updateViewportMetrics { [_engine.get() updateViewportMetrics:_viewportMetrics]; } +- (void)updateScreenMetrics { + [_engine.get() updateScreenMetrics:_screenMetrics]; +} + - (CGFloat)statusBarPadding { UIScreen* screen = self.view.window.screen; CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame; @@ -832,8 +837,9 @@ - (CGFloat)statusBarPadding { } - (void)viewDidLayoutSubviews { + UIScreen* screen = self.view.window.screen; CGSize viewSize = self.view.bounds.size; - CGFloat scale = [UIScreen mainScreen].scale; + CGFloat scale = screen.scale; // Purposefully place this not visible. _scrollView.get().frame = CGRectMake(0.0, 0.0, viewSize.width, 0.0); @@ -841,12 +847,16 @@ - (void)viewDidLayoutSubviews { // First time since creation that the dimensions of its view is known. bool firstViewBoundsUpdate = !_viewportMetrics.physical_width; - _viewportMetrics.device_pixel_ratio = scale; _viewportMetrics.physical_width = viewSize.width * scale; _viewportMetrics.physical_height = viewSize.height * scale; + _screenMetrics.device_pixel_ratio = scale; + _screenMetrics.physical_width = screen.bounds.size.width; + _screenMetrics.physical_height = screen.bounds.size.height; [self updateViewportPadding]; [self updateViewportMetrics]; + [self updateScreenPadding]; + [self updateScreenMetrics]; // This must run after updateViewportMetrics so that the surface creation tasks are queued after // the viewport metrics update tasks. @@ -871,6 +881,8 @@ - (void)viewDidLayoutSubviews { - (void)viewSafeAreaInsetsDidChange { [self updateViewportPadding]; [self updateViewportMetrics]; + [self updateScreenPadding]; + [self updateScreenMetrics]; [super viewSafeAreaInsetsDidChange]; } @@ -878,7 +890,7 @@ - (void)viewSafeAreaInsetsDidChange { // // Viewport padding represents the iOS safe area insets. - (void)updateViewportPadding { - CGFloat scale = [UIScreen mainScreen].scale; + CGFloat scale = self.view.window.screen.scale; if (@available(iOS 11, *)) { _viewportMetrics.physical_padding_top = self.view.safeAreaInsets.top * scale; _viewportMetrics.physical_padding_left = self.view.safeAreaInsets.left * scale; @@ -889,6 +901,21 @@ - (void)updateViewportPadding { } } +// Updates _screenMetrics physical padding. +// +// Screen padding represents the iOS safe area insets. +- (void)updateScreenPadding { + CGFloat scale = self.view.window.screen.scale; + if (@available(iOS 11, *)) { + _screenMetrics.physical_padding_top = self.view.safeAreaInsets.top * scale; + _screenMetrics.physical_padding_left = self.view.safeAreaInsets.left * scale; + _screenMetrics.physical_padding_right = self.view.safeAreaInsets.right * scale; + _screenMetrics.physical_padding_bottom = self.view.safeAreaInsets.bottom * scale; + } else { + _screenMetrics.physical_padding_top = [self statusBarPadding] * scale; + } +} + #pragma mark - Keyboard events - (void)keyboardWillChangeFrame:(NSNotification*)notification { @@ -915,16 +942,21 @@ - (void)keyboardWillChangeFrame:(NSNotification*)notification { // the keyboard height. The Dart side will compute a value accounting for the keyboard-consuming // bottom padding. _viewportMetrics.physical_view_inset_bottom = bottom * scale; + _screenMetrics.physical_view_inset_bottom = _viewportMetrics.physical_view_inset_bottom; } else { _viewportMetrics.physical_view_inset_bottom = 0; + _screenMetrics.physical_view_inset_bottom = 0; } [self updateViewportMetrics]; + [self updateScreenMetrics]; } - (void)keyboardWillBeHidden:(NSNotification*)notification { _viewportMetrics.physical_view_inset_bottom = 0; + _screenMetrics.physical_view_inset_bottom = 0; [self updateViewportMetrics]; + [self updateScreenMetrics]; } #pragma mark - Orientation updates diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 2cf8b74538ed5..608c1a33dec4c 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -74,6 +74,7 @@ void OnPlatformViewCreated(std::unique_ptr surface) override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override {} + void OnPlatformViewSetScreenMetrics(const ScreenMetrics& metrics) override {} void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index e00db7014b67c..cde561b2ac89c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" - #include +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/embedder/embedder.h" @@ -298,13 +297,21 @@ - (void)updateWindowMetrics { CGSize scaledSize = [view convertRectToBacking:view.bounds].size; double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width; - const FlutterWindowMetricsEvent event = { - .struct_size = sizeof(event), + // The screen is currently always the same size as the window. + const FlutterScreenMetricsEvent screenMetricsEvent = { + .struct_size = sizeof(screenMetricsEvent), .width = static_cast(scaledSize.width), .height = static_cast(scaledSize.height), .pixel_ratio = pixelRatio, }; - FlutterEngineSendWindowMetricsEvent(_engine, &event); + FlutterEngineSendScreenMetricsEvent(_engine, &screenMetricsEvent); + + const FlutterWindowMetricsEvent windowMetricsEvent = { + .struct_size = sizeof(windowMetricsEvent), + .width = static_cast(scaledSize.width), + .height = static_cast(scaledSize.height), + }; + FlutterEngineSendWindowMetricsEvent(_engine, &windowMetricsEvent); } - (void)sendPointerEvent:(const FlutterPointerEvent&)event { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 6e2f5bcf60ca6..8df0d592507f7 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1121,6 +1121,25 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent( flutter::ViewportMetrics metrics; + metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); + metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); + + return reinterpret_cast(engine)->SetViewportMetrics( + std::move(metrics)) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInvalidArguments, + "Viewport metrics were invalid."); +} + +FlutterEngineResult FlutterEngineSendScreenMetricsEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterScreenMetricsEvent* flutter_metrics) { + if (engine == nullptr || flutter_metrics == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); + } + + flutter::ScreenMetrics metrics; + metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); @@ -1131,11 +1150,11 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent( "Device pixel ratio was invalid. It must be greater than zero."); } - return reinterpret_cast(engine)->SetViewportMetrics( + return reinterpret_cast(engine)->SetScreenMetrics( std::move(metrics)) ? kSuccess : LOG_EMBEDDER_ERROR(kInvalidArguments, - "Viewport metrics were invalid."); + "Screen metrics were invalid."); } // Returns the flutter::PointerData::Change for the given FlutterPointerPhase. diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 67c5437ed9b26..019ac9bd7d6c5 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -336,9 +336,18 @@ typedef struct { size_t width; /// Physical height of the window. size_t height; +} FlutterWindowMetricsEvent; + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterScreenMetricsEvent). + size_t struct_size; + /// Physical width of the screen. + size_t width; + /// Physical height of the screen. + size_t height; /// Scale factor for the physical screen. double pixel_ratio; -} FlutterWindowMetricsEvent; +} FlutterScreenMetricsEvent; /// The phase of the pointer event. typedef enum { @@ -1321,6 +1330,11 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent* event); +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSendScreenMetricsEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterScreenMetricsEvent* event); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPointerEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 7d6f2c5d788f1..2d1ce99195906 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -113,6 +113,19 @@ bool EmbedderEngine::SetViewportMetrics(flutter::ViewportMetrics metrics) { return true; } +bool EmbedderEngine::SetScreenMetrics(flutter::ScreenMetrics metrics) { + if (!IsValid()) { + return false; + } + + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->SetScreenMetrics(std::move(metrics)); + return true; +} + bool EmbedderEngine::DispatchPointerDataPacket( std::unique_ptr packet) { if (!IsValid() || !packet) { diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 124f8af0cf9aa..dfefabda6e66c 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -51,6 +51,8 @@ class EmbedderEngine { bool SetViewportMetrics(flutter::ViewportMetrics metrics); + bool SetScreenMetrics(flutter::ScreenMetrics metrics); + bool DispatchPointerDataPacket( std::unique_ptr packet); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 95c25aaefb6c1..b4e8377740e4d 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -30,7 +30,7 @@ void sayHiFromCustomEntrypoint3() native 'SayHiFromCustomEntrypoint3'; @pragma('vm:entry-point') void invokePlatformTaskRunner() { - window.sendPlatformMessage('OhHi', null, null); + PlatformDispatcher.instance.sendPlatformMessage('OhHi', null, null); } @@ -55,19 +55,19 @@ void notifySemanticsEnabled(bool enabled) native 'NotifySemanticsEnabled'; void notifyAccessibilityFeatures(bool reduceMotion) native 'NotifyAccessibilityFeatures'; void notifySemanticsAction(int nodeId, int action, List data) native 'NotifySemanticsAction'; -/// Returns a future that completes when `window.onSemanticsEnabledChanged` -/// fires. +/// Returns a future that completes when +/// `PlatformDispatcher.instance.onSemanticsEnabledChanged` fires. Future get semanticsChanged { final Completer semanticsChanged = Completer(); - window.onSemanticsEnabledChanged = semanticsChanged.complete; + PlatformDispatcher.instance.onSemanticsEnabledChanged = semanticsChanged.complete; return semanticsChanged.future; } -/// Returns a future that completes when `window.onAccessibilityFeaturesChanged` -/// fires. +/// Returns a future that completes when +/// `PlatformDispatcher.instance.onAccessibilityFeaturesChanged` fires. Future get accessibilityFeaturesChanged { final Completer featuresChanged = Completer(); - window.onAccessibilityFeaturesChanged = featuresChanged.complete; + PlatformDispatcher.instance.onAccessibilityFeaturesChanged = featuresChanged.complete; return featuresChanged.future; } @@ -80,7 +80,7 @@ class SemanticsActionData { Future get semanticsAction { final Completer actionReceived = Completer(); - window.onSemanticsAction = (int id, SemanticsAction action, ByteData args) { + PlatformDispatcher.instance.onSemanticsAction = (int id, SemanticsAction action, ByteData args) { actionReceived.complete(SemanticsActionData(id, action, args)); }; return actionReceived.future; @@ -89,18 +89,18 @@ Future get semanticsAction { @pragma('vm:entry-point') void a11y_main() async { // ignore: non_constant_identifier_names // Return initial state (semantics disabled). - notifySemanticsEnabled(window.semanticsEnabled); + notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled); // Await semantics enabled from embedder. await semanticsChanged; - notifySemanticsEnabled(window.semanticsEnabled); + notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled); // Return initial state of accessibility features. - notifyAccessibilityFeatures(window.accessibilityFeatures.reduceMotion); + notifyAccessibilityFeatures(PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); // Await accessibility features changed from embedder. await accessibilityFeaturesChanged; - notifyAccessibilityFeatures(window.accessibilityFeatures.reduceMotion); + notifyAccessibilityFeatures(PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); // Fire semantics update. final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder() @@ -139,7 +139,7 @@ void a11y_main() async { // ignore: non_constant_identifier_names label: 'Archive', hint: 'archive message', ); - window.updateSemantics(builder.build()); + PlatformDispatcher.instance.updateSemantics(builder.build()); signalNativeTest(); // Await semantics action from embedder. @@ -149,13 +149,13 @@ void a11y_main() async { // ignore: non_constant_identifier_names // Await semantics disabled from embedder. await semanticsChanged; - notifySemanticsEnabled(window.semanticsEnabled); + notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled); } @pragma('vm:entry-point') void platform_messages_response() { - window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { + PlatformDispatcher.instance.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { callback(data); }; signalNativeTest(); @@ -163,7 +163,7 @@ void platform_messages_response() { @pragma('vm:entry-point') void platform_messages_no_response() { - window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { + PlatformDispatcher.instance.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { var list = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); signalNativeMessage(utf8.decode(list)); // This does nothing because no one is listening on the other side. But complete the loop anyway @@ -175,7 +175,7 @@ void platform_messages_no_response() { @pragma('vm:entry-point') void null_platform_messages() { - window.onPlatformMessage = + PlatformDispatcher.instance.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { // This checks if the platform_message null data is converted to Flutter null. signalNativeMessage((null == data).toString()); @@ -194,7 +194,7 @@ Picture CreateSimplePicture() { @pragma('vm:entry-point') void can_composite_platform_views() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture()); builder.pushOffset(1.0, 2.0); @@ -202,15 +202,15 @@ void can_composite_platform_views() { builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture()); builder.pop(); // offset signalNativeTest(); // Signal 2 - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_composite_platform_views_with_opacity() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); // Root node @@ -232,24 +232,24 @@ void can_composite_platform_views_with_opacity() { builder.pop(); signalNativeTest(); // Signal 2 - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_composite_with_opacity() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushOpacity(127); builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture()); builder.pop(); // offset signalNativeTest(); // Signal 2 - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } Picture CreateColoredBox(Color color, Size size) { @@ -263,7 +263,7 @@ Picture CreateColoredBox(Color color, Size size) { @pragma('vm:entry-point') void can_composite_platform_views_with_known_scene() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Color red = Color.fromARGB(127, 255, 0, 0); Color blue = Color.fromARGB(127, 0, 0, 255); Color gray = Color.fromARGB(127, 127, 127, 127); @@ -294,17 +294,17 @@ void can_composite_platform_views_with_known_scene() { builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); signalNativeTest(); // Signal 2 }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_composite_platform_views_with_root_layer_only() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Color red = Color.fromARGB(127, 255, 0, 0); Size size = Size(50.0, 150.0); @@ -315,17 +315,17 @@ void can_composite_platform_views_with_root_layer_only() { builder.addPicture(Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); signalNativeTest(); // Signal 2 }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_composite_platform_views_with_platform_layer_on_bottom() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Color red = Color.fromARGB(127, 255, 0, 0); Size size = Size(50.0, 150.0); @@ -341,17 +341,17 @@ void can_composite_platform_views_with_platform_layer_on_bottom() { builder.pop(); builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); signalNativeTest(); // Signal 2 }; signalNativeTest(); // Signal 1 - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_render_scene_without_custom_compositor() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Color red = Color.fromARGB(127, 255, 0, 0); Color green = Color.fromARGB(127, 0, 255, 0); Color blue = Color.fromARGB(127, 0, 0, 255); @@ -376,9 +376,9 @@ void can_render_scene_without_custom_compositor() { builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } Picture CreateGradientBox(Size size) { @@ -413,7 +413,7 @@ Picture CreateGradientBox(Size size) { @pragma('vm:entry-point') void render_gradient() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Size size = Size(800.0, 600.0); SceneBuilder builder = SceneBuilder(); @@ -424,14 +424,14 @@ void render_gradient() { builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void render_gradient_on_non_root_backing_store() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { Size size = Size(800.0, 600.0); Color red = Color.fromARGB(127, 255, 0, 0); @@ -448,14 +448,14 @@ void render_gradient_on_non_root_backing_store() { builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void verify_b141980393() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { // The platform view in the test case is screen sized but with margins of 31 // and 37 points from the top and bottom. double top_margin = 31.0; @@ -474,14 +474,14 @@ void verify_b141980393() { builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_display_platform_view_with_pixel_ratio() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushTransform(Float64List.fromList([ 2.0, 0.0, 0.0, 0.0, @@ -495,15 +495,15 @@ void can_display_platform_view_with_pixel_ratio() { builder.pop(); // offset builder.addPicture(Offset(0.0, 0.0), CreateColoredBox(Color.fromARGB(128, 255, 0, 0), Size(400.0, 300.0))); builder.pop(); // base - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void can_receive_locale_updates() { - window.onLocaleChanged = (){ - signalNativeCount(window.locales.length); + PlatformDispatcher.instance.onLocaleChanged = (){ + signalNativeCount(PlatformDispatcher.instance.locales.length); }; signalNativeTest(); } @@ -511,7 +511,7 @@ void can_receive_locale_updates() { // Verifies behavior tracked in https://github.com/flutter/flutter/issues/43732 @pragma('vm:entry-point') void verify_b143464703() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); // base @@ -538,14 +538,14 @@ void verify_b143464703() { builder.pop(); // 1 builder.pop(); // base - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void push_frames_over_and_over() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); builder.addPicture(Offset(0.0, 0.0), CreateColoredBox(Color.fromARGB(255, 128, 128, 128), Size(1024.0, 600.0))); @@ -553,17 +553,17 @@ void push_frames_over_and_over() { builder.addPlatformView(42, width: 1024.0, height: 540.0); builder.pop(); builder.pop(); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); signalNativeTest(); - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void platform_view_mutators() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); // base builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(800.0, 600.0))); @@ -577,14 +577,14 @@ void platform_view_mutators() { builder.pop(); // opacity builder.pop(); // base - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void platform_view_mutators_with_pixel_ratio() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); // base builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); @@ -598,29 +598,29 @@ void platform_view_mutators_with_pixel_ratio() { builder.pop(); // opacity builder.pop(); // base - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void empty_scene() { - window.onBeginFrame = (Duration duration) { - window.render(SceneBuilder().build()); + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.render(SceneBuilder().build()); signalNativeTest(); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void scene_with_no_container() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); signalNativeTest(); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } Picture CreateArcEndCapsPicture() { @@ -642,29 +642,29 @@ Picture CreateArcEndCapsPicture() { @pragma('vm:entry-point') void arc_end_caps_correct() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.addPicture(Offset(0.0, 0.0), CreateArcEndCapsPicture()); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void scene_builder_with_clips() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushClipRect(Rect.fromLTRB(10.0, 10.0, 390.0, 290.0)); builder.addPlatformView(42, width: 400.0, height: 300.0); builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void scene_builder_with_complex_clips() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 1024.0, 600.0)); @@ -675,9 +675,9 @@ void scene_builder_with_complex_clips() { builder.addPlatformView(42, width: 1024.0, height: 600.0); builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(1024.0, 600.0))); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } void sendObjectToNativeCode(dynamic object) native 'SendObjectToNativeCode'; @@ -691,41 +691,41 @@ void objects_can_be_posted() { @pragma('vm:entry-point') void empty_scene_posts_zero_layers_to_compositor() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); // Should not render anything. builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 300.0, 200.0)); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void compositor_can_post_only_platform_views() { - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); builder.addPlatformView(42, width: 300.0, height: 200.0); builder.addPlatformView(24, width: 300.0, height: 200.0); - window.render(builder.build()); + PlatformDispatcher.instance.render(builder.build()); }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } @pragma('vm:entry-point') void render_targets_are_recycled() { int frame_count = 0; - window.onBeginFrame = (Duration duration) { + PlatformDispatcher.instance.onBeginFrame = (Duration duration) { SceneBuilder builder = SceneBuilder(); for (int i = 0; i < 10; i++) { builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(30.0, 20.0))); builder.addPlatformView(42 + i, width: 30.0, height: 20.0); } - window.render(builder.build()); - window.scheduleFrame(); + PlatformDispatcher.instance.render(builder.build()); + PlatformDispatcher.instance.scheduleFrame(); frame_count++; if (frame_count == 8) { signalNativeTest(); } }; - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); } diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index a3ece90e032cc..930a45455d3eb 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -654,7 +654,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -747,7 +746,6 @@ TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -819,7 +817,6 @@ TEST_F(EmbedderTest, RasterCacheEnabled) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -920,7 +917,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1012,7 +1008,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1305,7 +1300,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1479,7 +1473,6 @@ TEST_F(EmbedderTest, event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1600,7 +1593,6 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1674,7 +1666,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1783,7 +1774,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1963,7 +1953,6 @@ TEST_F(EmbedderTest, // Flutter still thinks it is 800 x 600. Only the root surface is rotated. event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -1992,7 +1981,6 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositor) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2025,7 +2013,6 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorWithTransformation) { // Flutter still thinks it is 800 x 600. event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2051,7 +2038,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithoutCompositor) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2084,7 +2070,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) { // Flutter still thinks it is 800 x 600. event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2110,7 +2095,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositor) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2145,7 +2129,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) { // Flutter still thinks it is 800 x 600. event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2247,7 +2230,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2358,7 +2340,6 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) { // Flutter still thinks it is 800 x 600. event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -2459,7 +2440,6 @@ TEST_F(EmbedderTest, VerifyB141980393) { // achieved via a root surface transformation. event.width = flutter_application_rect.width(); event.height = flutter_application_rect.height(); - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -2512,7 +2492,6 @@ TEST_F(EmbedderTest, CaDeinitializeAnEngine) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kInvalidArguments); engine.reset(); @@ -2554,7 +2533,6 @@ TEST_F(EmbedderTest, CanCreateEmbedderWithCustomRenderTaskRunner) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); task_latch.Wait(); @@ -2614,7 +2592,6 @@ TEST_F(EmbedderTest, event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); }); @@ -2720,14 +2697,22 @@ TEST_F(EmbedderTest, auto engine = builder.LaunchEngine(); - // Send a window metrics events so frames may be scheduled. - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 400 * 2.0; - event.height = 300 * 2.0; - event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + // Send screen and window metrics events so frames may be scheduled. + FlutterScreenMetricsEvent screen_metrics_event = {}; + screen_metrics_event.struct_size = sizeof(screen_metrics_event); + screen_metrics_event.width = 400 * 2.0; + screen_metrics_event.height = 300 * 2.0; + screen_metrics_event.pixel_ratio = 2.0; + ASSERT_EQ( + FlutterEngineSendScreenMetricsEvent(engine.get(), &screen_metrics_event), + kSuccess); + FlutterWindowMetricsEvent window_metrics_event = {}; + window_metrics_event.struct_size = sizeof(window_metrics_event); + window_metrics_event.width = 400 * 2.0; + window_metrics_event.height = 300 * 2.0; + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), &window_metrics_event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2815,14 +2800,22 @@ TEST_F( auto engine = builder.LaunchEngine(); - // Send a window metrics events so frames may be scheduled. - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 400 * 2.0; - event.height = 300 * 2.0; - event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + // Send screen and window metrics events so frames may be scheduled. + FlutterScreenMetricsEvent screen_metrics_event = {}; + screen_metrics_event.struct_size = sizeof(screen_metrics_event); + screen_metrics_event.width = 400 * 2.0; + screen_metrics_event.height = 300 * 2.0; + screen_metrics_event.pixel_ratio = 2.0; + ASSERT_EQ( + FlutterEngineSendScreenMetricsEvent(engine.get(), &screen_metrics_event), + kSuccess); + FlutterWindowMetricsEvent window_metrics_event = {}; + window_metrics_event.struct_size = sizeof(window_metrics_event); + window_metrics_event.width = 400 * 2.0; + window_metrics_event.height = 300 * 2.0; + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), &window_metrics_event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2994,7 +2987,6 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { event.struct_size = sizeof(event); event.width = 1024; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -3036,7 +3028,6 @@ TEST_F(EmbedderTest, event.struct_size = sizeof(event); event.width = 1024; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -3074,7 +3065,6 @@ TEST_F(EmbedderTest, event.struct_size = sizeof(event); event.width = 1024; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -3180,7 +3170,6 @@ TEST_F(EmbedderTest, PlatformViewMutatorsAreValid) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -3271,14 +3260,22 @@ TEST_F(EmbedderTest, PlatformViewMutatorsAreValidWithPixelRatio) { auto engine = builder.LaunchEngine(); - // Send a window metrics events so frames may be scheduled. - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + // Send screen and window metrics events so frames may be scheduled. + FlutterScreenMetricsEvent screen_metrics_event = {}; + screen_metrics_event.struct_size = sizeof(screen_metrics_event); + screen_metrics_event.width = 400 * 2.0; + screen_metrics_event.height = 300 * 2.0; + screen_metrics_event.pixel_ratio = 2.0; + ASSERT_EQ( + FlutterEngineSendScreenMetricsEvent(engine.get(), &screen_metrics_event), + kSuccess); + FlutterWindowMetricsEvent window_metrics_event = {}; + window_metrics_event.struct_size = sizeof(window_metrics_event); + window_metrics_event.width = 400 * 2.0; + window_metrics_event.height = 300 * 2.0; + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), &window_metrics_event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -3374,14 +3371,22 @@ TEST_F(EmbedderTest, auto engine = builder.LaunchEngine(); - // Send a window metrics events so frames may be scheduled. - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + // Send screen and window metrics events so frames may be scheduled. + FlutterScreenMetricsEvent screen_metrics_event = {}; + screen_metrics_event.struct_size = sizeof(screen_metrics_event); + screen_metrics_event.width = 400 * 2.0; + screen_metrics_event.height = 300 * 2.0; + screen_metrics_event.pixel_ratio = 2.0; + ASSERT_EQ( + FlutterEngineSendScreenMetricsEvent(engine.get(), &screen_metrics_event), + kSuccess); + FlutterWindowMetricsEvent window_metrics_event = {}; + window_metrics_event.struct_size = sizeof(window_metrics_event); + window_metrics_event.width = 400 * 2.0; + window_metrics_event.height = 300 * 2.0; + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), &window_metrics_event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -3407,7 +3412,6 @@ TEST_F(EmbedderTest, EmptySceneIsAcceptable) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); latch.Wait(); @@ -3433,7 +3437,6 @@ TEST_F(EmbedderTest, SceneWithNoRootContainerIsAcceptable) { event.struct_size = sizeof(event); event.width = 800; event.height = 600; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); latch.Wait(); @@ -3466,7 +3469,6 @@ TEST_F(EmbedderTest, ArcEndCapsAreDrawnCorrectly) { event.struct_size = sizeof(event); event.width = 1024; event.height = 800; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -3590,7 +3592,6 @@ TEST_F(EmbedderTest, ClipsAreCorrectlyCalculated) { event.struct_size = sizeof(event); event.width = 400; event.height = 300; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -3672,7 +3673,6 @@ TEST_F(EmbedderTest, ComplexClipsAreCorrectlyCalculated) { event.struct_size = sizeof(event); event.width = 400; event.height = 300; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -4022,7 +4022,6 @@ TEST_F(EmbedderTest, CompositorCanPostZeroLayersForPresentation) { event.struct_size = sizeof(event); event.width = 300; event.height = 200; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -4085,7 +4084,6 @@ TEST_F(EmbedderTest, CompositorCanPostOnlyPlatformViews) { event.struct_size = sizeof(event); event.width = 300; event.height = 200; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); ASSERT_TRUE(engine.is_valid()); @@ -4124,7 +4122,6 @@ TEST_F(EmbedderTest, CompositorRenderTargetsAreRecycled) { event.struct_size = sizeof(event); event.width = 300; event.height = 200; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); @@ -4201,7 +4198,6 @@ TEST_F(EmbedderTest, CompositorRenderTargetsAreInStableOrder) { event.struct_size = sizeof(event); event.width = 300; event.height = 200; - event.pixel_ratio = 1.0; ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); diff --git a/shell/platform/fuchsia/flutter/compilation_trace.txt b/shell/platform/fuchsia/flutter/compilation_trace.txt index 95026cf438233..ffadbd1fb9cc3 100644 --- a/shell/platform/fuchsia/flutter/compilation_trace.txt +++ b/shell/platform/fuchsia/flutter/compilation_trace.txt @@ -3189,7 +3189,7 @@ dart:ui,TileMode,init:mirror dart:ui,TileMode,init:repeated dart:ui,TileMode,init:values dart:ui,Window,Window._ -dart:ui,Window,_defaultRouteName +dart:ui,Window,_initialRouteName dart:ui,Window,_sendPlatformMessage dart:ui,Window,_zonedPlatformMessageResponseCallback dart:ui,Window,get:_accessibilityFeatures @@ -3220,7 +3220,7 @@ dart:ui,Window,get:_textScaleFactor dart:ui,Window,get:_viewInsets dart:ui,Window,get:accessibilityFeatures dart:ui,Window,get:alwaysUse24HourFormat -dart:ui,Window,get:defaultRouteName +dart:ui,Window,get:initialRouteName dart:ui,Window,get:devicePixelRatio dart:ui,Window,get:initialLifecycleState dart:ui,Window,get:locales diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index 509575fdf6206..d52b4edf15ab2 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "lib/ui/window/screen_metrics.h" #define RAPIDJSON_HAS_STDSTRING 1 #include "platform_view.h" @@ -186,23 +187,38 @@ void PlatformView::UpdateViewportMetrics( void PlatformView::FlushViewportMetrics() { const auto scale = metrics_.scale; const auto scale_z = metrics_.scale_z; - - SetViewportMetrics({ - scale, // device_pixel_ratio - metrics_.size.width * scale, // physical_width - metrics_.size.height * scale, // physical_height - metrics_.size.depth * scale_z, // physical_depth - metrics_.padding.top * scale, // physical_padding_top - metrics_.padding.right * scale, // physical_padding_right - metrics_.padding.bottom * scale, // physical_padding_bottom - metrics_.padding.left * scale, // physical_padding_left - metrics_.view_inset.front * scale_z, // physical_view_inset_front - metrics_.view_inset.back * scale_z, // physical_view_inset_back - metrics_.view_inset.top * scale, // physical_view_inset_top - metrics_.view_inset.right * scale, // physical_view_inset_right - metrics_.view_inset.bottom * scale, // physical_view_inset_bottom - metrics_.view_inset.left * scale // physical_view_inset_left - }); + const flutter::ScreenMetrics screen_metrics{ + "", // display_name + scale, // device_pixel_ratio + 0, // physical_left + 0, // physical_top + metrics_.size.width * scale, // physical_width + metrics_.size.height * scale, // physical_height + metrics_.padding.top * scale, // physical_padding_top + metrics_.padding.right * scale, // physical_padding_right + metrics_.padding.bottom * scale, // physical_padding_bottom + metrics_.padding.left * scale, // physical_padding_left + metrics_.view_inset.top * scale, // physical_view_inset_top + metrics_.view_inset.right * scale, // physical_view_inset_right + metrics_.view_inset.bottom * scale, // physical_view_inset_bottom + metrics_.view_inset.left * scale // physical_view_inset_left + }; + SetScreenMetrics(screen_metrics); + SetViewportMetrics( + {0, // physical_left + 0, // physical_top + screen_metrics.physical_width, screen_metrics.physical_height, + metrics_.size.depth * scale_z, // physical_depth + screen_metrics.physical_padding_top, + screen_metrics.physical_padding_right, + screen_metrics.physical_padding_bottom, + screen_metrics.physical_padding_left, + metrics_.view_inset.front * scale_z, // physical_view_inset_front + metrics_.view_inset.back * scale_z, // physical_view_inset_back + screen_metrics.physical_view_inset_top, + screen_metrics.physical_view_inset_right, + screen_metrics.physical_view_inset_bottom, + screen_metrics.physical_view_inset_left}); } // |fuchsia::ui::input::InputMethodEditorClient| diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 6d6b30df8f1f6..7d405c82a382e 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -50,6 +50,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewSetViewportMetrics( const flutter::ViewportMetrics& metrics) {} // |flutter::PlatformView::Delegate| + void OnPlatformViewSetScreenMetrics(const flutter::ScreenMetrics& metrics) {} + // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchPlatformMessage( fml::RefPtr message) {} // |flutter::PlatformView::Delegate| diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index 8658b31203372..ac92a786f8f29 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -198,10 +198,23 @@ static double GetScreenCoordinatesPerInch() { static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, int width, int height) { + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = width; + event.height = height; + FlutterEngineSendWindowMetricsEvent(controller->engine->flutter_engine, + &event); +} + +// Sends a window metrics update to the Flutter engine using the given +// framebuffer size and the current window information in |state|. +static void SendScreenMetrics(FlutterDesktopWindowControllerState* controller, + int width, + int height) { double dpi = controller->window_wrapper->pixels_per_screen_coordinate * controller->monitor_screen_coordinates_per_inch; - FlutterWindowMetricsEvent event = {}; + FlutterScreenMetricsEvent event = {}; event.struct_size = sizeof(event); event.width = width; event.height = height; @@ -213,7 +226,7 @@ static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, } else { event.pixel_ratio = controller->window_wrapper->pixel_ratio_override; } - FlutterEngineSendWindowMetricsEvent(controller->engine->flutter_engine, + FlutterEngineSendScreenMetricsEvent(controller->engine->flutter_engine, &event); } @@ -246,6 +259,7 @@ static void GLFWFramebufferSizeCallback(GLFWwindow* window, controller->window_wrapper->pixels_per_screen_coordinate = width > 0 ? width_px / width : 1; + SendScreenMetrics(controller, width_px, height_px); SendWindowMetrics(controller, width_px, height_px); controller->window_wrapper->skip_next_window_refresh = true; } @@ -262,6 +276,7 @@ void GLFWWindowRefreshCallback(GLFWwindow* window) { int width_px, height_px; glfwGetFramebufferSize(window, &width_px, &height_px); if (width_px > 0 && height_px > 0) { + SendScreenMetrics(controller, width_px, height_px); SendWindowMetrics(controller, width_px, height_px); } } @@ -880,6 +895,7 @@ void FlutterDesktopWindowSetPixelRatioOverride( glfwGetFramebufferSize(flutter_window->window, &width_px, &height_px); if (width_px > 0 && height_px > 0) { auto* controller = GetWindowController(flutter_window->window); + SendScreenMetrics(controller, width_px, height_px); SendWindowMetrics(controller, width_px, height_px); } } diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index dd292d5f3b184..7f492ba154369 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -429,8 +429,7 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* self, void fl_engine_send_window_metrics_event(FlEngine* self, size_t width, - size_t height, - double pixel_ratio) { + size_t height) { g_return_if_fail(FL_IS_ENGINE(self)); if (self->engine == nullptr) @@ -440,10 +439,23 @@ void fl_engine_send_window_metrics_event(FlEngine* self, event.struct_size = sizeof(FlutterWindowMetricsEvent); event.width = width; event.height = height; - event.pixel_ratio = pixel_ratio; FlutterEngineSendWindowMetricsEvent(self->engine, &event); } +void fl_engine_send_screen_metrics_event(FlEngine* self, + size_t width, + size_t height, + double pixel_ratio) { + g_return_if_fail(FL_IS_ENGINE(self)); + + FlutterScreenMetricsEvent event = {}; + event.struct_size = sizeof(FlutterScreenMetricsEvent); + event.width = width; + event.height = height; + event.pixel_ratio = pixel_ratio; + FlutterEngineSendScreenMetricsEvent(self->engine, &event); +} + void fl_engine_send_mouse_pointer_event(FlEngine* self, FlutterPointerPhase phase, size_t timestamp, diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index 6c912979ba16c..854fb6995f767 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -92,11 +92,23 @@ gboolean fl_engine_start(FlEngine* engine, GError** error); * @engine: an #FlEngine. * @width: width of the window in pixels. * @height: height of the window in pixels. - * @pixel_ratio: scale factor for window. * * Sends a window metrics event to the engine. */ void fl_engine_send_window_metrics_event(FlEngine* engine, + size_t width, + size_t height); + +/** + * fl_engine_send_screen_metrics_event: + * @engine: a #FlEngine + * @width: width of the screen in pixels. + * @height: height of the screen in pixels. + * @pixel_ratio: scale factor for screen. + * + * Sends a screen metrics event to the engine. + */ +void fl_engine_send_screen_metrics_event(FlEngine* engine, size_t width, size_t height, double pixel_ratio); diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index c46919da3bff7..8564336d7da86 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -4,6 +4,8 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" +#include + #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_key_event_plugin.h" #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h" @@ -14,8 +16,6 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" -#include - static constexpr int kMicrosecondsPerMillisecond = 1000; struct _FlView { @@ -249,6 +249,7 @@ static void fl_view_size_allocate(GtkWidget* widget, } fl_view_send_window_metrics(self); + fl_view_send_screen_metrics(self); } // Implements GtkWidget::button_press_event. diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index 4976a70b064d0..68a5193ab81c0 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -195,12 +195,27 @@ void Win32FlutterWindow::SendWindowMetrics() { return; } - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = GetCurrentWidth(); - event.height = GetCurrentHeight(); - event.pixel_ratio = static_cast(GetCurrentDPI()) / base_dpi; - auto result = FlutterEngineSendWindowMetricsEvent(engine_, &event); + FlutterWindowMetricsEvent window_metrics_event = {}; + window_metrics_event.struct_size = sizeof(window_metrics_event); + window_metrics_event.width = GetCurrentWidth(); + window_metrics_event.height = GetCurrentHeight(); + auto result = + FlutterEngineSendWindowMetricsEvent(engine_, &window_metrics_event); +} + +void Win32FlutterWindow::SendScreenMetrics() { + if (engine_ == nullptr) { + return; + } + + FlutterScreenMetricsEvent screen_metrics_event = {}; + screen_metrics_event.struct_size = sizeof(screen_metrics_event); + screen_metrics_event.width = GetCurrentWidth(); + screen_metrics_event.height = GetCurrentHeight(); + screen_metrics_event.pixel_ratio = + static_cast(GetCurrentDPI()) / base_dpi; + auto result = + FlutterEngineSendScreenMetricsEvent(engine_, &screen_metrics_event); } // Updates |event_data| with the current location of the mouse cursor. diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index aff194a0e5ea4..d2d07e04e7b54 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -89,9 +89,13 @@ class Win32FlutterWindow : public Win32Window { bool SwapBuffers(); // Sends a window metrics update to the Flutter engine using current window - // dimensions in physical + // dimensions in physical pixels. void SendWindowMetrics(); + // Sends a screen metrics update to the Flutter engine using current screen + // dimensions in physical pixels. + void SendScreenMetrics(); + private: // Destroy current rendering surface if one has been allocated. void DestroyRenderSurface(); diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 73b6ce77257d0..aa68dfd338592 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -234,11 +234,16 @@ int RunTester(const flutter::Settings& settings, } }); - flutter::ViewportMetrics metrics; - metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400; // 800 at 3x resolution - metrics.physical_height = 1800; // 600 at 3x resolution - shell->GetPlatformView()->SetViewportMetrics(metrics); + flutter::ScreenMetrics screen_metrics; + screen_metrics.device_pixel_ratio = 3.0; + screen_metrics.physical_width = 2400; // 800 at 3x resolution + screen_metrics.physical_height = 1800; // 600 at 3x resolution + shell->GetPlatformView()->SetScreenMetrics(screen_metrics); + + flutter::ViewportMetrics window_metrics; + window_metrics.physical_width = 2400; // 800 at 3x resolution + window_metrics.physical_height = 1800; // 600 at 3x resolution + shell->GetPlatformView()->SetViewportMetrics(window_metrics); // Run the message loop and wait for the script to do its thing. fml::MessageLoop::GetCurrent().Run(); diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index ba9bfdbbb37e2..48e174aea10b3 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -27,7 +27,9 @@ part '../../lib/ui/hooks.dart'; part '../../lib/ui/lerp.dart'; part '../../lib/ui/natives.dart'; part '../../lib/ui/painting.dart'; +part '../../lib/ui/platform_dispatcher.dart'; part '../../lib/ui/pointer.dart'; +part '../../lib/ui/screen.dart'; part '../../lib/ui/semantics.dart'; part '../../lib/ui/text.dart'; part '../../lib/ui/window.dart'; @@ -44,16 +46,26 @@ void main() { PlatformMessageCallback? originalOnPlatformMessage; VoidCallback? originalOnTextScaleFactorChanged; - double? oldDPR; - Size? oldSize; - double? oldDepth; - WindowPadding? oldPadding; - WindowPadding? oldInsets; - WindowPadding? oldSystemGestureInsets; + Object? oldWindowId; + Object? oldScreenId; + Rect? oldGeometry; + double? oldDepth; + WindowPadding? oldPadding; + WindowPadding? oldInsets; + WindowPadding? oldSystemGestureInsets; void setUp() { - oldDPR = window.devicePixelRatio; - oldSize = window.physicalSize; + PlatformDispatcher.instance._viewConfigurations.clear(); + PlatformDispatcher.instance._screenConfigurations.clear(); + PlatformDispatcher.instance._views.clear(); + PlatformDispatcher.instance._screens.clear(); + PlatformDispatcher.instance._screenConfigurations[0] = const ScreenConfiguration(); + PlatformDispatcher.instance._screens[0] = Screen._(screenId: 0, platformDispatcher: PlatformDispatcher.instance); + PlatformDispatcher.instance._viewConfigurations[0] = ViewConfiguration(screen: PlatformDispatcher.instance._screens[0]); + PlatformDispatcher.instance._views[0] = FlutterWindow._(windowId: 0, platformDispatcher: PlatformDispatcher.instance); + oldWindowId = window._windowId; + oldScreenId = window.viewConfiguration.screen._screenId; + oldGeometry = window.viewConfiguration.geometry; oldDepth = window.physicalDepth; oldPadding = window.viewPadding; oldInsets = window.viewInsets; @@ -73,9 +85,12 @@ void main() { void tearDown() { _updateWindowMetrics( - oldDPR!, // DPR - oldSize!.width, // width - oldSize!.height, // height + oldWindowId!, // window id + oldScreenId!, // screen id + oldGeometry!.left, // window left coordinate + oldGeometry!.top, // window top coordinate + oldGeometry!.width, // width + oldGeometry!.height, // height oldDepth!, // depth oldPadding!.top, // padding top oldPadding!.right, // padding right @@ -146,19 +161,22 @@ void main() { test('onMetricsChanged preserves callback zone', () { late Zone innerZone; late Zone runZone; - late double devicePixelRatio; + late double left; runZoned(() { innerZone = Zone.current; window.onMetricsChanged = () { runZone = Zone.current; - devicePixelRatio = window.devicePixelRatio; + left = window.physicalGeometry.left; }; }); window.onMetricsChanged!(); _updateWindowMetrics( - 0.1234, // DPR + 0, // window id + 0, // screen id + 0.1234, // left + 0.0, // top 0.0, // width 0.0, // height 0.0, // depth @@ -177,7 +195,7 @@ void main() { ); expectNotEquals(runZone, null); expectIdentical(runZone, innerZone); - expectEquals(devicePixelRatio, 0.1234); + expectEquals(left, 0.1234); }); test('onLocaleChanged preserves callback zone', () { @@ -238,7 +256,7 @@ void main() { late Zone innerZone; late Zone runZone; - window._setNeedsReportTimings = (bool _) {}; + PlatformDispatcher.instance._setNeedsReportTimings = (bool _) {}; runZoned(() { innerZone = Zone.current; @@ -285,11 +303,13 @@ void main() { }; }); - _updateSemanticsEnabled(window._semanticsEnabled); + final bool newValue = !window.semanticsEnabled; + _updateSemanticsEnabled(newValue); expectNotEquals(runZone, null); expectIdentical(runZone, innerZone); expectNotEquals(enabled, null); - expectEquals(enabled, window._semanticsEnabled); + expectEquals(enabled, newValue); + expectEquals(enabled, window.semanticsEnabled); }); test('onSemanticsAction preserves callback zone', () { @@ -335,22 +355,39 @@ void main() { test('onTextScaleFactorChanged preserves callback zone', () { late Zone innerZone; - late Zone runZone; + late Zone runZoneTextScaleFactor; + Zone runZonePlatformBrightness; late double textScaleFactor; + Brightness platformBrightness; runZoned(() { innerZone = Zone.current; window.onTextScaleFactorChanged = () { - runZone = Zone.current; + runZoneTextScaleFactor = Zone.current; textScaleFactor = window.textScaleFactor; }; + window.onPlatformBrightnessChanged = () { + runZonePlatformBrightness = Zone.current; + platformBrightness = window.platformBrightness; + }; }); - window.onTextScaleFactorChanged!(); - _updateTextScaleFactor(0.5); - expectNotEquals(runZone, null); - expectIdentical(runZone, innerZone); - expectEquals(textScaleFactor, 0.5); + window.onTextScaleFactorChanged(); + + _updateUserSettingsData('{"textScaleFactor": 0.5, "platformBrightness": "light", "alwaysUse24HourFormat": true}'); + expect(runZoneTextScaleFactor, isNotNull); + expect(runZoneTextScaleFactor, same(innerZone)); + expect(textScaleFactor, equals(0.5)); + + textScaleFactor = null; + platformBrightness = null; + + window.onPlatformBrightnessChanged(); + + _updateUserSettingsData('{"textScaleFactor": 0.5, "platformBrightness": "dark", "alwaysUse24HourFormat": true}'); + expect(runZonePlatformBrightness, isNotNull); + expect(runZonePlatformBrightness, same(innerZone)); + expect(platformBrightness, equals(Brightness.dark)); }); test('onThemeBrightnessMode preserves callback zone', () { @@ -374,24 +411,28 @@ void main() { }); test('Window padding/insets/viewPadding/systemGestureInsets', () { - _updateWindowMetrics( - 1.0, // DPR - 800.0, // width - 600.0, // height - 100.0, // depth - 50.0, // padding top - 0.0, // padding right - 40.0, // padding bottom - 0.0, // padding left - 0.0, // inset top - 0.0, // inset right - 0.0, // inset bottom - 0.0, // inset left - 0.0, // system gesture inset top - 0.0, // system gesture inset right - 0.0, // system gesture inset bottom - 0.0, // system gesture inset left - ); + test('Window padding/insets/viewPadding/systemGestureInsets', () { + _updateWindowMetrics( + 0, // window id + 0, // screen id + 10.0, // left + 11.0, // top + 800.0, // width + 600.0, // height + 100.0, // depth + 50.0, // padding top + 0.0, // padding right + 40.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 0.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 0.0, // system gesture inset bottom + 0.0, // system gesture inset left + ); expectEquals(window.viewInsets.bottom, 0.0); expectEquals(window.viewPadding.bottom, 40.0); @@ -399,24 +440,27 @@ void main() { expectEquals(window.physicalDepth, 100.0); expectEquals(window.systemGestureInsets.bottom, 0.0); - _updateWindowMetrics( - 1.0, // DPR - 800.0, // width - 600.0, // height - 100.0, // depth - 50.0, // padding top - 0.0, // padding right - 40.0, // padding bottom - 0.0, // padding left - 0.0, // inset top - 0.0, // inset right - 400.0, // inset bottom - 0.0, // inset left - 0.0, // system gesture insets top - 0.0, // system gesture insets right - 44.0, // system gesture insets bottom - 0.0, // system gesture insets left - ); + _updateWindowMetrics( + 0, // window id + 0, // screen id + 10.0, // left + 11.0, // top + 800.0, // width + 600.0, // height + 100.0, // depth + 50.0, // padding top + 0.0, // padding right + 40.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 400.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 44.0, // system gesture inset bottom + 0.0, // system gesture inset left + ); expectEquals(window.viewInsets.bottom, 400.0); expectEquals(window.viewPadding.bottom, 40.0); diff --git a/testing/scenario_app/lib/src/animated_color_square.dart b/testing/scenario_app/lib/src/animated_color_square.dart index f1e9865f55309..b85f20d4d442b 100644 --- a/testing/scenario_app/lib/src/animated_color_square.dart +++ b/testing/scenario_app/lib/src/animated_color_square.dart @@ -16,10 +16,10 @@ import 'scenario.dart'; class AnimatedColorSquareScenario extends Scenario { /// Creates the AnimatedColorSquare scenario. /// - /// The [window] parameter must not be null. - AnimatedColorSquareScenario(Window window) - : assert(window != null), - super(window); + /// The [dispatcher] parameter must not be null. + AnimatedColorSquareScenario(PlatformDispatcher dispatcher) + : assert(dispatcher != null), + super(dispatcher); static const double _squareSize = 200; /// Used to animate the red value in the color of the square. diff --git a/testing/scenario_app/lib/src/channel_util.dart b/testing/scenario_app/lib/src/channel_util.dart index 5f8c4c0d1d296..bf8fd2fae7e0d 100644 --- a/testing/scenario_app/lib/src/channel_util.dart +++ b/testing/scenario_app/lib/src/channel_util.dart @@ -10,7 +10,7 @@ import 'package:meta/meta.dart'; /// Util method to replicate the behavior of a `MethodChannel` in the Flutter /// framework. void sendJsonMethodCall({ - @required Window window, + @required SingletonFlutterWindow window, @required String channel, @required String method, dynamic arguments, diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index 4a343ff9140d8..6e1ea2ba0936d 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -12,9 +12,9 @@ import 'scenario.dart'; /// Sends the recieved locale data back as semantics information. class LocaleInitialization extends Scenario { /// Constructor - LocaleInitialization(Window window) - : assert(window != null), - super(window); + LocaleInitialization(PlatformDispatcher dispatcher) + : assert(dispatcher != null), + super(dispatcher); int _tapCount = 0; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 293569c895727..a8efbaa7363d4 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -32,11 +32,11 @@ List _to64(num value) { class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -53,11 +53,11 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewNoOverlayIntersectionScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -78,11 +78,11 @@ class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatf class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewPartialIntersectionScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -103,11 +103,11 @@ class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatfor class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewTwoIntersectingOverlaysScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -141,11 +141,11 @@ class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePla class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -184,11 +184,11 @@ class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario wit class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + MultiPlatformViewWithoutOverlaysScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -223,11 +223,11 @@ class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatfo class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + /// The [dispatcher] parameter must not be null. + PlatformViewMaxOverlaysScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -271,12 +271,12 @@ class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewSce class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - MultiPlatformViewScenario(Window window, {this.firstId, this.secondId}) - : assert(window != null), - super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + /// The [dispatcher] parameter must not be null. + MultiPlatformViewScenario(PlatformDispatcher dispatcher, {this.firstId, this.secondId}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, 'platform view 1', firstId); + createPlatformView(dispatcher, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -307,12 +307,12 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) - : assert(window != null), - super(window) { - createPlatformView(window, 'platform view 1', firstId); - createPlatformView(window, 'platform view 2', secondId); + /// The [dispatcher] parameter must not be null. + MultiPlatformViewBackgroundForegroundScenario(PlatformDispatcher dispatcher, {this.firstId, this.secondId}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, 'platform view 1', firstId); + createPlatformView(dispatcher, 'platform view 2', secondId); _nextFrame = _firstFrame; } @@ -394,10 +394,10 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP /// Platform view with clip rect. class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); + PlatformViewClipRectScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : assert(dispatcher != null), + super(dispatcher) { + createPlatformView(dispatcher, text, id); } @override @@ -412,8 +412,8 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar /// Platform view with clip rrect. class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipRRectScenario(Window window, String text, {int id = 0}) - : super(window, text, id: id); + PlatformViewClipRRectScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : super(dispatcher, text, id: id); @override void onBeginFrame(Duration duration) { @@ -438,8 +438,8 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Platform view with clip path. class PlatformViewClipPathScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipPathScenario(Window window, String text, {int id = 0}) - : super(window, text, id: id); + PlatformViewClipPathScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : super(dispatcher, text, id: id); @override void onBeginFrame(Duration duration) { @@ -462,8 +462,8 @@ class PlatformViewClipPathScenario extends PlatformViewScenario { /// Platform view with transform. class PlatformViewTransformScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewTransformScenario(Window window, String text, {int id = 0}) - : super(window, text, id: id); + PlatformViewTransformScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : super(dispatcher, text, id: id); @override void onBeginFrame(Duration duration) { @@ -484,8 +484,8 @@ class PlatformViewTransformScenario extends PlatformViewScenario { /// Platform view with opacity. class PlatformViewOpacityScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewOpacityScenario(Window window, String text, {int id = 0}) - : super(window, text, id: id); + PlatformViewOpacityScenario(PlatformDispatcher dispatcher, String text, {int id = 0}) + : super(dispatcher, text, id: id); @override void onBeginFrame(Duration duration) { @@ -506,16 +506,16 @@ class PlatformViewForTouchIOSScenario extends Scenario bool _accept; /// Creates the PlatformView scenario. /// - /// The [window] parameter must not be null. - PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0, bool accept, bool rejectUntilTouchesEnded = false}) - : assert(window != null), + /// The [dispatcher] parameter must not be null. + PlatformViewForTouchIOSScenario(PlatformDispatcher dispatcher, String text, {int id = 0, bool accept, bool rejectUntilTouchesEnded = false}) + : assert(dispatcher != null), _accept = accept, _viewId = id, - super(window) { + super(dispatcher) { if (rejectUntilTouchesEnded) { - createPlatformView(window, text, id, viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded'); + createPlatformView(dispatcher, text, id, viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded'); } else { - createPlatformView(window, text, id); + createPlatformView(dispatcher, text, id); } } @@ -567,7 +567,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id, {String viewType = 'scenarios/textPlatformView'}) { + void createPlatformView(PlatformDispatcher dispatcher, String text, int id, {String viewType = 'scenarios/textPlatformView'}) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -618,7 +618,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { ...utf8.encode(text), ]); - window.sendPlatformMessage( + dispatcher.sendPlatformMessage( 'flutter/platform_views', message.buffer.asByteData(), (ByteData response) { diff --git a/testing/scenario_app/lib/src/poppable_screen.dart b/testing/scenario_app/lib/src/poppable_screen.dart index c2a1f91c71afe..e62f63847ccd5 100644 --- a/testing/scenario_app/lib/src/poppable_screen.dart +++ b/testing/scenario_app/lib/src/poppable_screen.dart @@ -14,10 +14,10 @@ import 'scenario.dart'; class PoppableScreenScenario extends Scenario with PlatformEchoMixin { /// Creates the PoppableScreenScenario. /// - /// The [window] parameter must not be null. - PoppableScreenScenario(Window window) - : assert(window != null), - super(window); + /// The [dispatcher] parameter must not be null. + PoppableScreenScenario(PlatformDispatcher dispatcher) + : assert(dispatcher != null), + super(dispatcher); // Rect for the pop button. Only defined once onMetricsChanged is called. Rect _buttonRect; diff --git a/testing/scenario_app/lib/src/scenario.dart b/testing/scenario_app/lib/src/scenario.dart index 4d53abcd0b32c..b0d57d071b75b 100644 --- a/testing/scenario_app/lib/src/scenario.dart +++ b/testing/scenario_app/lib/src/scenario.dart @@ -8,37 +8,37 @@ import 'dart:ui'; /// A scenario to run for testing. abstract class Scenario { - /// Creates a new scenario using a specific Window instance. - const Scenario(this.window); + /// Creates a new scenario using a specific FlutterWindow instance. + const Scenario(this.dispatcher); /// The window used by this scenario. May be mocked. - final Window window; + final PlatformDispatcher dispatcher; /// Called by the program when a frame is ready to be drawn. /// - /// See [Window.onBeginFrame] for more details. + /// See [FlutterWindow.onBeginFrame] for more details. void onBeginFrame(Duration duration) {} /// Called by the program when the microtasks from [onBeginFrame] have been /// flushed. /// - /// See [Window.onDrawFrame] for more details. + /// See [FlutterWindow.onDrawFrame] for more details. void onDrawFrame() {} /// Called by the program when the window metrics have changed. /// - /// See [Window.onMetricsChanged]. + /// See [FlutterWindow.onMetricsChanged]. void onMetricsChanged() {} /// Called by the program when a pointer event is received. /// - /// See [Window.onPointerDataPacket]. + /// See [FlutterWindow.onPointerDataPacket]. void onPointerDataPacket(PointerDataPacket packet) {} /// Called by the program when an engine side platform channel message is /// received. /// - /// See [Window.onPlatformMessage]. + /// See [FlutterWindow.onPlatformMessage]. void onPlatformMessage( String name, ByteData data, diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 861c71a354bfc..385583b4b3986 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -14,29 +14,29 @@ import 'send_text_focus_semantics.dart'; import 'touches_scenario.dart'; Map _scenarios = { - 'animated_color_square': AnimatedColorSquareScenario(window), - 'locale_initialization': LocaleInitialization(window), - 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), - 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), - 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), - 'platform_view_transform': PlatformViewTransformScenario(window, 'PlatformViewTransform', id: 4), - 'platform_view_opacity': PlatformViewOpacityScenario(window, 'PlatformViewOpacity', id: 5), - 'platform_view_multiple': MultiPlatformViewScenario(window, firstId: 6, secondId: 7), - 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), - 'poppable_screen': PoppableScreenScenario(window), - 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), - 'platform_view_gesture_reject_eager': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false), - 'platform_view_gesture_accept': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: true), - 'platform_view_gesture_reject_after_touches_ended': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false, rejectUntilTouchesEnded: true), - 'tap_status_bar': TouchesScenario(window), - 'text_semantics_focus': SendTextFocusScemantics(window), + 'animated_color_square': AnimatedColorSquareScenario(PlatformDispatcher.instance), + 'locale_initialization': LocaleInitialization(PlatformDispatcher.instance), + 'platform_view': PlatformViewScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_cliprect': PlatformViewClipRectScenario(PlatformDispatcher.instance, 'PlatformViewClipRect', id: 1), + 'platform_view_cliprrect': PlatformViewClipRRectScenario(PlatformDispatcher.instance, 'PlatformViewClipRRect', id: 2), + 'platform_view_clippath': PlatformViewClipPathScenario(PlatformDispatcher.instance, 'PlatformViewClipPath', id: 3), + 'platform_view_transform': PlatformViewTransformScenario(PlatformDispatcher.instance, 'PlatformViewTransform', id: 4), + 'platform_view_opacity': PlatformViewOpacityScenario(PlatformDispatcher.instance, 'PlatformViewOpacity', id: 5), + 'platform_view_multiple': MultiPlatformViewScenario(PlatformDispatcher.instance, firstId: 6, secondId: 7), + 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(PlatformDispatcher.instance, firstId: 8, secondId: 9), + 'poppable_screen': PoppableScreenScenario(PlatformDispatcher.instance), + 'platform_view_rotate': PlatformViewScenario(PlatformDispatcher.instance, 'Rotate Platform View', id: 10), + 'platform_view_gesture_reject_eager': PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: 11, accept: false), + 'platform_view_gesture_accept': PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: 11, accept: true), + 'platform_view_gesture_reject_after_touches_ended': PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: 11, accept: false, rejectUntilTouchesEnded: true), + 'tap_status_bar': TouchesScenario(PlatformDispatcher.instance), + 'text_semantics_focus': SendTextFocusScemantics(PlatformDispatcher.instance), }; Map _currentScenario = { diff --git a/testing/scenario_app/lib/src/send_text_focus_semantics.dart b/testing/scenario_app/lib/src/send_text_focus_semantics.dart index 577331be1131c..ebaaf2da4bc84 100644 --- a/testing/scenario_app/lib/src/send_text_focus_semantics.dart +++ b/testing/scenario_app/lib/src/send_text_focus_semantics.dart @@ -13,7 +13,7 @@ import 'scenario.dart'; /// A scenario that sends back messages when touches are received. class SendTextFocusScemantics extends Scenario { /// Constructor for `SendTextFocusScemantics`. - SendTextFocusScemantics(Window window) : super(window); + SendTextFocusScemantics(PlatformDispatcher dispatcher) : super(dispatcher); @override void onBeginFrame(Duration duration) { diff --git a/testing/scenario_app/lib/src/touches_scenario.dart b/testing/scenario_app/lib/src/touches_scenario.dart index 7afcf030fc8ab..e558858ed9682 100644 --- a/testing/scenario_app/lib/src/touches_scenario.dart +++ b/testing/scenario_app/lib/src/touches_scenario.dart @@ -10,7 +10,7 @@ import 'scenario.dart'; /// A scenario that sends back messages when touches are received. class TouchesScenario extends Scenario { /// Constructor for `TouchesScenario`. - TouchesScenario(Window window) : super(window); + TouchesScenario(PlatformDispatcher dispatcher) : super(dispatcher); @override void onPointerDataPacket(PointerDataPacket packet) { diff --git a/tools/const_finder/.dart_tool/package_config.json b/tools/const_finder/.dart_tool/package_config.json index 5ca024b38e7ab..1f3ca5d30af99 100644 --- a/tools/const_finder/.dart_tool/package_config.json +++ b/tools/const_finder/.dart_tool/package_config.json @@ -5,7 +5,7 @@ "name": "args", "rootUri": "../../../../third_party/dart/third_party/pkg/args", "packageUri": "lib/", - "languageVersion": "2.0" + "languageVersion": "2.3" }, { "name": "kernel", @@ -17,7 +17,7 @@ "name": "meta", "rootUri": "../../../../third_party/dart/pkg/meta", "packageUri": "lib/", - "languageVersion": "1.12" + "languageVersion": "2.9" }, { "name": "path", @@ -32,7 +32,7 @@ "languageVersion": "2.4" } ], - "generated": "2020-01-16T19:11:54.963296Z", + "generated": "2020-05-13T00:38:22.592345Z", "generator": "pub", - "generatorVersion": "2.7.0" + "generatorVersion": "2.9.0-7.0.dev.flutter-092ed38a87" } diff --git a/tools/const_finder/.packages b/tools/const_finder/.packages index 2a698fd32e89e..77b7ed055f82f 100644 --- a/tools/const_finder/.packages +++ b/tools/const_finder/.packages @@ -1,4 +1,4 @@ -# Generated by pub on 2020-01-16 11:11:54.947929. +# Generated by pub on 2020-05-12 17:38:22.578268. args:../../../third_party/dart/third_party/pkg/args/lib/ kernel:../../../third_party/dart/pkg/kernel/lib/ meta:../../../third_party/dart/pkg/meta/lib/ From ca2021cd2ace8dc9bfc51f4758958e1ab0aa0ea6 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 2 Jun 2020 14:00:15 -0700 Subject: [PATCH 02/14] Add a test --- lib/ui/BUILD.gn | 1 + .../platform_configuration_unittests.cc | 58 +++++++++++++++++++ lib/ui/window/screen.cc | 3 +- lib/ui/window/window.cc | 3 +- lib/ui/window/window.h | 2 + shell/common/shell_unittests.cc | 2 +- 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 lib/ui/window/platform_configuration_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 751d871acc5b0..9c6be3c7c2097 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -171,6 +171,7 @@ if (current_toolchain == host_toolchain) { "painting/image_decoder_test.h", "painting/image_decoder_unittests.cc", "painting/vertices_unittests.cc", + "window/platform_configuration_unittests.cc", "window/pointer_data_packet_converter_unittests.cc", ] diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc new file mode 100644 index 0000000000000..b1b9994708267 --- /dev/null +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "dart_api.h" +#include "runtime/dart_isolate.h" +#define FML_USED_ON_EMBEDDER + +#include + +#include "flutter/lib/ui/window/platform_configuration.h" + +#include "flutter/fml/mapping.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/testing/testing.h" +#include "gtest/gtest.h" +#include "lib/ui/text/font_collection.h" + +namespace flutter { +namespace testing { + +class DummyWindowClient : public WindowClient { + public: + DummyWindowClient() { + std::vector data; + isolate_data_.reset(new ::fml::DataMapping(data)); + } + virtual std::string InitialRouteName() { return "TestRoute"; } + virtual void ScheduleFrame() {} + virtual void Render(Scene* scene) {} + virtual void UpdateSemantics(SemanticsUpdate* update) {} + virtual void HandlePlatformMessage(fml::RefPtr message) {} + virtual FontCollection& GetFontCollection() { + return font_collection_; + } + virtual void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) {} + virtual void SetNeedsReportTimings(bool value) {} + virtual std::shared_ptr GetPersistentIsolateData() { return isolate_data_; } + + private: + FontCollection font_collection_; + std::shared_ptr isolate_data_; +}; + +TEST(PlatformConfigurationTest, PlatformConfigurationInitialization) { + DummyWindowClient client; + PlatformConfiguration configuration(&client); + + ASSERT_NE(configuration.get_window(0), nullptr); + ASSERT_NE(configuration.get_screen(0), nullptr); + ASSERT_EQ(configuration.get_window(0)->window_id(), 0); + ASSERT_EQ(configuration.get_window(0)->screen(), 0); + ASSERT_EQ(configuration.get_screen(0)->screen_id(), 0); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/window/screen.cc b/lib/ui/window/screen.cc index d1ba9097bf52a..a96ac2ccb6968 100644 --- a/lib/ui/window/screen.cc +++ b/lib/ui/window/screen.cc @@ -23,8 +23,9 @@ void Screen::UpdateScreenMetrics(const ScreenMetrics& metrics) { screen_metrics_ = metrics; std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) + if (!dart_state) { return; + } tonic::DartState::Scope scope(dart_state); tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateScreenMetrics", diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index fc9532112deff..b68837b44743f 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -25,8 +25,9 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { viewport_metrics_ = metrics; std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) + if (!dart_state) { return; + } tonic::DartState::Scope scope(dart_state); tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateWindowMetrics", diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 7c76b73bf862a..b84d2f07ac246 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -22,6 +22,8 @@ class Window final { ~Window(); + int window_id() const { return window_id_; } + int screen() const { return screen_id_; } const ViewportMetrics& viewport_metrics() { return viewport_metrics_; } diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 07c9742725b00..185a88f510cc6 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1229,7 +1229,7 @@ TEST_F(ShellTest, CanDecompressImageFromAsset) { } TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { - // Create 2 dummpy SkSL cache file IE (base32 encoding of A), II (base32 + // Create 2 dummy SkSL cache file IE (base32 encoding of A), II (base32 // encoding of B) with content x and y. fml::ScopedTemporaryDirectory temp_dir; PersistentCache::SetCacheDirectoryPath(temp_dir.path()); From b04bfcd6ba67e191044d09c2d11f8b3355e8a37f Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 3 Jun 2020 17:09:34 -0700 Subject: [PATCH 03/14] Fix some more tests --- ci/licenses_golden/licenses_flutter | 1 + .../platform_configuration_unittests.cc | 28 +++++++++---------- shell/platform/embedder/embedder.cc | 27 ++++++++++-------- shell/platform/glfw/flutter_glfw.cc | 2 +- shell/platform/linux/testing/mock_engine.cc | 7 +++++ 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c0b6860574acc..f96e9b25366b2 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -396,6 +396,7 @@ FILE: ../../../flutter/lib/ui/ui_dart_state.h FILE: ../../../flutter/lib/ui/window.dart FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h +FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc FILE: ../../../flutter/lib/ui/window/platform_message.cc FILE: ../../../flutter/lib/ui/window/platform_message.h FILE: ../../../flutter/lib/ui/window/platform_message_response.cc diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index b1b9994708267..4e3e5e4798dac 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -30,13 +30,13 @@ class DummyWindowClient : public WindowClient { virtual void Render(Scene* scene) {} virtual void UpdateSemantics(SemanticsUpdate* update) {} virtual void HandlePlatformMessage(fml::RefPtr message) {} - virtual FontCollection& GetFontCollection() { - return font_collection_; - } + virtual FontCollection& GetFontCollection() { return font_collection_; } virtual void UpdateIsolateDescription(const std::string isolate_name, int64_t isolate_port) {} virtual void SetNeedsReportTimings(bool value) {} - virtual std::shared_ptr GetPersistentIsolateData() { return isolate_data_; } + virtual std::shared_ptr GetPersistentIsolateData() { + return isolate_data_; + } private: FontCollection font_collection_; @@ -44,15 +44,15 @@ class DummyWindowClient : public WindowClient { }; TEST(PlatformConfigurationTest, PlatformConfigurationInitialization) { - DummyWindowClient client; - PlatformConfiguration configuration(&client); - - ASSERT_NE(configuration.get_window(0), nullptr); - ASSERT_NE(configuration.get_screen(0), nullptr); - ASSERT_EQ(configuration.get_window(0)->window_id(), 0); - ASSERT_EQ(configuration.get_window(0)->screen(), 0); - ASSERT_EQ(configuration.get_screen(0)->screen_id(), 0); + DummyWindowClient client; + PlatformConfiguration configuration(&client); + + ASSERT_NE(configuration.get_window(0), nullptr); + ASSERT_NE(configuration.get_screen(0), nullptr); + ASSERT_EQ(configuration.get_window(0)->window_id(), 0); + ASSERT_EQ(configuration.get_window(0)->screen(), 0); + ASSERT_EQ(configuration.get_screen(0)->screen_id(), 0); } -} // namespace testing -} // namespace flutter +} // namespace testing +} // namespace flutter diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 8df0d592507f7..50de0f234301f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1114,18 +1114,20 @@ FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterWindowMetricsEvent* flutter_metrics) { - if (engine == nullptr || flutter_metrics == nullptr) { + const FlutterWindowMetricsEvent* window_metrics_event) { + if (engine == nullptr || window_metrics_event == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } - flutter::ViewportMetrics metrics; + flutter::ViewportMetrics viewport_metrics; - metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); - metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); + viewport_metrics.physical_width = + SAFE_ACCESS(window_metrics_event, width, 0.0); + viewport_metrics.physical_height = + SAFE_ACCESS(window_metrics_event, height, 0.0); return reinterpret_cast(engine)->SetViewportMetrics( - std::move(metrics)) + std::move(viewport_metrics)) ? kSuccess : LOG_EMBEDDER_ERROR(kInvalidArguments, "Viewport metrics were invalid."); @@ -1138,20 +1140,21 @@ FlutterEngineResult FlutterEngineSendScreenMetricsEvent( return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } - flutter::ScreenMetrics metrics; + flutter::ScreenMetrics screen_metrics; - metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); - metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); - metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); + screen_metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); + screen_metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); + screen_metrics.device_pixel_ratio = + SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); - if (metrics.device_pixel_ratio <= 0.0) { + if (screen_metrics.device_pixel_ratio <= 0.0) { return LOG_EMBEDDER_ERROR( kInvalidArguments, "Device pixel ratio was invalid. It must be greater than zero."); } return reinterpret_cast(engine)->SetScreenMetrics( - std::move(metrics)) + std::move(screen_metrics)) ? kSuccess : LOG_EMBEDDER_ERROR(kInvalidArguments, "Screen metrics were invalid."); diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index ac92a786f8f29..fdb652bd64733 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -194,7 +194,7 @@ static double GetScreenCoordinatesPerInch() { } // Sends a window metrics update to the Flutter engine using the given -// framebuffer size and the current window information in |state|. +// framebuffer size and the current window informaftion in |state|. static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, int width, int height) { diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc index 8759553765dca..5a961a1fdbec2 100644 --- a/shell/platform/linux/testing/mock_engine.cc +++ b/shell/platform/linux/testing/mock_engine.cc @@ -201,6 +201,13 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent( return kSuccess; } +FlutterEngineResult FlutterEngineSendScreenMetricsEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterScreenMetricsEvent* event) { + EXPECT_TRUE(engine->running); + return kSuccess; +} + FlutterEngineResult FlutterEngineSendPointerEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent* events, From df4a4a80197908c42925d6a8088f7298c4475c23 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 8 Jun 2020 13:03:08 -0700 Subject: [PATCH 04/14] Sync to head, added lots of NNBD hints, and fixed a problem with screen initialization order on web. --- lib/ui/hooks.dart | 15 +- lib/ui/platform_dispatcher.dart | 318 +++++++++--------- lib/ui/screen.dart | 10 +- lib/ui/window/platform_configuration.cc | 6 +- .../src/engine/compositor/screen_metrics.dart | 8 +- .../engine/compositor/viewport_metrics.dart | 6 +- .../lib/src/engine/platform_dispatcher.dart | 240 ++++++------- lib/web_ui/lib/src/engine/window.dart | 24 +- .../lib/src/ui/platform_dispatcher.dart | 240 +++++++------ lib/web_ui/lib/src/ui/screen.dart | 2 +- lib/web_ui/lib/src/ui/window.dart | 8 +- 11 files changed, 428 insertions(+), 449 deletions(-) diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 601214dd8f167..c5f3007f7bf6e 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -31,8 +31,9 @@ void _updateWindowMetrics( double systemGestureInsetBottom, double systemGestureInsetLeft, ) { + assert(PlatformDispatcher.instance._screens[screenId] != null); final ViewConfiguration previousConfiguration = - PlatformDispatcher.instance._viewConfigurations[id] ?? const ViewConfiguration(); + PlatformDispatcher.instance._viewConfigurations[id] ?? ViewConfiguration(screen: PlatformDispatcher.instance._screens[screenId]); PlatformDispatcher.instance._viewConfigurations[id] = previousConfiguration.copyWith( screen: PlatformDispatcher.instance._screens[screenId], geometry: Rect.fromLTWH(left, top, width, height), @@ -228,18 +229,6 @@ void _updateUserSettingsData(String jsonData) { } } -@pragma('vm:entry-point') -// ignore: unused_element -void _resetWindowConfiguration(Object id) { - PlatformDispatcher.instance._viewConfigurations[id] = const ViewConfiguration(); -} - -@pragma('vm:entry-point') -// ignore: unused_element -void _resetScreenConfiguration(Object id) { - PlatformDispatcher.instance._screenConfigurations[id] = const ScreenConfiguration(); -} - @pragma('vm:entry-point') // ignore: unused_element void _updateLifecycleState(String state) { diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 43c85b43d977c..ef3f79df87324 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -7,10 +7,10 @@ part of dart.ui; // Callback types for events. -typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration/*!*/ configuration); -typedef ViewCreatedCallback = void Function(FlutterView view); -typedef ViewDisposedCallback = void Function(FlutterView view); +typedef ViewCreatedCallback = void Function(FlutterView/*!*/ view); +typedef ViewDisposedCallback = void Function(FlutterView/*!*/ view); /// Platform event dispatcher singleton. /// @@ -55,20 +55,20 @@ class PlatformDispatcher { /// inappropriate is if access to these APIs is required before invoking /// `runApp()`. In that case, it is acceptable (though unfortunate) to use the /// [PlatformDispatcher.instance] object statically. - static PlatformDispatcher get instance => _instance; - static final PlatformDispatcher _instance = PlatformDispatcher._(); + static PlatformDispatcher/*!*/ get instance => _instance; + static final PlatformDispatcher/*!*/ _instance = PlatformDispatcher._(); /// The current platform configuration. /// /// If values in this configuration change, [onMetricsChanged] will be called. - PlatformConfiguration get configuration => _configuration; - PlatformConfiguration _configuration = const PlatformConfiguration(); + PlatformConfiguration/*!*/ get configuration => _configuration; + PlatformConfiguration/*!*/ _configuration = const PlatformConfiguration(); /// Called when the platform configuration changes. - VoidCallback get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; - VoidCallback _onPlatformConfigurationChanged; - Zone _onPlatformConfigurationChangedZone; - set onPlatformConfigurationChanged(VoidCallback callback) { + VoidCallback/*?*/ get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + VoidCallback/*?*/ _onPlatformConfigurationChanged; + Zone/*!*/ _onPlatformConfigurationChangedZone = Zone.root; + set onPlatformConfigurationChanged(VoidCallback/*?*/ callback) { _onPlatformConfigurationChanged = callback; _onPlatformConfigurationChangedZone = Zone.current; } @@ -77,30 +77,30 @@ class PlatformDispatcher { /// /// If the list of screens or their configuration changes, [onMetricsChanged] /// will be called. - Iterable get screens => _screens.values; - Map _screens = {}; + Iterable/*!*/ get screens => _screens.values; + Map/*!*/ _screens = {}; // A map of opaque platform screen identifiers to screen configurations. - Map _screenConfigurations = {}; + Map/*!*/ _screenConfigurations = {}; /// The current list of views, including top level platform windows used by /// the application. /// /// If the list of views changes, [onViewCreated] or [onViewDisposed] will be /// called. If their configurations change, [onMetricsChanged] will be called. - Iterable get views => _views.values; - Map _views = {}; + Iterable/*!*/ get views => _views.values; + Map/*!*/ _views = {}; // A map of opaque platform view identifiers to view configurations. - Map _viewConfigurations = {}; + Map/*!*/ _viewConfigurations = {}; /// Is called after [createView] is called and returns with a new view. /// /// Passes the newly created [FlutterView]. - ViewCreatedCallback get onViewCreated => _onViewCreated; - ViewCreatedCallback _onViewCreated; - Zone _onViewCreatedZone; // ignore: unused_field - set onViewCreated(ViewCreatedCallback callback) { + ViewCreatedCallback/*?*/ get onViewCreated => _onViewCreated; + ViewCreatedCallback/*?*/ _onViewCreated; + Zone/*!*/ _onViewCreatedZone = Zone.root; // ignore: unused_field + set onViewCreated(ViewCreatedCallback/*?*/ callback) { _onViewCreated = callback; _onViewCreatedZone = Zone.current; } @@ -111,10 +111,10 @@ class PlatformDispatcher { /// [FlutterView.dispose] on the given [FlutterView]. /// /// If the disposal is to be ignored, just do nothing. - ViewDisposedCallback get onViewDisposed => _onViewDisposed; - ViewDisposedCallback _onViewDisposed; - Zone _onViewDisposedZone; // ignore: unused_field - set onViewDisposed(ViewDisposedCallback callback) { + ViewDisposedCallback/*?*/ get onViewDisposed => _onViewDisposed; + ViewDisposedCallback/*?*/ _onViewDisposed; + Zone/*!*/ _onViewDisposedZone = Zone.root; // ignore: unused_field + set onViewDisposed(ViewDisposedCallback/*?*/ callback) { _onViewDisposed = callback; _onViewDisposedZone = Zone.current; } @@ -137,10 +137,10 @@ class PlatformDispatcher { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback get onMetricsChanged => _onMetricsChanged; - VoidCallback _onMetricsChanged; - Zone _onMetricsChangedZone; // ignore: unused_field - set onMetricsChanged(VoidCallback callback) { + VoidCallback/*?*/ get onMetricsChanged => _onMetricsChanged; + VoidCallback/*?*/ _onMetricsChanged; + Zone/*!*/ _onMetricsChangedZone = Zone.root; // ignore: unused_field + set onMetricsChanged(VoidCallback/*?*/ callback) { _onMetricsChanged = callback; _onMetricsChangedZone = Zone.current; } @@ -157,10 +157,10 @@ class PlatformDispatcher { /// [PlatformDispatcher.scheduleFrame] has been called since the last time /// this callback was invoked. /// {@endtemplate} - FrameCallback get onBeginFrame => _onBeginFrame; - FrameCallback _onBeginFrame; - Zone _onBeginFrameZone; - set onBeginFrame(FrameCallback callback) { + FrameCallback/*?*/ get onBeginFrame => _onBeginFrame; + FrameCallback/*?*/ _onBeginFrame; + Zone/*?*/ _onBeginFrameZone = Zone.root; + set onBeginFrame(FrameCallback/*?*/ callback) { _onBeginFrame = callback; _onBeginFrameZone = Zone.current; } @@ -172,10 +172,10 @@ class PlatformDispatcher { /// This can be used to implement a second phase of frame rendering that /// happens after any deferred work queued by the [onBeginFrame] phase. /// {@endtemplate} - VoidCallback get onDrawFrame => _onDrawFrame; - VoidCallback _onDrawFrame; - Zone _onDrawFrameZone; - set onDrawFrame(VoidCallback callback) { + VoidCallback/*?*/ get onDrawFrame => _onDrawFrame; + VoidCallback/*?*/ _onDrawFrame; + Zone/*!*/ _onDrawFrameZone = Zone.root; + set onDrawFrame(VoidCallback/*?*/ callback) { _onDrawFrame = callback; _onDrawFrameZone = Zone.current; } @@ -189,10 +189,10 @@ class PlatformDispatcher { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback _onPointerDataPacket; - Zone _onPointerDataPacketZone; - set onPointerDataPacket(PointerDataPacketCallback callback) { + PointerDataPacketCallback/*?*/ get onPointerDataPacket => _onPointerDataPacket; + PointerDataPacketCallback/*?*/ _onPointerDataPacket; + Zone/*!*/ _onPointerDataPacketZone = Zone.root; + set onPointerDataPacket(PointerDataPacketCallback/*?*/ callback) { _onPointerDataPacket = callback; _onPointerDataPacketZone = Zone.current; } @@ -218,10 +218,10 @@ class PlatformDispatcher { /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. - TimingsCallback get onReportTimings => _onReportTimings; - TimingsCallback _onReportTimings; - Zone _onReportTimingsZone; - set onReportTimings(TimingsCallback callback) { + TimingsCallback/*?*/ get onReportTimings => _onReportTimings; + TimingsCallback/*?*/ _onReportTimings; + Zone/*!*/ _onReportTimingsZone = Zone.root; + set onReportTimings(TimingsCallback/*?*/ callback) { if ((callback == null) != (_onReportTimings == null)) { _setNeedsReportTimings(callback != null); } @@ -229,8 +229,8 @@ class PlatformDispatcher { _onReportTimingsZone = Zone.current; } - _SetNeedsReportTimingsFunc _setNeedsReportTimings; - void _nativeSetNeedsReportTimings(bool value) + /*late*/ _SetNeedsReportTimingsFunc/*!*/ _setNeedsReportTimings; + void _nativeSetNeedsReportTimings(bool/*!*/ value) native 'PlatformConfiguration_setNeedsReportTimings'; /// Creates a new view and returns the view created. @@ -243,7 +243,7 @@ class PlatformDispatcher { /// /// This function is currently not implemented, but is part of a planned /// feature. - Future createView(ViewConfigurationRequest request) async { + Future/*?*/ createView(ViewConfigurationRequest/*!*/ request) async { throw UnimplementedError(); // Awaits the platform view creation response, and calls onViewCreated // before returning. @@ -262,8 +262,8 @@ class PlatformDispatcher { /// This function is currently not implemented, but is part of a planned /// feature. Future configureView( - FlutterView view, - ViewConfigurationRequest configuration, + FlutterView/*!*/ view, + ViewConfigurationRequest/*!*/ configuration, ) async { throw UnimplementedError(); } @@ -275,7 +275,7 @@ class PlatformDispatcher { /// /// This function is currently not implemented, but is part of a planned /// feature. - Future disposeView(FlutterView view) async { + Future disposeView(FlutterView/*!*/ view) async { throw UnimplementedError(); } @@ -296,7 +296,7 @@ class PlatformDispatcher { } } - String _sendPlatformMessage(String name, PlatformMessageResponseCallback callback, ByteData data) + String _sendPlatformMessage(String/*!*/ name, PlatformMessageResponseCallback/*?*/ callback, ByteData/*?*/ data) native 'PlatformConfiguration_sendPlatformMessage'; /// Called whenever this platform dispatcher receives a message from a @@ -312,10 +312,10 @@ class PlatformDispatcher { /// /// The framework invokes this callback in the same zone in which the callback /// was set. - PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback _onPlatformMessage; - Zone _onPlatformMessageZone; - set onPlatformMessage(PlatformMessageCallback callback) { + PlatformMessageCallback/*?*/ get onPlatformMessage => _onPlatformMessage; + PlatformMessageCallback/*?*/ _onPlatformMessage; + Zone/*!*/ _onPlatformMessageZone = Zone.root; + set onPlatformMessage(PlatformMessageCallback/*?*/ callback) { _onPlatformMessage = callback; _onPlatformMessageZone = Zone.current; } @@ -333,7 +333,7 @@ class PlatformDispatcher { } // Store the zone in which the callback is being registered. - final Zone registrationZone = Zone.current; + final Zone/*!*/ registrationZone = Zone.current; return (ByteData data) { registrationZone.runUnaryGuarded(callback, data); @@ -399,20 +399,20 @@ class PlatformDispatcher { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene, [FlutterView view]) native 'PlatformConfiguration_render'; + void render(Scene/*!*/ scene, [FlutterView/*!*/ view]) native 'PlatformConfiguration_render'; /// Additional accessibility features that may be enabled by the platform. - AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; + AccessibilityFeatures/*!*/ get accessibilityFeatures => configuration.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] /// changes. /// /// The framework invokes this callback in the same zone in which the callback /// was set. - VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; - VoidCallback _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFeaturesChangedZone; - set onAccessibilityFeaturesChanged(VoidCallback callback) { + VoidCallback/*?*/ get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + VoidCallback/*?*/ _onAccessibilityFeaturesChanged; + Zone/*!*/ _onAccessibilityFeaturesChangedZone = Zone.root; + set onAccessibilityFeaturesChanged(VoidCallback/*?*/ callback) { _onAccessibilityFeaturesChanged = callback; _onAccessibilityFeaturesChangedZone = Zone.current; } @@ -425,7 +425,7 @@ class PlatformDispatcher { /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. - void updateSemantics(SemanticsUpdate update) native 'PlatformConfiguration_updateSemantics'; + void updateSemantics(SemanticsUpdate/*!*/ update) native 'PlatformConfiguration_updateSemantics'; /// The system-reported default locale of the device. /// @@ -437,7 +437,7 @@ class PlatformDispatcher { /// /// This is equivalent to `locales.first` and will provide an empty non-null /// locale if the [locales] list has not been set or is empty. - Locale get locale { + Locale/*?*/ get locale { if (configuration?.locales != null && configuration.locales.isNotEmpty) { return locales.first; } @@ -459,7 +459,7 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - List get locales => configuration.locales; + List/*?*/ get locales => configuration.locales; /// The locale that the platform's native locale resolution system resolves /// to. @@ -472,7 +472,7 @@ class PlatformDispatcher { /// directly in order to arrive at the most appropriate locale for the app. /// /// See [locales], which is the list of locales the user/device prefers. - Locale get platformResolvedLocale => configuration.platformResolvedLocale; + Locale/*?*/ get platformResolvedLocale => configuration.platformResolvedLocale; /// A callback that is invoked whenever [locale] changes value. /// @@ -483,10 +483,10 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onLocaleChanged => _onLocaleChanged; - VoidCallback _onLocaleChanged; - Zone _onLocaleChangedZone; // ignore: unused_field - set onLocaleChanged(VoidCallback callback) { + VoidCallback/*?*/ get onLocaleChanged => _onLocaleChanged; + VoidCallback/*?*/ _onLocaleChanged; + Zone/*!*/ _onLocaleChangedZone = Zone.root; // ignore: unused_field + set onLocaleChanged(VoidCallback/*?*/ callback) { _onLocaleChanged = callback; _onLocaleChangedZone = Zone.current; } @@ -497,17 +497,17 @@ class PlatformDispatcher { /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup with /// any buffered lifecycle state events. - String get initialLifecycleState { + String/*!*/ get initialLifecycleState { _initialLifecycleStateAccessed = true; return _initialLifecycleState; } - String _initialLifecycleState; + String/*!*/ _initialLifecycleState; /// Tracks if the initial state has been accessed. Once accessed, we will stop /// updating the [initialLifecycleState], as it is not the preferred way to /// access the state. - bool _initialLifecycleStateAccessed = false; + bool/*!*/ _initialLifecycleStateAccessed = false; /// The system-reported text scale. /// @@ -521,13 +521,13 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get textScaleFactor => configuration.textScaleFactor; + double/*!*/ get textScaleFactor => configuration.textScaleFactor; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// /// This option is used by [showTimePicker]. - bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + bool/*!*/ get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// @@ -538,10 +538,10 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback _onTextScaleFactorChanged; - Zone _onTextScaleFactorChangedZone; - set onTextScaleFactorChanged(VoidCallback callback) { + VoidCallback/*?*/ get onTextScaleFactorChanged => _onTextScaleFactorChanged; + VoidCallback/*?*/ _onTextScaleFactorChanged; + Zone/*!*/ _onTextScaleFactorChangedZone = Zone.root; + set onTextScaleFactorChanged(VoidCallback/*?*/ callback) { _onTextScaleFactorChanged = callback; _onTextScaleFactorChangedZone = Zone.current; } @@ -549,7 +549,7 @@ class PlatformDispatcher { /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. - Brightness get platformBrightness => configuration.platformBrightness; + Brightness/*!*/ get platformBrightness => configuration.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// @@ -560,10 +560,10 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback _onPlatformBrightnessChanged; - Zone _onPlatformBrightnessChangedZone; - set onPlatformBrightnessChanged(VoidCallback callback) { + VoidCallback/*?*/ get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + VoidCallback/*?*/ _onPlatformBrightnessChanged; + Zone/*!*/ _onPlatformBrightnessChangedZone = Zone.root; + set onPlatformBrightnessChanged(VoidCallback/*?*/ callback) { _onPlatformBrightnessChanged = callback; _onPlatformBrightnessChangedZone = Zone.current; } @@ -573,16 +573,16 @@ class PlatformDispatcher { /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. - bool get semanticsEnabled => configuration.semanticsEnabled; + bool/*!*/ get semanticsEnabled => configuration.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback _onSemanticsEnabledChanged; - Zone _onSemanticsEnabledChangedZone; - set onSemanticsEnabledChanged(VoidCallback callback) { + VoidCallback/*?*/ get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + VoidCallback/*?*/ _onSemanticsEnabledChanged; + Zone/*!*/ _onSemanticsEnabledChangedZone = Zone.root; + set onSemanticsEnabledChanged(VoidCallback/*?*/ callback) { _onSemanticsEnabledChanged = callback; _onSemanticsEnabledChangedZone = Zone.current; } @@ -595,10 +595,10 @@ class PlatformDispatcher { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback _onSemanticsAction; - Zone _onSemanticsActionZone; - set onSemanticsAction(SemanticsActionCallback callback) { + SemanticsActionCallback/*?*/ get onSemanticsAction => _onSemanticsAction; + SemanticsActionCallback/*?*/ _onSemanticsAction; + Zone/*!*/ _onSemanticsActionZone = Zone.root; + set onSemanticsAction(SemanticsActionCallback/*?*/ callback) { _onSemanticsAction = callback; _onSemanticsActionZone = Zone.current; } @@ -633,8 +633,8 @@ class PlatformDispatcher { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String get initialRouteName => _initialRouteName(); - String _initialRouteName() native 'PlatformConfiguration_initialRouteName'; + String/*!*/ get initialRouteName => _initialRouteName(); + String/*!*/ _initialRouteName() native 'PlatformConfiguration_initialRouteName'; } /// Configuration of the platform. @@ -651,21 +651,23 @@ class PlatformConfiguration { this.locales = const [], this.platformResolvedLocale, this.initialRouteName, - }) : assert(alwaysUse24HourFormat != null), + }) : assert(accessibilityFeatures != null), + assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), assert(platformBrightness != null), - assert(textScaleFactor != null); + assert(textScaleFactor != null), + assert(locales != null); /// Copy a [PlatformConfiguration] with some fields replaced. PlatformConfiguration copyWith({ - AccessibilityFeatures accessibilityFeatures, - bool alwaysUse24HourFormat, - bool semanticsEnabled, - Brightness platformBrightness, - double textScaleFactor, - List locales, - Locale platformResolvedLocale, - String initialRouteName, + AccessibilityFeatures/*?*/ accessibilityFeatures, + bool/*?*/ alwaysUse24HourFormat, + bool/*?*/ semanticsEnabled, + Brightness/*?*/ platformBrightness, + double/*?*/ textScaleFactor, + List/*?*/ locales, + Locale/*?*/ platformResolvedLocale, + String/*?*/ initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -680,33 +682,33 @@ class PlatformConfiguration { } /// Additional accessibility features that may be enabled by the platform. - final AccessibilityFeatures accessibilityFeatures; + final AccessibilityFeatures/*!*/ accessibilityFeatures; /// The setting indicating whether time should always be shown in the 24-hour /// format. - final bool alwaysUse24HourFormat; + final bool/*!*/ alwaysUse24HourFormat; /// Whether the user has requested that [updateSemantics] be called when the /// semantic contents of a view changes. - final bool semanticsEnabled; + final bool/*!*/ semanticsEnabled; /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. - final Brightness platformBrightness; + final Brightness/*!*/ platformBrightness; /// The system-reported text scale. - final double textScaleFactor; + final double/*!*/ textScaleFactor; /// The full system-reported supported locales of the device. - final List locales; + final List/*!*/ locales; /// The system-reported default locale of the device. - final Locale platformResolvedLocale; + final Locale/*?*/ platformResolvedLocale; /// The route or path that the embedder requested when the application was /// launched. - final String initialRouteName; + final String/*?*/ initialRouteName; } /// Immutable configuration information for a screen. @@ -724,19 +726,20 @@ class ScreenConfiguration { assert(geometry != null), assert(devicePixelRatio != null), assert(viewInsets != null), + assert(viewPadding != null), assert(systemGestureInsets != null), assert(padding != null); /// Makes a new copy of this [ViewConfigurationRequest] with some attributes /// replaced. ScreenConfiguration copyWith({ - String screenName, - Rect geometry, - double devicePixelRatio, - WindowPadding viewInsets, - WindowPadding viewPadding, - WindowPadding systemGestureInsets, - WindowPadding padding, + String/*?*/ screenName, + Rect/*?*/ geometry, + double/*?*/ devicePixelRatio, + WindowPadding/*?*/ viewInsets, + WindowPadding/*?*/ viewPadding, + WindowPadding/*?*/ systemGestureInsets, + WindowPadding/*?*/ padding, }) { return ScreenConfiguration( screenName: screenName ?? this.screenName, @@ -750,38 +753,38 @@ class ScreenConfiguration { } /// Platform-provided name for screen. - final String screenName; + final String/*!*/ screenName; /// Screen rect in Flutter logical pixels - final Rect geometry; + final Rect/*!*/ geometry; /// Device pixel ratio in device pixels to logical pixels. - final double devicePixelRatio; + final double/*!*/ devicePixelRatio; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but over which the operating /// system will likely place system UI, such as the keyboard or system menus, /// that fully obscures any content. - final WindowPadding viewInsets; + final WindowPadding/*!*/ viewInsets; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but which may be partially /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding viewPadding; + final WindowPadding/*!*/ viewPadding; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but where the operating system /// will consume input gestures for the sake of system navigation. - final WindowPadding systemGestureInsets; + final WindowPadding/*!*/ systemGestureInsets; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but which may be partially /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding padding; + final WindowPadding/*!*/ padding; @override String toString() { @@ -811,11 +814,11 @@ class ViewConfigurationRequest { /// Makes a new copy of this [ViewConfigurationRequest] with some attributes /// replaced. ViewConfigurationRequest copyWith({ - Screen screen, - Rect geometry, - bool visible, - ViewOrder order, - FlutterView orderView, + Screen/*?*/ screen, + Rect/*?*/ geometry, + bool/*?*/ visible, + ViewOrder/*?*/ order, + FlutterView/*?*/ orderView, }) { return ViewConfigurationRequest( screen: screen ?? this.screen, @@ -830,23 +833,23 @@ class ViewConfigurationRequest { /// /// If the platform supports spanning multiple screens, this is the screen /// that the upper left corner of the view appears on. - final Screen screen; + final Screen/*?*/ screen; /// The geometry requested for the view on the [screen], in logical pixels. /// /// This uses the device pixel ratio of the screen with the upper left corner /// of this view on it. - final Rect geometry; + final Rect/*?*/ geometry; /// Whether or not the view should be visible. /// /// If this request is given to [PlatformDispatcher.createView], then setting /// this to true means that the view will be made visible as soon as it is /// created. - final bool visible; + final bool/*?*/ visible; /// The depth ordering of this view relative to other views. - final ViewOrder order; + final ViewOrder/*?*/ order; /// The opaque ID of the view to place this view on a layer relative to, /// according to [order]. @@ -855,7 +858,7 @@ class ViewConfigurationRequest { /// [ViewOrder.belowOther]. /// /// This ID corresponds to the view that this one should be above or below. - final FlutterView orderView; + final FlutterView/*?*/ orderView; @override String toString() { @@ -897,7 +900,8 @@ class ViewConfiguration { this.viewPadding = WindowPadding.zero, this.systemGestureInsets = WindowPadding.zero, this.padding = WindowPadding.zero, - }) : assert(geometry != null), + }) : assert(screen != null), + assert(geometry != null), assert(depth != null), assert(visible != null), assert(viewInsets != null), @@ -907,15 +911,15 @@ class ViewConfiguration { /// Copy this configuration with some fields replaced. ViewConfiguration copyWith({ - Screen screen, - FlutterWindow window, - Rect geometry, - double depth, - bool visible, - WindowPadding viewInsets, - WindowPadding viewPadding, - WindowPadding systemGestureInsets, - WindowPadding padding, + Screen/*?*/ screen, + FlutterWindow/*?*/ window, + Rect/*?*/ geometry, + double/*?*/ depth, + bool/*?*/ visible, + WindowPadding/*?*/ viewInsets, + WindowPadding/*?*/ viewPadding, + WindowPadding/*?*/ systemGestureInsets, + WindowPadding/*?*/ padding, }) { return ViewConfiguration( screen: screen ?? this.screen, @@ -933,19 +937,19 @@ class ViewConfiguration { /// The screen that this view should appear on. /// /// This is the screen that the upper left corner of the view appears on. - final Screen screen; + final Screen/*!*/ screen; /// The top level view into which the view is placed and its geometry is /// relative to. /// /// If null, then this configuration represents a top level view itself. - final FlutterWindow window; + final FlutterWindow/*?*/ window; /// The geometry requested for the view on the [screen] or within its parent /// window, in logical pixels. /// /// This uses the device pixel ratio of the [screen]. - final Rect geometry; + final Rect/*!*/ geometry; /// The depth that is the maximum elevation that the view allows. /// @@ -958,10 +962,10 @@ class ViewConfiguration { /// The default value is [double.maxFinite], which is used for platforms that /// do not specify a maximum elevation. This property is currently only /// expected to be set to a non-default value on Fuchsia. - final double depth; + final double/*!*/ depth; /// Whether or not the view is currently visible on the screen. - final bool visible; + final bool/*!*/ visible; /// The view insets, as it intersects with [Screen.viewInsets] for the screen /// it is on. @@ -974,7 +978,7 @@ class ViewConfiguration { /// which the application can draw, but over which the operating system will /// likely place system UI, such as the keyboard or system menus, that fully /// obscures any content. - final WindowPadding viewInsets; + final WindowPadding/*!*/ viewInsets; /// The view insets, as it intersects with [ScreenConfiguration.viewPadding] /// for the screen it is on. @@ -988,7 +992,7 @@ class ViewConfiguration { /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding viewPadding; + final WindowPadding/*!*/ viewPadding; /// The view insets, as it intersects with /// [ScreenConfiguration.systemGestureInsets] for the screen it is on. @@ -1000,7 +1004,7 @@ class ViewConfiguration { /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but where the operating system /// will consume input gestures for the sake of system navigation. - final WindowPadding systemGestureInsets; + final WindowPadding/*!*/ systemGestureInsets; /// The view insets, as it intersects with [ScreenConfiguration.padding] for /// the screen it is on. @@ -1014,7 +1018,7 @@ class ViewConfiguration { /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding padding; + final WindowPadding/*!*/ padding; @override String toString() { diff --git a/lib/ui/screen.dart b/lib/ui/screen.dart index 7074d673cb2a9..70192ae156bbc 100644 --- a/lib/ui/screen.dart +++ b/lib/ui/screen.dart @@ -7,19 +7,19 @@ part of dart.ui; /// A class representing the screen that application windows are displayed on. class Screen { - Screen._({Object screenId, PlatformDispatcher platformDispatcher}) + const Screen._({Object/*!*/ screenId, PlatformDispatcher/*!*/ platformDispatcher}) : _screenId = screenId, _platformDispatcher = platformDispatcher; /// The opaque ID for this screen. - final Object _screenId; + final Object/*!*/ _screenId; /// The platform dispatcher that this screen is registered with. - final PlatformDispatcher _platformDispatcher; + final PlatformDispatcher/*!*/ _platformDispatcher; /// The configuration of this screen. - ScreenConfiguration get configuration { + ScreenConfiguration/*!*/ get configuration { assert(_platformDispatcher._screens.containsKey(_screenId)); return _platformDispatcher._screenConfigurations[_screenId]; } -} \ No newline at end of file +} diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 8dd2cdb98f433..7c2022034a35b 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -213,12 +213,12 @@ PlatformConfiguration::~PlatformConfiguration() {} void PlatformConfiguration::DidCreateIsolate() { library_.Set(tonic::DartState::Current(), Dart_LookupLibrary(tonic::ToDart("dart:ui"))); - for (auto const& window : windows_) { - window.second->DidCreateIsolate(); - } for (auto const& screen : screens_) { screen.second->DidCreateIsolate(); } + for (auto const& window : windows_) { + window.second->DidCreateIsolate(); + } } void PlatformConfiguration::UpdateLocales( diff --git a/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart b/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart index 4f4029750d820..cad3f3db12314 100644 --- a/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart +++ b/lib/web_ui/lib/src/engine/compositor/screen_metrics.dart @@ -6,13 +6,13 @@ part of engine; class ScreenMetrics { - final double devicePixelRatio; - final double physicalWidth; - final double physicalHeight; - const ScreenMetrics( this.devicePixelRatio, this.physicalWidth, this.physicalHeight, ); + + final double/*!*/ devicePixelRatio; + final double/*!*/ physicalWidth; + final double/*!*/ physicalHeight; } diff --git a/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart b/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart index 3696ee3969099..6038a889a258d 100644 --- a/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart +++ b/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart @@ -6,11 +6,11 @@ part of engine; class ViewportMetrics { - final double physicalWidth; - final double physicalHeight; - const ViewportMetrics( this.physicalWidth, this.physicalHeight, ); + + final double/*!*/ physicalWidth; + final double/*!*/ physicalHeight; } diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index ae1abb7829549..eb939a090295d 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -5,6 +5,11 @@ // @dart = 2.6 part of engine; +/// Requests that the browser schedule a frame. +/// +/// This may be overridden in tests, for example, to pump fake frames. +ui.VoidCallback/*?*/ scheduleFrameCallback; + /// Platform event dispatcher. /// /// This is the central entry point for platform messages and configuration @@ -17,21 +22,21 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } /// The [EnginePlatformDispatcher] singleton. - static EnginePlatformDispatcher get instance => _instance; - static final EnginePlatformDispatcher _instance = EnginePlatformDispatcher._(); + static EnginePlatformDispatcher/*!*/ get instance => _instance; + static final EnginePlatformDispatcher/*!*/ _instance = EnginePlatformDispatcher._(); /// The current platform configuration. @override - ui.PlatformConfiguration get configuration => _configuration; - ui.PlatformConfiguration _configuration = ui.PlatformConfiguration(locales: parseBrowserLanguages()); + ui.PlatformConfiguration/*!*/ get configuration => _configuration; + ui.PlatformConfiguration/*!*/ _configuration = ui.PlatformConfiguration(locales: parseBrowserLanguages()); /// Receives all events related to platform configuration changes. @override - ui.VoidCallback get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; - ui.VoidCallback _onPlatformConfigurationChanged; - Zone _onPlatformConfigurationChangedZone; + ui.VoidCallback/*?*/ get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + ui.VoidCallback/*?*/ _onPlatformConfigurationChanged; + Zone/*!*/ _onPlatformConfigurationChangedZone = Zone.root; @override - set onPlatformConfigurationChanged(ui.VoidCallback callback) { + set onPlatformConfigurationChanged(ui.VoidCallback/*?*/ callback) { _onPlatformConfigurationChanged = callback; _onPlatformConfigurationChangedZone = Zone.current; } @@ -43,24 +48,24 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } /// The current list of screens. - Iterable get screens => _screens.values; - Map _screens = {}; + Iterable/*!*/ get screens => _screens.values; + Map/*!*/ _screens = {}; /// A map of opaque platform screen identifiers to screen configurations. /// /// This should be considered a protected member, only to be used by /// [PlatformDispatcher] subclasses. - Map _screenConfigurations = {}; + Map _screenConfigurations = {}; /// The current list of windows, - Iterable get views => _windows.values; - Map _windows = {}; + Iterable/*!*/ get views => _windows.values; + Map/*!*/ _windows = {}; /// A map of opaque platform window identifiers to window configurations. /// /// This should be considered a protected member, only to be used by /// [PlatformDispatcher] subclasses. - Map _windowConfigurations = {}; + Map/*!*/ _windowConfigurations = {}; /// Opens a new window and returns the window created. /// @@ -70,7 +75,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// This function is currently not implemented, but is part of a planned /// feature. @override - Future createView(ui.ViewConfigurationRequest configuration) async { + Future createView(ui.ViewConfigurationRequest/*!*/ configuration) async { throw UnimplementedError(); // Awaits the platform window creation response, and calls onWindowOpened before returning. } @@ -84,7 +89,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// feature. @override Future configureView( - ui.FlutterView view, ui.ViewConfigurationRequest configuration) async { + ui.FlutterView/*!*/ view, ui.ViewConfigurationRequest/*!*/ configuration) async { throw UnimplementedError(); } @@ -95,7 +100,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// This function is currently not implemented, but is part of a planned /// feature. @override - Future disposeView(ui.FlutterView view) async { + Future disposeView(ui.FlutterView/*!*/ view) async { throw UnimplementedError(); } @@ -103,18 +108,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// Sends the newly opened window. @override - ui.ViewCreatedCallback get onViewCreated => _onWindowOpened; - ui.ViewCreatedCallback _onWindowOpened; - Zone _onWindowOpenedZone; // ignore: unused_field + ui.ViewCreatedCallback/*?*/ get onViewCreated => _onWindowOpened; + ui.ViewCreatedCallback/*?*/ _onWindowOpened; + Zone/*!*/ _onWindowOpenedZone = Zone.root; // ignore: unused_field @override - set onViewCreated(ui.ViewCreatedCallback callback) { + set onViewCreated(ui.ViewCreatedCallback/*?*/ callback) { _onWindowOpened = callback; _onWindowOpenedZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnWindowCreated(Object id) { + void invokeOnWindowCreated(Object/*!*/ id) { _invoke1(_onWindowOpened, _onWindowOpenedZone, id); } @@ -122,18 +127,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// Sends the window to be closed. @override - ui.ViewDisposedCallback get onViewDisposed => _onWindowClosed; - ui.ViewDisposedCallback _onWindowClosed; - Zone _onWindowClosedZone; // ignore: unused_field + ui.ViewDisposedCallback/*?*/ get onViewDisposed => _onWindowClosed; + ui.ViewDisposedCallback/*?*/ _onWindowClosed; + Zone/*!*/ _onWindowClosedZone = Zone.root; // ignore: unused_field @override - set onViewDisposed(ui.ViewDisposedCallback callback) { + set onViewDisposed(ui.ViewDisposedCallback/*?*/ callback) { _onWindowClosed = callback; _onWindowClosedZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnWindowDisposed(Object id) { + void invokeOnWindowDisposed(Object/*!*/ id) { _invoke1(_onWindowClosed, _onWindowClosedZone, id); } @@ -155,11 +160,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. @override - ui.VoidCallback get onMetricsChanged => _onMetricsChanged; - ui.VoidCallback _onMetricsChanged; - Zone _onMetricsChangedZone; // ignore: unused_field + ui.VoidCallback/*?*/ get onMetricsChanged => _onMetricsChanged; + ui.VoidCallback/*?*/ _onMetricsChanged; + Zone/*!*/ _onMetricsChangedZone = Zone.root; // ignore: unused_field @override - set onMetricsChanged(ui.VoidCallback callback) { + set onMetricsChanged(ui.VoidCallback/*?*/ callback) { _onMetricsChanged = callback; _onMetricsChangedZone = Zone.current; } @@ -171,8 +176,8 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } /// Returns device pixel ratio returned by browser. - static double get browserDevicePixelRatio { - double ratio = html.window.devicePixelRatio; + static double/*!*/ get browserDevicePixelRatio { + double/*?*/ ratio = html.window.devicePixelRatio; // Guard against WebOS returning 0. return (ratio == null || ratio == 0.0) ? 1.0 : ratio; } @@ -189,18 +194,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// callback was invoked. /// {@endtemplate} @override - ui.FrameCallback get onBeginFrame => _onBeginFrame; - ui.FrameCallback _onBeginFrame; - Zone _onBeginFrameZone; + ui.FrameCallback/*?*/ get onBeginFrame => _onBeginFrame; + ui.FrameCallback/*?*/ _onBeginFrame; + Zone/*!*/ _onBeginFrameZone = Zone.root; @override - set onBeginFrame(ui.FrameCallback callback) { + set onBeginFrame(ui.FrameCallback/*?*/ callback) { _onBeginFrame = callback; _onBeginFrameZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnBeginFrame(Duration duration) { + void invokeOnBeginFrame(Duration/*!*/ duration) { _invoke1(_onBeginFrame, _onBeginFrameZone, duration); } @@ -212,11 +217,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// happens after any deferred work queued by the [onBeginFrame] phase. /// {@endtemplate} @override - ui.VoidCallback get onDrawFrame => _onDrawFrame; - ui.VoidCallback _onDrawFrame; - Zone _onDrawFrameZone; + ui.VoidCallback/*?*/ get onDrawFrame => _onDrawFrame; + ui.VoidCallback/*?*/ _onDrawFrame; + Zone/*!*/ _onDrawFrameZone = Zone.root; @override - set onDrawFrame(ui.VoidCallback callback) { + set onDrawFrame(ui.VoidCallback/*?*/ callback) { _onDrawFrame = callback; _onDrawFrameZone = Zone.current; } @@ -237,18 +242,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [GestureBinding], the Flutter framework class which manages pointer /// events. @override - ui.PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; - ui.PointerDataPacketCallback _onPointerDataPacket; - Zone _onPointerDataPacketZone; + ui.PointerDataPacketCallback/*?*/ get onPointerDataPacket => _onPointerDataPacket; + ui.PointerDataPacketCallback/*?*/ _onPointerDataPacket; + Zone/*!*/ _onPointerDataPacketZone = Zone.root; @override - set onPointerDataPacket(ui.PointerDataPacketCallback callback) { + set onPointerDataPacket(ui.PointerDataPacketCallback/*?*/ callback) { _onPointerDataPacket = callback; _onPointerDataPacketZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnPointerDataPacket(ui.PointerDataPacket dataPacket) { + void invokeOnPointerDataPacket(ui.PointerDataPacket/*!*/ dataPacket) { _invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } @@ -274,18 +279,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. @override - ui.TimingsCallback get onReportTimings => _onReportTimings; - ui.TimingsCallback _onReportTimings; - Zone _onReportTimingsZone; + ui.TimingsCallback/*?*/ get onReportTimings => _onReportTimings; + ui.TimingsCallback/*?*/ _onReportTimings; + Zone/*!*/ _onReportTimingsZone = Zone.root; @override - set onReportTimings(ui.TimingsCallback callback) { + set onReportTimings(ui.TimingsCallback/*?*/ callback) { _onReportTimings = callback; _onReportTimingsZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnReportTimings(List timings) { + void invokeOnReportTimings(List/*!*/ timings) { _invoke1>(_onReportTimings, _onReportTimingsZone, timings); } @@ -299,18 +304,18 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } @override - ui.PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; - ui.PlatformMessageCallback _onPlatformMessage; - Zone _onPlatformMessageZone; + ui.PlatformMessageCallback/*?*/ get onPlatformMessage => _onPlatformMessage; + ui.PlatformMessageCallback/*?*/ _onPlatformMessage; + Zone/*!*/ _onPlatformMessageZone = Zone.root; @override - set onPlatformMessage(ui.PlatformMessageCallback callback) { + set onPlatformMessage(ui.PlatformMessageCallback/*?*/ callback) { _onPlatformMessage = callback; _onPlatformMessageZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnPlatformMessage(String name, ByteData data, ui.PlatformMessageResponseCallback callback) { + void invokeOnPlatformMessage(String/*!*/ name, ByteData/*?*/ data, ui.PlatformMessageResponseCallback/*?*/ callback) { _invoke3( _onPlatformMessage, _onPlatformMessageZone, @@ -322,7 +327,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. - static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback callback) { + static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback/*?*/ callback) { if (callback == null) return null; @@ -482,7 +487,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // callback(null); } - int _getHapticFeedbackDuration(String type) { + int _getHapticFeedbackDuration(String/*!*/ type) { switch (type) { case 'HapticFeedbackType.lightImpact': return DomRenderer.vibrateLightImpact; @@ -504,12 +509,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. - void scheduleFrame() { - if (ui.webOnlyScheduleFrameCallback == null) { + @override + void scheduleFrame() { + if (scheduleFrameCallback == null) { throw new Exception( - 'webOnlyScheduleFrameCallback must be initialized first.'); + 'scheduleFrameCallback must be initialized first.'); } - ui.webOnlyScheduleFrameCallback(); + scheduleFrameCallback(); } /// Updates the application's rendering on the GPU with the newly provided @@ -537,7 +543,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. @override - void render(ui.Scene/*!*/ scene, [ui.FlutterView view]) { + void render(ui.Scene/*!*/ scene, [ui.FlutterView/*!*/ view]) { if (experimentalUseSkia) { final LayerScene layerScene = scene; rasterizer.draw(layerScene.layerTree); @@ -548,16 +554,16 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } /// Additional accessibility features that may be enabled by the platform. - ui.AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; + ui.AccessibilityFeatures/*!*/ get accessibilityFeatures => configuration.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - ui.VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; - ui.VoidCallback _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFeaturesChangedZone; - set onAccessibilityFeaturesChanged(ui.VoidCallback callback) { + ui.VoidCallback/*?*/ get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + ui.VoidCallback/*?*/ _onAccessibilityFeaturesChanged; + Zone/*!*/ _onAccessibilityFeaturesChangedZone = Zone.root; + set onAccessibilityFeaturesChanged(ui.VoidCallback/*?*/ callback) { _onAccessibilityFeaturesChanged = callback; _onAccessibilityFeaturesChangedZone = Zone.current; } @@ -575,7 +581,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. - void updateSemantics(ui.SemanticsUpdate update) { + void updateSemantics(ui.SemanticsUpdate/*!*/ update) { EngineSemanticsOwner.instance.updateSemantics(update); } @@ -587,7 +593,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, /// which explains browser quirks in the implementation notes. - ui.Locale get locale => locales.first; + ui.Locale/*!*/ get locale => locales.first; /// The full system-reported supported locales of the device. /// @@ -603,7 +609,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - List get locales => configuration.locales; + List/*!*/ get locales => configuration.locales; /// The locale that the platform's native locale resolution system resolves to. /// @@ -615,7 +621,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// in order to arrive at the most appropriate locale for the app. /// /// See [locales], which is the list of locales the user/device prefers. - ui.Locale get platformResolvedLocale => configuration.platformResolvedLocale; + ui.Locale/*!*/ get platformResolvedLocale => configuration.platformResolvedLocale; /// A callback that is invoked whenever [locale] changes value. /// @@ -626,16 +632,16 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - ui.VoidCallback get onLocaleChanged => _onLocaleChanged; - ui.VoidCallback _onLocaleChanged; - Zone _onLocaleChangedZone; // ignore: unused_field - set onLocaleChanged(ui.VoidCallback callback) { + ui.VoidCallback/*?*/ get onLocaleChanged => _onLocaleChanged; + ui.VoidCallback/*?*/ _onLocaleChanged; + Zone/*!*/ _onLocaleChangedZone = Zone.root; // ignore: unused_field + set onLocaleChanged(ui.VoidCallback/*?*/ callback) { _onLocaleChanged = callback; _onLocaleChangedZone = Zone.current; } /// The locale used when we fail to get the list from the browser. - static const _defaultLocale = const ui.Locale('en', 'US'); + static const ui.Locale/*!*/ _defaultLocale = const ui.Locale('en', 'US'); /// Sets locales to an empty list. /// @@ -650,7 +656,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { _configuration = _configuration.copyWith(locales: parseBrowserLanguages()); } - static List parseBrowserLanguages() { + static List/*!*/ parseBrowserLanguages() { // TODO(yjbanov): find a solution for IE final bool languagesFeatureMissing = !js_util.hasProperty(html.window.navigator, 'languages'); if (languagesFeatureMissing || html.window.navigator.languages.isEmpty) { @@ -685,8 +691,8 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. - String get initialLifecycleState => _initialLifecycleState; - String _initialLifecycleState; + String/*?*/ get initialLifecycleState => _initialLifecycleState; + String/*?*/ _initialLifecycleState; /// The system-reported text scale. /// @@ -700,13 +706,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get textScaleFactor => configuration.textScaleFactor; + double/*!*/ get textScaleFactor => configuration.textScaleFactor; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// /// This option is used by [showTimePicker]. - bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + bool/*!*/ get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// @@ -717,10 +723,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - ui.VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; - ui.VoidCallback _onTextScaleFactorChanged; - Zone _onTextScaleFactorChangedZone; - set onTextScaleFactorChanged(ui.VoidCallback callback) { + ui.VoidCallback/*?*/ get onTextScaleFactorChanged => _onTextScaleFactorChanged; + ui.VoidCallback/*?*/ _onTextScaleFactorChanged; + Zone/*!*/ _onTextScaleFactorChangedZone = Zone.root; + set onTextScaleFactorChanged(ui.VoidCallback/*?*/ callback) { _onTextScaleFactorChanged = callback; _onTextScaleFactorChangedZone = Zone.current; } @@ -733,11 +739,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to [Brightness.light]. - ui.Brightness get platformBrightness => configuration.platformBrightness; + ui.Brightness/*!*/ get platformBrightness => configuration.platformBrightness; /// Updates [_platformBrightness] and invokes [onPlatformBrightnessChanged] /// callback if [_platformBrightness] changed. - void _updatePlatformBrightness(ui.Brightness value) { + void _updatePlatformBrightness(ui.Brightness/*!*/ value) { if (configuration.platformBrightness != value && onPlatformBrightnessChanged != null) { _configuration = configuration.copyWith(platformBrightness: value); @@ -747,13 +753,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } /// Reference to css media query that indicates the user theme preference on the web. - final html.MediaQueryList _brightnessMediaQuery = + final html.MediaQueryList/*!*/ _brightnessMediaQuery = html.window.matchMedia('(prefers-color-scheme: dark)'); /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. /// /// Updates the [_platformBrightness] with the new user preference. - html.EventListener _brightnessMediaQueryListener; + html.EventListener/*?*/ _brightnessMediaQueryListener; /// Set the callback function for listening changes in [_brightnessMediaQuery] value. void _addBrightnessMediaQueryListener() { @@ -787,10 +793,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - ui.VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - ui.VoidCallback _onPlatformBrightnessChanged; - Zone _onPlatformBrightnessChangedZone; - set onPlatformBrightnessChanged(ui.VoidCallback callback) { + ui.VoidCallback/*?*/ get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + ui.VoidCallback/*?*/ _onPlatformBrightnessChanged; + Zone/*!*/ _onPlatformBrightnessChangedZone = Zone.root; + set onPlatformBrightnessChanged(ui.VoidCallback/*?*/ callback) { _onPlatformBrightnessChanged = callback; _onPlatformBrightnessChangedZone = Zone.current; } @@ -806,16 +812,16 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. - bool get semanticsEnabled => configuration.semanticsEnabled; + bool/*!*/ get semanticsEnabled => configuration.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - ui.VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - ui.VoidCallback _onSemanticsEnabledChanged; - Zone _onSemanticsEnabledChangedZone; - set onSemanticsEnabledChanged(ui.VoidCallback callback) { + ui.VoidCallback/*?*/ get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + ui.VoidCallback/*?*/ _onSemanticsEnabledChanged; + Zone/*!*/ _onSemanticsEnabledChangedZone = Zone.root; + set onSemanticsEnabledChanged(ui.VoidCallback/*?*/ callback) { _onSemanticsEnabledChanged = callback; _onSemanticsEnabledChangedZone = Zone.current; } @@ -834,17 +840,17 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - ui.SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; - ui.SemanticsActionCallback _onSemanticsAction; - Zone _onSemanticsActionZone; - set onSemanticsAction(ui.SemanticsActionCallback callback) { + ui.SemanticsActionCallback/*?*/ get onSemanticsAction => _onSemanticsAction; + ui.SemanticsActionCallback/*?*/ _onSemanticsAction; + Zone/*!*/ _onSemanticsActionZone = Zone.root; + set onSemanticsAction(ui.SemanticsActionCallback/*?*/ callback) { _onSemanticsAction = callback; _onSemanticsActionZone = Zone.current; } /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnSemanticsAction(int id, ui.SemanticsAction action, ByteData args) { + void invokeOnSemanticsAction(int/*!*/ id, ui.SemanticsAction/*!*/ action, ByteData/*?*/ args) { _invoke3(_onSemanticsAction, _onSemanticsActionZone, id, action, args); } @@ -879,17 +885,17 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. @override - String get initialRouteName => _initialRouteName ??= _browserHistory.currentPath; + String/*!*/ get initialRouteName => _initialRouteName ??= _browserHistory.currentPath; /// Lazily initialized when the `initialRouteName` getter is invoked. /// /// The reason for the lazy initialization is to give enough time for the app /// to set [locationStrategy] in `lib/src/ui/initialization.dart`. - String _initialRouteName; + String/*?*/ _initialRouteName; /// Handles the browser history integration to allow users to use the back /// button, etc. - final BrowserHistory _browserHistory = BrowserHistory(); + final BrowserHistory/*!*/ _browserHistory = BrowserHistory(); /// Simulates clicking the browser's back button. Future webOnlyBack() => _browserHistory.back(); @@ -898,12 +904,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Setting this member will automatically update [_browserHistory]. /// /// By setting this to null, the browser history will be disabled. - set locationStrategy(LocationStrategy strategy) { + set locationStrategy(LocationStrategy/*!*/ strategy) { _browserHistory.locationStrategy = strategy; } @visibleForTesting - Rasterizer rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; + Rasterizer/*?*/ rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; /// In Flutter, platform messages are exchanged between threads so the /// messages and responses have to be exchanged asynchronously. We simulate @@ -920,7 +926,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } } -bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { +bool _handleWebTestEnd2EndMessage(MethodCodec/*!*/ codec, ByteData/*!*/ data) { final MethodCall decoded = codec.decodeMethodCall(data); double ratio = double.parse(decoded.arguments); switch(decoded.method) { @@ -933,7 +939,7 @@ bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { } /// Invokes [callback] inside the given [zone]. -void _invoke(void callback(), Zone zone) { +void _invoke(void callback/*?*/(), Zone/*!*/ zone) { if (callback == null) return; @@ -947,7 +953,7 @@ void _invoke(void callback(), Zone zone) { } /// Invokes [callback] inside the given [zone] passing it [arg]. -void _invoke1(void callback(A a), Zone zone, A arg) { +void _invoke1(void callback/*?*/(A/*?*/ a), Zone zone/*!*/, A/*?*/ arg) { if (callback == null) return; @@ -961,7 +967,13 @@ void _invoke1(void callback(A a), Zone zone, A arg) { } /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { +void _invoke3( + void callback/*?*/(A1/*?*/ a1, A2/*?*/ a2, A3/*?*/ a3), + Zone/*!*/ zone, + A1/*?*/ arg1, + A2/*?*/ arg2, + A3/*?*/ arg3, + ) { if (callback == null) return; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index b376f7ee54ab5..6a66bffbef7de 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -6,12 +6,7 @@ part of engine; /// When set to true, all platform messages will be printed to the console. -const bool _debugPrintPlatformMessages = false; - -/// Requests that the browser schedule a frame. -/// -/// This may be overridden in tests, for example, to pump fake frames. -ui.VoidCallback scheduleFrameCallback; +const bool/*!*/ _debugPrintPlatformMessages = false; /// The Web implementation of [ui.FlutterWindow]. class EngineFlutterWindow extends ui.FlutterWindow { @@ -188,23 +183,6 @@ class EngineFlutterWindowView extends ui.FlutterWindowView { final Object _viewId; - @override - void scheduleFrame() { - if (scheduleFrameCallback == null) { - throw new Exception( - 'scheduleFrameCallback must be initialized first.'); - } - scheduleFrameCallback(); - } - - /// Change the strategy to use for handling browser history location. - /// Setting this member will automatically update [_browserHistory]. - /// - /// By setting this to null, the browser history will be disabled. - set locationStrategy(LocationStrategy strategy) { - _browserHistory.locationStrategy = strategy; - } - final ui.PlatformDispatcher platformDispatcher; /// Returns the currently active location strategy. diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 6fc5018f8677a..fd66a55e0e665 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -10,103 +10,96 @@ typedef ViewCreatedCallback = void Function(FlutterView view); typedef ViewDisposedCallback = void Function(FlutterView view); abstract class PlatformDispatcher { - static PlatformDispatcher get instance => engine.EnginePlatformDispatcher.instance; + static PlatformDispatcher/*!*/ get instance => engine.EnginePlatformDispatcher.instance; - PlatformConfiguration get configuration; - VoidCallback get onPlatformConfigurationChanged; - set onPlatformConfigurationChanged(VoidCallback callback); + PlatformConfiguration/*!*/ get configuration; + VoidCallback/*?*/ get onPlatformConfigurationChanged; + set onPlatformConfigurationChanged(VoidCallback/*?*/ callback); - Iterable get screens; + Iterable/*!*/ get screens; - Iterable get views; - ViewCreatedCallback get onViewCreated; - set onViewCreated(ViewCreatedCallback callback); - ViewDisposedCallback get onViewDisposed; - set onViewDisposed(ViewDisposedCallback callback); - Future createView(ViewConfigurationRequest request); - Future configureView(FlutterView view, ViewConfigurationRequest configuration); - Future disposeView(FlutterView view); + Iterable/*!*/ get views; + ViewCreatedCallback/*?*/ get onViewCreated; + set onViewCreated(ViewCreatedCallback/*?*/ callback); + ViewDisposedCallback/*?*/ get onViewDisposed; + set onViewDisposed(ViewDisposedCallback/*?*/ callback); + Future createView(ViewConfigurationRequest/*!*/ request); + Future configureView(FlutterView/*!*/ view, ViewConfigurationRequest/*!*/ configuration); + Future disposeView(FlutterView/*!*/ view); - VoidCallback get onMetricsChanged; - set onMetricsChanged(VoidCallback callback); + VoidCallback/*?*/ get onMetricsChanged; + set onMetricsChanged(VoidCallback/*?*/ callback); - FrameCallback get onBeginFrame; - set onBeginFrame(FrameCallback callback); + FrameCallback/*?*/ get onBeginFrame; + set onBeginFrame(FrameCallback/*?*/ callback); - VoidCallback get onDrawFrame; - set onDrawFrame(VoidCallback callback); + VoidCallback/*?*/ get onDrawFrame; + set onDrawFrame(VoidCallback/*?*/ callback); - PointerDataPacketCallback get onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback callback); + PointerDataPacketCallback/*?*/ get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback/*?*/ callback); - TimingsCallback get onReportTimings; - set onReportTimings(TimingsCallback callback); + TimingsCallback/*?*/ get onReportTimings; + set onReportTimings(TimingsCallback/*?*/ callback); void sendPlatformMessage( - String name, - ByteData data, - PlatformMessageResponseCallback callback, + String/*!*/ name, + ByteData/*?*/ data, + PlatformMessageResponseCallback/*?*/ callback, ); - PlatformMessageCallback get onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback callback); + PlatformMessageCallback/*?*/ get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback/*?*/ callback); - void scheduleFrame() { - if (webOnlyScheduleFrameCallback == null) { - throw new Exception('webOnlyScheduleFrameCallback must be initialized first.'); - } - webOnlyScheduleFrameCallback(); - } + void scheduleFrame(); - void render(Scene scene, [FlutterView view]); + void render(Scene/*!*/ scene, [FlutterView/*!*/ view]); - AccessibilityFeatures get accessibilityFeatures; + AccessibilityFeatures/*!*/ get accessibilityFeatures; - VoidCallback get onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback callback); + VoidCallback/*?*/ get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback/*?*/ callback); - void updateSemantics(SemanticsUpdate update); + void updateSemantics(SemanticsUpdate/*!*/ update); - Locale get locale; + Locale/*!*/ get locale; - List get locales => configuration.locales; + List/*!*/ get locales => configuration.locales; - Locale get platformResolvedLocale => configuration.platformResolvedLocale; + Locale/*!*/ get platformResolvedLocale => configuration.platformResolvedLocale; - VoidCallback get onLocaleChanged; - set onLocaleChanged(VoidCallback callback); + VoidCallback/*?*/ get onLocaleChanged; + set onLocaleChanged(VoidCallback/*?*/ callback); - bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + bool/*!*/ get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; - double get textScaleFactor => configuration.textScaleFactor; + double/*!*/ get textScaleFactor => configuration.textScaleFactor; - VoidCallback get onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback callback); + VoidCallback/*?*/ get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback/*?*/ callback); - Brightness get platformBrightness => configuration.platformBrightness; + Brightness/*!*/ get platformBrightness => configuration.platformBrightness; - VoidCallback get onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback callback); + VoidCallback/*?*/ get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback/*?*/ callback); - bool get semanticsEnabled => configuration.semanticsEnabled; + bool/*!*/ get semanticsEnabled => configuration.semanticsEnabled; - VoidCallback get onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback callback); + VoidCallback/*?*/ get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback/*?*/ callback); - SemanticsActionCallback get onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback callback); + SemanticsActionCallback/*?*/ get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback/*?*/ callback); - String get initialRouteName; + String/*!*/ get initialRouteName; - void setIsolateDebugName(String name) {} + void setIsolateDebugName(String/*!*/ name) {} - ByteData getPersistentIsolateData() => null; + ByteData/*?*/ getPersistentIsolateData() => null; - String get initialLifecycleState; + String/*?*/ get initialLifecycleState; } -VoidCallback webOnlyScheduleFrameCallback; - class PlatformConfiguration { const PlatformConfiguration({ this.accessibilityFeatures = const AccessibilityFeatures._(0), @@ -117,20 +110,22 @@ class PlatformConfiguration { this.locales = const [], this.platformResolvedLocale, this.initialRouteName, - }) : assert(alwaysUse24HourFormat != null), + }) : assert(accessibilityFeatures != null), + assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), assert(platformBrightness != null), - assert(textScaleFactor != null); + assert(textScaleFactor != null), + assert(locales != null); PlatformConfiguration copyWith({ - AccessibilityFeatures accessibilityFeatures, - bool alwaysUse24HourFormat, - bool semanticsEnabled, - Brightness platformBrightness, - double textScaleFactor, - List locales, - Locale platformResolvedLocale, - String initialRouteName, + AccessibilityFeatures/*?*/ accessibilityFeatures, + bool/*?*/ alwaysUse24HourFormat, + bool/*?*/ semanticsEnabled, + Brightness/*?*/ platformBrightness, + double/*?*/ textScaleFactor, + List/*?*/ locales, + Locale/*?*/ platformResolvedLocale, + String/*?*/ initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -144,14 +139,14 @@ class PlatformConfiguration { ); } - final AccessibilityFeatures accessibilityFeatures; - final bool alwaysUse24HourFormat; - final bool semanticsEnabled; - final Brightness platformBrightness; - final double textScaleFactor; - final List locales; - final Locale platformResolvedLocale; - final String initialRouteName; + final AccessibilityFeatures/*!*/ accessibilityFeatures; + final bool/*!*/ alwaysUse24HourFormat; + final bool/*!*/ semanticsEnabled; + final Brightness/*!*/ platformBrightness; + final double/*!*/ textScaleFactor; + final List/*!*/ locales; + final Locale/*?*/ platformResolvedLocale; + final String/*?*/ initialRouteName; } class ScreenConfiguration { @@ -167,17 +162,18 @@ class ScreenConfiguration { assert(geometry != null), assert(devicePixelRatio != null), assert(viewInsets != null), + assert(viewPadding != null), assert(systemGestureInsets != null), assert(padding != null); ScreenConfiguration copyWith({ - String screenName, - Rect geometry, - double devicePixelRatio, - WindowPadding viewInsets, - WindowPadding viewPadding, - WindowPadding systemGestureInsets, - WindowPadding padding, + String/*?*/ screenName, + Rect/*?*/ geometry, + double/*?*/ devicePixelRatio, + WindowPadding/*?*/ viewInsets, + WindowPadding/*?*/ viewPadding, + WindowPadding/*?*/ systemGestureInsets, + WindowPadding/*?*/ padding, }) { return ScreenConfiguration( screenName: screenName ?? this.screenName, @@ -190,13 +186,13 @@ class ScreenConfiguration { ); } - final String screenName; - final Rect geometry; - final double devicePixelRatio; - final WindowPadding viewInsets; - final WindowPadding viewPadding; - final WindowPadding systemGestureInsets; - final WindowPadding padding; + final String/*!*/ screenName; + final Rect/*!*/ geometry; + final double/*!*/ devicePixelRatio; + final WindowPadding/*!*/ viewInsets; + final WindowPadding/*!*/ viewPadding; + final WindowPadding/*!*/ systemGestureInsets; + final WindowPadding/*!*/ padding; } @@ -212,11 +208,11 @@ class ViewConfigurationRequest { assert(screen != null || geometry != null || order != null || visible != null, 'At least one parameter must be non-null'); ViewConfigurationRequest copyWith({ - Screen screen, - Rect geometry, - bool visible, - ViewOrder order, - FlutterView orderView, + Screen/*?*/ screen, + Rect/*?*/ geometry, + bool/*?*/ visible, + ViewOrder/*?*/ order, + FlutterView/*?*/ orderView, }) { return ViewConfigurationRequest( screen: screen ?? this.screen, @@ -227,11 +223,11 @@ class ViewConfigurationRequest { ); } - final Screen screen; - final Rect geometry; - final bool visible; - final ViewOrder order; - final FlutterView orderView; + final Screen/*?*/ screen; + final Rect/*?*/ geometry; + final bool/*?*/ visible; + final ViewOrder/*?*/ order; + final FlutterView/*?*/ orderView; @override String toString() { @@ -270,15 +266,15 @@ class ViewConfiguration { assert(padding != null); ViewConfiguration copyWith({ - Screen screen, - FlutterWindow window, - Rect geometry, - double depth, - bool visible, - WindowPadding viewInsets, - WindowPadding viewPadding, - WindowPadding systemGestureInsets, - WindowPadding padding, + Screen/*?*/ screen, + FlutterWindow/*?*/ window, + Rect/*?*/ geometry, + double/*?*/ depth, + bool/*?*/ visible, + WindowPadding/*?*/ viewInsets, + WindowPadding/*?*/ viewPadding, + WindowPadding/*?*/ systemGestureInsets, + WindowPadding/*?*/ padding, }) { return ViewConfiguration( screen: screen ?? this.screen, @@ -293,15 +289,15 @@ class ViewConfiguration { ); } - final Screen screen; - final FlutterWindow window; - final Rect geometry; - final double depth; - final bool visible; - final WindowPadding viewInsets; - final WindowPadding viewPadding; - final WindowPadding systemGestureInsets; - final WindowPadding padding; + final Screen/*!*/ screen; + final FlutterWindow/*?*/ window; + final Rect/*!*/ geometry; + final double/*!*/ depth; + final bool/*!*/ visible; + final WindowPadding/*!*/ viewInsets; + final WindowPadding/*!*/ viewPadding; + final WindowPadding/*!*/ systemGestureInsets; + final WindowPadding/*!*/ padding; @override String toString() { diff --git a/lib/web_ui/lib/src/ui/screen.dart b/lib/web_ui/lib/src/ui/screen.dart index 125d89665ad9f..bb5c4fef0a178 100644 --- a/lib/web_ui/lib/src/ui/screen.dart +++ b/lib/web_ui/lib/src/ui/screen.dart @@ -8,5 +8,5 @@ part of ui; /// A class representing the screen that application windows are displayed on. abstract class Screen { /// The configuration of this screen. - ScreenConfiguration get configuration; + ScreenConfiguration/*!*/ get configuration; } diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 7c7ef1ee19235..7efcd7edb64de 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -214,17 +214,17 @@ abstract class FlutterView { abstract class FlutterWindowView extends FlutterView { @override - ViewConfiguration get viewConfiguration; + ViewConfiguration/*!*/ get viewConfiguration; } abstract class FlutterWindow extends FlutterView { @override - ViewConfiguration get viewConfiguration; + ViewConfiguration/*!*/ get viewConfiguration; } abstract class SingletonFlutterWindow extends FlutterWindow { - VoidCallback get onMetricsChanged => platformDispatcher.onMetricsChanged; - set onMetricsChanged(VoidCallback callback) { + VoidCallback/*?*/ get onMetricsChanged => platformDispatcher.onMetricsChanged; + set onMetricsChanged(VoidCallback/*?*/ callback) { platformDispatcher.onMetricsChanged = callback; } From 2eb9710afd7cc93957be265dd544312d82556eae Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 8 Jun 2020 17:48:01 -0700 Subject: [PATCH 05/14] Cleaning up --- lib/ui/compositing.dart | 12 +- lib/ui/hooks.dart | 4 +- lib/ui/platform_dispatcher.dart | 78 +--------- lib/ui/screen.dart | 2 + lib/ui/semantics.dart | 17 +-- lib/ui/window.dart | 134 +++++++----------- lib/ui/window/screen.cc | 2 +- lib/ui/window/screen_metrics.cc | 8 +- lib/ui/window/screen_metrics.h | 6 +- .../lib/src/engine/platform_dispatcher.dart | 91 ++---------- lib/web_ui/lib/src/ui/compositing.dart | 10 +- .../lib/src/ui/platform_dispatcher.dart | 7 - lib/web_ui/lib/src/ui/semantics.dart | 10 +- lib/web_ui/lib/src/ui/window.dart | 1 - shell/common/engine.h | 2 +- shell/common/platform_view.h | 6 +- .../platform/fuchsia/flutter/platform_view.cc | 2 +- .../scenario_app/lib/src/channel_util.dart | 4 +- .../scenario_app/lib/src/poppable_screen.dart | 2 +- testing/scenario_app/lib/src/scenario.dart | 10 +- testing/scenario_app/lib/src/scenarios.dart | 2 +- .../lib/src/send_text_focus_semantics.dart | 8 +- 22 files changed, 113 insertions(+), 305 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index a62c2918151a8..a76f87ac2f94a 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -10,8 +10,8 @@ part of dart.ui; /// /// To create a Scene object, use a [SceneBuilder]. /// -/// Scene objects can be displayed on the screen using the -/// [FlutterWindow.render] method. +/// Scene objects can be displayed on the screen using the [FlutterView.render] +/// method. @pragma('vm:entry-point') class Scene extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated @@ -179,7 +179,7 @@ class PhysicalShapeEngineLayer extends _EngineLayerWrapper { /// Builds a [Scene] containing the given visuals. /// -/// A [Scene] can then be rendered using [FlutterWindow.render]. +/// A [Scene] can then be rendered using [FlutterView.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add @@ -650,8 +650,8 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call - /// [FlutterWindow.render]). The UI thread frame time is the total time - /// spent executing the [FlutterWindow.onBeginFrame] callback. The "raster + /// [FlutterView.render]). The UI thread frame time is the total time + /// spent executing the [PlatformDispatcher.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. @@ -795,7 +795,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [FlutterWindow.render]. + /// screen with [FlutterView.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index c5f3007f7bf6e..0c4bfd32a99d3 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -76,7 +76,7 @@ void _updateWindowMetrics( // ignore: unused_element void _updateScreenMetrics( Object/*!*/ id, - String/*!*/ displayName, + String/*!*/ screenName, double/*!*/ left, double/*!*/ top, double/*!*/ width, @@ -98,7 +98,7 @@ void _updateScreenMetrics( final ScreenConfiguration previousConfiguration = PlatformDispatcher.instance._screenConfigurations[id] ?? const ScreenConfiguration(); PlatformDispatcher.instance._screenConfigurations[id] = previousConfiguration.copyWith( - screenName: displayName, + screenName: screenName, geometry: Rect.fromLTWH(left, top, width, height), devicePixelRatio: devicePixelRatio, viewPadding: WindowPadding._( diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index ef3f79df87324..bc7b1a3cb84f3 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -5,10 +5,7 @@ // @dart = 2.6 part of dart.ui; -// Callback types for events. - typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration/*!*/ configuration); - typedef ViewCreatedCallback = void Function(FlutterView/*!*/ view); typedef ViewDisposedCallback = void Function(FlutterView/*!*/ view); @@ -19,8 +16,8 @@ typedef ViewDisposedCallback = void Function(FlutterView/*!*/ view); /// This is the central entry point for platform messages and configuration /// events from the platform. /// -/// It exposes the size of the screen(s), the core scheduler API, the input -/// event callback, the graphics drawing API, and other such core services. +/// It exposes the he core scheduler API, the input event callback, the graphics +/// drawing API, and other such core services. /// /// It manages the list of the application's [views] and the [screens] attached /// to the device, as well as the [configuration] of various platform @@ -94,31 +91,6 @@ class PlatformDispatcher { // A map of opaque platform view identifiers to view configurations. Map/*!*/ _viewConfigurations = {}; - /// Is called after [createView] is called and returns with a new view. - /// - /// Passes the newly created [FlutterView]. - ViewCreatedCallback/*?*/ get onViewCreated => _onViewCreated; - ViewCreatedCallback/*?*/ _onViewCreated; - Zone/*!*/ _onViewCreatedZone = Zone.root; // ignore: unused_field - set onViewCreated(ViewCreatedCallback/*?*/ callback) { - _onViewCreated = callback; - _onViewCreatedZone = Zone.current; - } - - /// The callback called when a view disposal is requested by the platform. - /// - /// If the application wishes to allow this disposal, it should call - /// [FlutterView.dispose] on the given [FlutterView]. - /// - /// If the disposal is to be ignored, just do nothing. - ViewDisposedCallback/*?*/ get onViewDisposed => _onViewDisposed; - ViewDisposedCallback/*?*/ _onViewDisposed; - Zone/*!*/ _onViewDisposedZone = Zone.root; // ignore: unused_field - set onViewDisposed(ViewDisposedCallback/*?*/ callback) { - _onViewDisposed = callback; - _onViewDisposedZone = Zone.current; - } - /// A callback that is invoked whenever any [ViewConfiguration] field in the /// [views] or [ScreenConfiguration] field in the [screens] changes, or when a /// view or screen is added or removed. @@ -233,52 +205,6 @@ class PlatformDispatcher { void _nativeSetNeedsReportTimings(bool/*!*/ value) native 'PlatformConfiguration_setNeedsReportTimings'; - /// Creates a new view and returns the view created. - /// - /// The configuration obtained and the one requested may not match, depending - /// on what the platform was able to accommodate. - /// - /// The future returns when the view has been created, and the view has been - /// added to [views]. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - Future/*?*/ createView(ViewConfigurationRequest/*!*/ request) async { - throw UnimplementedError(); - // Awaits the platform view creation response, and calls onViewCreated - // before returning. - } - - /// Reconfigures an existing view. - /// - /// This can be used to resize, show, hide, or change the order of the given - /// view, according to what is in the [ViewConfigurationRequest]. - /// - /// The configuration obtained and the one requested may not match, depending - /// on what the platform was able to accommodate. - /// - /// The Future returns when the view has been reconfigured. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - Future configureView( - FlutterView/*!*/ view, - ViewConfigurationRequest/*!*/ configuration, - ) async { - throw UnimplementedError(); - } - - /// Requests permanently closing a view. - /// - /// The Future completes when the view has been disposed and has been removed - /// from [views]. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - Future disposeView(FlutterView/*!*/ view) async { - throw UnimplementedError(); - } - /// Sends a message to a platform-specific plugin. /// /// The `name` parameter determines which plugin receives the message. The diff --git a/lib/ui/screen.dart b/lib/ui/screen.dart index 70192ae156bbc..0c44facc0ee2d 100644 --- a/lib/ui/screen.dart +++ b/lib/ui/screen.dart @@ -6,6 +6,8 @@ part of dart.ui; /// A class representing the screen that application windows are displayed on. +/// +/// Each screen can have separate dimensions, and a separate device pixel ratio. class Screen { const Screen._({Object/*!*/ screenId, PlatformDispatcher/*!*/ platformDispatcher}) : _screenId = screenId, diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index 0f8eca5d50b00..082ea115f76fe 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -609,7 +609,8 @@ class SemanticsFlag { /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [FlutterWindow.updateSemantics] to update the semantics conveyed to the user. +/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the +/// user. @pragma('vm:entry-point') class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates an empty [SemanticsUpdateBuilder] object. @@ -637,10 +638,10 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [FlutterWindow.onSemanticsAction] will be called with `id` and one of - /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [FlutterWindow.onSemanticsAction] callback might be called - /// with an action that is no longer possible. + /// node, the [PlatformDispatcher.onSemanticsAction] will be called with `id` + /// and one of the possible [SemanticsAction]s. Because the semantics tree is + /// maintained asynchronously, the [PlatformDispatcher.onSemanticsAction] + /// callback might be called with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property /// describes the current value of the node as a string. The `increasedValue` @@ -816,8 +817,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [FlutterWindow.updateSemantics] to actually - /// update the semantics retained by the system. + /// The returned object can be passed to [PlatformDispatcher.updateSemantics] + /// to actually update the semantics retained by the system. SemanticsUpdate build() { final SemanticsUpdate semanticsUpdate = SemanticsUpdate._(); _build(semanticsUpdate); @@ -831,7 +832,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree -/// using the [FlutterWindow.updateSemantics] method. +/// using the [PlatformDispatcher.updateSemantics] method. @pragma('vm:entry-point') class SemanticsUpdate extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 6fda0a564fcf6..2f67bfec40588 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -8,10 +8,10 @@ part of dart.ui; /// Signature of callbacks that have no arguments and return no data. typedef VoidCallback = void Function(); -/// Signature for [FlutterWindow.onBeginFrame]. +/// Signature for [PlatformDispatcher.onBeginFrame]. typedef FrameCallback = void Function(Duration duration); -/// Signature for [FlutterWindow.onReportTimings]. +/// Signature for [PlatformDispatcher.onReportTimings]. /// /// {@template dart.ui.TimingsCallback.list} /// The callback takes a list of [FrameTiming] because it may not be @@ -25,19 +25,19 @@ typedef FrameCallback = void Function(Duration duration); /// {@endtemplate} typedef TimingsCallback = void Function(List timings); -/// Signature for [FlutterWindow.onPointerDataPacket]. +/// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); -/// Signature for [FlutterWindow.onSemanticsAction]. +/// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); /// Signature for responses to platform messages. /// -/// Used as a parameter to [FlutterWindow.sendPlatformMessage] and -/// [FlutterWindow.onPlatformMessage]. +/// Used as a parameter to [PlatformDispatcher.sendPlatformMessage] and +/// [PlatformDispatcher.onPlatformMessage]. typedef PlatformMessageResponseCallback = void Function(ByteData? data); -/// Signature for [FlutterWindow.onPlatformMessage]. +/// Signature for [PlatformDispatcher.onPlatformMessage]. typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); // Signature for _setNeedsReportTimings. @@ -72,9 +72,9 @@ enum FramePhase { /// /// If you're using the whole Flutter framework, please use /// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using -/// [FlutterWindow.onReportTimings] directly because +/// [PlatformDispatcher.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If -/// [SchedulerBinding] is unavailable, then see [FlutterWindow.onReportTimings] +/// [SchedulerBinding] is unavailable, then see [PlatformDispatcher.onReportTimings] /// for how to get this. /// /// The metrics in debug mode (`flutter run` without any flags) may be very @@ -88,7 +88,7 @@ class FrameTiming { /// [FramePhase.values]. /// /// This constructor is usually only called by the Flutter engine, or a test. - /// To get the [FrameTiming] of your app, see [FlutterWindow.onReportTimings]. + /// To get the [FrameTiming] of your app, see [PlatformDispatcher.onReportTimings]. FrameTiming(List timestamps) : assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps; @@ -101,11 +101,11 @@ class FrameTiming { /// The duration to build the frame on the UI thread. /// - /// The build starts approximately when [FlutterWindow.onBeginFrame] is called. The - /// [Duration] in the [FlutterWindow.onBeginFrame] callback is exactly the + /// The build starts approximately when [PlatformDispatcher.onBeginFrame] is called. The + /// [Duration] in the [PlatformDispatcher.onBeginFrame] callback is exactly the /// `Duration(microseconds: timestampInMicroseconds(FramePhase.buildStart))`. /// - /// The build finishes when [FlutterWindow.render] is called. + /// The build finishes when [FlutterView.render] is called. /// /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} /// To ensure smooth animations of X fps, this should not exceed 1000/X @@ -177,7 +177,7 @@ enum AppLifecycleState { /// user input, and running in the background. /// /// When the application is in this state, the engine will not call the - /// [FlutterWindow.onBeginFrame] and [FlutterWindow.onDrawFrame] callbacks. + /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] callbacks. paused, /// The application is still hosted on a flutter engine but is detached from @@ -192,8 +192,8 @@ enum AppLifecycleState { /// A representation of distances for each of the four edges of a rectangle, /// used to encode the view insets and padding that applications should place -/// around their user interface, as exposed by [FlutterWindow.viewInsets] and -/// [FlutterWindow.padding]. View insets and padding are preferably read via +/// around their user interface, as exposed by [FlutterView.viewInsets] and +/// [FlutterView.padding]. View insets and padding are preferably read via /// [MediaQuery.of]. /// /// For a generic class that represents distances around a rectangle, see the @@ -245,7 +245,7 @@ class WindowPadding { /// /// See also: /// -/// * [FlutterWindow.locale], which specifies the system's currently selected +/// * [PlatformDispatcher.locale], which specifies the system's currently selected /// [Locale]. class Locale { /// Creates a new Locale object. The first argument is the @@ -527,10 +527,7 @@ class Locale { /// A view into which a Flutter [Scene] is drawn. /// /// Each [FlutterView] has its own layer tree that is rendered into an area -/// inside of the [FlutterWindow] whenever [render] is called with a [Scene]. -/// -/// New views can be created by the [PlatformDispatcher], using -/// [PlatformDispatcher.createView]. +/// inside of a [FlutterWindow] whenever [render] is called with a [Scene]. /// /// ## Insets and Padding /// @@ -542,11 +539,11 @@ class Locale { /// represents the system keyboard, which can cover over the bottom view padding /// when visible. /// -/// The [FlutterWindow.viewInsets] are the physical pixels which the operating +/// The [viewInsets] are the physical pixels which the operating /// system reserves for system UI, such as the keyboard, which would fully /// obscure any content drawn in that area. /// -/// The [FlutterWindow.viewPadding] are the physical pixels on each side of the +/// The [viewPadding] are the physical pixels on each side of the /// display that may be partially obscured by system UI or by physical /// intrusions into the display, such as an overscan region on a television or a /// "notch" on a phone. Unlike the insets, these areas may have portions that @@ -556,24 +553,24 @@ class Locale { /// opaque keyboard or a partially translucent status bar, which cover an area /// without gaps. /// -/// The [FlutterWindow.padding] property is computed from both -/// [FlutterWindow.viewInsets] and [FlutterWindow.viewPadding]. It will allow a +/// The [padding] property is computed from both +/// [viewInsets] and [viewPadding]. It will allow a /// view inset to consume view padding where appropriate, such as when a phone's /// keyboard is covering the bottom view padding and so "absorbs" it. /// /// Clients that want to position elements relative to the view padding -/// regardless of the view insets should use the [FlutterWindow.viewPadding] +/// regardless of the view insets should use the [viewPadding] /// property, e.g. if you wish to draw a widget at the center of the screen with /// respect to the iPhone "safe area" regardless of whether the keyboard is /// showing. /// -/// [FlutterWindow.padding] is useful for clients that want to know how much +/// [padding] is useful for clients that want to know how much /// padding should be accounted for without concern for the current inset(s) /// state, e.g. determining whether a gesture should be considered for scrolling /// purposes. This value varies based on the current state of the insets. For /// example, a visible keyboard will consume all gestures in the bottom part of -/// the [FlutterWindow.viewPadding] anyway, so there is no need to account for -/// that in the [FlutterWindow.padding], which is always safe to use for such +/// the [viewPadding] anyway, so there is no need to account for +/// that in the [padding], which is always safe to use for such /// calculations. /// /// See also: @@ -583,10 +580,10 @@ class Locale { abstract class FlutterView { /// The platform dispatcher that this view is registered with, and gets its /// information from. - PlatformDispatcher get platformDispatcher; + PlatformDispatcher/*!*/ get platformDispatcher; /// The configuration of this view. - ViewConfiguration get viewConfiguration; + ViewConfiguration/*!*/ get viewConfiguration; /// The number of device pixels for each logical pixel for the screen this /// view is displayed on. @@ -657,7 +654,7 @@ abstract class FlutterView { /// observe when this value changes. Size get physicalSize => viewConfiguration.geometry.size; - /// The physical depth is the maximum elevation that the `FlutterView` allows. + /// The physical depth is the maximum elevation that the [FlutterView] allows. /// /// Physical layers drawn at or above this elevation will have their elevation /// clamped to this value. This can happen if the physical layer itself has @@ -676,9 +673,9 @@ abstract class FlutterView { /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [FlutterWindow.viewInsets], - /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in - /// more detail in the documentation for [FlutterWindow]. + /// The relationship between this [viewInsets], + /// [viewPadding], and [padding] are described in + /// more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -695,16 +692,16 @@ abstract class FlutterView { /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// - /// Unlike [FlutterWindow.padding], this value does not change relative to - /// [FlutterWindow.viewInsets]. For example, on an iPhone X, it will not + /// Unlike [padding], this value does not change relative to + /// [viewInsets]. For example, on an iPhone X, it will not /// change in response to the soft keyboard being visible or hidden, whereas - /// [FlutterWindow.padding] will. + /// [padding] will. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [FlutterWindow.viewInsets], - /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in - /// more detail in the documentation for [FlutterWindow]. + /// The relationship between this [viewInsets], + /// [viewPadding], and [padding] are described in + /// more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -741,16 +738,16 @@ abstract class FlutterView { /// This value is calculated by taking `max(0.0, FlutterView.viewPadding - /// FlutterView.viewInsets)`. This will treat a system IME that increases the /// bottom inset as consuming that much of the bottom padding. For example, on - /// an iPhone X, [FlutterView.padding.bottom] is the same as - /// [FlutterView.viewPadding.bottom] when the soft keyboard is not drawn (to + /// an iPhone X, [padding.bottom] is the same as + /// [viewPadding.bottom] when the soft keyboard is not drawn (to /// account for the bottom soft button area), but will be `0.0` when the soft /// keyboard is visible. /// /// When this changes, [onMetricsChanged] is called. /// - /// The relationship between this [FlutterWindow.viewInsets], - /// [FlutterWindow.viewPadding], and [FlutterWindow.padding] are described in - /// more detail in the documentation for [FlutterWindow]. + /// The relationship between this [viewInsets], + /// [viewPadding], and [padding] are described in + /// more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -789,42 +786,6 @@ abstract class FlutterView { /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. void render(Scene scene) => platformDispatcher.render(scene, this); - - /// Dispose of this view, closing it permanently. - /// - /// This function should be called in response to a call to - /// [PlatformDispatcher.onViewDisposed], or to permanently dispose of a view - /// that the application would like to close. - void dispose() => platformDispatcher.disposeView(this); -} - -/// A type of [FlutterView] that can be hosted inside of a [FlutterWindow], into -/// which a Flutter [Scene] is drawn. -/// -/// A [FlutterWindowView] has its own layer tree that is rendered into an area -/// inside of a [FlutterWindow] when [render] is called with a [Scene]. -/// -/// A [FlutterWindow] is a subclass of a [FlutterView] that is a separate window -/// which can host other [FlutterView]s. -/// -/// A `FlutterWindowView` can only be created by [PlatformDispatcher.createView] -/// where the requested configuration includes a parent [FlutterWindow] set as -/// the [ViewConfiguration.window]. -class FlutterWindowView extends FlutterView { - FlutterWindowView._({Object viewId, this.platformDispatcher}) - : _viewId = viewId; - - /// The opaque ID for this view. - final Object _viewId; - - @override - final PlatformDispatcher platformDispatcher; - - @override - ViewConfiguration get viewConfiguration { - assert(platformDispatcher._viewConfigurations.containsKey(_viewId)); - return platformDispatcher._viewConfigurations[_viewId]; - } } /// A top-level platform window displaying a Flutter layer tree drawn from a @@ -870,7 +831,7 @@ class FlutterWindow extends FlutterView { /// This class provides backward compatibility with code that was written before /// Flutter supported multiple top level windows. New code should refer to the /// [WidgetsBinding.instance.platformDispatcher] or [FlutterWindow] class -/// directly. +/// directly to modify or retrieve these properties. /// /// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` /// if `WidgetsBinding` is unavailable. But we strongly advise avoiding a static @@ -881,8 +842,8 @@ class SingletonFlutterWindow extends FlutterWindow { : super._(windowId: windowId, platformDispatcher: platformDispatcher); /// A callback that is invoked whenever the [devicePixelRatio], - /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] - /// values change. + /// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], + /// [PlatformDispatcher.screens], or [systemGestureInsets] values change. /// /// {@template flutter.lib.ui.window.forwardWarning} /// @@ -1419,5 +1380,6 @@ enum Brightness { /// See also: /// /// * [PlatformDispatcher.views], contains the current list of Flutter windows -/// belonging to the application. +/// belonging to the application, including top level application windows +/// like this one. final SingletonFlutterWindow window = SingletonFlutterWindow._(windowId: 0, platformDispatcher: PlatformDispatcher.instance); diff --git a/lib/ui/window/screen.cc b/lib/ui/window/screen.cc index a96ac2ccb6968..675bb6a2ceedd 100644 --- a/lib/ui/window/screen.cc +++ b/lib/ui/window/screen.cc @@ -31,7 +31,7 @@ void Screen::UpdateScreenMetrics(const ScreenMetrics& metrics) { library_.value(), "_updateScreenMetrics", { tonic::ToDart(screen_id_), - tonic::ToDart(metrics.display_name), + tonic::ToDart(metrics.screen_name), tonic::ToDart(metrics.physical_left), tonic::ToDart(metrics.physical_top), tonic::ToDart(metrics.physical_width), diff --git a/lib/ui/window/screen_metrics.cc b/lib/ui/window/screen_metrics.cc index b8a2dfb2a0830..ac7f309c1d873 100644 --- a/lib/ui/window/screen_metrics.cc +++ b/lib/ui/window/screen_metrics.cc @@ -8,7 +8,7 @@ namespace flutter { -ScreenMetrics::ScreenMetrics(std::string p_display_name, +ScreenMetrics::ScreenMetrics(std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -26,7 +26,7 @@ ScreenMetrics::ScreenMetrics(std::string p_display_name, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, double p_physical_system_gesture_inset_left) - : display_name(p_display_name), + : screen_name(p_screen_name), device_pixel_ratio(p_device_pixel_ratio), physical_left(p_physical_left), physical_top(p_physical_top), @@ -52,7 +52,7 @@ ScreenMetrics::ScreenMetrics(std::string p_display_name, FML_DCHECK(physical_height >= 0); } -ScreenMetrics::ScreenMetrics(std::string p_display_name, +ScreenMetrics::ScreenMetrics(std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -66,7 +66,7 @@ ScreenMetrics::ScreenMetrics(std::string p_display_name, double p_physical_view_inset_right, double p_physical_view_inset_bottom, double p_physical_view_inset_left) - : display_name(p_display_name), + : screen_name(p_screen_name), device_pixel_ratio(p_device_pixel_ratio), physical_left(p_physical_left), physical_top(p_physical_top), diff --git a/lib/ui/window/screen_metrics.h b/lib/ui/window/screen_metrics.h index 63263e0b874a4..cfc808b78d26c 100644 --- a/lib/ui/window/screen_metrics.h +++ b/lib/ui/window/screen_metrics.h @@ -16,7 +16,7 @@ struct ScreenMetrics { ScreenMetrics(const ScreenMetrics& other) = default; // Create a ScreenMetrics instance. - ScreenMetrics(std::string p_display_name, + ScreenMetrics(std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -36,7 +36,7 @@ struct ScreenMetrics { double p_physical_system_gesture_inset_left); // Create a ScreenMetrics instance without system gesture insets. - ScreenMetrics(std::string p_display_name, + ScreenMetrics(std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -55,7 +55,7 @@ struct ScreenMetrics { double p_physical_width, double p_physical_height); - std::string display_name = ""; + std::string screen_name = ""; double device_pixel_ratio = 1.0; double physical_left = 0; double physical_top = 0; diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index eb939a090295d..f8a91002bec04 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -67,81 +67,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// [PlatformDispatcher] subclasses. Map/*!*/ _windowConfigurations = {}; - /// Opens a new window and returns the window created. - /// - /// The configuration obtained and the one requested may not match, depending - /// on what the platform was able to accommodate. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - @override - Future createView(ui.ViewConfigurationRequest/*!*/ configuration) async { - throw UnimplementedError(); - // Awaits the platform window creation response, and calls onWindowOpened before returning. - } - - /// Reconfigures an existing window. - /// - /// The configuration obtained and the one requested may not match, depending - /// on what the platform was able to accommodate. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - @override - Future configureView( - ui.FlutterView/*!*/ view, ui.ViewConfigurationRequest/*!*/ configuration) async { - throw UnimplementedError(); - } - - /// Requests closing a window. - /// - /// Future completes when the window has been closed. - /// - /// This function is currently not implemented, but is part of a planned - /// feature. - @override - Future disposeView(ui.FlutterView/*!*/ view) async { - throw UnimplementedError(); - } - - /// Is called when [createView] is called. - /// - /// Sends the newly opened window. - @override - ui.ViewCreatedCallback/*?*/ get onViewCreated => _onWindowOpened; - ui.ViewCreatedCallback/*?*/ _onWindowOpened; - Zone/*!*/ _onWindowOpenedZone = Zone.root; // ignore: unused_field - @override - set onViewCreated(ui.ViewCreatedCallback/*?*/ callback) { - _onWindowOpened = callback; - _onWindowOpenedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnWindowCreated(Object/*!*/ id) { - _invoke1(_onWindowOpened, _onWindowOpenedZone, id); - } - - /// Is called when a window closure is requested by the platform. - /// - /// Sends the window to be closed. - @override - ui.ViewDisposedCallback/*?*/ get onViewDisposed => _onWindowClosed; - ui.ViewDisposedCallback/*?*/ _onWindowClosed; - Zone/*!*/ _onWindowClosedZone = Zone.root; // ignore: unused_field - @override - set onViewDisposed(ui.ViewDisposedCallback/*?*/ callback) { - _onWindowClosed = callback; - _onWindowClosedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnWindowDisposed(Object/*!*/ id) { - _invoke1(_onWindowClosed, _onWindowClosedZone, id); - } - /// A callback that is invoked whenever the platform's [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] /// values change, for example when the device is rotated or when the @@ -260,7 +185,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// - /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use + /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use /// [Window.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. /// @@ -939,7 +864,7 @@ bool _handleWebTestEnd2EndMessage(MethodCodec/*!*/ codec, ByteData/*!*/ data) { } /// Invokes [callback] inside the given [zone]. -void _invoke(void callback/*?*/(), Zone/*!*/ zone) { +void _invoke(void callback()/*?*/, Zone/*!*/ zone) { if (callback == null) return; @@ -953,7 +878,7 @@ void _invoke(void callback/*?*/(), Zone/*!*/ zone) { } /// Invokes [callback] inside the given [zone] passing it [arg]. -void _invoke1(void callback/*?*/(A/*?*/ a), Zone zone/*!*/, A/*?*/ arg) { +void _invoke1(void callback(A a)/*?*/, Zone zone/*!*/, A arg) { if (callback == null) return; @@ -967,12 +892,12 @@ void _invoke1(void callback/*?*/(A/*?*/ a), Zone zone/*!*/, A/*?*/ arg) } /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3( - void callback/*?*/(A1/*?*/ a1, A2/*?*/ a2, A3/*?*/ a3), +void _invoke3( + void callback(A1 a1, A2 a2, A3 a3)/*?*/, Zone/*!*/ zone, - A1/*?*/ arg1, - A2/*?*/ arg2, - A3/*?*/ arg3, + A1 arg1, + A2 arg2, + A3 arg3, ) { if (callback == null) return; diff --git a/lib/web_ui/lib/src/ui/compositing.dart b/lib/web_ui/lib/src/ui/compositing.dart index 8ab00f38cd413..05895d75c47b1 100644 --- a/lib/web_ui/lib/src/ui/compositing.dart +++ b/lib/web_ui/lib/src/ui/compositing.dart @@ -10,7 +10,7 @@ part of ui; /// To create a Scene object, use a [SceneBuilder]. /// /// Scene objects can be displayed on the screen using the -/// [FlutterWindow.render] method. +/// [FlutterView.render] method. abstract class Scene { /// Creates a raster image representation of the current state of the scene. /// This is a slow operation that is performed on a background thread. @@ -105,7 +105,7 @@ abstract class PhysicalShapeEngineLayer implements EngineLayer {} /// Builds a [Scene] containing the given visuals. /// -/// A [Scene] can then be rendered using [FlutterWindow.render]. +/// A [Scene] can then be rendered using [FlutterView.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add @@ -297,8 +297,8 @@ abstract class SceneBuilder { /// /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call - /// [FlutterWindow.render]). The UI thread frame time is the total time - /// spent executing the [FlutterWindow.onBeginFrame] callback. The "raster + /// [FlutterView.render]). The UI thread frame time is the total time + /// spent executing the [PlatformDispatcher.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. @@ -400,7 +400,7 @@ abstract class SceneBuilder { /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [FlutterWindow.render]. + /// screen with [FlutterView.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index fd66a55e0e665..9c1fd1b8c7464 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -19,13 +19,6 @@ abstract class PlatformDispatcher { Iterable/*!*/ get screens; Iterable/*!*/ get views; - ViewCreatedCallback/*?*/ get onViewCreated; - set onViewCreated(ViewCreatedCallback/*?*/ callback); - ViewDisposedCallback/*?*/ get onViewDisposed; - set onViewDisposed(ViewDisposedCallback/*?*/ callback); - Future createView(ViewConfigurationRequest/*!*/ request); - Future configureView(FlutterView/*!*/ view, ViewConfigurationRequest/*!*/ configuration); - Future disposeView(FlutterView/*!*/ view); VoidCallback/*?*/ get onMetricsChanged; set onMetricsChanged(VoidCallback/*?*/ callback); diff --git a/lib/web_ui/lib/src/ui/semantics.dart b/lib/web_ui/lib/src/ui/semantics.dart index 141fea1748e5a..958522f2f5ffd 100644 --- a/lib/web_ui/lib/src/ui/semantics.dart +++ b/lib/web_ui/lib/src/ui/semantics.dart @@ -610,7 +610,7 @@ class SemanticsFlag { /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [FlutterWindow.updateSemantics] to update the semantics conveyed to the user. +/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the user. class SemanticsUpdateBuilder { /// Creates an empty [SemanticsUpdateBuilder] object. SemanticsUpdateBuilder(); @@ -638,9 +638,9 @@ class SemanticsUpdateBuilder { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [FlutterWindow.onSemanticsAction] will be called with `id` and one of + /// node, the [PlatformDispatcher.onSemanticsAction] will be called with `id` and one of /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [FlutterWindow.onSemanticsAction] callback might be called + /// asynchronously, the [PlatformDispatcher.onSemanticsAction] callback might be called /// with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property @@ -734,7 +734,7 @@ class SemanticsUpdateBuilder { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [FlutterWindow.updateSemantics] to actually + /// The returned object can be passed to [PlatformDispatcher.updateSemantics] to actually /// update the semantics retained by the system. SemanticsUpdate build() { return SemanticsUpdate._( @@ -748,7 +748,7 @@ class SemanticsUpdateBuilder { /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree -/// using the [FlutterWindow.updateSemantics] method. +/// using the [PlatformDispatcher.updateSemantics] method. abstract class SemanticsUpdate { /// This class is created by the engine, and should not be instantiated /// or extended directly. diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 7efcd7edb64de..a9d3251b889c7 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -209,7 +209,6 @@ abstract class FlutterView { WindowPadding get padding => viewConfiguration.padding; void render(Scene scene) => platformDispatcher.render(scene, this); - void dispose() => platformDispatcher.disposeView(this); } abstract class FlutterWindowView extends FlutterView { diff --git a/shell/common/engine.h b/shell/common/engine.h index 9c1affd35c077..e44d8a253eff1 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -657,7 +657,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { //---------------------------------------------------------------------------- /// @brief Updates the screen metrics for the currently running Flutter /// application. The screen metrics detail the size of the - /// screen that the rendering viewport is in in texels as well + /// screen that the rendering viewport is on in texels as well /// as edge insets if present. /// /// @see `ScreenMetrics`, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 68a41ebe4bcd6..0d1f96c9c89f2 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -101,8 +101,8 @@ class PlatformView { const ViewportMetrics& metrics) = 0; //-------------------------------------------------------------------------- - /// @brief Notifies the delegate the screen metrics of the platform - /// screen have been updated. + /// @brief Notifies the delegate that the screen metrics of the + /// platform screen have been updated. /// /// @param[in] metrics The updated screen metrics. /// @@ -392,7 +392,7 @@ class PlatformView { void SetViewportMetrics(const ViewportMetrics& metrics); //---------------------------------------------------------------------------- - /// @brief Used by embedders to specify the updated screen metrics. In + /// @brief Used by embedders to specify updated screen metrics. In /// response to this call, on the raster thread, the rasterizer /// may need to be reconfigured to the updated viewport dimensions /// if they are affected by the change in screen metrics. On the diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index d52b4edf15ab2..553861a555772 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -188,7 +188,7 @@ void PlatformView::FlushViewportMetrics() { const auto scale = metrics_.scale; const auto scale_z = metrics_.scale_z; const flutter::ScreenMetrics screen_metrics{ - "", // display_name + "", // screen_name scale, // device_pixel_ratio 0, // physical_left 0, // physical_top diff --git a/testing/scenario_app/lib/src/channel_util.dart b/testing/scenario_app/lib/src/channel_util.dart index bf8fd2fae7e0d..aca1f8c459077 100644 --- a/testing/scenario_app/lib/src/channel_util.dart +++ b/testing/scenario_app/lib/src/channel_util.dart @@ -10,13 +10,13 @@ import 'package:meta/meta.dart'; /// Util method to replicate the behavior of a `MethodChannel` in the Flutter /// framework. void sendJsonMethodCall({ - @required SingletonFlutterWindow window, + @required PlatformDispatcher dispatcher, @required String channel, @required String method, dynamic arguments, PlatformMessageResponseCallback callback, }) { - window.sendPlatformMessage( + dispatcher.sendPlatformMessage( channel, // This recreates a combination of OptionalMethodChannel, JSONMethodCodec, // and _DefaultBinaryMessenger in the framework. diff --git a/testing/scenario_app/lib/src/poppable_screen.dart b/testing/scenario_app/lib/src/poppable_screen.dart index e62f63847ccd5..189621dbe2ddd 100644 --- a/testing/scenario_app/lib/src/poppable_screen.dart +++ b/testing/scenario_app/lib/src/poppable_screen.dart @@ -74,7 +74,7 @@ class PoppableScreenScenario extends Scenario with PlatformEchoMixin { void _pop() { sendJsonMethodCall( - window: window, + dispatcher: dispatcher, // 'flutter/platform' is the hardcoded name of the 'platform' // `SystemChannel` from the `SystemNavigator` API. // https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/system_navigator.dart. diff --git a/testing/scenario_app/lib/src/scenario.dart b/testing/scenario_app/lib/src/scenario.dart index b0d57d071b75b..b556f5d0b325c 100644 --- a/testing/scenario_app/lib/src/scenario.dart +++ b/testing/scenario_app/lib/src/scenario.dart @@ -16,29 +16,29 @@ abstract class Scenario { /// Called by the program when a frame is ready to be drawn. /// - /// See [FlutterWindow.onBeginFrame] for more details. + /// See [PlatformDispatcher.onBeginFrame] for more details. void onBeginFrame(Duration duration) {} /// Called by the program when the microtasks from [onBeginFrame] have been /// flushed. /// - /// See [FlutterWindow.onDrawFrame] for more details. + /// See [PlatformDispatcher.onDrawFrame] for more details. void onDrawFrame() {} /// Called by the program when the window metrics have changed. /// - /// See [FlutterWindow.onMetricsChanged]. + /// See [PlatformDispatcher.onMetricsChanged]. void onMetricsChanged() {} /// Called by the program when a pointer event is received. /// - /// See [FlutterWindow.onPointerDataPacket]. + /// See [PlatformDispatcher.onPointerDataPacket]. void onPointerDataPacket(PointerDataPacket packet) {} /// Called by the program when an engine side platform channel message is /// received. /// - /// See [FlutterWindow.onPlatformMessage]. + /// See [PlatformDispatcher.onPlatformMessage]. void onPlatformMessage( String name, ByteData data, diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 385583b4b3986..628fdac1ca6e5 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -36,7 +36,7 @@ Map _scenarios = { 'platform_view_gesture_accept': PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: 11, accept: true), 'platform_view_gesture_reject_after_touches_ended': PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: 11, accept: false, rejectUntilTouchesEnded: true), 'tap_status_bar': TouchesScenario(PlatformDispatcher.instance), - 'text_semantics_focus': SendTextFocusScemantics(PlatformDispatcher.instance), + 'text_semantics_focus': SendTextFocusSemantics(PlatformDispatcher.instance), }; Map _currentScenario = { diff --git a/testing/scenario_app/lib/src/send_text_focus_semantics.dart b/testing/scenario_app/lib/src/send_text_focus_semantics.dart index ebaaf2da4bc84..8d70dd6e2f19b 100644 --- a/testing/scenario_app/lib/src/send_text_focus_semantics.dart +++ b/testing/scenario_app/lib/src/send_text_focus_semantics.dart @@ -11,9 +11,9 @@ import 'channel_util.dart'; import 'scenario.dart'; /// A scenario that sends back messages when touches are received. -class SendTextFocusScemantics extends Scenario { +class SendTextFocusSemantics extends Scenario { /// Constructor for `SendTextFocusScemantics`. - SendTextFocusScemantics(PlatformDispatcher dispatcher) : super(dispatcher); + SendTextFocusSemantics(PlatformDispatcher dispatcher) : super(dispatcher); @override void onBeginFrame(Duration duration) { @@ -72,7 +72,7 @@ class SendTextFocusScemantics extends Scenario { // This mimics the framework which shows the FlutterTextInputView before // updating the TextInputSemanticsObject. sendJsonMethodCall( - window: window, + dispatcher: dispatcher, channel: 'flutter/textinput', method: 'TextInput.setClient', arguments: [ @@ -84,7 +84,7 @@ class SendTextFocusScemantics extends Scenario { ); sendJsonMethodCall( - window: window, + dispatcher: dispatcher, channel: 'flutter/textinput', method: 'TextInput.show', ); From b3f65ee74e0f7eafbaec44eb4fc5d9e5af45e50b Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 9 Jun 2020 13:40:37 -0700 Subject: [PATCH 06/14] Merge changes --- shell/platform/linux/fl_view.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 8564336d7da86..fbc7f7ac69df9 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -99,16 +99,26 @@ static gboolean fl_view_send_pointer_button_event(FlView* self, return TRUE; } -// Updates the engine with the current window metrics. -static void fl_view_send_window_metrics(FlView* self) { +// Updates the engine with the current screen metrics. +static void fl_view_send_screen_metrics(FlView* self) { GtkAllocation allocation; gtk_widget_get_allocation(GTK_WIDGET(self), &allocation); gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); - fl_engine_send_window_metrics_event( + fl_engine_send_screen_metrics_event( self->engine, allocation.width * scale_factor, allocation.height * scale_factor, scale_factor); } +// Updates the engine with the current window metrics. +static void fl_view_send_window_metrics(FlView* self) { + GtkAllocation allocation; + gtk_widget_get_allocation(GTK_WIDGET(self), &allocation); + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); + fl_engine_send_window_metrics_event(self->engine, + allocation.width * scale_factor, + allocation.height * scale_factor); +} + // Implements FlPluginRegistry::get_registrar_for_plugin. static FlPluginRegistrar* fl_view_get_registrar_for_plugin( FlPluginRegistry* registry, @@ -175,6 +185,7 @@ static void fl_view_notify(GObject* object, GParamSpec* pspec) { FlView* self = FL_VIEW(object); if (strcmp(pspec->name, "scale-factor") == 0) { + fl_view_send_screen_metrics(self); fl_view_send_window_metrics(self); } @@ -248,8 +259,8 @@ static void fl_view_size_allocate(GtkWidget* widget, allocation->height); } - fl_view_send_window_metrics(self); fl_view_send_screen_metrics(self); + fl_view_send_window_metrics(self); } // Implements GtkWidget::button_press_event. From c1bc11e5b8ed6a2722a33a30caa87151e8efd291 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 9 Jun 2020 17:34:34 -0700 Subject: [PATCH 07/14] Remove more future APIs --- lib/ui/platform_dispatcher.dart | 97 +------------------ lib/web_ui/lib/src/engine/window.dart | 4 - .../lib/src/ui/platform_dispatcher.dart | 50 ---------- 3 files changed, 1 insertion(+), 150 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index bc7b1a3cb84f3..c1afa44be3810 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -656,7 +656,7 @@ class ScreenConfiguration { assert(systemGestureInsets != null), assert(padding != null); - /// Makes a new copy of this [ViewConfigurationRequest] with some attributes + /// Makes a new copy of this [ScreenConfiguration] with some attributes /// replaced. ScreenConfiguration copyWith({ String/*?*/ screenName, @@ -718,102 +718,7 @@ class ScreenConfiguration { } } -/// Class that holds the information needed for a view configuration request. -/// -/// Used to request a different configuration of a [FlutterView], so that -/// multiple view parameters can be configured simultaneously. -/// -/// Parameters that shouldn't change with this request may be null. At least one -/// parameter must be set. -class ViewConfigurationRequest { - /// Const constructor for a [ViewConfigurationRequest]. - const ViewConfigurationRequest({ - this.screen, - this.geometry, - this.visible, - this.order, - this.orderView, - }) : assert(orderView != null || (order != ViewOrder.aboveOther && order != ViewOrder.belowOther)), - assert(orderView == null || (order != ViewOrder.top && order != ViewOrder.bottom)), - assert(screen != null || geometry != null || order != null || visible != null, 'At least one parameter must be non-null'); - - /// Makes a new copy of this [ViewConfigurationRequest] with some attributes - /// replaced. - ViewConfigurationRequest copyWith({ - Screen/*?*/ screen, - Rect/*?*/ geometry, - bool/*?*/ visible, - ViewOrder/*?*/ order, - FlutterView/*?*/ orderView, - }) { - return ViewConfigurationRequest( - screen: screen ?? this.screen, - geometry: geometry ?? this.geometry, - visible: visible ?? this.visible, - order: order ?? this.order, - orderView: orderView ?? this.orderView, - ); - } - - /// The screen that this view should appear on. - /// - /// If the platform supports spanning multiple screens, this is the screen - /// that the upper left corner of the view appears on. - final Screen/*?*/ screen; - - /// The geometry requested for the view on the [screen], in logical pixels. - /// - /// This uses the device pixel ratio of the screen with the upper left corner - /// of this view on it. - final Rect/*?*/ geometry; - - /// Whether or not the view should be visible. - /// - /// If this request is given to [PlatformDispatcher.createView], then setting - /// this to true means that the view will be made visible as soon as it is - /// created. - final bool/*?*/ visible; - - /// The depth ordering of this view relative to other views. - final ViewOrder/*?*/ order; - - /// The opaque ID of the view to place this view on a layer relative to, - /// according to [order]. - /// - /// Only used (and required) if [order] is [ViewOrder.aboveOther] or - /// [ViewOrder.belowOther]. - /// - /// This ID corresponds to the view that this one should be above or below. - final FlutterView/*?*/ orderView; - - @override - String toString() { - return '$runtimeType[screen: $screen, geometry: $geometry, order: $order]'; - } -} - -/// An enum describing how to layer this view in a [ViewConfigurationRequest]. -enum ViewOrder { - /// Place this view immediately above the - /// [ViewConfigurationRequest.orderView]. - aboveOther, - - /// Place this view immediately above the - /// [ViewConfigurationRequest.orderView]. - belowOther, - - /// Place this view on top of all other views. - top, - - /// Place this view below all other views. - bottom, -} - /// An immutable view configuration. -/// -/// See also: -/// * [ViewConfigurationRequest], a class used to request a change to the -/// view configuration using [PlatformDispatcher.configureView]. class ViewConfiguration { /// A const constructor for an immutable [ViewConfiguration]. const ViewConfiguration({ diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 6a66bffbef7de..5167803899992 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -173,10 +173,6 @@ class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { } /// A type of [FlutterView] that can be hosted inside of a [FlutterWindow]. -/// -/// A window view can only be created by [PlatformDispatcher.createView] where -/// the requested configuration includes a parent [FlutterWindow] set as the -/// [ViewConfiguration.window]. class EngineFlutterWindowView extends ui.FlutterWindowView { EngineFlutterWindowView._({Object viewId, this.platformDispatcher}) : _viewId = viewId; diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 9c1fd1b8c7464..15f82c3ce4666 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -188,56 +188,6 @@ class ScreenConfiguration { final WindowPadding/*!*/ padding; } - -class ViewConfigurationRequest { - const ViewConfigurationRequest({ - this.screen, - this.geometry, - this.visible, - this.order, - this.orderView, - }) : assert(orderView != null || (order != ViewOrder.aboveOther && order != ViewOrder.belowOther)), - assert(orderView == null || (order != ViewOrder.top && order != ViewOrder.bottom)), - assert(screen != null || geometry != null || order != null || visible != null, 'At least one parameter must be non-null'); - - ViewConfigurationRequest copyWith({ - Screen/*?*/ screen, - Rect/*?*/ geometry, - bool/*?*/ visible, - ViewOrder/*?*/ order, - FlutterView/*?*/ orderView, - }) { - return ViewConfigurationRequest( - screen: screen ?? this.screen, - geometry: geometry ?? this.geometry, - visible: visible ?? this.visible, - order: order ?? this.order, - orderView: orderView ?? this.orderView, - ); - } - - final Screen/*?*/ screen; - final Rect/*?*/ geometry; - final bool/*?*/ visible; - final ViewOrder/*?*/ order; - final FlutterView/*?*/ orderView; - - @override - String toString() { - return '$runtimeType[screen: $screen, geometry: $geometry, order: $order]'; - } -} - -enum ViewOrder { - aboveOther, - - belowOther, - - top, - - bottom, -} - class ViewConfiguration { const ViewConfiguration({ this.screen, From c781a03d5585f068092884856db20a0afb0aaa85 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 9 Jun 2020 18:11:13 -0700 Subject: [PATCH 08/14] Reverting rename of defaultRouteName => initialRouteName because it is unnecessarily breaking --- lib/ui/hooks.dart | 7 +- lib/ui/platform_dispatcher.dart | 14 +- lib/ui/window.dart | 19 +- lib/ui/window/platform_configuration.cc | 6 +- lib/ui/window/platform_configuration.h | 2 +- .../platform_configuration_unittests.cc | 2 +- .../lib/src/engine/platform_dispatcher.dart | 10 +- .../lib/src/ui/platform_dispatcher.dart | 12 +- lib/web_ui/lib/src/ui/window.dart | 275 ------------------ lib/web_ui/test/window_test.dart | 18 +- runtime/runtime_controller.cc | 4 +- runtime/runtime_controller.h | 2 +- runtime/runtime_delegate.h | 2 +- shell/common/engine.cc | 2 +- shell/common/engine.h | 2 +- .../framework/Source/FlutterViewController.mm | 11 +- .../fuchsia/flutter/compilation_trace.txt | 4 +- shell/platform/glfw/flutter_glfw.cc | 2 +- .../dart/window_hooks_integration_test.dart | 2 +- .../.dart_tool/package_config.json | 8 +- tools/const_finder/.packages | 2 +- 21 files changed, 65 insertions(+), 341 deletions(-) diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 0c4bfd32a99d3..f58cc79df4a82 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -424,7 +424,12 @@ void _invoke1(void callback(A a)?, Zone zone, A arg) { /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. void _invoke3( - void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg1, A2 arg2, A3 arg3) { + void callback(A1 a1, A2 a2, A3 a3) ?, + Zone zone, + A1 arg1, + A2 arg2, + A3 arg3, +) { if (callback == null) { return; } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index c1afa44be3810..a348aa9718c8b 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -6,8 +6,6 @@ part of dart.ui; typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration/*!*/ configuration); -typedef ViewCreatedCallback = void Function(FlutterView/*!*/ view); -typedef ViewDisposedCallback = void Function(FlutterView/*!*/ view); /// Platform event dispatcher singleton. /// @@ -559,8 +557,8 @@ class PlatformDispatcher { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String/*!*/ get initialRouteName => _initialRouteName(); - String/*!*/ _initialRouteName() native 'PlatformConfiguration_initialRouteName'; + String/*!*/ get defaultRouteName => _defaultRouteName(); + String/*!*/ _defaultRouteName() native 'PlatformConfiguration_defaultRouteName'; } /// Configuration of the platform. @@ -576,7 +574,7 @@ class PlatformConfiguration { this.textScaleFactor = 1.0, this.locales = const [], this.platformResolvedLocale, - this.initialRouteName, + this.defaultRouteName, }) : assert(accessibilityFeatures != null), assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), @@ -593,7 +591,7 @@ class PlatformConfiguration { double/*?*/ textScaleFactor, List/*?*/ locales, Locale/*?*/ platformResolvedLocale, - String/*?*/ initialRouteName, + String/*?*/ defaultRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -603,7 +601,7 @@ class PlatformConfiguration { textScaleFactor: textScaleFactor ?? this.textScaleFactor, locales: locales ?? this.locales, platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, - initialRouteName: initialRouteName ?? this.initialRouteName, + defaultRouteName: defaultRouteName ?? this.defaultRouteName, ); } @@ -634,7 +632,7 @@ class PlatformConfiguration { /// The route or path that the embedder requested when the application was /// launched. - final String/*?*/ initialRouteName; + final String/*?*/ defaultRouteName; } /// Immutable configuration information for a screen. diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 2f67bfec40588..96e63d42d2951 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -825,13 +825,12 @@ class FlutterWindow extends FlutterView { /// A [FlutterWindow] that includes access to setting callbacks and retrieving /// properties that reside on the [PlatformDispatcher]. /// -/// It is the type of the legacy global [window] singleton, and the -/// `WidgetsBinding.instance.window` singleton. +/// It is the type of the `WidgetsBinding.instance.window` singleton and the +/// legacy global [window] singleton. /// /// This class provides backward compatibility with code that was written before -/// Flutter supported multiple top level windows. New code should refer to the -/// [WidgetsBinding.instance.platformDispatcher] or [FlutterWindow] class -/// directly to modify or retrieve these properties. +/// Flutter supported multiple top level windows. To modify or retrieve these +/// properties, new code should use `WidgetsBinding.instance.platformDispatcher`. /// /// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` /// if `WidgetsBinding` is unavailable. But we strongly advise avoiding a static @@ -1138,16 +1137,16 @@ class SingletonFlutterWindow extends FlutterWindow { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String get initialRouteName => platformDispatcher.initialRouteName; + String get defaultRouteName => platformDispatcher.defaultRouteName; /// Requests that, at the next appropriate opportunity, the [onBeginFrame] /// and [onDrawFrame] callbacks be invoked. /// /// {@template flutter.lib.ui.window.functionForwardWarning} - /// Calling this function calls the same function on the [PlatformDispatcher] - /// singleton, so instead of calling it here, you should consider calling it - /// on `WidgetsBinding.instance.platformDispatcher` instead (or, as a last - /// resort when `WidgetsBinding` isn't available, on + /// Calling this function forwards the call to the same function on the + /// [PlatformDispatcher] singleton, so instead of calling it here, you should + /// consider calling it on `WidgetsBinding.instance.platformDispatcher` + /// instead (or, as a last resort when `WidgetsBinding` isn't available, on /// [PlatformDispatcher.instance]). The reason this function forwards to the /// [PlatformDispatcher] is to avoid breaking code that was written before /// Flutter supported multiple windows. diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 7c2022034a35b..7bd34644df210 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -19,12 +19,12 @@ namespace flutter { namespace { -void InitialRouteName(Dart_NativeArguments args) { +void DefaultRouteName(Dart_NativeArguments args) { UIDartState::ThrowIfUIOperationsProhibited(); std::string routeName = UIDartState::Current() ->platform_configuration() ->client() - ->InitialRouteName(); + ->DefaultRouteName(); Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); } @@ -429,7 +429,7 @@ void PlatformConfiguration::CompletePlatformMessageResponse( void PlatformConfiguration::RegisterNatives( tonic::DartLibraryNatives* natives) { natives->Register({ - {"PlatformConfiguration_initialRouteName", InitialRouteName, 1, true}, + {"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true}, {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true}, {"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4, true}, diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index d738003d4a239..7fa424a205030 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -53,7 +53,7 @@ Dart_Handle ToByteData(const std::vector& buffer); class WindowClient { public: - virtual std::string InitialRouteName() = 0; + virtual std::string DefaultRouteName() = 0; virtual void ScheduleFrame() = 0; virtual void Render(Scene* scene) = 0; virtual void UpdateSemantics(SemanticsUpdate* update) = 0; diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 4e3e5e4798dac..243132058c31d 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -25,7 +25,7 @@ class DummyWindowClient : public WindowClient { std::vector data; isolate_data_.reset(new ::fml::DataMapping(data)); } - virtual std::string InitialRouteName() { return "TestRoute"; } + virtual std::string DefaultRouteName() { return "TestRoute"; } virtual void ScheduleFrame() {} virtual void Render(Scene* scene) {} virtual void UpdateSemantics(SemanticsUpdate* update) {} diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index f8a91002bec04..e0d9aa90aa81e 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -391,9 +391,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { break; } // As soon as Flutter starts taking control of the app navigation, we - // should reset [_initialRouteName] to "/" so it doesn't have any + // should reset [_defaultRouteName] to "/" so it doesn't have any // further effect after this point. - _initialRouteName = '/'; + _defaultRouteName = '/'; return; } @@ -810,13 +810,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. @override - String/*!*/ get initialRouteName => _initialRouteName ??= _browserHistory.currentPath; + String/*!*/ get defaultRouteName => _defaultRouteName ??= _browserHistory.currentPath; - /// Lazily initialized when the `initialRouteName` getter is invoked. + /// Lazily initialized when the `defaultRouteName` getter is invoked. /// /// The reason for the lazy initialization is to give enough time for the app /// to set [locationStrategy] in `lib/src/ui/initialization.dart`. - String/*?*/ _initialRouteName; + String/*?*/ _defaultRouteName; /// Handles the browser history integration to allow users to use the back /// button, etc. diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 15f82c3ce4666..e7b9d6ccd1f76 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -6,8 +6,6 @@ part of ui; typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); -typedef ViewCreatedCallback = void Function(FlutterView view); -typedef ViewDisposedCallback = void Function(FlutterView view); abstract class PlatformDispatcher { static PlatformDispatcher/*!*/ get instance => engine.EnginePlatformDispatcher.instance; @@ -84,7 +82,7 @@ abstract class PlatformDispatcher { SemanticsActionCallback/*?*/ get onSemanticsAction; set onSemanticsAction(SemanticsActionCallback/*?*/ callback); - String/*!*/ get initialRouteName; + String/*!*/ get defaultRouteName; void setIsolateDebugName(String/*!*/ name) {} @@ -102,7 +100,7 @@ class PlatformConfiguration { this.textScaleFactor = 1.0, this.locales = const [], this.platformResolvedLocale, - this.initialRouteName, + this.defaultRouteName, }) : assert(accessibilityFeatures != null), assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), @@ -118,7 +116,7 @@ class PlatformConfiguration { double/*?*/ textScaleFactor, List/*?*/ locales, Locale/*?*/ platformResolvedLocale, - String/*?*/ initialRouteName, + String/*?*/ defaultRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -128,7 +126,7 @@ class PlatformConfiguration { textScaleFactor: textScaleFactor ?? this.textScaleFactor, locales: locales ?? this.locales, platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, - initialRouteName: initialRouteName ?? this.initialRouteName, + defaultRouteName: defaultRouteName ?? this.defaultRouteName, ); } @@ -139,7 +137,7 @@ class PlatformConfiguration { final double/*!*/ textScaleFactor; final List/*!*/ locales; final Locale/*?*/ platformResolvedLocale; - final String/*?*/ initialRouteName; + final String/*?*/ defaultRouteName; } class ScreenConfiguration { diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index a9d3251b889c7..e95333824a97e 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -253,167 +253,35 @@ abstract class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPlatformBrightnessChanged = callback; } - /// A callback that is invoked to notify the application that it is an - /// appropriate time to provide a scene using the [SceneBuilder] API and the - /// [render] method. When possible, this is driven by the hardware VSync - /// signal. This is only called if [scheduleFrame] has been called since the - /// last time this callback was invoked. - /// - /// The [onDrawFrame] callback is invoked immediately after [onBeginFrame], - /// after draining any microtasks (e.g. completions of any [Future]s) queued - /// by the [onBeginFrame] handler. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. FrameCallback? get onBeginFrame; set onBeginFrame(FrameCallback? callback); - /// A callback that is invoked to report the [FrameTiming] of recently - /// rasterized frames. - /// - /// This can be used to see if the application has missed frames (through - /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high - /// latencies (through [FrameTiming.totalSpan]). - /// - /// Unlike [Timeline], the timing information here is available in the release - /// mode (additional to the profile and the debug mode). Hence this can be - /// used to monitor the application's performance in the wild. - /// - /// The callback may not be immediately triggered after each frame. Instead, - /// it tries to batch frames together and send all their timings at once to - /// decrease the overhead (as this is available in the release mode). The - /// timing of any frame will be sent within about 1 second even if there are - /// no later frames to batch. TimingsCallback? get onReportTimings; set onReportTimings(TimingsCallback? callback); - /// A callback that is invoked for each frame after [onBeginFrame] has - /// completed and after the microtask queue has been drained. This can be - /// used to implement a second phase of frame rendering that happens - /// after any deferred work queued by the [onBeginFrame] phase. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. VoidCallback? get onDrawFrame; set onDrawFrame(VoidCallback? callback); - /// A callback that is invoked when pointer data is available. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. - /// - /// See also: - /// - /// * [GestureBinding], the Flutter framework class which manages pointer - /// events. PointerDataPacketCallback? get onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback); - /// The route or path that the embedder requested when the application was - /// launched. - /// - /// This will be the string "`/`" if no particular route was requested. - /// - /// ## Android - /// - /// On Android, calling - /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) - /// will set this value. The value must be set sufficiently early, i.e. before - /// the [runApp] call is executed in Dart, for this to have any effect on the - /// framework. The `createFlutterView` method in your `FlutterActivity` - /// subclass is a suitable time to set the value. The application's - /// `AndroidManifest.xml` file must also be updated to have a suitable - /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). - /// - /// ## iOS - /// - /// On iOS, calling - /// [`FlutterViewController.setInitialRoute`](/objcdoc/Classes/FlutterViewController.html#/c:objc%28cs%29FlutterViewController%28im%29setInitialRoute:) - /// will set this value. The value must be set sufficiently early, i.e. before - /// the [runApp] call is executed in Dart, for this to have any effect on the - /// framework. The `application:didFinishLaunchingWithOptions:` method is a - /// suitable time to set this value. - /// - /// See also: - /// - /// * [Navigator], a widget that handles routing. - /// * [SystemChannels.navigation], which handles subsequent navigation - /// requests from the embedder. String get defaultRouteName; - /// Whether the user has requested that [updateSemantics] be called when - /// the semantic contents of window changes. - /// - /// The [onSemanticsEnabledChanged] callback is called whenever this value - /// changes. - /// - /// This defaults to `true` on the Web because we may never receive a signal - /// that an assistive technology is turned on. bool get semanticsEnabled => engine.EngineSemanticsOwner.instance.semanticsEnabled; - /// A callback that is invoked when the value of [semanticsEnabled] changes. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. VoidCallback? get onSemanticsEnabledChanged; set onSemanticsEnabledChanged(VoidCallback? callback); - /// A callback that is invoked whenever the user requests an action to be - /// performed. - /// - /// This callback is used when the user expresses the action they wish to - /// perform based on the semantics supplied by [updateSemantics]. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. SemanticsActionCallback? get onSemanticsAction; set onSemanticsAction(SemanticsActionCallback? callback); - /// A callback that is invoked when the value of [accessibilityFlags] changes. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. VoidCallback? get onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback); - /// Called whenever this window receives a message from a platform-specific - /// plugin. - /// - /// The `name` parameter determines which plugin sent the message. The `data` - /// parameter is the payload and is typically UTF-8 encoded JSON but can be - /// arbitrary data. - /// - /// Message handlers must call the function given in the `callback` parameter. - /// If the handler does not need to respond, the handler should pass null to - /// the callback. - /// - /// The framework invokes this callback in the same zone in which the - /// callback was set. PlatformMessageCallback? get onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback); - /// Change the retained semantics data about this window. - /// - /// If [semanticsEnabled] is true, the user has requested that this funciton - /// be called whenever the semantic content of this window changes. - /// - /// In either case, this function disposes the given update, which means the - /// semantics update cannot be used further. void updateSemantics(SemanticsUpdate update) { engine.EngineSemanticsOwner.instance.updateSemantics(update); } @@ -426,34 +294,9 @@ abstract class SingletonFlutterWindow extends FlutterWindow { PlatformMessageResponseCallback? callback, ); - /// Additional accessibility features that may be enabled by the platform. AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; AccessibilityFeatures _accessibilityFeatures = AccessibilityFeatures._(0); - /// Updates the application's rendering on the GPU with the newly provided - /// [Scene]. This function must be called within the scope of the - /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function - /// is called a second time during a single [onBeginFrame]/[onDrawFrame] - /// callback sequence or called outside the scope of those callbacks, the call - /// will be ignored. - /// - /// To record graphical operations, first create a [PictureRecorder], then - /// construct a [Canvas], passing that [PictureRecorder] to its constructor. - /// After issuing all the graphical operations, call the - /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain - /// the final [Picture] that represents the issued graphical operations. - /// - /// Next, create a [SceneBuilder], and add the [Picture] to it using - /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can - /// then obtain a [Scene] object, which you can display to the user via this - /// [render] function. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. void render(Scene scene); String get initialLifecycleState => 'AppLifecycleState.resumed'; @@ -476,32 +319,11 @@ class AccessibilityFeatures { // A bitfield which represents each enabled feature. final int _index; - /// Whether there is a running accessibility service which is changing the - /// interaction model of the device. - /// - /// For example, TalkBack on Android and VoiceOver on iOS enable this flag. bool get accessibleNavigation => _kAccessibleNavigation & _index != 0; - - /// The platform is inverting the colors of the application. bool get invertColors => _kInvertColorsIndex & _index != 0; - - /// The platform is requesting that animations be disabled or simplified. bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; - - /// The platform is requesting that text be rendered at a bold font weight. - /// - /// Only supported on iOS. bool get boldText => _kBoldTextIndex & _index != 0; - - /// The platform is requesting that certain animations be simplified and - /// parallax effects removed. - /// - /// Only supported on iOS. bool get reduceMotion => _kReduceMotionIndex & _index != 0; - - /// The platform is requesting that UI be rendered with darker colors. - /// - /// Only supported on iOS. bool get highContrast => _kHighContrastIndex & _index != 0; @override @@ -605,58 +427,23 @@ enum FramePhase { } class FrameTiming { - /// Construct [FrameTiming] with raw timestamps in microseconds. - /// - /// List [timestamps] must have the same number of elements as - /// [FramePhase.values]. - /// - /// This constructor is usually only called by the Flutter engine, or a test. - /// To get the [FrameTiming] of your app, see [Window.onReportTimings]. FrameTiming(List timestamps) : assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps; - /// This is a raw timestamp in microseconds from some epoch. The epoch in all - /// [FrameTiming] is the same, but it may not match [DateTime]'s epoch. int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); - /// The duration to build the frame on the UI thread. - /// - /// The build starts approximately when [Window.onBeginFrame] is called. The - /// [Duration] in the [Window.onBeginFrame] callback is exactly the - /// `Duration(microseconds: timestampInMicroseconds(FramePhase.buildStart))`. - /// - /// The build finishes when [Window.render] is called. - /// - /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} - /// To ensure smooth animations of X fps, this should not exceed 1000/X - /// milliseconds. - /// {@endtemplate} - /// {@template dart.ui.FrameTiming.fps_milliseconds} - /// That's about 16ms for 60fps, and 8ms for 120fps. - /// {@endtemplate} Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - /// The duration to rasterize the frame on the raster thread. - /// - /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} - /// {@macro dart.ui.FrameTiming.fps_milliseconds} Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); - /// The timespan between build start and raster finish. - /// - /// To achieve the lowest latency on an X fps display, this should not exceed - /// 1000/X milliseconds. - /// {@macro dart.ui.FrameTiming.fps_milliseconds} - /// - /// See also [buildDuration] and [rasterDuration]. Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.buildStart); @@ -671,66 +458,4 @@ class FrameTiming { } } -/// The [Window] singleton. This object exposes the size of the display, the -/// core scheduler API, the input event callback, the graphics drawing API, and -/// other such core services. -Window get window => engine.window; - FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; - set onBeginFrame(FrameCallback? callback) { - platformDispatcher.onBeginFrame = callback; - } - VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; - set onDrawFrame(VoidCallback? callback) { - platformDispatcher.onDrawFrame = callback; - } - TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; - set onReportTimings(TimingsCallback? callback) { - platformDispatcher.onReportTimings = callback; - } - PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback? callback) { - platformDispatcher.onPointerDataPacket = callback; - } - String get initialRouteName => platformDispatcher.initialRouteName; - void scheduleFrame() => platformDispatcher.scheduleFrame(); - void render(Scene scene) => platformDispatcher.render(scene, this); - bool get semanticsEnabled => platformDispatcher.semanticsEnabled; - VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback? callback) { - platformDispatcher.onSemanticsEnabledChanged = callback; - } - SemanticsActionCallback? get onSemanticsAction => platformDispatcher.onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback? callback) { - platformDispatcher.onSemanticsAction = callback; - } - AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; - VoidCallback? get onAccessibilityFeaturesChanged => - platformDispatcher.onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback? callback) { - platformDispatcher.onAccessibilityFeaturesChanged = callback; - String name, - ByteData? data, - PlatformMessageResponseCallback? callback, - ) { - platformDispatcher.sendPlatformMessage(name, data, callback); - } - PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback? callback) { - platformDispatcher.onPlatformMessage = callback; - } - bool get accessibleNavigation => _kAccessibleNavigation & _index != 0; - bool get invertColors => _kInvertColorsIndex & _index != 0; - bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; - bool get boldText => _kBoldTextIndex & _index != 0; - bool get reduceMotion => _kReduceMotionIndex & _index != 0; - bool get highContrast => _kHighContrastIndex & _index != 0; - FrameTiming(List timestamps) - int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; - Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); - Duration get buildDuration => - _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - Duration get rasterDuration => - _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); - Duration get totalSpan => - _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.buildStart); SingletonFlutterWindow get window => engine.window; diff --git a/lib/web_ui/test/window_test.dart b/lib/web_ui/test/window_test.dart index 7a71bd99b701e..f8fc411bec6fa 100644 --- a/lib/web_ui/test/window_test.dart +++ b/lib/web_ui/test/window_test.dart @@ -22,20 +22,20 @@ set strategy(TestLocationStrategy newStrategy) { } void main() { - test('window.initialRouteName should not change', () { + test('window.defaultRouteName should not change', () { window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); - expect(window.initialRouteName, '/initial'); + expect(window.defaultRouteName, '/initial'); - // Changing the URL in the address bar later shouldn't affect [window.initialRouteName]. + // Changing the URL in the address bar later shouldn't affect [window.defaultRouteName]. window.locationStrategy.replaceState(null, null, '/newpath'); - expect(window.initialRouteName, '/initial'); + expect(window.defaultRouteName, '/initial'); }); - test('window.initialRouteName should reset after navigation platform message', () { + test('window.defaultRouteName should reset after navigation platform message', () { window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); // Reading it multiple times should return the same value. - expect(window.initialRouteName, '/initial'); - expect(window.initialRouteName, '/initial'); + expect(window.defaultRouteName, '/initial'); + expect(window.defaultRouteName, '/initial'); window.sendPlatformMessage( 'flutter/navigation', @@ -45,9 +45,9 @@ void main() { )), emptyCallback, ); - // After a navigation platform message, [window.initialRouteName] should + // After a navigation platform message, [window.defaultRouteName] should // reset to "/". - expect(window.initialRouteName, '/'); + expect(window.defaultRouteName, '/'); }); test('can disable location strategy', () async { diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 12aa04fa6b630..5be9623aeaaaa 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -303,8 +303,8 @@ Window* RuntimeController::GetWindowIfAvailable() { } // |WindowClient| -std::string RuntimeController::InitialRouteName() { - return client_.InitialRouteName(); +std::string RuntimeController::DefaultRouteName() { + return client_.DefaultRouteName(); } // |WindowClient| diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 2728aed467c68..3012db17fc352 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -490,7 +490,7 @@ class RuntimeController final : public WindowClient { bool FlushRuntimeStateToIsolate(); // |WindowClient| - std::string InitialRouteName() override; + std::string DefaultRouteName() override; // |WindowClient| void ScheduleFrame() override; diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index edf4d16cc4b36..20059827b8150 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -19,7 +19,7 @@ namespace flutter { class RuntimeDelegate { public: - virtual std::string InitialRouteName() = 0; + virtual std::string DefaultRouteName() = 0; virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index c540177c4dca8..38662a7c05f19 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -446,7 +446,7 @@ void Engine::StartAnimatorIfPossible() { animator_->Start(); } -std::string Engine::InitialRouteName() { +std::string Engine::DefaultRouteName() { if (!initial_route_.empty()) { return initial_route_; } diff --git a/shell/common/engine.h b/shell/common/engine.h index e44d8a253eff1..6dc6941d15698 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -789,7 +789,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { fml::WeakPtrFactory weak_factory_; // |RuntimeDelegate| - std::string InitialRouteName() override; + std::string DefaultRouteName() override; // |RuntimeDelegate| void Render(std::unique_ptr layer_tree) override; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 2cf42bf4be200..210ddaaac8318 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -837,9 +837,8 @@ - (CGFloat)statusBarPadding { } - (void)viewDidLayoutSubviews { - UIScreen* screen = self.view.window.screen; CGSize viewSize = self.view.bounds.size; - CGFloat scale = screen.scale; + CGFloat scale = [UIScreen mainScreen].scale; // Purposefully place this not visible. _scrollView.get().frame = CGRectMake(0.0, 0.0, viewSize.width, 0.0); @@ -850,8 +849,8 @@ - (void)viewDidLayoutSubviews { _viewportMetrics.physical_width = viewSize.width * scale; _viewportMetrics.physical_height = viewSize.height * scale; _screenMetrics.device_pixel_ratio = scale; - _screenMetrics.physical_width = screen.bounds.size.width; - _screenMetrics.physical_height = screen.bounds.size.height; + _screenMetrics.physical_width = [UIScreen mainScreen].bounds.size.width; + _screenMetrics.physical_height = [UIScreen mainScreen].bounds.size.height; [self updateViewportPadding]; [self updateViewportMetrics]; @@ -890,7 +889,7 @@ - (void)viewSafeAreaInsetsDidChange { // // Viewport padding represents the iOS safe area insets. - (void)updateViewportPadding { - CGFloat scale = self.view.window.screen.scale; + CGFloat scale = [UIScreen mainScreen].scale; if (@available(iOS 11, *)) { _viewportMetrics.physical_padding_top = self.view.safeAreaInsets.top * scale; _viewportMetrics.physical_padding_left = self.view.safeAreaInsets.left * scale; @@ -905,7 +904,7 @@ - (void)updateViewportPadding { // // Screen padding represents the iOS safe area insets. - (void)updateScreenPadding { - CGFloat scale = self.view.window.screen.scale; + CGFloat scale = [UIScreen mainScreen].scale; if (@available(iOS 11, *)) { _screenMetrics.physical_padding_top = self.view.safeAreaInsets.top * scale; _screenMetrics.physical_padding_left = self.view.safeAreaInsets.left * scale; diff --git a/shell/platform/fuchsia/flutter/compilation_trace.txt b/shell/platform/fuchsia/flutter/compilation_trace.txt index ffadbd1fb9cc3..95026cf438233 100644 --- a/shell/platform/fuchsia/flutter/compilation_trace.txt +++ b/shell/platform/fuchsia/flutter/compilation_trace.txt @@ -3189,7 +3189,7 @@ dart:ui,TileMode,init:mirror dart:ui,TileMode,init:repeated dart:ui,TileMode,init:values dart:ui,Window,Window._ -dart:ui,Window,_initialRouteName +dart:ui,Window,_defaultRouteName dart:ui,Window,_sendPlatformMessage dart:ui,Window,_zonedPlatformMessageResponseCallback dart:ui,Window,get:_accessibilityFeatures @@ -3220,7 +3220,7 @@ dart:ui,Window,get:_textScaleFactor dart:ui,Window,get:_viewInsets dart:ui,Window,get:accessibilityFeatures dart:ui,Window,get:alwaysUse24HourFormat -dart:ui,Window,get:initialRouteName +dart:ui,Window,get:defaultRouteName dart:ui,Window,get:devicePixelRatio dart:ui,Window,get:initialLifecycleState dart:ui,Window,get:locales diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index fdb652bd64733..ac92a786f8f29 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -194,7 +194,7 @@ static double GetScreenCoordinatesPerInch() { } // Sends a window metrics update to the Flutter engine using the given -// framebuffer size and the current window informaftion in |state|. +// framebuffer size and the current window information in |state|. static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, int width, int height) { diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 48e174aea10b3..077ecfb18928b 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -458,7 +458,7 @@ void main() { 0.0, // inset left 0.0, // system gesture inset top 0.0, // system gesture inset right - 44.0, // system gesture inset bottom + 44.0, // system gesture inset bottom 0.0, // system gesture inset left ); diff --git a/tools/const_finder/.dart_tool/package_config.json b/tools/const_finder/.dart_tool/package_config.json index 1f3ca5d30af99..5ca024b38e7ab 100644 --- a/tools/const_finder/.dart_tool/package_config.json +++ b/tools/const_finder/.dart_tool/package_config.json @@ -5,7 +5,7 @@ "name": "args", "rootUri": "../../../../third_party/dart/third_party/pkg/args", "packageUri": "lib/", - "languageVersion": "2.3" + "languageVersion": "2.0" }, { "name": "kernel", @@ -17,7 +17,7 @@ "name": "meta", "rootUri": "../../../../third_party/dart/pkg/meta", "packageUri": "lib/", - "languageVersion": "2.9" + "languageVersion": "1.12" }, { "name": "path", @@ -32,7 +32,7 @@ "languageVersion": "2.4" } ], - "generated": "2020-05-13T00:38:22.592345Z", + "generated": "2020-01-16T19:11:54.963296Z", "generator": "pub", - "generatorVersion": "2.9.0-7.0.dev.flutter-092ed38a87" + "generatorVersion": "2.7.0" } diff --git a/tools/const_finder/.packages b/tools/const_finder/.packages index 77b7ed055f82f..2a698fd32e89e 100644 --- a/tools/const_finder/.packages +++ b/tools/const_finder/.packages @@ -1,4 +1,4 @@ -# Generated by pub on 2020-05-12 17:38:22.578268. +# Generated by pub on 2020-01-16 11:11:54.947929. args:../../../third_party/dart/third_party/pkg/args/lib/ kernel:../../../third_party/dart/pkg/kernel/lib/ meta:../../../third_party/dart/pkg/meta/lib/ From 51f6ef19ac2d39427829e3ca06bdf1f85b6183b9 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 10 Jun 2020 09:35:59 -0700 Subject: [PATCH 09/14] Rename defaultRouteName to initialRouteName for everything except for window class, to avoid breaking change. --- lib/ui/platform_dispatcher.dart | 12 ++++++------ lib/ui/window.dart | 2 +- lib/ui/window/platform_configuration.cc | 6 +++--- lib/ui/window/platform_configuration.h | 2 +- lib/ui/window/platform_configuration_unittests.cc | 2 +- lib/web_ui/lib/src/engine/platform_dispatcher.dart | 10 +++++----- lib/web_ui/lib/src/ui/platform_dispatcher.dart | 10 +++++----- lib/web_ui/lib/src/ui/window.dart | 2 +- runtime/runtime_controller.cc | 4 ++-- runtime/runtime_controller.h | 2 +- runtime/runtime_delegate.h | 2 +- shell/common/engine.cc | 2 +- shell/common/engine.h | 2 +- shell/platform/fuchsia/flutter/compilation_trace.txt | 2 +- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index a348aa9718c8b..24c2247417321 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -557,8 +557,8 @@ class PlatformDispatcher { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String/*!*/ get defaultRouteName => _defaultRouteName(); - String/*!*/ _defaultRouteName() native 'PlatformConfiguration_defaultRouteName'; + String/*!*/ get initialRouteName => _initialRouteName(); + String/*!*/ _initialRouteName() native 'PlatformConfiguration_initialRouteName'; } /// Configuration of the platform. @@ -574,7 +574,7 @@ class PlatformConfiguration { this.textScaleFactor = 1.0, this.locales = const [], this.platformResolvedLocale, - this.defaultRouteName, + this.initialRouteName, }) : assert(accessibilityFeatures != null), assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), @@ -591,7 +591,7 @@ class PlatformConfiguration { double/*?*/ textScaleFactor, List/*?*/ locales, Locale/*?*/ platformResolvedLocale, - String/*?*/ defaultRouteName, + String/*?*/ initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -601,7 +601,7 @@ class PlatformConfiguration { textScaleFactor: textScaleFactor ?? this.textScaleFactor, locales: locales ?? this.locales, platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, - defaultRouteName: defaultRouteName ?? this.defaultRouteName, + initialRouteName: initialRouteName ?? this.initialRouteName, ); } @@ -632,7 +632,7 @@ class PlatformConfiguration { /// The route or path that the embedder requested when the application was /// launched. - final String/*?*/ defaultRouteName; + final String/*?*/ initialRouteName; } /// Immutable configuration information for a screen. diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 96e63d42d2951..0b470420db0c3 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -1137,7 +1137,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String get defaultRouteName => platformDispatcher.defaultRouteName; + String get defaultRouteName => platformDispatcher.initialRouteName; /// Requests that, at the next appropriate opportunity, the [onBeginFrame] /// and [onDrawFrame] callbacks be invoked. diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 7bd34644df210..7c2022034a35b 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -19,12 +19,12 @@ namespace flutter { namespace { -void DefaultRouteName(Dart_NativeArguments args) { +void InitialRouteName(Dart_NativeArguments args) { UIDartState::ThrowIfUIOperationsProhibited(); std::string routeName = UIDartState::Current() ->platform_configuration() ->client() - ->DefaultRouteName(); + ->InitialRouteName(); Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); } @@ -429,7 +429,7 @@ void PlatformConfiguration::CompletePlatformMessageResponse( void PlatformConfiguration::RegisterNatives( tonic::DartLibraryNatives* natives) { natives->Register({ - {"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true}, + {"PlatformConfiguration_initialRouteName", InitialRouteName, 1, true}, {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true}, {"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4, true}, diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 7fa424a205030..d738003d4a239 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -53,7 +53,7 @@ Dart_Handle ToByteData(const std::vector& buffer); class WindowClient { public: - virtual std::string DefaultRouteName() = 0; + virtual std::string InitialRouteName() = 0; virtual void ScheduleFrame() = 0; virtual void Render(Scene* scene) = 0; virtual void UpdateSemantics(SemanticsUpdate* update) = 0; diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 243132058c31d..4e3e5e4798dac 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -25,7 +25,7 @@ class DummyWindowClient : public WindowClient { std::vector data; isolate_data_.reset(new ::fml::DataMapping(data)); } - virtual std::string DefaultRouteName() { return "TestRoute"; } + virtual std::string InitialRouteName() { return "TestRoute"; } virtual void ScheduleFrame() {} virtual void Render(Scene* scene) {} virtual void UpdateSemantics(SemanticsUpdate* update) {} diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index e0d9aa90aa81e..f8a91002bec04 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -391,9 +391,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { break; } // As soon as Flutter starts taking control of the app navigation, we - // should reset [_defaultRouteName] to "/" so it doesn't have any + // should reset [_initialRouteName] to "/" so it doesn't have any // further effect after this point. - _defaultRouteName = '/'; + _initialRouteName = '/'; return; } @@ -810,13 +810,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. @override - String/*!*/ get defaultRouteName => _defaultRouteName ??= _browserHistory.currentPath; + String/*!*/ get initialRouteName => _initialRouteName ??= _browserHistory.currentPath; - /// Lazily initialized when the `defaultRouteName` getter is invoked. + /// Lazily initialized when the `initialRouteName` getter is invoked. /// /// The reason for the lazy initialization is to give enough time for the app /// to set [locationStrategy] in `lib/src/ui/initialization.dart`. - String/*?*/ _defaultRouteName; + String/*?*/ _initialRouteName; /// Handles the browser history integration to allow users to use the back /// button, etc. diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index e7b9d6ccd1f76..d6ed097f15616 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -82,7 +82,7 @@ abstract class PlatformDispatcher { SemanticsActionCallback/*?*/ get onSemanticsAction; set onSemanticsAction(SemanticsActionCallback/*?*/ callback); - String/*!*/ get defaultRouteName; + String/*!*/ get initialRouteName; void setIsolateDebugName(String/*!*/ name) {} @@ -100,7 +100,7 @@ class PlatformConfiguration { this.textScaleFactor = 1.0, this.locales = const [], this.platformResolvedLocale, - this.defaultRouteName, + this.initialRouteName, }) : assert(accessibilityFeatures != null), assert(alwaysUse24HourFormat != null), assert(semanticsEnabled != null), @@ -116,7 +116,7 @@ class PlatformConfiguration { double/*?*/ textScaleFactor, List/*?*/ locales, Locale/*?*/ platformResolvedLocale, - String/*?*/ defaultRouteName, + String/*?*/ initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -126,7 +126,7 @@ class PlatformConfiguration { textScaleFactor: textScaleFactor ?? this.textScaleFactor, locales: locales ?? this.locales, platformResolvedLocale: platformResolvedLocale ?? this.platformResolvedLocale, - defaultRouteName: defaultRouteName ?? this.defaultRouteName, + initialRouteName: initialRouteName ?? this.initialRouteName, ); } @@ -137,7 +137,7 @@ class PlatformConfiguration { final double/*!*/ textScaleFactor; final List/*!*/ locales; final Locale/*?*/ platformResolvedLocale; - final String/*?*/ defaultRouteName; + final String/*?*/ initialRouteName; } class ScreenConfiguration { diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index e95333824a97e..e203d3b12c2ec 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -265,7 +265,7 @@ abstract class SingletonFlutterWindow extends FlutterWindow { PointerDataPacketCallback? get onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback); - String get defaultRouteName; + String get defaultRouteName => platformDispatcher.initialRouteName; bool get semanticsEnabled => engine.EngineSemanticsOwner.instance.semanticsEnabled; diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 5be9623aeaaaa..12aa04fa6b630 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -303,8 +303,8 @@ Window* RuntimeController::GetWindowIfAvailable() { } // |WindowClient| -std::string RuntimeController::DefaultRouteName() { - return client_.DefaultRouteName(); +std::string RuntimeController::InitialRouteName() { + return client_.InitialRouteName(); } // |WindowClient| diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 3012db17fc352..2728aed467c68 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -490,7 +490,7 @@ class RuntimeController final : public WindowClient { bool FlushRuntimeStateToIsolate(); // |WindowClient| - std::string DefaultRouteName() override; + std::string InitialRouteName() override; // |WindowClient| void ScheduleFrame() override; diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 20059827b8150..edf4d16cc4b36 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -19,7 +19,7 @@ namespace flutter { class RuntimeDelegate { public: - virtual std::string DefaultRouteName() = 0; + virtual std::string InitialRouteName() = 0; virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 38662a7c05f19..c540177c4dca8 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -446,7 +446,7 @@ void Engine::StartAnimatorIfPossible() { animator_->Start(); } -std::string Engine::DefaultRouteName() { +std::string Engine::InitialRouteName() { if (!initial_route_.empty()) { return initial_route_; } diff --git a/shell/common/engine.h b/shell/common/engine.h index 6dc6941d15698..e44d8a253eff1 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -789,7 +789,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { fml::WeakPtrFactory weak_factory_; // |RuntimeDelegate| - std::string DefaultRouteName() override; + std::string InitialRouteName() override; // |RuntimeDelegate| void Render(std::unique_ptr layer_tree) override; diff --git a/shell/platform/fuchsia/flutter/compilation_trace.txt b/shell/platform/fuchsia/flutter/compilation_trace.txt index 95026cf438233..185a350e0bbea 100644 --- a/shell/platform/fuchsia/flutter/compilation_trace.txt +++ b/shell/platform/fuchsia/flutter/compilation_trace.txt @@ -3189,7 +3189,7 @@ dart:ui,TileMode,init:mirror dart:ui,TileMode,init:repeated dart:ui,TileMode,init:values dart:ui,Window,Window._ -dart:ui,Window,_defaultRouteName +dart:ui,Window,_initialRouteName dart:ui,Window,_sendPlatformMessage dart:ui,Window,_zonedPlatformMessageResponseCallback dart:ui,Window,get:_accessibilityFeatures From e6be29a6cead00ac84480b54a267b21ee87897d4 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 10 Jun 2020 12:58:44 -0700 Subject: [PATCH 10/14] Add screen accessor and testing. --- lib/ui/window.dart | 3 + .../dart/window_hooks_integration_test.dart | 69 +++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 0b470420db0c3..4c4c9d402320c 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -585,6 +585,9 @@ abstract class FlutterView { /// The configuration of this view. ViewConfiguration/*!*/ get viewConfiguration; + /// The [Screen] that this [FlutterView] is displayed on. + Screen get screen => viewConfiguration.screen; + /// The number of device pixels for each logical pixel for the screen this /// view is displayed on. /// diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 077ecfb18928b..e85cc255cabe7 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -462,10 +462,69 @@ void main() { 0.0, // system gesture inset left ); - expectEquals(window.viewInsets.bottom, 400.0); - expectEquals(window.viewPadding.bottom, 40.0); - expectEquals(window.padding.bottom, 0.0); - expectEquals(window.physicalDepth, 100.0); - expectEquals(window.systemGestureInsets.bottom, 44.0); + expect(window.viewInsets.bottom, 400.0); + expect(window.viewPadding.bottom, 40.0); + expect(window.padding.bottom, 0.0); + expect(window.physicalDepth, 100.0); + expect(window.systemGestureInsets.bottom, 44.0); + }); + + test('Screen padding/insets/viewPadding/systemGestureInsets', () { + _updateScreenMetrics( + 0, // screen id + 'S0', // screen name + 10.0, // left + 11.0, // top + 800.0, // width + 600.0, // height + 2.5, // device pixel ratio + 50.0, // padding top + 0.0, // padding right + 40.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 0.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 0.0, // system gesture inset bottom + 0.0, // system gesture inset left + ); + + expect(window.screen.configuration.viewInsets.bottom, 0.0); + expect(window.screen.configuration.viewPadding.bottom, 40.0); + expect(window.screen.configuration.padding.bottom, 40.0); + expect(window.screen.configuration.devicePixelRatio, 2.5); + expect(window.screen.configuration.systemGestureInsets.bottom, 0.0); + + _updateScreenMetrics( + 0, // window id + 'S0', // screen name + 10.0, // left + 11.0, // top + 800.0, // width + 600.0, // height + 2.5, // device pixel ratio + 50.0, // padding top + 0.0, // padding right + 40.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 400.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 44.0, // system gesture inset bottom + 0.0, // system gesture inset left + ); + + expect(window.screen.configuration.viewInsets.bottom, 400.0); + expect(window.screen.configuration.viewPadding.bottom, 40.0); + expect(window.screen.configuration.padding.bottom, 0.0); + expect(window.screen.configuration.devicePixelRatio, 2.5); + expect(window.screen.configuration.systemGestureInsets.bottom, 44.0); + }); }); } From 282e7aef22d3c9623ee0d51431e4534df6b0a538 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 17 Jun 2020 13:01:40 -0700 Subject: [PATCH 11/14] Review Changes --- lib/ui/compositing.dart | 14 +- lib/ui/hooks.dart | 283 ++------ lib/ui/natives.dart | 2 +- lib/ui/platform_dispatcher.dart | 625 +++++++++++++----- lib/ui/screen.dart | 21 +- lib/ui/window.dart | 184 +++--- .../lib/src/engine/platform_dispatcher.dart | 10 + lib/web_ui/lib/src/engine/window.dart | 14 +- lib/web_ui/lib/src/ui/semantics.dart | 15 +- lib/web_ui/test/window_test.dart | 12 +- .../dart/window_hooks_integration_test.dart | 103 ++- 11 files changed, 682 insertions(+), 601 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index a76f87ac2f94a..50c1f018651a1 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -648,13 +648,13 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. /// - /// The "UI thread" is the thread that includes all the execution of - /// the main Dart isolate (the isolate that can call - /// [FlutterView.render]). The UI thread frame time is the total time - /// spent executing the [PlatformDispatcher.onBeginFrame] callback. The "raster - /// thread" is the thread (running on the CPU) that subsequently - /// processes the [Scene] provided by the Dart code to turn it into - /// GPU commands and send it to the GPU. + /// The "UI thread" is the thread that includes all the execution of the main + /// Dart isolate (the isolate that can call [FlutterView.render]). The UI + /// thread frame time is the total time spent executing the + /// [PlatformDispatcher.onBeginFrame] callback. The "raster thread" is the + /// thread (running on the CPU) that subsequently processes the [Scene] + /// provided by the Dart code to turn it into GPU commands and send it to the + /// GPU. /// /// See also the [PerformanceOverlayOption] enum in the rendering library. /// for more details. diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index f58cc79df4a82..22ec18ac4da11 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -31,107 +31,72 @@ void _updateWindowMetrics( double systemGestureInsetBottom, double systemGestureInsetLeft, ) { - assert(PlatformDispatcher.instance._screens[screenId] != null); - final ViewConfiguration previousConfiguration = - PlatformDispatcher.instance._viewConfigurations[id] ?? ViewConfiguration(screen: PlatformDispatcher.instance._screens[screenId]); - PlatformDispatcher.instance._viewConfigurations[id] = previousConfiguration.copyWith( - screen: PlatformDispatcher.instance._screens[screenId], - geometry: Rect.fromLTWH(left, top, width, height), - depth: depth, - viewPadding: WindowPadding._( - top: viewPaddingTop, - right: viewPaddingRight, - bottom: viewPaddingBottom, - left: viewPaddingLeft, - ), - viewInsets: WindowPadding._( - top: viewInsetTop, - right: viewInsetRight, - bottom: viewInsetBottom, - left: viewInsetLeft, - ), - padding: WindowPadding._( - top: math.max(0.0, viewPaddingTop - viewInsetTop), - right: math.max(0.0, viewPaddingRight - viewInsetRight), - bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), - left: math.max(0.0, viewPaddingLeft - viewInsetLeft), - ), - systemGestureInsets: WindowPadding._( - top: math.max(0.0, systemGestureInsetTop), - right: math.max(0.0, systemGestureInsetRight), - bottom: math.max(0.0, systemGestureInsetBottom), - left: math.max(0.0, systemGestureInsetLeft), - ), - ); - if (!PlatformDispatcher.instance._views.containsKey(id)) { - PlatformDispatcher.instance._views[id] = FlutterWindow._(windowId: id, platformDispatcher: PlatformDispatcher.instance); - } - _invoke( - PlatformDispatcher.instance.onMetricsChanged, - PlatformDispatcher.instance._onMetricsChangedZone, + PlatformDispatcher.instance._updateViewMetrics( + id, + screenId, + left, + top, + width, + height, + depth, + viewPaddingTop, + viewPaddingRight, + viewPaddingBottom, + viewPaddingLeft, + viewInsetTop, + viewInsetRight, + viewInsetBottom, + viewInsetLeft, + systemGestureInsetTop, + systemGestureInsetRight, + systemGestureInsetBottom, + systemGestureInsetLeft, ); } @pragma('vm:entry-point') // ignore: unused_element void _updateScreenMetrics( - Object/*!*/ id, - String/*!*/ screenName, - double/*!*/ left, - double/*!*/ top, - double/*!*/ width, - double/*!*/ height, - double/*!*/ devicePixelRatio, - double/*!*/ viewPaddingTop, - double/*!*/ viewPaddingRight, - double/*!*/ viewPaddingBottom, - double/*!*/ viewPaddingLeft, - double/*!*/ viewInsetTop, - double/*!*/ viewInsetRight, - double/*!*/ viewInsetBottom, - double/*!*/ viewInsetLeft, - double/*!*/ systemGestureInsetTop, - double/*!*/ systemGestureInsetRight, - double/*!*/ systemGestureInsetBottom, - double/*!*/ systemGestureInsetLeft, + Object id, + String screenName, + double left, + double top, + double width, + double height, + double devicePixelRatio, + double viewPaddingTop, + double viewPaddingRight, + double viewPaddingBottom, + double viewPaddingLeft, + double viewInsetTop, + double viewInsetRight, + double viewInsetBottom, + double viewInsetLeft, + double systemGestureInsetTop, + double systemGestureInsetRight, + double systemGestureInsetBottom, + double systemGestureInsetLeft, ) { - final ScreenConfiguration previousConfiguration = - PlatformDispatcher.instance._screenConfigurations[id] ?? const ScreenConfiguration(); - PlatformDispatcher.instance._screenConfigurations[id] = previousConfiguration.copyWith( - screenName: screenName, - geometry: Rect.fromLTWH(left, top, width, height), - devicePixelRatio: devicePixelRatio, - viewPadding: WindowPadding._( - top: viewPaddingTop, - right: viewPaddingRight, - bottom: viewPaddingBottom, - left: viewPaddingLeft, - ), - viewInsets: WindowPadding._( - top: viewInsetTop, - right: viewInsetRight, - bottom: viewInsetBottom, - left: viewInsetLeft, - ), - padding: WindowPadding._( - top: math.max(0.0, viewPaddingTop - viewInsetTop), - right: math.max(0.0, viewPaddingRight - viewInsetRight), - bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), - left: math.max(0.0, viewPaddingLeft - viewInsetLeft), - ), - systemGestureInsets: WindowPadding._( - top: math.max(0.0, systemGestureInsetTop), - right: math.max(0.0, systemGestureInsetRight), - bottom: math.max(0.0, systemGestureInsetBottom), - left: math.max(0.0, systemGestureInsetLeft), - ), - ); - if (!PlatformDispatcher.instance._screens.containsKey(id)) { - PlatformDispatcher.instance._screens[id] = Screen._(screenId: id, platformDispatcher: PlatformDispatcher.instance); - } - _invoke( - PlatformDispatcher.instance.onMetricsChanged, - PlatformDispatcher.instance._onMetricsChangedZone, + PlatformDispatcher.instance._updateScreenMetrics( + id, + screenName, + left, + top, + width, + height, + devicePixelRatio, + viewPaddingTop, + viewPaddingRight, + viewPaddingBottom, + viewPaddingLeft, + viewInsetTop, + viewInsetRight, + viewInsetBottom, + viewInsetLeft, + systemGestureInsetTop, + systemGestureInsetRight, + systemGestureInsetBottom, + systemGestureInsetLeft, ); } @@ -151,38 +116,7 @@ _LocaleClosure? _getLocaleClosure() => _localeClosure; @pragma('vm:entry-point') // ignore: unused_element void _updateLocales(List locales) { - const int stringsPerLocale = 4; - final int numLocales = locales.length ~/ stringsPerLocale; - final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; - final List newLocales = []; - bool localesDiffer = numLocales != previousConfiguration.locales.length; - for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { - final String countryCode = locales[localeIndex * stringsPerLocale + 1]; - final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; - - newLocales.add(Locale.fromSubtags( - languageCode: locales[localeIndex * stringsPerLocale], - countryCode: countryCode.isEmpty ? null : countryCode, - scriptCode: scriptCode.isEmpty ? null : scriptCode, - )); - if (!localesDiffer && newLocales.last != previousConfiguration.locales[localeIndex]) { - localesDiffer = true; - } - } - if (!localesDiffer) { - return; - } - PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( - locales: newLocales, - ); - _invoke( - PlatformDispatcher.instance.onPlatformConfigurationChanged, - PlatformDispatcher.instance._onPlatformConfigurationChangedZone, - ); - _invoke( - PlatformDispatcher.instance.onLocaleChanged, - PlatformDispatcher.instance._onLocaleChangedZone, - ); + PlatformDispatcher.instance._updateLocales(locales); } @pragma('vm:entry-point') @@ -192,115 +126,32 @@ void _updateUserSettingsData(String jsonData) { if (data.isEmpty) { return; } - - final double textScaleFactor = (data['textScaleFactor'] as num).toDouble(); - final bool alwaysUse24HourFormat = data['alwaysUse24HourFormat'] as bool; - final Brightness platformBrightness = - data['platformBrightness'] as String == 'dark' ? Brightness.dark : Brightness.light; - final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; - final bool platformBrightnessChanged = - previousConfiguration.platformBrightness != platformBrightness; - final bool textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; - final bool alwaysUse24HourFormatChanged = - previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; - if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged) { - return; - } - PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( - textScaleFactor: textScaleFactor, - alwaysUse24HourFormat: alwaysUse24HourFormat, - platformBrightness: platformBrightness, - ); - _invoke( - PlatformDispatcher.instance.onPlatformConfigurationChanged, - PlatformDispatcher.instance._onPlatformConfigurationChangedZone, - ); - if (textScaleFactorChanged) { - _invoke( - PlatformDispatcher.instance.onTextScaleFactorChanged, - PlatformDispatcher.instance._onTextScaleFactorChangedZone, - ); - } - if (platformBrightnessChanged) { - _invoke( - PlatformDispatcher.instance.onPlatformBrightnessChanged, - PlatformDispatcher.instance._onPlatformBrightnessChangedZone, - ); - } + PlatformDispatcher.instance._updateUserSettingsData(data); } @pragma('vm:entry-point') // ignore: unused_element void _updateLifecycleState(String state) { - // We do not update the state if the state has already been used to initialize - // the lifecycleState. - if (!PlatformDispatcher.instance._initialLifecycleStateAccessed) - PlatformDispatcher.instance._initialLifecycleState = state; + PlatformDispatcher.instance._updateLifecycleState(state); } @pragma('vm:entry-point') // ignore: unused_element void _updateSemanticsEnabled(bool enabled) { - final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; - if (previousConfiguration.semanticsEnabled == enabled) { - return; - } - PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( - semanticsEnabled: enabled, - ); - _invoke(PlatformDispatcher.instance.onPlatformConfigurationChanged, - PlatformDispatcher.instance._onPlatformConfigurationChangedZone); - _invoke(PlatformDispatcher.instance.onSemanticsEnabledChanged, - PlatformDispatcher.instance._onSemanticsEnabledChangedZone); + PlatformDispatcher.instance._updateSemanticsEnabled(enabled); } @pragma('vm:entry-point') // ignore: unused_element void _updateAccessibilityFeatures(int values) { final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values); - final PlatformConfiguration previousConfiguration = PlatformDispatcher.instance.configuration; - if (newFeatures == previousConfiguration.accessibilityFeatures) { - return; - } - PlatformDispatcher.instance._configuration = previousConfiguration.copyWith( - accessibilityFeatures: newFeatures, - ); - _invoke( - PlatformDispatcher.instance.onPlatformConfigurationChanged, - PlatformDispatcher.instance._onPlatformConfigurationChangedZone, - ); - _invoke( - PlatformDispatcher.instance.onAccessibilityFeaturesChanged, - PlatformDispatcher.instance._onAccessibilityFeaturesChangedZone, - ); + PlatformDispatcher.instance._updateAccessibilityFeatures(newFeatures); } @pragma('vm:entry-point') // ignore: unused_element void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { - if (name == ChannelBuffers.kControlChannelName) { - try { - channelBuffers.handleMessage(data!); - } catch (ex) { - _printDebug('Message to "$name" caused exception $ex'); - } finally { - PlatformDispatcher.instance._respondToPlatformMessage(responseId, null); - } - } else if (PlatformDispatcher.instance.onPlatformMessage != null) { - _invoke3( - PlatformDispatcher.instance.onPlatformMessage, - PlatformDispatcher.instance._onPlatformMessageZone, - name, - data, - (ByteData? responseData) { - PlatformDispatcher.instance._respondToPlatformMessage(responseId, responseData); - }, - ); - } else { - channelBuffers.push(name, data, (ByteData? responseData) { - PlatformDispatcher.instance._respondToPlatformMessage(responseId, responseData); - }); - } + PlatformDispatcher.instance._dispatchPlatformMessage(name, data, responseId); } @pragma('vm:entry-point') @@ -424,8 +275,8 @@ void _invoke1(void callback(A a)?, Zone zone, A arg) { /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. void _invoke3( - void callback(A1 a1, A2 a2, A3 a3) ?, - Zone zone, + void callback(A1 a1, A2 a2, A3 a3)?, + Zone zone, A1 arg1, A2 arg2, A3 arg3, diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index b20382c7166ed..c56b5bdec54a5 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -45,7 +45,7 @@ Future _scheduleFrame( void _setupHooks() { // ignore: unused_element assert(() { // In debug mode, register the schedule frame extension. - developer.registerExtension('ext.ui.PlatformDispatcher.instance.scheduleFrame', _scheduleFrame); + developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); return true; }()); } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 24c2247417321..bfa6f05cc35d2 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +// @dart = 2.9 part of dart.ui; -typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration/*!*/ configuration); +/// Callback type for [PlatformDispatcher.onPlatformConfigurationChanged]. +/// +/// Receives the new `configuration`. +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); /// Platform event dispatcher singleton. /// @@ -14,18 +17,21 @@ typedef PlatformConfigurationChangedCallback = void Function(PlatformConfigurati /// This is the central entry point for platform messages and configuration /// events from the platform. /// -/// It exposes the he core scheduler API, the input event callback, the graphics +/// It exposes the core scheduler API, the input event callback, the graphics /// drawing API, and other such core services. /// /// It manages the list of the application's [views] and the [screens] attached /// to the device, as well as the [configuration] of various platform /// attributes. /// -/// Please try to avoid statically referencing this singleton though -/// [PlatformDispatcher.instance] and instead use a binding for dependency -/// resolution such as `WidgetsBinding.instance.platformDispatcher`. See -/// [PlatformDispatcher.instance] for more information about why this is -/// preferred. +/// It is the type of the `WidgetsBinding.instance.platformDispatcher` +/// singleton. +/// +/// While there is also a [PlatformDispatcher.instance] singleton object in +/// `dart:ui`, unless you are implementing your own binding, consider avoiding a +/// static reference to it. See the documentation for +/// [PlatformDispatcher.instance] for more details about why you might want to +/// avoid using it directly. class PlatformDispatcher { /// Private constructor, since only dart:ui is supposed to create one of /// these. @@ -35,8 +41,8 @@ class PlatformDispatcher { /// The [PlatformDispatcher] singleton. /// - /// Please try to avoid statically referencing this and instead use a binding - /// for dependency resolution such as + /// Instead of accessing this directly, consider avoiding a static reference + /// to this and instead use a binding for dependency resolution such as /// `WidgetsBinding.instance.platformDispatcher`. /// /// Static access of this object means that Flutter has few, if any options to @@ -46,24 +52,27 @@ class PlatformDispatcher { /// reasonable for a future of Flutter where we legitimately want to select an /// appropriate implementation at runtime. /// - /// The only place that `WidgetsBinding.instance.platformDispatcher` is - /// inappropriate is if access to these APIs is required before invoking - /// `runApp()`. In that case, it is acceptable (though unfortunate) to use the - /// [PlatformDispatcher.instance] object statically. - static PlatformDispatcher/*!*/ get instance => _instance; - static final PlatformDispatcher/*!*/ _instance = PlatformDispatcher._(); + /// Flutter's binding uses this instance, and if you are implementing your own + /// binding, of course, it may make sense to use the + /// [PlatformDispatcher.instance] object directly. + /// + /// If you need to access these APIs before invoking `runApp()`, but are using + /// Flutter bindings, instantiate the bindings yourself by calling + /// `WidgetsFlutterBinding.ensureInitialized()` and then use + /// `WidgetsBinding.instance.platformDispatcher`. + static final PlatformDispatcher instance = PlatformDispatcher._(); /// The current platform configuration. /// /// If values in this configuration change, [onMetricsChanged] will be called. - PlatformConfiguration/*!*/ get configuration => _configuration; - PlatformConfiguration/*!*/ _configuration = const PlatformConfiguration(); + PlatformConfiguration get configuration => _configuration; + PlatformConfiguration _configuration = const PlatformConfiguration(); /// Called when the platform configuration changes. - VoidCallback/*?*/ get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; - VoidCallback/*?*/ _onPlatformConfigurationChanged; - Zone/*!*/ _onPlatformConfigurationChangedZone = Zone.root; - set onPlatformConfigurationChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + VoidCallback? _onPlatformConfigurationChanged; + Zone _onPlatformConfigurationChangedZone = Zone.root; + set onPlatformConfigurationChanged(VoidCallback? callback) { _onPlatformConfigurationChanged = callback; _onPlatformConfigurationChangedZone = Zone.current; } @@ -72,22 +81,138 @@ class PlatformDispatcher { /// /// If the list of screens or their configuration changes, [onMetricsChanged] /// will be called. - Iterable/*!*/ get screens => _screens.values; - Map/*!*/ _screens = {}; + Iterable get screens => _screens.values; + Map _screens = {}; // A map of opaque platform screen identifiers to screen configurations. - Map/*!*/ _screenConfigurations = {}; + Map _screenConfigurations = {}; /// The current list of views, including top level platform windows used by /// the application. /// /// If the list of views changes, [onViewCreated] or [onViewDisposed] will be /// called. If their configurations change, [onMetricsChanged] will be called. - Iterable/*!*/ get views => _views.values; - Map/*!*/ _views = {}; + Iterable get views => _views.values; + Map _views = {}; // A map of opaque platform view identifiers to view configurations. - Map/*!*/ _viewConfigurations = {}; + Map _viewConfigurations = {}; + + void _updateViewMetrics( + Object id, + Object screenId, + double left, + double top, + double width, + double height, + double depth, + double viewPaddingTop, + double viewPaddingRight, + double viewPaddingBottom, + double viewPaddingLeft, + double viewInsetTop, + double viewInsetRight, + double viewInsetBottom, + double viewInsetLeft, + double systemGestureInsetTop, + double systemGestureInsetRight, + double systemGestureInsetBottom, + double systemGestureInsetLeft, + ) { + assert(_screens[screenId] != null); + final ViewConfiguration previousConfiguration = _viewConfigurations[id] ?? ViewConfiguration(_screens[screenId] ?? Screen.invalid); + _viewConfigurations[id] = previousConfiguration.copyWith( + screen: _screens[screenId], + geometry: Rect.fromLTWH(left, top, width, height), + depth: depth, + viewPadding: WindowPadding._( + top: viewPaddingTop, + right: viewPaddingRight, + bottom: viewPaddingBottom, + left: viewPaddingLeft, + ), + viewInsets: WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft, + ), + padding: WindowPadding._( + top: math.max(0.0, viewPaddingTop - viewInsetTop), + right: math.max(0.0, viewPaddingRight - viewInsetRight), + bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), + left: math.max(0.0, viewPaddingLeft - viewInsetLeft), + ), + systemGestureInsets: WindowPadding._( + top: math.max(0.0, systemGestureInsetTop), + right: math.max(0.0, systemGestureInsetRight), + bottom: math.max(0.0, systemGestureInsetBottom), + left: math.max(0.0, systemGestureInsetLeft), + ), + ); + if (!_views.containsKey(id)) { + _views[id] = FlutterWindow._(id, PlatformDispatcher.instance); + } + _invoke(onMetricsChanged, _onMetricsChangedZone); + } + + void _updateScreenMetrics( + Object id, + String screenName, + double left, + double top, + double width, + double height, + double devicePixelRatio, + double viewPaddingTop, + double viewPaddingRight, + double viewPaddingBottom, + double viewPaddingLeft, + double viewInsetTop, + double viewInsetRight, + double viewInsetBottom, + double viewInsetLeft, + double systemGestureInsetTop, + double systemGestureInsetRight, + double systemGestureInsetBottom, + double systemGestureInsetLeft, + ) { + final ScreenConfiguration previousConfiguration = + _screenConfigurations[id] ?? const ScreenConfiguration(); + _screenConfigurations[id] = previousConfiguration.copyWith( + screenName: screenName, + geometry: Rect.fromLTWH(left, top, width, height), + devicePixelRatio: devicePixelRatio, + viewPadding: WindowPadding._( + top: viewPaddingTop, + right: viewPaddingRight, + bottom: viewPaddingBottom, + left: viewPaddingLeft, + ), + viewInsets: WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft, + ), + padding: WindowPadding._( + top: math.max(0.0, viewPaddingTop - viewInsetTop), + right: math.max(0.0, viewPaddingRight - viewInsetRight), + bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), + left: math.max(0.0, viewPaddingLeft - viewInsetLeft), + ), + systemGestureInsets: WindowPadding._( + top: math.max(0.0, systemGestureInsetTop), + right: math.max(0.0, systemGestureInsetRight), + bottom: math.max(0.0, systemGestureInsetBottom), + left: math.max(0.0, systemGestureInsetLeft), + ), + ); + if (!_screens.containsKey(id)) { + _screens[id] = Screen._(id, this); + } + _invoke(onMetricsChanged, _onMetricsChangedZone); + } /// A callback that is invoked whenever any [ViewConfiguration] field in the /// [views] or [ScreenConfiguration] field in the [screens] changes, or when a @@ -107,10 +232,10 @@ class PlatformDispatcher { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback/*?*/ get onMetricsChanged => _onMetricsChanged; - VoidCallback/*?*/ _onMetricsChanged; - Zone/*!*/ _onMetricsChangedZone = Zone.root; // ignore: unused_field - set onMetricsChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onMetricsChanged => _onMetricsChanged; + VoidCallback? _onMetricsChanged; + Zone _onMetricsChangedZone = Zone.root; // ignore: unused_field + set onMetricsChanged(VoidCallback? callback) { _onMetricsChanged = callback; _onMetricsChangedZone = Zone.current; } @@ -127,10 +252,10 @@ class PlatformDispatcher { /// [PlatformDispatcher.scheduleFrame] has been called since the last time /// this callback was invoked. /// {@endtemplate} - FrameCallback/*?*/ get onBeginFrame => _onBeginFrame; - FrameCallback/*?*/ _onBeginFrame; - Zone/*?*/ _onBeginFrameZone = Zone.root; - set onBeginFrame(FrameCallback/*?*/ callback) { + FrameCallback? get onBeginFrame => _onBeginFrame; + FrameCallback? _onBeginFrame; + Zone _onBeginFrameZone = Zone.root; + set onBeginFrame(FrameCallback? callback) { _onBeginFrame = callback; _onBeginFrameZone = Zone.current; } @@ -142,10 +267,10 @@ class PlatformDispatcher { /// This can be used to implement a second phase of frame rendering that /// happens after any deferred work queued by the [onBeginFrame] phase. /// {@endtemplate} - VoidCallback/*?*/ get onDrawFrame => _onDrawFrame; - VoidCallback/*?*/ _onDrawFrame; - Zone/*!*/ _onDrawFrameZone = Zone.root; - set onDrawFrame(VoidCallback/*?*/ callback) { + VoidCallback? get onDrawFrame => _onDrawFrame; + VoidCallback? _onDrawFrame; + Zone _onDrawFrameZone = Zone.root; + set onDrawFrame(VoidCallback? callback) { _onDrawFrame = callback; _onDrawFrameZone = Zone.current; } @@ -159,10 +284,10 @@ class PlatformDispatcher { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback/*?*/ get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback/*?*/ _onPointerDataPacket; - Zone/*!*/ _onPointerDataPacketZone = Zone.root; - set onPointerDataPacket(PointerDataPacketCallback/*?*/ callback) { + PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; + PointerDataPacketCallback? _onPointerDataPacket; + Zone _onPointerDataPacketZone = Zone.root; + set onPointerDataPacket(PointerDataPacketCallback? callback) { _onPointerDataPacket = callback; _onPointerDataPacketZone = Zone.current; } @@ -188,10 +313,10 @@ class PlatformDispatcher { /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. - TimingsCallback/*?*/ get onReportTimings => _onReportTimings; - TimingsCallback/*?*/ _onReportTimings; - Zone/*!*/ _onReportTimingsZone = Zone.root; - set onReportTimings(TimingsCallback/*?*/ callback) { + TimingsCallback? get onReportTimings => _onReportTimings; + TimingsCallback? _onReportTimings; + Zone _onReportTimingsZone = Zone.root; + set onReportTimings(TimingsCallback? callback) { if ((callback == null) != (_onReportTimings == null)) { _setNeedsReportTimings(callback != null); } @@ -199,8 +324,8 @@ class PlatformDispatcher { _onReportTimingsZone = Zone.current; } - /*late*/ _SetNeedsReportTimingsFunc/*!*/ _setNeedsReportTimings; - void _nativeSetNeedsReportTimings(bool/*!*/ value) + late _SetNeedsReportTimingsFunc _setNeedsReportTimings; + void _nativeSetNeedsReportTimings(bool value) native 'PlatformConfiguration_setNeedsReportTimings'; /// Sends a message to a platform-specific plugin. @@ -212,15 +337,15 @@ class PlatformDispatcher { /// /// The framework invokes [callback] in the same zone in which this method was /// called. - void sendPlatformMessage(String/*!*/ name, ByteData/*?*/ data, PlatformMessageResponseCallback/*?*/ callback) { - final String error = + void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { + final String? error = _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); if (error != null) { throw Exception(error); } } - String _sendPlatformMessage(String/*!*/ name, PlatformMessageResponseCallback/*?*/ callback, ByteData/*?*/ data) + String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) native 'PlatformConfiguration_sendPlatformMessage'; /// Called whenever this platform dispatcher receives a message from a @@ -236,30 +361,56 @@ class PlatformDispatcher { /// /// The framework invokes this callback in the same zone in which the callback /// was set. - PlatformMessageCallback/*?*/ get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback/*?*/ _onPlatformMessage; - Zone/*!*/ _onPlatformMessageZone = Zone.root; - set onPlatformMessage(PlatformMessageCallback/*?*/ callback) { + PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; + PlatformMessageCallback? _onPlatformMessage; + Zone _onPlatformMessageZone = Zone.root; + set onPlatformMessage(PlatformMessageCallback? callback) { _onPlatformMessage = callback; _onPlatformMessageZone = Zone.current; } /// Called by [_dispatchPlatformMessage]. - void _respondToPlatformMessage(int/*!*/ responseId, ByteData/*?*/ data) + void _respondToPlatformMessage(int responseId, ByteData? data) native 'PlatformConfiguration_respondToPlatformMessage'; + void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { + if (name == ChannelBuffers.kControlChannelName) { + try { + channelBuffers.handleMessage(data!); + } catch (ex) { + _printDebug('Message to "$name" caused exception $ex'); + } finally { + _respondToPlatformMessage(responseId, null); + } + } else if (onPlatformMessage != null) { + _invoke3( + onPlatformMessage, + _onPlatformMessageZone, + name, + data, + (ByteData? responseData) { + _respondToPlatformMessage(responseId, responseData); + }, + ); + } else { + channelBuffers.push(name, data, (ByteData? responseData) { + _respondToPlatformMessage(responseId, responseData); + }); + } + } + /// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. - static PlatformMessageResponseCallback/*?*/ _zonedPlatformMessageResponseCallback( - PlatformMessageResponseCallback/*?*/ callback) { + static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback( + PlatformMessageResponseCallback? callback) { if (callback == null) { return null; } // Store the zone in which the callback is being registered. - final Zone/*!*/ registrationZone = Zone.current; + final Zone registrationZone = Zone.current; - return (ByteData data) { + return (ByteData? data) { registrationZone.runUnaryGuarded(callback, data); }; } @@ -273,7 +424,7 @@ class PlatformDispatcher { /// This can be combined with flutter tools `--isolate-filter` flag to debug /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. /// Note that this does not rename any child isolates of the root. - void setIsolateDebugName(String/*!*/ name) native 'PlatformConfiguration_setIsolateDebugName'; + void setIsolateDebugName(String name) native 'PlatformConfiguration_setIsolateDebugName'; /// The embedder can specify data that the isolate can request synchronously /// on launch. This accessor fetches that data. @@ -284,7 +435,7 @@ class PlatformDispatcher { /// /// For asynchronous communication between the embedder and isolate, a /// platform channel may be used. - ByteData/*?*/ getPersistentIsolateData() native 'PlatformConfiguration_getPersistentIsolateData'; + ByteData? getPersistentIsolateData() native 'PlatformConfiguration_getPersistentIsolateData'; /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and /// [onDrawFrame] callbacks be invoked. @@ -299,8 +450,8 @@ class PlatformDispatcher { /// [Scene]. This function must be called within the scope of the /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. /// - /// If given, draws the scene into the given `view`. If no `view` is given, - /// then the scene is drawn into the default [window]. + /// If given, draws the scene into the given `view`. If `view` is null, or no + /// `view` is given, then the scene is drawn into the default [window]. /// /// If this function is called a second time during a single /// [onBeginFrame]/[onDrawFrame] callback sequence or called outside the scope @@ -323,24 +474,33 @@ class PlatformDispatcher { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene/*!*/ scene, [FlutterView/*!*/ view]) native 'PlatformConfiguration_render'; + void render(Scene scene, [FlutterView? view]) native 'PlatformConfiguration_render'; /// Additional accessibility features that may be enabled by the platform. - AccessibilityFeatures/*!*/ get accessibilityFeatures => configuration.accessibilityFeatures; + AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] /// changes. /// /// The framework invokes this callback in the same zone in which the callback /// was set. - VoidCallback/*?*/ get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; - VoidCallback/*?*/ _onAccessibilityFeaturesChanged; - Zone/*!*/ _onAccessibilityFeaturesChangedZone = Zone.root; - set onAccessibilityFeaturesChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + VoidCallback? _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone = Zone.root; + set onAccessibilityFeaturesChanged(VoidCallback? callback) { _onAccessibilityFeaturesChanged = callback; _onAccessibilityFeaturesChangedZone = Zone.current; } + void _updateAccessibilityFeatures(AccessibilityFeatures newFeatures) { + if (newFeatures == configuration.accessibilityFeatures) { + return; + } + _configuration = configuration.copyWith(accessibilityFeatures: newFeatures); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + } + /// Change the retained semantics data about this platform dispatcher. /// /// If [semanticsEnabled] is true, the user has requested that this function @@ -349,7 +509,7 @@ class PlatformDispatcher { /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. - void updateSemantics(SemanticsUpdate/*!*/ update) native 'PlatformConfiguration_updateSemantics'; + void updateSemantics(SemanticsUpdate update) native 'PlatformConfiguration_updateSemantics'; /// The system-reported default locale of the device. /// @@ -361,9 +521,9 @@ class PlatformDispatcher { /// /// This is equivalent to `locales.first` and will provide an empty non-null /// locale if the [locales] list has not been set or is empty. - Locale/*?*/ get locale { - if (configuration?.locales != null && configuration.locales.isNotEmpty) { - return locales.first; + Locale? get locale { + if (configuration.locales.isNotEmpty) { + return configuration.locales.first; } return null; } @@ -383,7 +543,56 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - List/*?*/ get locales => configuration.locales; + List? get locales => configuration.locales; + + void _updateLocales(List locales) { + const int stringsPerLocale = 4; + final int numLocales = locales.length ~/ stringsPerLocale; + final PlatformConfiguration previousConfiguration = configuration; + final List newLocales = []; + bool localesDiffer = numLocales != previousConfiguration.locales.length; + for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { + final String countryCode = locales[localeIndex * stringsPerLocale + 1]; + final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; + + newLocales.add(Locale.fromSubtags( + languageCode: locales[localeIndex * stringsPerLocale], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + )); + if (!localesDiffer && newLocales.last != previousConfiguration.locales[localeIndex]) { + localesDiffer = true; + } + } + if (!localesDiffer) { + return; + } + _configuration = previousConfiguration.copyWith(locales: newLocales); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onLocaleChanged, _onLocaleChangedZone); + } + + void _updatePlatformResolvedLocale(List localeData) { + if (localeData.length != 4) { + return; + } + final String countryCode = localeData[1]; + final String scriptCode = localeData[2]; + final PlatformConfiguration previousConfiguration = configuration; + final Locale resolvedLocale = Locale.fromSubtags( + languageCode: localeData[0], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + ); + if (previousConfiguration.platformResolvedLocale == resolvedLocale) { + return; + } + + _configuration = previousConfiguration.copyWith(platformResolvedLocale: resolvedLocale); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onLocaleChanged, _onLocaleChangedZone); + } + /// The locale that the platform's native locale resolution system resolves /// to. @@ -396,7 +605,7 @@ class PlatformDispatcher { /// directly in order to arrive at the most appropriate locale for the app. /// /// See [locales], which is the list of locales the user/device prefers. - Locale/*?*/ get platformResolvedLocale => configuration.platformResolvedLocale; + Locale? get platformResolvedLocale => configuration.platformResolvedLocale; /// A callback that is invoked whenever [locale] changes value. /// @@ -407,10 +616,10 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback/*?*/ get onLocaleChanged => _onLocaleChanged; - VoidCallback/*?*/ _onLocaleChanged; - Zone/*!*/ _onLocaleChangedZone = Zone.root; // ignore: unused_field - set onLocaleChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onLocaleChanged => _onLocaleChanged; + VoidCallback? _onLocaleChanged; + Zone _onLocaleChangedZone = Zone.root; // ignore: unused_field + set onLocaleChanged(VoidCallback? callback) { _onLocaleChanged = callback; _onLocaleChangedZone = Zone.current; } @@ -421,17 +630,23 @@ class PlatformDispatcher { /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup with /// any buffered lifecycle state events. - String/*!*/ get initialLifecycleState { + String get initialLifecycleState { _initialLifecycleStateAccessed = true; return _initialLifecycleState; } + late String _initialLifecycleState; - String/*!*/ _initialLifecycleState; + void _updateLifecycleState(String state) { + // We do not update the state if the state has already been used to initialize + // the lifecycleState. + if (!_initialLifecycleStateAccessed) + _initialLifecycleState = state; + } /// Tracks if the initial state has been accessed. Once accessed, we will stop /// updating the [initialLifecycleState], as it is not the preferred way to /// access the state. - bool/*!*/ _initialLifecycleStateAccessed = false; + bool _initialLifecycleStateAccessed = false; /// The system-reported text scale. /// @@ -445,13 +660,13 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double/*!*/ get textScaleFactor => configuration.textScaleFactor; + double get textScaleFactor => configuration.textScaleFactor; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// /// This option is used by [showTimePicker]. - bool/*!*/ get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// @@ -462,10 +677,10 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback/*?*/ get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback/*?*/ _onTextScaleFactorChanged; - Zone/*!*/ _onTextScaleFactorChangedZone = Zone.root; - set onTextScaleFactorChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; + VoidCallback? _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone = Zone.root; + set onTextScaleFactorChanged(VoidCallback? callback) { _onTextScaleFactorChanged = callback; _onTextScaleFactorChangedZone = Zone.current; } @@ -473,7 +688,7 @@ class PlatformDispatcher { /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. - Brightness/*!*/ get platformBrightness => configuration.platformBrightness; + Brightness get platformBrightness => configuration.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// @@ -484,33 +699,70 @@ class PlatformDispatcher { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback/*?*/ get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback/*?*/ _onPlatformBrightnessChanged; - Zone/*!*/ _onPlatformBrightnessChangedZone = Zone.root; - set onPlatformBrightnessChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + VoidCallback? _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone = Zone.root; + set onPlatformBrightnessChanged(VoidCallback? callback) { _onPlatformBrightnessChanged = callback; _onPlatformBrightnessChangedZone = Zone.current; } + void _updateUserSettingsData(Map data) { + final double textScaleFactor = (data['textScaleFactor'] as num).toDouble(); + final bool alwaysUse24HourFormat = data['alwaysUse24HourFormat'] as bool; + final Brightness platformBrightness = + data['platformBrightness'] as String == 'dark' ? Brightness.dark : Brightness.light; + final PlatformConfiguration previousConfiguration = configuration; + final bool platformBrightnessChanged = + previousConfiguration.platformBrightness != platformBrightness; + final bool textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; + final bool alwaysUse24HourFormatChanged = + previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; + if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged) { + return; + } + _configuration = previousConfiguration.copyWith( + textScaleFactor: textScaleFactor, + alwaysUse24HourFormat: alwaysUse24HourFormat, + platformBrightness: platformBrightness, + ); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + if (textScaleFactorChanged) { + _invoke(onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + if (platformBrightnessChanged) { + _invoke(onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + } + /// Whether the user has requested that [updateSemantics] be called when the /// semantic contents of a view changes. /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. - bool/*!*/ get semanticsEnabled => configuration.semanticsEnabled; + bool get semanticsEnabled => configuration.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback/*?*/ get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback/*?*/ _onSemanticsEnabledChanged; - Zone/*!*/ _onSemanticsEnabledChangedZone = Zone.root; - set onSemanticsEnabledChanged(VoidCallback/*?*/ callback) { + VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + VoidCallback? _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone = Zone.root; + set onSemanticsEnabledChanged(VoidCallback? callback) { _onSemanticsEnabledChanged = callback; _onSemanticsEnabledChangedZone = Zone.current; } + void _updateSemanticsEnabled(bool enabled) { + if (configuration.semanticsEnabled == enabled) { + return; + } + _configuration = configuration.copyWith(semanticsEnabled: enabled); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + /// A callback that is invoked whenever the user requests an action to be /// performed. /// @@ -519,10 +771,10 @@ class PlatformDispatcher { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback/*?*/ get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback/*?*/ _onSemanticsAction; - Zone/*!*/ _onSemanticsActionZone = Zone.root; - set onSemanticsAction(SemanticsActionCallback/*?*/ callback) { + SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; + SemanticsActionCallback? _onSemanticsAction; + Zone _onSemanticsActionZone = Zone.root; + set onSemanticsAction(SemanticsActionCallback? callback) { _onSemanticsAction = callback; _onSemanticsActionZone = Zone.current; } @@ -557,13 +809,20 @@ class PlatformDispatcher { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String/*!*/ get initialRouteName => _initialRouteName(); - String/*!*/ _initialRouteName() native 'PlatformConfiguration_initialRouteName'; + String get initialRouteName => _initialRouteName(); + String _initialRouteName() native 'PlatformConfiguration_initialRouteName'; } -/// Configuration of the platform. +/// The immutable configuration details of the platform that the application is +/// running on. /// -/// Immutable class (but can't use @immutable in dart:ui) +/// This is used by the [PlatformDispatcher] to hold the platform's +/// configuration details so that they can be atomically updated by the engine. +/// +/// See also: +/// +/// * [PlatformDispatcher.configuration], which provides access this +/// information. class PlatformConfiguration { /// Const constructor for [PlatformConfiguration]. const PlatformConfiguration({ @@ -575,23 +834,18 @@ class PlatformConfiguration { this.locales = const [], this.platformResolvedLocale, this.initialRouteName, - }) : assert(accessibilityFeatures != null), - assert(alwaysUse24HourFormat != null), - assert(semanticsEnabled != null), - assert(platformBrightness != null), - assert(textScaleFactor != null), - assert(locales != null); + }); /// Copy a [PlatformConfiguration] with some fields replaced. PlatformConfiguration copyWith({ - AccessibilityFeatures/*?*/ accessibilityFeatures, - bool/*?*/ alwaysUse24HourFormat, - bool/*?*/ semanticsEnabled, - Brightness/*?*/ platformBrightness, - double/*?*/ textScaleFactor, - List/*?*/ locales, - Locale/*?*/ platformResolvedLocale, - String/*?*/ initialRouteName, + AccessibilityFeatures? accessibilityFeatures, + bool? alwaysUse24HourFormat, + bool? semanticsEnabled, + Brightness? platformBrightness, + double? textScaleFactor, + List? locales, + Locale? platformResolvedLocale, + String? initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -606,36 +860,45 @@ class PlatformConfiguration { } /// Additional accessibility features that may be enabled by the platform. - final AccessibilityFeatures/*!*/ accessibilityFeatures; + final AccessibilityFeatures accessibilityFeatures; /// The setting indicating whether time should always be shown in the 24-hour /// format. - final bool/*!*/ alwaysUse24HourFormat; + final bool alwaysUse24HourFormat; /// Whether the user has requested that [updateSemantics] be called when the /// semantic contents of a view changes. - final bool/*!*/ semanticsEnabled; + final bool semanticsEnabled; /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. - final Brightness/*!*/ platformBrightness; + final Brightness platformBrightness; /// The system-reported text scale. - final double/*!*/ textScaleFactor; + final double textScaleFactor; /// The full system-reported supported locales of the device. - final List/*!*/ locales; + final List locales; /// The system-reported default locale of the device. - final Locale/*?*/ platformResolvedLocale; + final Locale? platformResolvedLocale; /// The route or path that the embedder requested when the application was /// launched. - final String/*?*/ initialRouteName; + final String? initialRouteName; } -/// Immutable configuration information for a screen. +/// The immutable configuration information for a screen. +/// +/// This is used by the [PlatformDispatcher] to hold a screen's configuration +/// details so that they can be atomically updated by the engine. +/// +/// See also: +/// +/// * [Screen], which represents a particular screen, including this information. +/// * [PlatformDispatcher.screens], which provides access to the screens on +/// this device. class ScreenConfiguration { /// Const constructor for [ScreenConfiguration] information. const ScreenConfiguration({ @@ -646,24 +909,18 @@ class ScreenConfiguration { this.viewPadding = WindowPadding.zero, this.systemGestureInsets = WindowPadding.zero, this.padding = WindowPadding.zero, - }) : assert(screenName != null), - assert(geometry != null), - assert(devicePixelRatio != null), - assert(viewInsets != null), - assert(viewPadding != null), - assert(systemGestureInsets != null), - assert(padding != null); + }); /// Makes a new copy of this [ScreenConfiguration] with some attributes /// replaced. ScreenConfiguration copyWith({ - String/*?*/ screenName, - Rect/*?*/ geometry, - double/*?*/ devicePixelRatio, - WindowPadding/*?*/ viewInsets, - WindowPadding/*?*/ viewPadding, - WindowPadding/*?*/ systemGestureInsets, - WindowPadding/*?*/ padding, + String? screenName, + Rect? geometry, + double? devicePixelRatio, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, }) { return ScreenConfiguration( screenName: screenName ?? this.screenName, @@ -677,38 +934,38 @@ class ScreenConfiguration { } /// Platform-provided name for screen. - final String/*!*/ screenName; + final String screenName; /// Screen rect in Flutter logical pixels - final Rect/*!*/ geometry; + final Rect geometry; /// Device pixel ratio in device pixels to logical pixels. - final double/*!*/ devicePixelRatio; + final double devicePixelRatio; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but over which the operating /// system will likely place system UI, such as the keyboard or system menus, /// that fully obscures any content. - final WindowPadding/*!*/ viewInsets; + final WindowPadding viewInsets; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but which may be partially /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding/*!*/ viewPadding; + final WindowPadding viewPadding; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but where the operating system /// will consume input gestures for the sake of system navigation. - final WindowPadding/*!*/ systemGestureInsets; + final WindowPadding systemGestureInsets; /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but which may be partially /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding/*!*/ padding; + final WindowPadding padding; @override String toString() { @@ -716,12 +973,21 @@ class ScreenConfiguration { } } -/// An immutable view configuration. +/// The immutable configuration information for a view. +/// +/// This is used by the [PlatformDispatcher] to hold a view's configuration +/// details so that they can be atomically updated by the engine. +/// +/// See also: +/// +/// * [FlutterView], which represents a particular view, including this information. +/// * [PlatformDispatcher.views], which provides access to the views on +/// this device. class ViewConfiguration { /// A const constructor for an immutable [ViewConfiguration]. - const ViewConfiguration({ + const ViewConfiguration( this.screen, - this.window, + { this.window, this.geometry = Rect.zero, this.depth = double.maxFinite, this.visible = false, @@ -729,29 +995,22 @@ class ViewConfiguration { this.viewPadding = WindowPadding.zero, this.systemGestureInsets = WindowPadding.zero, this.padding = WindowPadding.zero, - }) : assert(screen != null), - assert(geometry != null), - assert(depth != null), - assert(visible != null), - assert(viewInsets != null), - assert(viewPadding != null), - assert(systemGestureInsets != null), - assert(padding != null); + }); /// Copy this configuration with some fields replaced. ViewConfiguration copyWith({ - Screen/*?*/ screen, - FlutterWindow/*?*/ window, - Rect/*?*/ geometry, - double/*?*/ depth, - bool/*?*/ visible, - WindowPadding/*?*/ viewInsets, - WindowPadding/*?*/ viewPadding, - WindowPadding/*?*/ systemGestureInsets, - WindowPadding/*?*/ padding, + Screen? screen, + FlutterWindow? window, + Rect? geometry, + double? depth, + bool? visible, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, }) { return ViewConfiguration( - screen: screen ?? this.screen, + screen ?? this.screen, window: window ?? this.window, geometry: geometry ?? this.geometry, depth: depth ?? this.depth, @@ -766,19 +1025,19 @@ class ViewConfiguration { /// The screen that this view should appear on. /// /// This is the screen that the upper left corner of the view appears on. - final Screen/*!*/ screen; + final Screen screen; /// The top level view into which the view is placed and its geometry is /// relative to. /// /// If null, then this configuration represents a top level view itself. - final FlutterWindow/*?*/ window; + final FlutterWindow? window; /// The geometry requested for the view on the [screen] or within its parent /// window, in logical pixels. /// /// This uses the device pixel ratio of the [screen]. - final Rect/*!*/ geometry; + final Rect geometry; /// The depth that is the maximum elevation that the view allows. /// @@ -791,10 +1050,10 @@ class ViewConfiguration { /// The default value is [double.maxFinite], which is used for platforms that /// do not specify a maximum elevation. This property is currently only /// expected to be set to a non-default value on Fuchsia. - final double/*!*/ depth; + final double depth; /// Whether or not the view is currently visible on the screen. - final bool/*!*/ visible; + final bool visible; /// The view insets, as it intersects with [Screen.viewInsets] for the screen /// it is on. @@ -807,7 +1066,7 @@ class ViewConfiguration { /// which the application can draw, but over which the operating system will /// likely place system UI, such as the keyboard or system menus, that fully /// obscures any content. - final WindowPadding/*!*/ viewInsets; + final WindowPadding viewInsets; /// The view insets, as it intersects with [ScreenConfiguration.viewPadding] /// for the screen it is on. @@ -821,7 +1080,7 @@ class ViewConfiguration { /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding/*!*/ viewPadding; + final WindowPadding viewPadding; /// The view insets, as it intersects with /// [ScreenConfiguration.systemGestureInsets] for the screen it is on. @@ -833,7 +1092,7 @@ class ViewConfiguration { /// The number of physical pixels on each side of this screen rectangle into /// which the application can place a view, but where the operating system /// will consume input gestures for the sake of system navigation. - final WindowPadding/*!*/ systemGestureInsets; + final WindowPadding systemGestureInsets; /// The view insets, as it intersects with [ScreenConfiguration.padding] for /// the screen it is on. @@ -847,7 +1106,7 @@ class ViewConfiguration { /// obscured by system UI (such as the system notification area), or physical /// intrusions in the display (e.g. overscan regions on television screens or /// phone sensor housings). - final WindowPadding/*!*/ padding; + final WindowPadding padding; @override String toString() { diff --git a/lib/ui/screen.dart b/lib/ui/screen.dart index 0c44facc0ee2d..26d92861964ec 100644 --- a/lib/ui/screen.dart +++ b/lib/ui/screen.dart @@ -2,26 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +// @dart = 2.9 part of dart.ui; /// A class representing the screen that application windows are displayed on. /// /// Each screen can have separate dimensions, and a separate device pixel ratio. class Screen { - const Screen._({Object/*!*/ screenId, PlatformDispatcher/*!*/ platformDispatcher}) + const Screen._(Object screenId, PlatformDispatcher? platformDispatcher) : _screenId = screenId, _platformDispatcher = platformDispatcher; /// The opaque ID for this screen. - final Object/*!*/ _screenId; + final Object _screenId; + + /// The value to use to indicate an invalid value for a [Screen] object. + static const Screen invalid = Screen._(-1, null); /// The platform dispatcher that this screen is registered with. - final PlatformDispatcher/*!*/ _platformDispatcher; + /// + /// Will only be null if the [Screen] is invalid. + final PlatformDispatcher? _platformDispatcher; - /// The configuration of this screen. - ScreenConfiguration/*!*/ get configuration { - assert(_platformDispatcher._screens.containsKey(_screenId)); - return _platformDispatcher._screenConfigurations[_screenId]; + /// The configuration details of this screen. + ScreenConfiguration get configuration { + assert(_platformDispatcher?._screens.containsKey(_screenId) ?? false); + return _platformDispatcher?._screenConfigurations[_screenId] ?? const ScreenConfiguration(); } } diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 4c4c9d402320c..7db3d69853813 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -177,7 +177,8 @@ enum AppLifecycleState { /// user input, and running in the background. /// /// When the application is in this state, the engine will not call the - /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] callbacks. + /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] + /// callbacks. paused, /// The application is still hosted on a flutter engine but is detached from @@ -245,8 +246,8 @@ class WindowPadding { /// /// See also: /// -/// * [PlatformDispatcher.locale], which specifies the system's currently selected -/// [Locale]. +/// * [PlatformDispatcher.locale], which specifies the system's currently +/// selected [Locale]. class Locale { /// Creates a new Locale object. The first argument is the /// primary language subtag, the second is the region (also @@ -543,47 +544,44 @@ class Locale { /// system reserves for system UI, such as the keyboard, which would fully /// obscure any content drawn in that area. /// -/// The [viewPadding] are the physical pixels on each side of the -/// display that may be partially obscured by system UI or by physical -/// intrusions into the display, such as an overscan region on a television or a -/// "notch" on a phone. Unlike the insets, these areas may have portions that -/// show the user view-painted pixels without being obscured, such as a -/// notch at the top of a phone that covers only a subset of the area. Insets, -/// on the other hand, either partially or fully obscure the window, such as an -/// opaque keyboard or a partially translucent status bar, which cover an area -/// without gaps. +/// The [viewPadding] are the physical pixels on each side of the display that +/// may be partially obscured by system UI or by physical intrusions into the +/// display, such as an overscan region on a television or a "notch" on a phone. +/// Unlike the insets, these areas may have portions that show the user +/// view-painted pixels without being obscured, such as a notch at the top of a +/// phone that covers only a subset of the area. Insets, on the other hand, +/// either partially or fully obscure the window, such as an opaque keyboard or +/// a partially translucent status bar, which cover an area without gaps. /// -/// The [padding] property is computed from both -/// [viewInsets] and [viewPadding]. It will allow a -/// view inset to consume view padding where appropriate, such as when a phone's -/// keyboard is covering the bottom view padding and so "absorbs" it. +/// The [padding] property is computed from both [viewInsets] and [viewPadding]. +/// It will allow a view inset to consume view padding where appropriate, such +/// as when a phone's keyboard is covering the bottom view padding and so +/// "absorbs" it. /// /// Clients that want to position elements relative to the view padding -/// regardless of the view insets should use the [viewPadding] -/// property, e.g. if you wish to draw a widget at the center of the screen with -/// respect to the iPhone "safe area" regardless of whether the keyboard is -/// showing. +/// regardless of the view insets should use the [viewPadding] property, e.g. if +/// you wish to draw a widget at the center of the screen with respect to the +/// iPhone "safe area" regardless of whether the keyboard is showing. /// -/// [padding] is useful for clients that want to know how much -/// padding should be accounted for without concern for the current inset(s) -/// state, e.g. determining whether a gesture should be considered for scrolling -/// purposes. This value varies based on the current state of the insets. For -/// example, a visible keyboard will consume all gestures in the bottom part of -/// the [viewPadding] anyway, so there is no need to account for -/// that in the [padding], which is always safe to use for such -/// calculations. +/// [padding] is useful for clients that want to know how much padding should be +/// accounted for without concern for the current inset(s) state, e.g. +/// determining whether a gesture should be considered for scrolling purposes. +/// This value varies based on the current state of the insets. For example, a +/// visible keyboard will consume all gestures in the bottom part of the +/// [viewPadding] anyway, so there is no need to account for that in the +/// [padding], which is always safe to use for such calculations. /// /// See also: /// /// * [FlutterWindow], a special case of a [FlutterView] that is represented on /// the platform as a separate window which can host other [FlutterView]s. abstract class FlutterView { - /// The platform dispatcher that this view is registered with, and gets its - /// information from. - PlatformDispatcher/*!*/ get platformDispatcher; + /// The [PlatformDispatcher] that this view is registered with, that it gets + /// its configuration information from. + PlatformDispatcher get platformDispatcher; - /// The configuration of this view. - ViewConfiguration/*!*/ get viewConfiguration; + /// The configuration information for this view. + ViewConfiguration get viewConfiguration; /// The [Screen] that this [FlutterView] is displayed on. Screen get screen => viewConfiguration.screen; @@ -741,16 +739,14 @@ abstract class FlutterView { /// This value is calculated by taking `max(0.0, FlutterView.viewPadding - /// FlutterView.viewInsets)`. This will treat a system IME that increases the /// bottom inset as consuming that much of the bottom padding. For example, on - /// an iPhone X, [padding.bottom] is the same as - /// [viewPadding.bottom] when the soft keyboard is not drawn (to - /// account for the bottom soft button area), but will be `0.0` when the soft - /// keyboard is visible. + /// an iPhone X, [padding.bottom] is the same as [viewPadding.bottom] when the + /// soft keyboard is not drawn (to account for the bottom soft button area), + /// but will be `0.0` when the soft keyboard is visible. /// /// When this changes, [onMetricsChanged] is called. /// - /// The relationship between this [viewInsets], - /// [viewPadding], and [padding] are described in - /// more detail in the documentation for [FlutterView]. + /// The relationship between this [viewInsets], [viewPadding], and [padding] + /// are described in more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -798,17 +794,12 @@ abstract class FlutterView { /// `WidgetsBinding.instance.platformDispatcher.views`. Only views that are of /// type [FlutterWindow] are top level platform windows. /// -/// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` -/// if `WidgetsBinding` is unavailable, but we strongly advise avoiding a static -/// reference to it. See the documentation for [PlatformDispatcher.instance] for -/// more details about why it should be avoided. -/// /// See also: /// /// * [PlatformDispatcher], which manages the current list of [FlutterView] /// (and thus [FlutterWindow]) instances. class FlutterWindow extends FlutterView { - FlutterWindow._({Object windowId, this.platformDispatcher}) + FlutterWindow._(Object windowId, this.platformDispatcher) : _windowId = windowId; /// The opaque ID for this view. @@ -821,33 +812,37 @@ class FlutterWindow extends FlutterView { @override ViewConfiguration get viewConfiguration { assert(platformDispatcher._viewConfigurations.containsKey(_windowId)); - return platformDispatcher._viewConfigurations[_windowId]; + return platformDispatcher._viewConfigurations[_windowId] ?? const ViewConfiguration(Screen.invalid); } } -/// A [FlutterWindow] that includes access to setting callbacks and retrieving -/// properties that reside on the [PlatformDispatcher]. +/// The [FlutterWindow] in apps that only have one window. /// -/// It is the type of the `WidgetsBinding.instance.window` singleton and the -/// legacy global [window] singleton. +/// This is a [FlutterWindow] that includes convenience access to setting +/// callbacks and retrieving properties that reside on the [PlatformDispatcher] +/// for the common case of an application with only one window. /// -/// This class provides backward compatibility with code that was written before -/// Flutter supported multiple top level windows. To modify or retrieve these -/// properties, new code should use `WidgetsBinding.instance.platformDispatcher`. +/// It is typically accessed using the `WidgetsBinding.instance.window` +/// singleton. For applications that have more than one window, consider using +/// the the APIs on `WidgetsBinding.instance.platformDispatcher` directly. /// -/// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` -/// if `WidgetsBinding` is unavailable. But we strongly advise avoiding a static -/// reference to it. See the documentation for [PlatformDispatcher.instance] for -/// more details about why it should be avoided. +/// While there is also a [window] singleton object in `dart:ui`, unless you are +/// implementing your own binding, consider avoiding a static reference to it. +/// See the documentation for [window] for more details about why you might want +/// to avoid using it directly. class SingletonFlutterWindow extends FlutterWindow { - SingletonFlutterWindow._({Object windowId, PlatformDispatcher platformDispatcher}) - : super._(windowId: windowId, platformDispatcher: platformDispatcher); + SingletonFlutterWindow._(Object windowId, PlatformDispatcher platformDispatcher) + : super._(windowId, platformDispatcher); /// A callback that is invoked whenever the [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], /// [PlatformDispatcher.screens], or [systemGestureInsets] values change. /// - /// {@template flutter.lib.ui.window.forwardWarning} + /// {@template flutter.lib.ui.window.callbackForwardWarning} + /// Setting this callback also sets the callback on the [PlatformDispatcher] + /// singleton. In applications with more than one window, consider setting it + /// on `WidgetsBinding.instance.platformDispatcher` directly. + /// {@endtemplate} /// /// See [PlatformDispatcher.onMetricsChanged] for more information. VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; @@ -859,13 +854,9 @@ class SingletonFlutterWindow extends FlutterWindow { /// /// {@template flutter.lib.ui.window.accessorForwardWarning} /// Accessing this value returns the value contained in the - /// [PlatformDispatcher] singleton, so instead of getting it from here, you - /// should consider getting it from - /// `WidgetsBinding.instance.platformDispatcher` instead (or, as a last resort - /// when `WidgetsBinding` isn't available, from - /// [PlatformDispatcher.instance]). The reason this value forwards to the - /// [PlatformDispatcher] is to avoid breaking code that was written before - /// Flutter supported multiple windows. + /// [PlatformDispatcher] singleton, in applications with more than one window, + /// consider getting it from `WidgetsBinding.instance.platformDispatcher` + /// directly. /// {@endtemplate} /// /// This establishes the language and formatting conventions that window @@ -926,7 +917,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked whenever [locale] changes value. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -948,7 +939,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. - String get initialLifecycleState => platformDispatcher.initialLifecycleState; + String? get initialLifecycleState => platformDispatcher.initialLifecycleState; /// The system-reported text scale. /// @@ -976,7 +967,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked whenever [textScaleFactor] changes value. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -1000,7 +991,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked whenever [platformBrightness] changes value. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -1018,7 +1009,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// time to provide a scene using the [SceneBuilder] API and the [render] /// method. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// When possible, this is driven by the hardware VSync signal. This is only /// called if [scheduleFrame] has been called since the last time this @@ -1045,7 +1036,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked for each frame after [onBeginFrame] has /// completed and after the microtask queue has been drained. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// This can be used to implement a second phase of frame rendering that /// happens after any deferred work queued by the [onBeginFrame] phase. @@ -1067,9 +1058,9 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// - /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use + /// Consider using [SchedulerBinding.addTimingsCallback] instead of /// [FlutterWindow.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. /// @@ -1094,7 +1085,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked when pointer data is available. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -1147,12 +1138,9 @@ class SingletonFlutterWindow extends FlutterWindow { /// /// {@template flutter.lib.ui.window.functionForwardWarning} /// Calling this function forwards the call to the same function on the - /// [PlatformDispatcher] singleton, so instead of calling it here, you should + /// [PlatformDispatcher] singleton. In applications with more than one window, /// consider calling it on `WidgetsBinding.instance.platformDispatcher` - /// instead (or, as a last resort when `WidgetsBinding` isn't available, on - /// [PlatformDispatcher.instance]). The reason this function forwards to the - /// [PlatformDispatcher] is to avoid breaking code that was written before - /// Flutter supported multiple windows. + /// directly. /// {@endtemplate} /// /// See also: @@ -1172,7 +1160,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked when the value of [semanticsEnabled] changes. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -1184,7 +1172,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked whenever the user requests an action to be /// performed. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// This callback is used when the user expresses the action they wish to /// perform based on the semantics supplied by [updateSemantics]. @@ -1201,7 +1189,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -1241,7 +1229,7 @@ class SingletonFlutterWindow extends FlutterWindow { /// Called whenever this window receives a message from a platform-specific /// plugin. /// - /// {@macro flutter.lib.ui.window.forwardWarning} + /// {@macro flutter.lib.ui.window.callbackForwardWarning} /// /// The `name` parameter determines which plugin sent the message. The `data` /// parameter is the payload and is typically UTF-8 encoded JSON but can be @@ -1353,19 +1341,14 @@ enum Brightness { light, } -/// The [SingletonFlutterWindow] representing the "only" window on platforms +/// The [SingletonFlutterWindow] representing "the" window on platforms /// where there is only one window, such as single-display mobile devices. /// -/// This is a legacy object for code that was written before Flutter supported -/// multiple windows. New code should use the API on -/// `WidgetsBinding.instance.platformDispatcher`, or, where that is not -/// available, on [PlatformDispatcher.instance]. -/// /// On platforms with more than one window, this window typically represents the /// first window created, but it may not be visible. /// -/// Please try to avoid statically referencing this and instead use a -/// binding for dependency resolution such as `WidgetsBinding.instance.window`. +/// It is better to avoid statically referencing this and instead use a binding +/// for dependency resolution such as `WidgetsBinding.instance.window`. /// /// Static access of this "window" object means that Flutter has few, if any /// options to fake or mock the given object in tests. Even in cases where Dart @@ -1374,14 +1357,17 @@ enum Brightness { /// reasonable for a future of Flutter where we legitimately want to select an /// appropriate implementation at runtime. /// -/// The only place that using `WidgetsBinding.instance.platformDispatcher` is -/// inappropriate is if access to these APIs is required before invoking -/// `runApp()`. In that case, it is acceptable (though unfortunate) to use the -/// [PlatformDispatcher.instance] object statically. +/// Flutter's binding uses this singleton, and if you are implementing your own +/// binding, of course, it may make sense to use the [window] object directly. +/// +/// If you need to access these APIs before invoking `runApp()`, but are using +/// Flutter bindings, instantiate the bindings yourself by calling +/// `WidgetsFlutterBinding.ensureInitialized()` and then use +/// `WidgetsBinding.instance.window`. /// /// See also: /// /// * [PlatformDispatcher.views], contains the current list of Flutter windows /// belonging to the application, including top level application windows /// like this one. -final SingletonFlutterWindow window = SingletonFlutterWindow._(windowId: 0, platformDispatcher: PlatformDispatcher.instance); +final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index f8a91002bec04..04a33d73034fe 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -19,6 +19,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// these. EnginePlatformDispatcher._() { _addBrightnessMediaQueryListener(); + js.context['_flutter_web_set_location_strategy'] = (LocationStrategy strategy) { + locationStrategy = strategy; + }; + registerHotRestartListener(() { + js.context['_flutter_web_set_location_strategy'] = null; + }); } /// The [EnginePlatformDispatcher] singleton. @@ -833,6 +839,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { _browserHistory.locationStrategy = strategy; } + /// Returns the currently active location strategy. + @visibleForTesting + LocationStrategy get locationStrategy => _browserHistory.locationStrategy; + @visibleForTesting Rasterizer/*?*/ rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 5167803899992..3fec62fe3ef92 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -11,15 +11,7 @@ const bool/*!*/ _debugPrintPlatformMessages = false; /// The Web implementation of [ui.FlutterWindow]. class EngineFlutterWindow extends ui.FlutterWindow { EngineFlutterWindow({Object windowId, this.platformDispatcher}) - : _windowId = windowId { - _addBrightnessMediaQueryListener(); - js.context['_flutter_web_set_location_strategy'] = (LocationStrategy strategy) { - locationStrategy = strategy; - }; - registerHotRestartListener(() { - js.context['_flutter_web_set_location_strategy'] = null; - }); - } + : _windowId = windowId; final Object _windowId; final ui.PlatformDispatcher platformDispatcher; @@ -181,10 +173,6 @@ class EngineFlutterWindowView extends ui.FlutterWindowView { final ui.PlatformDispatcher platformDispatcher; - /// Returns the currently active location strategy. - @visibleForTesting - LocationStrategy get locationStrategy => _browserHistory.locationStrategy; - @override ui.ViewConfiguration get viewConfiguration { final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; diff --git a/lib/web_ui/lib/src/ui/semantics.dart b/lib/web_ui/lib/src/ui/semantics.dart index 958522f2f5ffd..66a0237dcb0cf 100644 --- a/lib/web_ui/lib/src/ui/semantics.dart +++ b/lib/web_ui/lib/src/ui/semantics.dart @@ -610,7 +610,8 @@ class SemanticsFlag { /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the user. +/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the +/// user. class SemanticsUpdateBuilder { /// Creates an empty [SemanticsUpdateBuilder] object. SemanticsUpdateBuilder(); @@ -638,10 +639,10 @@ class SemanticsUpdateBuilder { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [PlatformDispatcher.onSemanticsAction] will be called with `id` and one of - /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [PlatformDispatcher.onSemanticsAction] callback might be called - /// with an action that is no longer possible. + /// node, the [PlatformDispatcher.onSemanticsAction] will be called with `id` + /// and one of the possible [SemanticsAction]s. Because the semantics tree is + /// maintained asynchronously, the [PlatformDispatcher.onSemanticsAction] + /// callback might be called with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property /// describes the current value of the node as a string. The `increasedValue` @@ -734,8 +735,8 @@ class SemanticsUpdateBuilder { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [PlatformDispatcher.updateSemantics] to actually - /// update the semantics retained by the system. + /// The returned object can be passed to [PlatformDispatcher.updateSemantics] + /// to actually update the semantics retained by the system. SemanticsUpdate build() { return SemanticsUpdate._( nodeUpdates: _nodeUpdates, diff --git a/lib/web_ui/test/window_test.dart b/lib/web_ui/test/window_test.dart index f8fc411bec6fa..11b6af25012ae 100644 --- a/lib/web_ui/test/window_test.dart +++ b/lib/web_ui/test/window_test.dart @@ -23,16 +23,16 @@ set strategy(TestLocationStrategy newStrategy) { void main() { test('window.defaultRouteName should not change', () { - window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); + EnginePlatformDispatcher.instance.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); expect(window.defaultRouteName, '/initial'); // Changing the URL in the address bar later shouldn't affect [window.defaultRouteName]. - window.locationStrategy.replaceState(null, null, '/newpath'); + EnginePlatformDispatcher.instance.locationStrategy.replaceState(null, null, '/newpath'); expect(window.defaultRouteName, '/initial'); }); test('window.defaultRouteName should reset after navigation platform message', () { - window.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); + EnginePlatformDispatcher.instance.locationStrategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); // Reading it multiple times should return the same value. expect(window.defaultRouteName, '/initial'); expect(window.defaultRouteName, '/initial'); @@ -54,9 +54,9 @@ void main() { final testStrategy = TestLocationStrategy.fromEntry( TestHistoryEntry(null, null, '/'), ); - window.locationStrategy = testStrategy; + EnginePlatformDispatcher.instance.locationStrategy = testStrategy; - expect(window.locationStrategy, testStrategy); + expect(EnginePlatformDispatcher.instance.locationStrategy, testStrategy); // A single listener should've been setup. expect(testStrategy.listeners, hasLength(1)); // The initial entry should be there, plus another "flutter" entry. @@ -67,7 +67,7 @@ void main() { // Now, let's disable location strategy and make sure things get cleaned up. expect(() => jsSetLocationStrategy(null), returnsNormally); - expect(window.locationStrategy, isNull); + expect(EnginePlatformDispatcher.instance.locationStrategy, isNull); // The listener is removed asynchronously. await Future.delayed(const Duration(milliseconds: 10)); diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index e85cc255cabe7..8d4d9da8b835b 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -46,26 +46,27 @@ void main() { PlatformMessageCallback? originalOnPlatformMessage; VoidCallback? originalOnTextScaleFactorChanged; - Object? oldWindowId; - Object? oldScreenId; - Rect? oldGeometry; - double? oldDepth; - WindowPadding? oldPadding; - WindowPadding? oldInsets; - WindowPadding? oldSystemGestureInsets; + Object? oldWindowId; + Object? oldScreenId; + Rect? oldGeometry; + double? oldDepth; + WindowPadding? oldPadding; + WindowPadding? oldInsets; + WindowPadding? oldSystemGestureInsets; void setUp() { PlatformDispatcher.instance._viewConfigurations.clear(); - PlatformDispatcher.instance._screenConfigurations.clear(); - PlatformDispatcher.instance._views.clear(); - PlatformDispatcher.instance._screens.clear(); - PlatformDispatcher.instance._screenConfigurations[0] = const ScreenConfiguration(); - PlatformDispatcher.instance._screens[0] = Screen._(screenId: 0, platformDispatcher: PlatformDispatcher.instance); - PlatformDispatcher.instance._viewConfigurations[0] = ViewConfiguration(screen: PlatformDispatcher.instance._screens[0]); - PlatformDispatcher.instance._views[0] = FlutterWindow._(windowId: 0, platformDispatcher: PlatformDispatcher.instance); - oldWindowId = window._windowId; + PlatformDispatcher.instance._screenConfigurations.clear(); + PlatformDispatcher.instance._views.clear(); + PlatformDispatcher.instance._screens.clear(); + PlatformDispatcher.instance._screenConfigurations[0] = const ScreenConfiguration(); + final Screen newScreen = Screen._(0, PlatformDispatcher.instance); + PlatformDispatcher.instance._screens[0] = newScreen; + PlatformDispatcher.instance._viewConfigurations[0] = ViewConfiguration(newScreen); + PlatformDispatcher.instance._views[0] = FlutterWindow._(0, PlatformDispatcher.instance); + oldWindowId = window._windowId; oldScreenId = window.viewConfiguration.screen._screenId; - oldGeometry = window.viewConfiguration.geometry; + oldGeometry = window.viewConfiguration.geometry; oldDepth = window.physicalDepth; oldPadding = window.viewPadding; oldInsets = window.viewInsets; @@ -356,9 +357,9 @@ void main() { test('onTextScaleFactorChanged preserves callback zone', () { late Zone innerZone; late Zone runZoneTextScaleFactor; - Zone runZonePlatformBrightness; - late double textScaleFactor; - Brightness platformBrightness; + late Zone runZonePlatformBrightness; + double? textScaleFactor; + Brightness? platformBrightness; runZoned(() { innerZone = Zone.current; @@ -372,41 +373,21 @@ void main() { }; }); - window.onTextScaleFactorChanged(); + PlatformDispatcher.instance.onTextScaleFactorChanged?.call(); _updateUserSettingsData('{"textScaleFactor": 0.5, "platformBrightness": "light", "alwaysUse24HourFormat": true}'); - expect(runZoneTextScaleFactor, isNotNull); - expect(runZoneTextScaleFactor, same(innerZone)); - expect(textScaleFactor, equals(0.5)); + expectNotEquals(runZoneTextScaleFactor, null); + expectEquals(runZoneTextScaleFactor, innerZone); + expectEquals(textScaleFactor, 0.5); textScaleFactor = null; platformBrightness = null; - window.onPlatformBrightnessChanged(); + PlatformDispatcher.instance.onPlatformBrightnessChanged?.call(); _updateUserSettingsData('{"textScaleFactor": 0.5, "platformBrightness": "dark", "alwaysUse24HourFormat": true}'); - expect(runZonePlatformBrightness, isNotNull); - expect(runZonePlatformBrightness, same(innerZone)); - expect(platformBrightness, equals(Brightness.dark)); - }); - - test('onThemeBrightnessMode preserves callback zone', () { - late Zone innerZone; - late Zone runZone; - late Brightness platformBrightness; - - runZoned(() { - innerZone = Zone.current; - window.onPlatformBrightnessChanged = () { - runZone = Zone.current; - platformBrightness = window.platformBrightness; - }; - }); - - window.onPlatformBrightnessChanged!(); - _updatePlatformBrightness('dark'); - expectNotEquals(runZone, null); - expectIdentical(runZone, innerZone); + expectNotEquals(runZonePlatformBrightness, null); + expectEquals(runZonePlatformBrightness, innerZone); expectEquals(platformBrightness, Brightness.dark); }); @@ -462,11 +443,11 @@ void main() { 0.0, // system gesture inset left ); - expect(window.viewInsets.bottom, 400.0); - expect(window.viewPadding.bottom, 40.0); - expect(window.padding.bottom, 0.0); - expect(window.physicalDepth, 100.0); - expect(window.systemGestureInsets.bottom, 44.0); + expectEquals(window.viewInsets.bottom, 400.0); + expectEquals(window.viewPadding.bottom, 40.0); + expectEquals(window.padding.bottom, 0.0); + expectEquals(window.physicalDepth, 100.0); + expectEquals(window.systemGestureInsets.bottom, 44.0); }); test('Screen padding/insets/viewPadding/systemGestureInsets', () { @@ -492,11 +473,11 @@ void main() { 0.0, // system gesture inset left ); - expect(window.screen.configuration.viewInsets.bottom, 0.0); - expect(window.screen.configuration.viewPadding.bottom, 40.0); - expect(window.screen.configuration.padding.bottom, 40.0); - expect(window.screen.configuration.devicePixelRatio, 2.5); - expect(window.screen.configuration.systemGestureInsets.bottom, 0.0); + expectEquals(window.screen.configuration.viewInsets.bottom, 0.0); + expectEquals(window.screen.configuration.viewPadding.bottom, 40.0); + expectEquals(window.screen.configuration.padding.bottom, 40.0); + expectEquals(window.screen.configuration.devicePixelRatio, 2.5); + expectEquals(window.screen.configuration.systemGestureInsets.bottom, 0.0); _updateScreenMetrics( 0, // window id @@ -520,11 +501,11 @@ void main() { 0.0, // system gesture inset left ); - expect(window.screen.configuration.viewInsets.bottom, 400.0); - expect(window.screen.configuration.viewPadding.bottom, 40.0); - expect(window.screen.configuration.padding.bottom, 0.0); - expect(window.screen.configuration.devicePixelRatio, 2.5); - expect(window.screen.configuration.systemGestureInsets.bottom, 44.0); + expectEquals(window.screen.configuration.viewInsets.bottom, 400.0); + expectEquals(window.screen.configuration.viewPadding.bottom, 40.0); + expectEquals(window.screen.configuration.padding.bottom, 0.0); + expectEquals(window.screen.configuration.devicePixelRatio, 2.5); + expectEquals(window.screen.configuration.systemGestureInsets.bottom, 44.0); }); }); } From 25634c6e90e97befec37deeddd6292aa74d4bcc5 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 22 Jun 2020 18:01:28 -0700 Subject: [PATCH 12/14] Merge Changes --- lib/ui/platform_dispatcher.dart | 35 ++++-- lib/ui/window.dart | 106 ++++++++---------- lib/ui/window/platform_configuration.cc | 58 +++++----- lib/ui/window/platform_configuration.h | 10 +- .../lib/src/engine/platform_dispatcher.dart | 23 ++-- lib/web_ui/lib/src/engine/window.dart | 22 +--- 6 files changed, 117 insertions(+), 137 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index bfa6f05cc35d2..1a4f94cf3676b 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -593,19 +593,34 @@ class PlatformDispatcher { _invoke(onLocaleChanged, _onLocaleChangedZone); } - - /// The locale that the platform's native locale resolution system resolves - /// to. + /// Performs the platform-native locale resolution. /// - /// This value may differ between platforms and is meant to allow Flutter's - /// locale resolution algorithms access to a locale that is consistent with - /// other apps on the device. Using this property is optional. + /// Each platform may return different results. /// - /// This value may be used in a custom [localeListResolutionCallback] or used - /// directly in order to arrive at the most appropriate locale for the app. + /// If the platform fails to resolve a locale, then this will return null. /// - /// See [locales], which is the list of locales the user/device prefers. - Locale? get platformResolvedLocale => configuration.platformResolvedLocale; + /// This method returns synchronously and is a direct call to + /// platform specific APIs without invoking method channels. + Locale? computePlatformResolvedLocale(List supportedLocales) { + final List supportedLocalesData = []; + for (Locale locale in supportedLocales) { + supportedLocalesData.add(locale.languageCode); + supportedLocalesData.add(locale.countryCode); + supportedLocalesData.add(locale.scriptCode); + } + + final List result = _computePlatformResolvedLocale(supportedLocalesData); + + if (result.isNotEmpty) { + return Locale.fromSubtags( + languageCode: result[0], + countryCode: result[1] == '' ? null : result[1], + scriptCode: result[2] == '' ? null : result[2]); + } + return null; + } + List _computePlatformResolvedLocale(List supportedLocalesData) native 'Window_computePlatformResolvedLocale'; + /// A callback that is invoked whenever [locale] changes value. /// diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 7db3d69853813..0312270122ffb 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -74,8 +74,8 @@ enum FramePhase { /// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using /// [PlatformDispatcher.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If -/// [SchedulerBinding] is unavailable, then see [PlatformDispatcher.onReportTimings] -/// for how to get this. +/// [SchedulerBinding] is unavailable, then see +/// [PlatformDispatcher.onReportTimings] for how to get this. /// /// The metrics in debug mode (`flutter run` without any flags) may be very /// different from those in profile and release modes due to the debug overhead. @@ -88,7 +88,8 @@ class FrameTiming { /// [FramePhase.values]. /// /// This constructor is usually only called by the Flutter engine, or a test. - /// To get the [FrameTiming] of your app, see [PlatformDispatcher.onReportTimings]. + /// To get the [FrameTiming] of your app, see + /// [PlatformDispatcher.onReportTimings]. FrameTiming(List timestamps) : assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps; @@ -101,9 +102,10 @@ class FrameTiming { /// The duration to build the frame on the UI thread. /// - /// The build starts approximately when [PlatformDispatcher.onBeginFrame] is called. The - /// [Duration] in the [PlatformDispatcher.onBeginFrame] callback is exactly the - /// `Duration(microseconds: timestampInMicroseconds(FramePhase.buildStart))`. + /// The build starts approximately when [PlatformDispatcher.onBeginFrame] is + /// called. The [Duration] in the [PlatformDispatcher.onBeginFrame] callback + /// is exactly the `Duration(microseconds: + /// timestampInMicroseconds(FramePhase.buildStart))`. /// /// The build finishes when [FlutterView.render] is called. /// @@ -674,9 +676,8 @@ abstract class FlutterView { /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [viewInsets], - /// [viewPadding], and [padding] are described in - /// more detail in the documentation for [FlutterView]. + /// The relationship between this [viewInsets], [viewPadding], and [padding] + /// are described in more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -693,16 +694,14 @@ abstract class FlutterView { /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// - /// Unlike [padding], this value does not change relative to - /// [viewInsets]. For example, on an iPhone X, it will not - /// change in response to the soft keyboard being visible or hidden, whereas - /// [padding] will. + /// Unlike [padding], this value does not change relative to [viewInsets]. For + /// example, on an iPhone X, it will not change in response to the soft + /// keyboard being visible or hidden, whereas [padding] will. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [viewInsets], - /// [viewPadding], and [padding] are described in - /// more detail in the documentation for [FlutterView]. + /// The relationship between this [viewInsets], [viewPadding], and [padding] + /// are described in more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -756,35 +755,6 @@ abstract class FlutterView { /// * [Scaffold], which automatically applies the padding in material design /// applications. WindowPadding get padding => viewConfiguration.padding; - - /// Updates the view's rendering on the GPU with the newly provided - /// [Scene]. - /// - /// This function must be called within the scope of the - /// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame] - /// callbacks being invoked. If this function is called a second time during a - /// single [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame] - /// callback sequence or called outside the scope of those callbacks, the call - /// will be ignored. - /// - /// To record graphical operations, first create a [PictureRecorder], then - /// construct a [Canvas], passing that [PictureRecorder] to its constructor. - /// After issuing all the graphical operations, call the - /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain - /// the final [Picture] that represents the issued graphical operations. - /// - /// Next, create a [SceneBuilder], and add the [Picture] to it using - /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can - /// then obtain a [Scene] object, which you can display to the user via this - /// [render] function. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. - void render(Scene scene) => platformDispatcher.render(scene, this); } /// A top-level platform window displaying a Flutter layer tree drawn from a @@ -896,24 +866,8 @@ class SingletonFlutterWindow extends FlutterWindow { /// This method returns synchronously and is a direct call to /// platform specific APIs without invoking method channels. Locale? computePlatformResolvedLocale(List supportedLocales) { - final List supportedLocalesData = []; - for (Locale locale in supportedLocales) { - supportedLocalesData.add(locale.languageCode); - supportedLocalesData.add(locale.countryCode); - supportedLocalesData.add(locale.scriptCode); - } - - final List result = _computePlatformResolvedLocale(supportedLocalesData); - - if (result.isNotEmpty) { - return Locale.fromSubtags( - languageCode: result[0], - countryCode: result[1] == '' ? null : result[1], - scriptCode: result[2] == '' ? null : result[2]); - } - return null; + platformDispatcher.computePlatformResolvedLocale(supportedLocales); } - List _computePlatformResolvedLocale(List supportedLocalesData) native 'Window_computePlatformResolvedLocale'; /// A callback that is invoked whenever [locale] changes value. /// @@ -1149,6 +1103,34 @@ class SingletonFlutterWindow extends FlutterWindow { /// scheduling of frames. void scheduleFrame() => platformDispatcher.scheduleFrame(); + /// Updates the view's rendering on the GPU with the newly provided [Scene]. + /// + /// This function must be called within the scope of the + /// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame] + /// callbacks being invoked. If this function is called a second time during a + /// single [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame] + /// callback sequence or called outside the scope of those callbacks, the call + /// will be ignored. + /// + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. + /// + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + void render(Scene scene) => platformDispatcher.render(scene, this); + /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. /// diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 7c2022034a35b..2e868c7ad9081 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -12,11 +12,11 @@ #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/dart_microtask_queue.h" #include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/typed_data/dart_byte_data.h" namespace flutter { - namespace { void InitialRouteName(Dart_NativeArguments args) { @@ -38,7 +38,6 @@ void Render(Dart_NativeArguments args) { Dart_Handle exception = nullptr; Scene* scene = tonic::DartConverter::FromArguments(args, 1, exception); - // The view argument is currently ignored. if (exception) { Dart_ThrowException(exception); return; @@ -180,25 +179,12 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { persistent_isolate_data->GetSize())); } -} // namespace - Dart_Handle ToByteData(const std::vector& buffer) { - Dart_Handle data_handle = - Dart_NewTypedData(Dart_TypedData_kByteData, buffer.size()); - if (Dart_IsError(data_handle)) - return data_handle; - - Dart_TypedData_Type type; - void* data = nullptr; - intptr_t num_bytes = 0; - FML_CHECK(!Dart_IsError( - Dart_TypedDataAcquireData(data_handle, &type, &data, &num_bytes))); - - memcpy(data, buffer.data(), num_bytes); - Dart_TypedDataReleaseData(data_handle); - return data_handle; + return tonic::DartByteData::Create(buffer.data(), buffer.size()); } +} // namespace + WindowClient::~WindowClient() {} PlatformConfiguration::PlatformConfiguration(WindowClient* client) @@ -234,19 +220,6 @@ void PlatformConfiguration::UpdateLocales( })); } -void PlatformConfiguration::UpdatePlatformResolvedLocale( - const std::vector& locale) { - std::shared_ptr dart_state = library_.dart_state().lock(); - if (!dart_state) - return; - tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_updatePlatformResolvedLocale", - { - tonic::ToDart>(locale), - })); -} - void PlatformConfiguration::UpdateUserSettingsData(const std::string& data) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) @@ -426,6 +399,27 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } +Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { + std::vector supportedLocales = + tonic::DartConverter>::FromDart( + supportedLocalesHandle); + + std::vector results = + *UIDartState::Current() + ->window() + ->client() + ->ComputePlatformResolvedLocale(supportedLocales); + + return tonic::DartConverter>::ToDart(results); +} + +static void _ComputePlatformResolvedLocale(Dart_NativeArguments args) { + UIDartState::ThrowIfUIOperationsProhibited(); + Dart_Handle result = + ComputePlatformResolvedLocale(Dart_GetNativeArgument(args, 1)); + Dart_SetReturnValue(args, result); +} + void PlatformConfiguration::RegisterNatives( tonic::DartLibraryNatives* natives) { natives->Register({ @@ -445,6 +439,8 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_getPersistentIsolateData", GetPersistentIsolateData, 1, true}, + {"PlatformConfiguration_computePlatformResolvedLocale", _ComputePlatformResolvedLocale, + 2, true}, }); } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index d738003d4a239..347a29251a70f 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -12,6 +12,7 @@ #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/semantics/semantics_update.h" +#include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/screen.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -37,8 +38,6 @@ class FontCollection; class PlatformMessage; class Scene; -Dart_Handle ToByteData(const std::vector& buffer); - // Must match the AccessibilityFeatureFlag enum in framework. enum class AccessibilityFeatureFlag : int32_t { kAccessibleNavigation = 1 << 0, @@ -63,6 +62,9 @@ class WindowClient { int64_t isolate_port) = 0; virtual void SetNeedsReportTimings(bool value) = 0; virtual std::shared_ptr GetPersistentIsolateData() = 0; + virtual std::unique_ptr> + ComputePlatformResolvedLocale( + const std::vector& supported_locale_data) = 0; protected: virtual ~WindowClient(); @@ -78,7 +80,6 @@ class PlatformConfiguration final { void DidCreateIsolate(); void UpdateLocales(const std::vector& locales); - void UpdatePlatformResolvedLocale(const std::vector& locale); void UpdateUserSettingsData(const std::string& data); void UpdateLifecycleState(const std::string& data); void UpdateSemanticsEnabled(bool enabled); @@ -101,9 +102,10 @@ class PlatformConfiguration final { Screen* get_screen(int screen_id) { return screens_[screen_id].get(); } private: + WindowClient* client_; tonic::DartPersistentValue library_; ViewportMetrics viewport_metrics_; - WindowClient* client_; + std::unordered_map> windows_; std::unordered_map> screens_; diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 04a33d73034fe..2850c67f63b84 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -5,12 +5,15 @@ // @dart = 2.6 part of engine; +/// When set to true, all platform messages will be printed to the console. +const bool/*!*/ _debugPrintPlatformMessages = false; + /// Requests that the browser schedule a frame. /// /// This may be overridden in tests, for example, to pump fake frames. ui.VoidCallback/*?*/ scheduleFrameCallback; -/// Platform event dispatcher. +/// The Web implementation of [ui.PlatformDispatcher]. /// /// This is the central entry point for platform messages and configuration /// events from the platform. @@ -225,14 +228,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { _invoke1>(_onReportTimings, _onReportTimingsZone, timings); } - @override - void sendPlatformMessage( - String/*!*/ name, - ByteData/*?*/ data, - ui.PlatformMessageResponseCallback/*?*/ callback, - ) { - _sendPlatformMessage(name, data, _zonedPlatformMessageResponseCallback(callback)); - } @override ui.PlatformMessageCallback/*?*/ get onPlatformMessage => _onPlatformMessage; @@ -256,6 +251,16 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { ); } + @override + void sendPlatformMessage( + String/*!*/ name, + ByteData/*?*/ data, + ui.PlatformMessageResponseCallback/*?*/ callback, + ) { + _sendPlatformMessage( + name, data, _zonedPlatformMessageResponseCallback(callback)); + } + /// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback/*?*/ callback) { diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 3fec62fe3ef92..c0f980cec98bd 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -5,9 +5,6 @@ // @dart = 2.6 part of engine; -/// When set to true, all platform messages will be printed to the console. -const bool/*!*/ _debugPrintPlatformMessages = false; - /// The Web implementation of [ui.FlutterWindow]. class EngineFlutterWindow extends ui.FlutterWindow { EngineFlutterWindow({Object windowId, this.platformDispatcher}) @@ -140,7 +137,7 @@ class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { height = html.window.innerHeight * devicePixelRatio; width = html.window.innerWidth * devicePixelRatio; } - // First confirm both heught and width is effected. + // First confirm both height and width are affected. if (_physicalSize.height != height && _physicalSize.width != width) { // If prior to rotation height is bigger than width it should be the // opposite after the rotation and vice versa. @@ -164,23 +161,6 @@ class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { ui.Size webOnlyDebugPhysicalSizeOverride; } -/// A type of [FlutterView] that can be hosted inside of a [FlutterWindow]. -class EngineFlutterWindowView extends ui.FlutterWindowView { - EngineFlutterWindowView._({Object viewId, this.platformDispatcher}) - : _viewId = viewId; - - final Object _viewId; - - final ui.PlatformDispatcher platformDispatcher; - - @override - ui.ViewConfiguration get viewConfiguration { - final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; - assert(engineDispatcher._windowConfigurations.containsKey(_viewId)); - return engineDispatcher._windowConfigurations[_viewId]; - } -} - /// The window singleton. /// /// `dart:ui` window delegates to this value. However, this value has a wider From a235ea6d88a7b77dd23a190cee348e3e83e0246a Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 23 Jun 2020 13:10:41 -0700 Subject: [PATCH 13/14] Fix bad merge, and other issues --- lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/platform_configuration.h | 2 - .../platform_configuration_unittests.cc | 5 + .../lib/src/engine/platform_dispatcher.dart | 10 +- lib/web_ui/lib/src/engine/screen.dart | 5 +- lib/web_ui/lib/src/engine/window.dart | 95 ++++++++- .../lib/src/ui/platform_dispatcher.dart | 196 +++++++++--------- lib/web_ui/lib/src/ui/screen.dart | 10 +- lib/web_ui/lib/src/ui/window.dart | 57 ++--- 9 files changed, 237 insertions(+), 145 deletions(-) diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 2e868c7ad9081..28547e58bf13c 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -406,7 +406,7 @@ Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { std::vector results = *UIDartState::Current() - ->window() + ->platform_configuration() ->client() ->ComputePlatformResolvedLocale(supportedLocales); diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 347a29251a70f..c8f7c9283f842 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -48,8 +48,6 @@ enum class AccessibilityFeatureFlag : int32_t { kHighContrast = 1 << 5, }; -Dart_Handle ToByteData(const std::vector& buffer); - class WindowClient { public: virtual std::string InitialRouteName() = 0; diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 4e3e5e4798dac..74a4e82311eef 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -37,6 +37,11 @@ class DummyWindowClient : public WindowClient { virtual std::shared_ptr GetPersistentIsolateData() { return isolate_data_; } + virtual std::unique_ptr> + ComputePlatformResolvedLocale( + const std::vector& supported_locale_data) { + return nullptr; + }; private: FontCollection font_collection_; diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 2850c67f63b84..8e63ccbe4e7dc 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -374,7 +374,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { if (experimentalUseSkia) { rasterizer.viewEmbedder.handlePlatformViewCall(data, callback); } else { - handlePlatformViewCall(data, callback); + ui.handlePlatformViewCall(data, callback); } return; @@ -557,7 +557,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// in order to arrive at the most appropriate locale for the app. /// /// See [locales], which is the list of locales the user/device prefers. - ui.Locale/*!*/ get platformResolvedLocale => configuration.platformResolvedLocale; + ui.Locale/*?*/ computePlatformResolvedLocale(List supportedLocales) { + // TODO(garyq): Implement on web. + return null; + } /// A callback that is invoked whenever [locale] changes value. /// @@ -627,8 +630,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. - String/*?*/ get initialLifecycleState => _initialLifecycleState; - String/*?*/ _initialLifecycleState; + String/*?*/ get initialLifecycleState => 'AppLifecycleState.resumed'; /// The system-reported text scale. /// diff --git a/lib/web_ui/lib/src/engine/screen.dart b/lib/web_ui/lib/src/engine/screen.dart index af12843ed1e42..f45d6640c70eb 100644 --- a/lib/web_ui/lib/src/engine/screen.dart +++ b/lib/web_ui/lib/src/engine/screen.dart @@ -7,10 +7,13 @@ part of engine; /// A class representing the screen that application windows are displayed on. class EngineScreen extends ui.Screen { - EngineScreen({Object screenId, ui.PlatformDispatcher platformDispatcher}) + const EngineScreen(Object screenId, ui.PlatformDispatcher/*?*/ platformDispatcher) : _screenId = screenId, _platformDispatcher = platformDispatcher; + /// The value to use to indicate an invalid value for a [Screen] object. + static const ui.Screen invalid = EngineScreen(-1, null); + /// The opaque ID for this screen. final Object _screenId; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index c0f980cec98bd..26e5256fce82f 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -26,7 +26,7 @@ class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { EngineSingletonFlutterWindow({Object windowId, this.platformDispatcher}) : _windowId = windowId { final EnginePlatformDispatcher engineDispatcher = platformDispatcher as EnginePlatformDispatcher; - final ui.Screen newScreen = EngineScreen(screenId: 0, platformDispatcher: platformDispatcher); + final ui.Screen newScreen = EngineScreen(0, platformDispatcher); engineDispatcher._screens[0] = newScreen; engineDispatcher._screenConfigurations[0] = ui.ScreenConfiguration(); engineDispatcher._windows[windowId] = this; @@ -159,6 +159,99 @@ class EngineSingletonFlutterWindow extends ui.SingletonFlutterWindow { /// Overrides the value of [physicalSize] in tests. ui.Size webOnlyDebugPhysicalSizeOverride; + + ui.VoidCallback/*?*/ get onMetricsChanged => platformDispatcher.onMetricsChanged; + set onMetricsChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onMetricsChanged = callback; + } + + ui.Locale get locale => platformDispatcher.locale; + List get locales => platformDispatcher.locales; + + ui.Locale/*?*/ computePlatformResolvedLocale(List supportedLocales) { + // TODO(garyq): Implement on web. + return null; + } + + ui.VoidCallback/*?*/ get onLocaleChanged => platformDispatcher.onLocaleChanged; + set onLocaleChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onLocaleChanged = callback; + } + + double get textScaleFactor => platformDispatcher.textScaleFactor; + bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; + + ui.VoidCallback/*?*/ get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; + set onTextScaleFactorChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onTextScaleFactorChanged = callback; + } + + ui.Brightness get platformBrightness => platformDispatcher.platformBrightness; + + ui.VoidCallback/*?*/ get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onPlatformBrightnessChanged = callback; + } + + ui.FrameCallback/*?*/ get onBeginFrame => platformDispatcher.onBeginFrame; + set onBeginFrame(ui.FrameCallback/*?*/ callback) { + platformDispatcher.onBeginFrame = callback; + } + + ui.TimingsCallback/*?*/ get onReportTimings => platformDispatcher.onReportTimings; + set onReportTimings(ui.TimingsCallback/*?*/ callback) { + platformDispatcher.onReportTimings = callback; + } + + ui.VoidCallback/*?*/ get onDrawFrame => platformDispatcher.onDrawFrame; + set onDrawFrame(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onDrawFrame = callback; + } + + ui.PointerDataPacketCallback/*?*/ get onPointerDataPacket => platformDispatcher.onPointerDataPacket; + set onPointerDataPacket(ui.PointerDataPacketCallback/*?*/ callback) { + platformDispatcher.onPointerDataPacket = callback; + } + + String get defaultRouteName => platformDispatcher.initialRouteName; + + bool get semanticsEnabled => EngineSemanticsOwner.instance.semanticsEnabled; + + ui.VoidCallback/*?*/ get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onSemanticsEnabledChanged = callback; + } + + ui.SemanticsActionCallback/*?*/ get onSemanticsAction => platformDispatcher.onSemanticsAction; + set onSemanticsAction(ui.SemanticsActionCallback/*?*/ callback) { + platformDispatcher.onSemanticsAction = callback; + } + + ui.VoidCallback/*?*/ get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(ui.VoidCallback/*?*/ callback) { + platformDispatcher.onAccessibilityFeaturesChanged = callback; + } + + ui.PlatformMessageCallback/*?*/ get onPlatformMessage => platformDispatcher.onPlatformMessage; + set onPlatformMessage(ui.PlatformMessageCallback/*?*/ callback) { + platformDispatcher.onPlatformMessage = callback; + } + + void sendPlatformMessage( + String name, + ByteData/*?*/ data, + ui.PlatformMessageResponseCallback/*?*/ callback, + ) { + platformDispatcher.sendPlatformMessage(name, data, callback); + } + + void updateSemantics(ui.SemanticsUpdate update) => platformDispatcher.updateSemantics(update); + + ui.AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; + + void render(ui.Scene scene) => platformDispatcher.render(scene, this); + + String get initialLifecycleState => 'AppLifecycleState.resumed'; } /// The window singleton. diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index d6ed097f15616..d81446bdf8bdc 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -2,93 +2,93 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +// @dart = 2.9 part of ui; typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); abstract class PlatformDispatcher { - static PlatformDispatcher/*!*/ get instance => engine.EnginePlatformDispatcher.instance; + static PlatformDispatcher get instance => engine.EnginePlatformDispatcher.instance; - PlatformConfiguration/*!*/ get configuration; - VoidCallback/*?*/ get onPlatformConfigurationChanged; - set onPlatformConfigurationChanged(VoidCallback/*?*/ callback); + PlatformConfiguration get configuration; + VoidCallback? get onPlatformConfigurationChanged; + set onPlatformConfigurationChanged(VoidCallback? callback); - Iterable/*!*/ get screens; + Iterable get screens; - Iterable/*!*/ get views; + Iterable get views; - VoidCallback/*?*/ get onMetricsChanged; - set onMetricsChanged(VoidCallback/*?*/ callback); + VoidCallback? get onMetricsChanged; + set onMetricsChanged(VoidCallback? callback); - FrameCallback/*?*/ get onBeginFrame; - set onBeginFrame(FrameCallback/*?*/ callback); + FrameCallback? get onBeginFrame; + set onBeginFrame(FrameCallback? callback); - VoidCallback/*?*/ get onDrawFrame; - set onDrawFrame(VoidCallback/*?*/ callback); + VoidCallback? get onDrawFrame; + set onDrawFrame(VoidCallback? callback); - PointerDataPacketCallback/*?*/ get onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback/*?*/ callback); + PointerDataPacketCallback? get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback? callback); - TimingsCallback/*?*/ get onReportTimings; - set onReportTimings(TimingsCallback/*?*/ callback); + TimingsCallback? get onReportTimings; + set onReportTimings(TimingsCallback? callback); void sendPlatformMessage( - String/*!*/ name, - ByteData/*?*/ data, - PlatformMessageResponseCallback/*?*/ callback, + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, ); - PlatformMessageCallback/*?*/ get onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback/*?*/ callback); + PlatformMessageCallback? get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback? callback); void scheduleFrame(); - void render(Scene/*!*/ scene, [FlutterView/*!*/ view]); + void render(Scene scene, [FlutterView view]); - AccessibilityFeatures/*!*/ get accessibilityFeatures; + AccessibilityFeatures get accessibilityFeatures; - VoidCallback/*?*/ get onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback/*?*/ callback); + VoidCallback? get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback? callback); - void updateSemantics(SemanticsUpdate/*!*/ update); - - Locale/*!*/ get locale; + void updateSemantics(SemanticsUpdate update) { + engine.EngineSemanticsOwner.instance.updateSemantics(update); + } - List/*!*/ get locales => configuration.locales; + Locale get locale; - Locale/*!*/ get platformResolvedLocale => configuration.platformResolvedLocale; + List get locales => configuration.locales; - VoidCallback/*?*/ get onLocaleChanged; - set onLocaleChanged(VoidCallback/*?*/ callback); + Locale? computePlatformResolvedLocale(List supportedLocales); - bool/*!*/ get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + VoidCallback? get onLocaleChanged; + set onLocaleChanged(VoidCallback? callback); - double/*!*/ get textScaleFactor => configuration.textScaleFactor; + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; - VoidCallback/*?*/ get onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback/*?*/ callback); + double get textScaleFactor => configuration.textScaleFactor; - Brightness/*!*/ get platformBrightness => configuration.platformBrightness; + VoidCallback? get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback? callback); - VoidCallback/*?*/ get onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback/*?*/ callback); + Brightness get platformBrightness => configuration.platformBrightness; - bool/*!*/ get semanticsEnabled => configuration.semanticsEnabled; + VoidCallback? get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback? callback); - VoidCallback/*?*/ get onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback/*?*/ callback); + bool get semanticsEnabled => configuration.semanticsEnabled; - SemanticsActionCallback/*?*/ get onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback/*?*/ callback); + VoidCallback? get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback? callback); - String/*!*/ get initialRouteName; + SemanticsActionCallback? get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback? callback); - void setIsolateDebugName(String/*!*/ name) {} + String get initialRouteName; - ByteData/*?*/ getPersistentIsolateData() => null; + void setIsolateDebugName(String name) {} - String/*?*/ get initialLifecycleState; + ByteData? getPersistentIsolateData() => null; } class PlatformConfiguration { @@ -109,14 +109,14 @@ class PlatformConfiguration { assert(locales != null); PlatformConfiguration copyWith({ - AccessibilityFeatures/*?*/ accessibilityFeatures, - bool/*?*/ alwaysUse24HourFormat, - bool/*?*/ semanticsEnabled, - Brightness/*?*/ platformBrightness, - double/*?*/ textScaleFactor, - List/*?*/ locales, - Locale/*?*/ platformResolvedLocale, - String/*?*/ initialRouteName, + AccessibilityFeatures? accessibilityFeatures, + bool? alwaysUse24HourFormat, + bool? semanticsEnabled, + Brightness? platformBrightness, + double? textScaleFactor, + List? locales, + Locale? platformResolvedLocale, + String? initialRouteName, }) { return PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, @@ -130,14 +130,14 @@ class PlatformConfiguration { ); } - final AccessibilityFeatures/*!*/ accessibilityFeatures; - final bool/*!*/ alwaysUse24HourFormat; - final bool/*!*/ semanticsEnabled; - final Brightness/*!*/ platformBrightness; - final double/*!*/ textScaleFactor; - final List/*!*/ locales; - final Locale/*?*/ platformResolvedLocale; - final String/*?*/ initialRouteName; + final AccessibilityFeatures accessibilityFeatures; + final bool alwaysUse24HourFormat; + final bool semanticsEnabled; + final Brightness platformBrightness; + final double textScaleFactor; + final List locales; + final Locale? platformResolvedLocale; + final String? initialRouteName; } class ScreenConfiguration { @@ -158,13 +158,13 @@ class ScreenConfiguration { assert(padding != null); ScreenConfiguration copyWith({ - String/*?*/ screenName, - Rect/*?*/ geometry, - double/*?*/ devicePixelRatio, - WindowPadding/*?*/ viewInsets, - WindowPadding/*?*/ viewPadding, - WindowPadding/*?*/ systemGestureInsets, - WindowPadding/*?*/ padding, + String? screenName, + Rect? geometry, + double? devicePixelRatio, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, }) { return ScreenConfiguration( screenName: screenName ?? this.screenName, @@ -177,18 +177,18 @@ class ScreenConfiguration { ); } - final String/*!*/ screenName; - final Rect/*!*/ geometry; - final double/*!*/ devicePixelRatio; - final WindowPadding/*!*/ viewInsets; - final WindowPadding/*!*/ viewPadding; - final WindowPadding/*!*/ systemGestureInsets; - final WindowPadding/*!*/ padding; + final String screenName; + final Rect geometry; + final double devicePixelRatio; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; } class ViewConfiguration { const ViewConfiguration({ - this.screen, + this.screen = Screen.invalid, this.window, this.geometry = Rect.zero, this.depth = double.maxFinite, @@ -207,15 +207,15 @@ class ViewConfiguration { assert(padding != null); ViewConfiguration copyWith({ - Screen/*?*/ screen, - FlutterWindow/*?*/ window, - Rect/*?*/ geometry, - double/*?*/ depth, - bool/*?*/ visible, - WindowPadding/*?*/ viewInsets, - WindowPadding/*?*/ viewPadding, - WindowPadding/*?*/ systemGestureInsets, - WindowPadding/*?*/ padding, + Screen? screen, + FlutterWindow? window, + Rect? geometry, + double? depth, + bool? visible, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, }) { return ViewConfiguration( screen: screen ?? this.screen, @@ -230,15 +230,15 @@ class ViewConfiguration { ); } - final Screen/*!*/ screen; - final FlutterWindow/*?*/ window; - final Rect/*!*/ geometry; - final double/*!*/ depth; - final bool/*!*/ visible; - final WindowPadding/*!*/ viewInsets; - final WindowPadding/*!*/ viewPadding; - final WindowPadding/*!*/ systemGestureInsets; - final WindowPadding/*!*/ padding; + final Screen screen; + final FlutterWindow? window; + final Rect geometry; + final double depth; + final bool visible; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; @override String toString() { diff --git a/lib/web_ui/lib/src/ui/screen.dart b/lib/web_ui/lib/src/ui/screen.dart index bb5c4fef0a178..2866d9da7ab52 100644 --- a/lib/web_ui/lib/src/ui/screen.dart +++ b/lib/web_ui/lib/src/ui/screen.dart @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +// @dart = 2.9 part of ui; /// A class representing the screen that application windows are displayed on. abstract class Screen { + /// A const constructor to allow subclasses to be const. + const Screen(); + + /// The value to use to indicate an invalid value for a [Screen] object. + static const Screen invalid = engine.EngineScreen.invalid; + /// The configuration of this screen. - ScreenConfiguration/*!*/ get configuration; + ScreenConfiguration get configuration; } diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index e203d3b12c2ec..f2aa81e31e69f 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -213,45 +213,36 @@ abstract class FlutterView { abstract class FlutterWindowView extends FlutterView { @override - ViewConfiguration/*!*/ get viewConfiguration; + ViewConfiguration get viewConfiguration; } abstract class FlutterWindow extends FlutterView { @override - ViewConfiguration/*!*/ get viewConfiguration; + ViewConfiguration get viewConfiguration; } abstract class SingletonFlutterWindow extends FlutterWindow { - VoidCallback/*?*/ get onMetricsChanged => platformDispatcher.onMetricsChanged; - set onMetricsChanged(VoidCallback/*?*/ callback) { - platformDispatcher.onMetricsChanged = callback; - } + VoidCallback? get onMetricsChanged; + set onMetricsChanged(VoidCallback? callback); - Locale get locale => platformDispatcher.locale; - List get locales => platformDispatcher.locales; + Locale get locale; + List get locales; - Locale? get platformResolvedLocale => platformDispatcher.platformResolvedLocale; + Locale? computePlatformResolvedLocale(List supportedLocales); - VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; - set onLocaleChanged(VoidCallback? callback) { - platformDispatcher.onLocaleChanged = callback; - } + VoidCallback? get onLocaleChanged; + set onLocaleChanged(VoidCallback? callback); - String get initialLifecycleState => platformDispatcher.initialLifecycleState; - double get textScaleFactor => platformDispatcher.textScaleFactor; - bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; + double get textScaleFactor; + bool get alwaysUse24HourFormat; - VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback? callback) { - platformDispatcher.onTextScaleFactorChanged = callback; - } + VoidCallback? get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback? callback); - Brightness get platformBrightness => platformDispatcher.platformBrightness; + Brightness get platformBrightness; - VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback? callback) { - platformDispatcher.onPlatformBrightnessChanged = callback; - } + VoidCallback? get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback? callback); FrameCallback? get onBeginFrame; set onBeginFrame(FrameCallback? callback); @@ -265,10 +256,9 @@ abstract class SingletonFlutterWindow extends FlutterWindow { PointerDataPacketCallback? get onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback); - String get defaultRouteName => platformDispatcher.initialRouteName; + String get defaultRouteName; - bool get semanticsEnabled => - engine.EngineSemanticsOwner.instance.semanticsEnabled; + bool get semanticsEnabled; VoidCallback? get onSemanticsEnabledChanged; set onSemanticsEnabledChanged(VoidCallback? callback); @@ -282,11 +272,7 @@ abstract class SingletonFlutterWindow extends FlutterWindow { PlatformMessageCallback? get onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback); - void updateSemantics(SemanticsUpdate update) { - engine.EngineSemanticsOwner.instance.updateSemantics(update); - } - - void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); + void updateSemantics(SemanticsUpdate update); void sendPlatformMessage( String name, @@ -294,12 +280,11 @@ abstract class SingletonFlutterWindow extends FlutterWindow { PlatformMessageResponseCallback? callback, ); - AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; - AccessibilityFeatures _accessibilityFeatures = AccessibilityFeatures._(0); + AccessibilityFeatures get accessibilityFeatures; void render(Scene scene); - String get initialLifecycleState => 'AppLifecycleState.resumed'; + String get initialLifecycleState; void setIsolateDebugName(String name) {} From f670d0782fd87d947b9d1708573fbc13ecae1549 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 23 Jun 2020 17:10:55 -0700 Subject: [PATCH 14/14] Wire screen and window id through the embedding. --- lib/ui/window/platform_configuration.cc | 8 +- lib/ui/window/screen.cc | 5 +- lib/ui/window/screen.h | 5 +- lib/ui/window/screen_metrics.cc | 18 ++- lib/ui/window/screen_metrics.h | 10 +- lib/ui/window/viewport_metrics.cc | 25 +++- lib/ui/window/viewport_metrics.h | 15 ++- lib/ui/window/window.cc | 9 +- lib/ui/window/window.h | 8 +- shell/common/shell_test.cc | 4 +- shell/common/shell_unittests.cc | 12 +- .../embedding/android/FlutterView.java | 22 ++-- .../flutter/embedding/engine/FlutterJNI.java | 86 +++++++++++- .../engine/renderer/FlutterRenderer.java | 118 ++++++++++++++++- .../android/io/flutter/view/FlutterView.java | 123 ++++++++++++++---- .../android/platform_view_android_jni_impl.cc | 65 ++++++++- .../macos/framework/Source/FlutterEngine.mm | 10 +- shell/platform/embedder/embedder.h | 9 ++ 18 files changed, 466 insertions(+), 86 deletions(-) diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 28547e58bf13c..f3ddc9d8523da 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -9,6 +9,7 @@ #include "flutter/lib/ui/window/platform_message_response_dart.h" #include "flutter/lib/ui/window/screen.h" #include "flutter/lib/ui/window/window.h" +#include "lib/ui/window/screen_metrics.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_library_natives.h" @@ -189,9 +190,10 @@ WindowClient::~WindowClient() {} PlatformConfiguration::PlatformConfiguration(WindowClient* client) : client_(client) { - // For now, there's only one window and one screen. - screens_.insert(std::make_pair(0, std::unique_ptr(new Screen(0)))); - windows_.insert(std::make_pair(0, std::unique_ptr(new Window(0, 0)))); + // For now, there's only one window and one screen. Their configurations will + // be updated at a later time. + screens_.insert(std::make_pair(0, std::unique_ptr(new Screen(ScreenMetrics(0, 1.0, 0.0, 0.0))))); + windows_.insert(std::make_pair(0, std::unique_ptr(new Window(ViewportMetrics(0, 0, 0.0, 0.0))))); } PlatformConfiguration::~PlatformConfiguration() {} diff --git a/lib/ui/window/screen.cc b/lib/ui/window/screen.cc index 675bb6a2ceedd..cee21ad3e9cf9 100644 --- a/lib/ui/window/screen.cc +++ b/lib/ui/window/screen.cc @@ -4,12 +4,13 @@ #include "flutter/lib/ui/window/screen.h" +#include "lib/ui/window/screen_metrics.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/logging/dart_invoke.h" namespace flutter { -Screen::Screen(int screen_id) : screen_id_(screen_id) {} +Screen::Screen(ScreenMetrics metrics) : screen_metrics_(metrics) {} Screen::~Screen() {} @@ -30,7 +31,7 @@ void Screen::UpdateScreenMetrics(const ScreenMetrics& metrics) { tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateScreenMetrics", { - tonic::ToDart(screen_id_), + tonic::ToDart(metrics.screen_id), tonic::ToDart(metrics.screen_name), tonic::ToDart(metrics.physical_left), tonic::ToDart(metrics.physical_top), diff --git a/lib/ui/window/screen.h b/lib/ui/window/screen.h index da7f1cae7261a..8a08161b240ae 100644 --- a/lib/ui/window/screen.h +++ b/lib/ui/window/screen.h @@ -16,7 +16,7 @@ namespace flutter { class Screen final { public: - explicit Screen(int id); + explicit Screen(ScreenMetrics metrics); ~Screen(); @@ -24,13 +24,12 @@ class Screen final { const ScreenMetrics& screen_metrics() { return screen_metrics_; } - int screen_id() { return screen_id_; } + int screen_id() { return screen_metrics_.screen_id; } void UpdateScreenMetrics(const ScreenMetrics& metrics); private: tonic::DartPersistentValue library_; - int screen_id_; ScreenMetrics screen_metrics_; }; diff --git a/lib/ui/window/screen_metrics.cc b/lib/ui/window/screen_metrics.cc index ac7f309c1d873..1e09729ba4328 100644 --- a/lib/ui/window/screen_metrics.cc +++ b/lib/ui/window/screen_metrics.cc @@ -8,7 +8,8 @@ namespace flutter { -ScreenMetrics::ScreenMetrics(std::string p_screen_name, +ScreenMetrics::ScreenMetrics(int64_t p_screen_id, + std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -26,7 +27,8 @@ ScreenMetrics::ScreenMetrics(std::string p_screen_name, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, double p_physical_system_gesture_inset_left) - : screen_name(p_screen_name), + : screen_id(p_screen_id), + screen_name(p_screen_name), device_pixel_ratio(p_device_pixel_ratio), physical_left(p_physical_left), physical_top(p_physical_top), @@ -52,7 +54,8 @@ ScreenMetrics::ScreenMetrics(std::string p_screen_name, FML_DCHECK(physical_height >= 0); } -ScreenMetrics::ScreenMetrics(std::string p_screen_name, +ScreenMetrics::ScreenMetrics(int64_t p_screen_id, + std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -66,7 +69,8 @@ ScreenMetrics::ScreenMetrics(std::string p_screen_name, double p_physical_view_inset_right, double p_physical_view_inset_bottom, double p_physical_view_inset_left) - : screen_name(p_screen_name), + : screen_id(p_screen_id), + screen_name(p_screen_name), device_pixel_ratio(p_device_pixel_ratio), physical_left(p_physical_left), physical_top(p_physical_top), @@ -86,10 +90,12 @@ ScreenMetrics::ScreenMetrics(std::string p_screen_name, FML_DCHECK(physical_height >= 0); } -ScreenMetrics::ScreenMetrics(double p_device_pixel_ratio, +ScreenMetrics::ScreenMetrics(int64_t p_screen_id, + double p_device_pixel_ratio, double p_physical_width, double p_physical_height) - : device_pixel_ratio(p_device_pixel_ratio), + : screen_id(p_screen_id), + device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height) { // Ensure we don't have nonsensical dimensions. diff --git a/lib/ui/window/screen_metrics.h b/lib/ui/window/screen_metrics.h index cfc808b78d26c..1b3ea2fe78d51 100644 --- a/lib/ui/window/screen_metrics.h +++ b/lib/ui/window/screen_metrics.h @@ -16,7 +16,8 @@ struct ScreenMetrics { ScreenMetrics(const ScreenMetrics& other) = default; // Create a ScreenMetrics instance. - ScreenMetrics(std::string p_screen_name, + ScreenMetrics(int64_t screen_id, + std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -36,7 +37,8 @@ struct ScreenMetrics { double p_physical_system_gesture_inset_left); // Create a ScreenMetrics instance without system gesture insets. - ScreenMetrics(std::string p_screen_name, + ScreenMetrics(int64_t screen_id, + std::string p_screen_name, double p_device_pixel_ratio, double p_physical_left, double p_physical_top, @@ -51,10 +53,12 @@ struct ScreenMetrics { double p_physical_view_inset_bottom, double p_physical_view_inset_left); - ScreenMetrics(double p_device_pixel_ratio, + ScreenMetrics(int64_t screen_id, + double p_device_pixel_ratio, double p_physical_width, double p_physical_height); + int64_t screen_id = -1; std::string screen_name = ""; double device_pixel_ratio = 1.0; double physical_left = 0; diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index aa1c9059b4f87..325b5cb4bed31 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -8,7 +8,9 @@ namespace flutter { -ViewportMetrics::ViewportMetrics(double p_physical_left, +ViewportMetrics::ViewportMetrics(int64_t p_view_id, + int64_t p_screen_id, + double p_physical_left, double p_physical_top, double p_physical_width, double p_physical_height, @@ -24,7 +26,9 @@ ViewportMetrics::ViewportMetrics(double p_physical_left, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, double p_physical_system_gesture_inset_left) - : physical_left(p_physical_left), + : view_id(p_view_id), + screen_id(p_screen_id), + physical_left(p_physical_left), physical_top(p_physical_top), physical_width(p_physical_width), physical_height(p_physical_height), @@ -47,7 +51,9 @@ ViewportMetrics::ViewportMetrics(double p_physical_left, FML_DCHECK(physical_height >= 0); } -ViewportMetrics::ViewportMetrics(double p_physical_left, +ViewportMetrics::ViewportMetrics(int64_t p_view_id, + int64_t p_screen_id, + double p_physical_left, double p_physical_top, double p_physical_width, double p_physical_height, @@ -62,7 +68,9 @@ ViewportMetrics::ViewportMetrics(double p_physical_left, double p_physical_view_inset_right, double p_physical_view_inset_bottom, double p_physical_view_inset_left) - : physical_left(p_physical_left), + : view_id(p_view_id), + screen_id(p_screen_id), + physical_left(p_physical_left), physical_top(p_physical_top), physical_width(p_physical_width), physical_height(p_physical_height), @@ -82,9 +90,14 @@ ViewportMetrics::ViewportMetrics(double p_physical_left, FML_DCHECK(physical_height >= 0); } -ViewportMetrics::ViewportMetrics(double p_physical_width, +ViewportMetrics::ViewportMetrics(int64_t p_view_id, + int64_t p_screen_id, + double p_physical_width, double p_physical_height) - : physical_width(p_physical_width), physical_height(p_physical_height) { + : view_id(p_view_id), + screen_id(p_screen_id), + physical_width(p_physical_width), + physical_height(p_physical_height) { // Ensure we don't have nonsensical dimensions. FML_DCHECK(physical_width >= 0); FML_DCHECK(physical_height >= 0); diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 7cef610160326..a7e87dcaae362 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -20,7 +20,9 @@ struct ViewportMetrics { ViewportMetrics(const ViewportMetrics& other) = default; // Create a 2D ViewportMetrics instance. - ViewportMetrics(double p_physical_left, + ViewportMetrics(int64_t view_id, + int64_t screen_id, + double p_physical_left, double p_physical_top, double p_physical_width, double p_physical_height, @@ -38,7 +40,9 @@ struct ViewportMetrics { double p_physical_system_gesture_inset_left); // Create a ViewportMetrics instance that contains z information. - ViewportMetrics(double p_physical_left, + ViewportMetrics(int64_t view_id, + int64_t screen_id, + double p_physical_left, double p_physical_top, double p_physical_width, double p_physical_height, @@ -54,8 +58,13 @@ struct ViewportMetrics { double p_physical_view_inset_bottom, double p_physical_view_inset_left); - ViewportMetrics(double p_physical_width, double p_physical_height); + ViewportMetrics(int64_t view_id, + int64_t screen_id, + double p_physical_width, + double p_physical_height); + int64_t view_id = -1; + int64_t screen_id = -1; double physical_left = 0; double physical_top = 0; double physical_width = 0; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index b68837b44743f..61385688933d5 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -4,14 +4,15 @@ #include "flutter/lib/ui/window/window.h" +#include "lib/ui/window/viewport_metrics.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/logging/dart_invoke.h" namespace flutter { -Window::Window(int window_id, int screen_id) - : window_id_(window_id), screen_id_(screen_id) {} +Window::Window(ViewportMetrics metrics) + : viewport_metrics_(metrics) {} Window::~Window() {} @@ -32,8 +33,8 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateWindowMetrics", { - tonic::ToDart(window_id_), - tonic::ToDart(screen_id_), + tonic::ToDart(metrics.view_id), + tonic::ToDart(metrics.screen_id), tonic::ToDart(metrics.physical_top), tonic::ToDart(metrics.physical_left), tonic::ToDart(metrics.physical_width), diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index b84d2f07ac246..7e16391676405 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -18,13 +18,13 @@ namespace flutter { class Window final { public: - explicit Window(int window_id, int screen_id); + explicit Window(ViewportMetrics metrics); ~Window(); - int window_id() const { return window_id_; } + int window_id() const { return viewport_metrics_.view_id; } - int screen() const { return screen_id_; } + int screen() const { return viewport_metrics_.screen_id; } const ViewportMetrics& viewport_metrics() { return viewport_metrics_; } @@ -34,8 +34,6 @@ class Window final { private: tonic::DartPersistentValue library_; - int window_id_; - int screen_id_; ViewportMetrics viewport_metrics_; }; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 320c7f075c9c0..ad9055bc1cad8 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -126,8 +126,8 @@ void ShellTest::PumpOneFrame(Shell* shell, double width, double height, LayerTreeBuilder builder) { - PumpOneFrame(shell, flutter::ViewportMetrics(width, height), - flutter::ScreenMetrics(1.0, width, height), std::move(builder)); + PumpOneFrame(shell, flutter::ViewportMetrics(0, 0, width, height), + flutter::ScreenMetrics(0, 1.0, width, height), std::move(builder)); } void ShellTest::PumpOneFrame(Shell* shell, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 185a88f510cc6..56e0bb66b3864 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -677,8 +677,8 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), flutter::ViewportMetrics(0, 0), - flutter::ScreenMetrics(1.0, 0, 0)); + PumpOneFrame(shell.get(), flutter::ViewportMetrics(0, 0, 0.0, 0.0), + flutter::ScreenMetrics(0, 1.0, 0, 0)); fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); ASSERT_FALSE(result.ok()); @@ -797,7 +797,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - flutter::ViewportMetrics(400, 200)); + flutter::ViewportMetrics(0, 0, 400, 200)); }); PumpOneFrame(shell.get()); @@ -817,7 +817,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - flutter::ViewportMetrics(800, 400)); + flutter::ViewportMetrics(0, 0, 800, 400)); }); PumpOneFrame(shell.get()); @@ -836,7 +836,7 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - flutter::ViewportMetrics(400, 200)); + flutter::ViewportMetrics(0, 0, 400, 200)); }); PumpOneFrame(shell.get()); @@ -865,7 +865,7 @@ TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( - flutter::ViewportMetrics(400, 200)); + flutter::ViewportMetrics(0, 0, 400, 200)); }); PumpOneFrame(shell.get()); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 2fd22da61d4e1..22a7ecbb62eb1 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -103,9 +103,11 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC @Nullable private AndroidTouchProcessor androidTouchProcessor; @Nullable private AccessibilityBridge accessibilityBridge; - // Directly implemented View behavior that communicates with Flutter. + // Directly implemented View and screen behavior that communicates with Flutter. private final FlutterRenderer.ViewportMetrics viewportMetrics = new FlutterRenderer.ViewportMetrics(); + private final FlutterRenderer.ScreenMetrics screenMetrics = + new FlutterRenderer.ScreenMetrics(); private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener = new AccessibilityBridge.OnAccessibilityChangeListener() { @@ -415,7 +417,7 @@ protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) + height); viewportMetrics.width = width; viewportMetrics.height = height; - sendViewportMetricsToFlutter(); + sendMetricsToFlutter(); } // TODO(garyq): Add support for notch cutout API: https://github.com/flutter/flutter/issues/56592 @@ -568,7 +570,7 @@ public final WindowInsets onApplyWindowInsets(@NonNull WindowInsets insets) { + ", Bottom: " + viewportMetrics.viewInsetBottom); - sendViewportMetricsToFlutter(); + sendMetricsToFlutter(); return newInsets; } @@ -613,7 +615,7 @@ protected boolean fitSystemWindows(@NonNull Rect insets) { + ", Right: " + viewportMetrics.viewInsetRight); - sendViewportMetricsToFlutter(); + sendMetricsToFlutter(); return true; } else { return super.fitSystemWindows(insets); @@ -875,7 +877,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { // Push View and Context related information from Android to Flutter. sendUserSettingsToFlutter(); localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration()); - sendViewportMetricsToFlutter(); + sendMetricsToFlutter(); flutterEngine.getPlatformViewsController().attachToView(this); @@ -958,7 +960,7 @@ public FlutterEngine getAttachedFlutterEngine() { } /** - * Adds a {@link FlutterEngineAttachmentListener}, which is notifed whenever this {@code + * Adds a {@link FlutterEngineAttachmentListener}, which is notified whenever this {@code * FlutterView} attached to/detaches from a {@link FlutterEngine}. */ @VisibleForTesting @@ -1005,17 +1007,17 @@ public void removeFlutterEngineAttachmentListener( .send(); } - // TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI - private void sendViewportMetricsToFlutter() { + private void sendMetricsToFlutter() { if (!isAttachedToFlutterEngine()) { Log.w( TAG, - "Tried to send viewport metrics from Android to Flutter but this " + "Tried to send metrics from Android to Flutter but this " + "FlutterView was not attached to a FlutterEngine."); return; } - viewportMetrics.devicePixelRatio = getResources().getDisplayMetrics().density; + screenMetrics.devicePixelRatio = getResources().getDisplayMetrics().density; + flutterEngine.getRenderer().setScreenMetrics(screenMetrics); flutterEngine.getRenderer().setViewportMetrics(viewportMetrics); } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 5fb4e337c6ef5..9ddd22a7f1f58 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -355,7 +355,10 @@ public void onSurfaceDestroyed() { */ @UiThread public void setViewportMetrics( - float devicePixelRatio, + int viewId, + int screenId, + int physicalLeft, + int physicalTop, int physicalWidth, int physicalHeight, int physicalPaddingTop, @@ -374,7 +377,10 @@ public void setViewportMetrics( ensureAttachedToNative(); nativeSetViewportMetrics( nativePlatformViewId, - devicePixelRatio, + viewId, + screenId, + physicalLeft, + physicalTop, physicalWidth, physicalHeight, physicalPaddingTop, @@ -393,7 +399,83 @@ public void setViewportMetrics( private native void nativeSetViewportMetrics( long nativePlatformViewId, + long viewId, + long screenId, + int physicalLeft, + int physicalTop, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft, + int systemGestureInsetTop, + int systemGestureInsetRight, + int systemGestureInsetBottom, + int systemGestureInsetLeft); + + /** + * Call this method to notify Flutter of the current device viewport metrics that are applies to + * the Flutter UI that is being rendered. + * + *

This method should be invoked with initial values upon attaching to native. Then, it should + * be invoked any time those metrics change while {@code FlutterJNI} is attached to native. + */ + @UiThread + public void setScreenMetrics( + long screenId, + String screenName, + float devicePixelRatio, + int physicalLeft, + int physicalTop, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft, + int systemGestureInsetTop, + int systemGestureInsetRight, + int systemGestureInsetBottom, + int systemGestureInsetLeft) { + ensureRunningOnMainThread(); + ensureAttachedToNative(); + nativeSetScreenMetrics( + screenId, + screenName, + devicePixelRatio, + physicalLeft, + physicalTop, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft, + systemGestureInsetTop, + systemGestureInsetRight, + systemGestureInsetBottom, + systemGestureInsetLeft); + } + + private native void nativeSetScreenMetrics( + long screenId, + String screenName, float devicePixelRatio, + int physicalLeft, + int physicalTop, int physicalWidth, int physicalHeight, int physicalPaddingTop, diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 919971849fda4..de6a9b75c8d06 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -224,7 +224,16 @@ public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) { Log.v( TAG, "Setting viewport metrics\n" - + "Size: " + + "View ID: " + + viewportMetrics.viewId + + "Screen ID: " + + viewportMetrics.screenId + + "Geometry: " + + "(" + + viewportMetrics.left + + ", " + + viewportMetrics.top + + ") " + viewportMetrics.width + " x " + viewportMetrics.height @@ -256,8 +265,11 @@ public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) { + ", B: " + viewportMetrics.viewInsetBottom); - flutterJNI.setViewportMetrics( - viewportMetrics.devicePixelRatio, + flutterJNI.setViewportMetrics( + viewportMetrics.viewId, + viewportMetrics.screenId, + viewportMetrics.left, + viewportMetrics.top, viewportMetrics.width, viewportMetrics.height, viewportMetrics.paddingTop, @@ -274,6 +286,75 @@ public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) { viewportMetrics.systemGestureInsetLeft); } + public void setScreenMetrics(@NonNull ScreenMetrics screenMetrics) { + Log.v( + TAG, + "Setting viewport metrics\n" + + "Screen ID: " + + screenMetrics.screenId + + "Screen Name: " + + screenMetrics.screenName + + "Device Pixel Ratio: " + + screenMetrics.devicePixelRatio + + "Geometry: " + + "(" + + screenMetrics.left + + ", " + + screenMetrics.top + + ") " + + screenMetrics.width + + " x " + + screenMetrics.height + + "\n" + + "Padding - L: " + + screenMetrics.paddingLeft + + ", T: " + + screenMetrics.paddingTop + + ", R: " + + screenMetrics.paddingRight + + ", B: " + + screenMetrics.paddingBottom + + "\n" + + "Insets - L: " + + screenMetrics.viewInsetLeft + + ", T: " + + screenMetrics.viewInsetTop + + ", R: " + + screenMetrics.viewInsetRight + + ", B: " + + screenMetrics.viewInsetBottom + + "\n" + + "System Gesture Insets - L: " + + screenMetrics.systemGestureInsetLeft + + ", T: " + + screenMetrics.systemGestureInsetTop + + ", R: " + + screenMetrics.systemGestureInsetRight + + ", B: " + + screenMetrics.viewInsetBottom); + + flutterJNI.setScreenMetrics( + screenMetrics.screenId, + screenMetrics.screenName, + screenMetrics.devicePixelRatio, + screenMetrics.left, + screenMetrics.top, + screenMetrics.width, + screenMetrics.height, + screenMetrics.paddingTop, + screenMetrics.paddingRight, + screenMetrics.paddingBottom, + screenMetrics.paddingLeft, + screenMetrics.viewInsetTop, + screenMetrics.viewInsetRight, + screenMetrics.viewInsetBottom, + screenMetrics.viewInsetLeft, + screenMetrics.systemGestureInsetTop, + screenMetrics.systemGestureInsetRight, + screenMetrics.systemGestureInsetBottom, + screenMetrics.systemGestureInsetLeft); + } + // TODO(mattcarroll): describe the native behavior that this invokes // TODO(mattcarroll): determine if this is nullable or nonnull public Bitmap getBitmap() { @@ -328,7 +409,38 @@ public void dispatchSemanticsAction( * pixels, not logical pixels. */ public static final class ViewportMetrics { + public int viewId = 0; + public int screenId = 0; + public int left = 0; + public int top = 0; + public int width = 0; + public int height = 0; + public int paddingTop = 0; + public int paddingRight = 0; + public int paddingBottom = 0; + public int paddingLeft = 0; + public int viewInsetTop = 0; + public int viewInsetRight = 0; + public int viewInsetBottom = 0; + public int viewInsetLeft = 0; + public int systemGestureInsetTop = 0; + public int systemGestureInsetRight = 0; + public int systemGestureInsetBottom = 0; + public int systemGestureInsetLeft = 0; + } + + /** + * Mutable data structure that holds all screen metrics properties that Flutter cares about. + * + *

All distance measurements, e.g., width, height, padding, viewInsets, are measured in device + * pixels, not logical pixels. + */ + public static final class ScreenMetrics { + public int screenId = 0; + public String screenName = ""; public float devicePixelRatio = 1.0f; + public int left = 0; + public int top = 0; public int width = 0; public int height = 0; public int paddingTop = 0; diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index cd0446b95d446..2f83115feca03 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -94,7 +94,32 @@ public interface Provider { private static final String TAG = "FlutterView"; static final class ViewportMetrics { + int viewId = 0; + int screenId = 0; + int physicalLeft = 0; + int physicalTop = 0; + int physicalWidth = 0; + int physicalHeight = 0; + int physicalPaddingTop = 0; + int physicalPaddingRight = 0; + int physicalPaddingBottom = 0; + int physicalPaddingLeft = 0; + int physicalViewInsetTop = 0; + int physicalViewInsetRight = 0; + int physicalViewInsetBottom = 0; + int physicalViewInsetLeft = 0; + int systemGestureInsetTop = 0; + int systemGestureInsetRight = 0; + int systemGestureInsetBottom = 0; + int systemGestureInsetLeft = 0; + } + + static final class ScreenMetrics { + int screenId = 0; + String screenName = ""; float devicePixelRatio = 1.0f; + int physicalLeft = 0; + int physicalTop = 0; int physicalWidth = 0; int physicalHeight = 0; int physicalPaddingTop = 0; @@ -129,6 +154,7 @@ static final class ViewportMetrics { private AccessibilityBridge mAccessibilityNodeProvider; private final SurfaceHolder.Callback mSurfaceCallback; private final ViewportMetrics mMetrics; + private final ScreenMetrics mScreenMetrics; private final List mActivityLifecycleListeners; private final List mFirstFrameListeners; private final AtomicLong nextTextureId = new AtomicLong(0L); @@ -171,7 +197,8 @@ public FlutterView(Context context, AttributeSet attrs, FlutterNativeView native flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI()); mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled(); mMetrics = new ViewportMetrics(); - mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + mScreenMetrics = new ScreenMetrics(); + mScreenMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; setFocusable(true); setFocusableInTouchMode(true); @@ -413,7 +440,7 @@ protected void onConfigurationChanged(Configuration newConfig) { } float getDevicePixelRatio() { - return mMetrics.devicePixelRatio; + return mScreenMetrics.devicePixelRatio; } public FlutterNativeView detach() { @@ -508,7 +535,10 @@ public boolean onGenericMotionEvent(MotionEvent event) { protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { mMetrics.physicalWidth = width; mMetrics.physicalHeight = height; + mScreenMetrics.physicalWidth = width; + mScreenMetrics.physicalHeight = height; updateViewportMetrics(); + updateScreenMetrics(); super.onSizeChanged(width, height, oldWidth, oldHeight); } @@ -599,35 +629,44 @@ public final WindowInsets onApplyWindowInsets(WindowInsets insets) { } // The padding on top should be removed when the statusbar is hidden. - mMetrics.physicalPaddingTop = statusBarHidden ? 0 : insets.getSystemWindowInsetTop(); - mMetrics.physicalPaddingRight = + mScreenMetrics.physicalPaddingTop = statusBarHidden ? 0 : insets.getSystemWindowInsetTop(); + mScreenMetrics.physicalPaddingRight = zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH ? 0 : insets.getSystemWindowInsetRight(); - mMetrics.physicalPaddingBottom = 0; - mMetrics.physicalPaddingLeft = + mScreenMetrics.physicalPaddingBottom = 0; + mScreenMetrics.physicalPaddingLeft = zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH ? 0 : insets.getSystemWindowInsetLeft(); // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). - mMetrics.physicalViewInsetTop = 0; - mMetrics.physicalViewInsetRight = 0; + mScreenMetrics.physicalViewInsetTop = 0; + mScreenMetrics.physicalViewInsetRight = 0; // We perform hidden navbar and keyboard handling if the navbar is set to hidden. Otherwise, // the navbar padding should always be provided. - mMetrics.physicalViewInsetBottom = + mScreenMetrics.physicalViewInsetBottom = navigationBarHidden ? guessBottomKeyboardInset(insets) : insets.getSystemWindowInsetBottom(); - mMetrics.physicalViewInsetLeft = 0; + mScreenMetrics.physicalViewInsetLeft = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { Insets systemGestureInsets = insets.getSystemGestureInsets(); - mMetrics.systemGestureInsetTop = systemGestureInsets.top; - mMetrics.systemGestureInsetRight = systemGestureInsets.right; - mMetrics.systemGestureInsetBottom = systemGestureInsets.bottom; - mMetrics.systemGestureInsetLeft = systemGestureInsets.left; + mScreenMetrics.systemGestureInsetTop = systemGestureInsets.top; + mScreenMetrics.systemGestureInsetRight = systemGestureInsets.right; + mScreenMetrics.systemGestureInsetBottom = systemGestureInsets.bottom; + mScreenMetrics.systemGestureInsetLeft = systemGestureInsets.left; } + mMetrics.physicalPaddingTop = mScreenMetrics.physicalPaddingTop; + mMetrics.physicalPaddingRight = mScreenMetrics.physicalPaddingRight; + mMetrics.physicalPaddingBottom = mScreenMetrics.physicalPaddingBottom; + mMetrics.physicalPaddingLeft = mScreenMetrics.physicalPaddingLeft; + mMetrics.physicalViewInsetTop = mScreenMetrics.physicalViewInsetTop; + mMetrics.physicalViewInsetRight = mScreenMetrics.physicalViewInsetRight; + mMetrics.physicalViewInsetBottom = mScreenMetrics.physicalViewInsetBottom; + mMetrics.physicalViewInsetLeft = mScreenMetrics.physicalViewInsetLeft; + updateScreenMetrics(); updateViewportMetrics(); return super.onApplyWindowInsets(insets); } @@ -637,16 +676,25 @@ public final WindowInsets onApplyWindowInsets(WindowInsets insets) { protected boolean fitSystemWindows(Rect insets) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { // Status bar, left/right system insets partially obscure content (padding). - mMetrics.physicalPaddingTop = insets.top; - mMetrics.physicalPaddingRight = insets.right; - mMetrics.physicalPaddingBottom = 0; - mMetrics.physicalPaddingLeft = insets.left; + mScreenMetrics.physicalPaddingTop = insets.top; + mScreenMetrics.physicalPaddingRight = insets.right; + mScreenMetrics.physicalPaddingBottom = 0; + mScreenMetrics.physicalPaddingLeft = insets.left; // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). - mMetrics.physicalViewInsetTop = 0; - mMetrics.physicalViewInsetRight = 0; - mMetrics.physicalViewInsetBottom = insets.bottom; - mMetrics.physicalViewInsetLeft = 0; + mScreenMetrics.physicalViewInsetTop = 0; + mScreenMetrics.physicalViewInsetRight = 0; + mScreenMetrics.physicalViewInsetBottom = insets.bottom; + mScreenMetrics.physicalViewInsetLeft = 0; + mMetrics.physicalPaddingTop = mScreenMetrics.physicalPaddingTop; + mMetrics.physicalPaddingRight = mScreenMetrics.physicalPaddingRight; + mMetrics.physicalPaddingBottom = mScreenMetrics.physicalPaddingBottom; + mMetrics.physicalPaddingLeft = mScreenMetrics.physicalPaddingLeft; + mMetrics.physicalViewInsetTop = mScreenMetrics.physicalViewInsetTop; + mMetrics.physicalViewInsetRight = mScreenMetrics.physicalViewInsetRight; + mMetrics.physicalViewInsetBottom = mScreenMetrics.physicalViewInsetBottom; + mMetrics.physicalViewInsetLeft = mScreenMetrics.physicalViewInsetLeft; + updateScreenMetrics(); updateViewportMetrics(); return true; } else { @@ -696,7 +744,10 @@ private void updateViewportMetrics() { mNativeView .getFlutterJNI() .setViewportMetrics( - mMetrics.devicePixelRatio, + mMetrics.viewId, + mMetrics.screenId, + mMetrics.physicalLeft, + mMetrics.physicalTop, mMetrics.physicalWidth, mMetrics.physicalHeight, mMetrics.physicalPaddingTop, @@ -713,6 +764,32 @@ private void updateViewportMetrics() { mMetrics.systemGestureInsetLeft); } + private void updateScreenMetrics() { + if (!isAttached()) return; + mNativeView + .getFlutterJNI() + .setScreenMetrics( + mScreenMetrics.screenId, + mScreenMetrics.screenName, + mScreenMetrics.devicePixelRatio, + mScreenMetrics.physicalLeft, + mScreenMetrics.physicalTop, + mScreenMetrics.physicalWidth, + mScreenMetrics.physicalHeight, + mScreenMetrics.physicalPaddingTop, + mScreenMetrics.physicalPaddingRight, + mScreenMetrics.physicalPaddingBottom, + mScreenMetrics.physicalPaddingLeft, + mScreenMetrics.physicalViewInsetTop, + mScreenMetrics.physicalViewInsetRight, + mScreenMetrics.physicalViewInsetBottom, + mScreenMetrics.physicalViewInsetLeft, + mScreenMetrics.systemGestureInsetTop, + mScreenMetrics.systemGestureInsetRight, + mScreenMetrics.systemGestureInsetBottom, + mScreenMetrics.systemGestureInsetLeft); + } + // Called by FlutterNativeView to notify first Flutter frame rendered. public void onFirstFrame() { didRenderFirstFrame = true; diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 17239099d5a43..68b399837005c 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -211,7 +211,10 @@ static jobject LookupCallbackInformation(JNIEnv* env, static void SetViewportMetrics(JNIEnv* env, jobject jcaller, jlong shell_holder, - jfloat devicePixelRatio, + jint view_id, + jint screen_id, + jint physicalLeft, + jint physicalTop, jint physicalWidth, jint physicalHeight, jint physicalPaddingTop, @@ -227,7 +230,10 @@ static void SetViewportMetrics(JNIEnv* env, jint systemGestureInsetBottom, jint systemGestureInsetLeft) { const flutter::ViewportMetrics metrics{ - static_cast(devicePixelRatio), + static_cast(view_id), + static_cast(screen_id), + static_cast(physicalLeft), + static_cast(physicalTop), static_cast(physicalWidth), static_cast(physicalHeight), static_cast(physicalPaddingTop), @@ -247,6 +253,54 @@ static void SetViewportMetrics(JNIEnv* env, ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); } +static void SetScreenMetrics(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jint screen_id, + jstring screen_name, + jfloat devicePixelRatio, + jint physicalLeft, + jint physicalTop, + jint physicalWidth, + jint physicalHeight, + jint physicalPaddingTop, + jint physicalPaddingRight, + jint physicalPaddingBottom, + jint physicalPaddingLeft, + jint physicalViewInsetTop, + jint physicalViewInsetRight, + jint physicalViewInsetBottom, + jint physicalViewInsetLeft, + jint systemGestureInsetTop, + jint systemGestureInsetRight, + jint systemGestureInsetBottom, + jint systemGestureInsetLeft) { + const flutter::ScreenMetrics metrics{ + static_cast(screen_id), + fml::jni::JavaStringToString(env, screen_name), + static_cast(devicePixelRatio), + static_cast(physicalLeft), + static_cast(physicalTop), + static_cast(physicalWidth), + static_cast(physicalHeight), + static_cast(physicalPaddingTop), + static_cast(physicalPaddingRight), + static_cast(physicalPaddingBottom), + static_cast(physicalPaddingLeft), + static_cast(physicalViewInsetTop), + static_cast(physicalViewInsetRight), + static_cast(physicalViewInsetBottom), + static_cast(physicalViewInsetLeft), + static_cast(systemGestureInsetTop), + static_cast(systemGestureInsetRight), + static_cast(systemGestureInsetBottom), + static_cast(systemGestureInsetLeft), + }; + + ANDROID_SHELL_HOLDER->GetPlatformView()->SetScreenMetrics(metrics); +} + + static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( Rasterizer::ScreenshotType::UncompressedImage, false); @@ -549,9 +603,14 @@ bool RegisterApi(JNIEnv* env) { }, { .name = "nativeSetViewportMetrics", - .signature = "(JFIIIIIIIIIIIIII)V", + .signature = "(JIIIIIIIIIIIIIIIIII)V", .fnPtr = reinterpret_cast(&SetViewportMetrics), }, + { + .name = "nativeSetScreenMetrics", + .signature = "(JILjava/lang/String;FIIIIIIIIIIIIIIII)V", + .fnPtr = reinterpret_cast(&SetScreenMetrics), + }, { .name = "nativeDispatchPointerDataPacket", .signature = "(JLjava/nio/ByteBuffer;I)V", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index cde561b2ac89c..949d8cb98eb56 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" + #include -#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h" @@ -297,12 +298,15 @@ - (void)updateWindowMetrics { CGSize scaledSize = [view convertRectToBacking:view.bounds].size; double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width; - // The screen is currently always the same size as the window. + // TODO(gspencergoog): Currently, there is only one screen, and it is always + // the same size as the window. This will change as screen support is added. + // See https://github.com/flutter/flutter/issues/60131 const FlutterScreenMetricsEvent screenMetricsEvent = { .struct_size = sizeof(screenMetricsEvent), .width = static_cast(scaledSize.width), .height = static_cast(scaledSize.height), .pixel_ratio = pixelRatio, + .screen_id = 0, }; FlutterEngineSendScreenMetricsEvent(_engine, &screenMetricsEvent); @@ -310,6 +314,8 @@ - (void)updateWindowMetrics { .struct_size = sizeof(windowMetricsEvent), .width = static_cast(scaledSize.width), .height = static_cast(scaledSize.height), + .pixel_ratio = pixelRatio, + .window_id = 0, }; FlutterEngineSendWindowMetricsEvent(_engine, &windowMetricsEvent); } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 019ac9bd7d6c5..7b2499000a4c0 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -336,6 +336,13 @@ typedef struct { size_t width; /// Physical height of the window. size_t height; + /// Pixel density scale factor for the window. This will be identical to the + /// value of the pixel ratio for the screen that this window appears on. + double pixel_ratio; + /// The id for the window that corresponds to these metrics. + size_t window_id; + /// The id of the screen that this window appears on. + size_t screen_id; } FlutterWindowMetricsEvent; typedef struct { @@ -347,6 +354,8 @@ typedef struct { size_t height; /// Scale factor for the physical screen. double pixel_ratio; + /// The id for the screen that corresponds to these metrics. + size_t screen_id; } FlutterScreenMetricsEvent; /// The phase of the pointer event.