diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc index 41049c39fa8..71e87239126 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc @@ -203,8 +203,10 @@ void FlatlandExternalViewEmbedder::SubmitFrame( if (viewport.pending_create_viewport_callback) { if (view_size.fWidth && view_size.fHeight) { - viewport.pending_create_viewport_callback(view_size); + viewport.pending_create_viewport_callback( + view_size, viewport.pending_occlusion_hint); viewport.size = view_size; + viewport.occlusion_hint = viewport.pending_occlusion_hint; } else { FML_DLOG(WARNING) << "Failed to create viewport because width or height is zero."; @@ -226,16 +228,22 @@ void FlatlandExternalViewEmbedder::SubmitFrame( // TODO(fxbug.dev/94000): Set HitTestBehavior. // TODO(fxbug.dev/94000): Set opacity. - // Set size - // TODO(): Set occlusion hint, and focusable. - if (view_size != viewport.size) { + // Set size and occlusion hint. + if (view_size != viewport.size || + viewport.pending_occlusion_hint != viewport.occlusion_hint) { fuchsia::ui::composition::ViewportProperties properties; properties.set_logical_size( {static_cast(view_size.fWidth), static_cast(view_size.fHeight)}); + properties.set_inset( + {static_cast(viewport.pending_occlusion_hint.fTop), + static_cast(viewport.pending_occlusion_hint.fRight), + static_cast(viewport.pending_occlusion_hint.fBottom), + static_cast(viewport.pending_occlusion_hint.fLeft)}); flatland_->flatland()->SetViewportProperties(viewport.viewport_id, std::move(properties)); viewport.size = view_size; + viewport.occlusion_hint = viewport.pending_occlusion_hint; } // Attach the FlatlandView to the main scene graph. @@ -374,11 +382,15 @@ void FlatlandExternalViewEmbedder::CreateView( fuchsia::ui::composition::ChildViewWatcherHandle child_view_watcher; new_view.pending_create_viewport_callback = [this, transform_id, viewport_id, view_id, - child_view_watcher_request = - child_view_watcher.NewRequest()](const SkSize& size) mutable { + child_view_watcher_request = child_view_watcher.NewRequest()]( + const SkSize& size, const SkRect& inset) mutable { fuchsia::ui::composition::ViewportProperties properties; properties.set_logical_size({static_cast(size.fWidth), static_cast(size.fHeight)}); + properties.set_inset({static_cast(inset.fTop), + static_cast(inset.fRight), + static_cast(inset.fBottom), + static_cast(inset.fLeft)}); flatland_->flatland()->CreateViewport( viewport_id, {zx::channel((zx_handle_t)view_id)}, std::move(properties), std::move(child_view_watcher_request)); @@ -424,8 +436,10 @@ void FlatlandExternalViewEmbedder::SetViewProperties( auto found = flatland_views_.find(view_id); FML_CHECK(found != flatland_views_.end()); - // TODO(fxbug.dev/94000): Set occlusion_hint, hit_testable and focusable. Note - // that pending_create_viewport_callback might not have run at this point. + // Note that pending_create_viewport_callback might not have run at this + // point. + auto& viewport = found->second; + viewport.pending_occlusion_hint = occlusion_hint; } void FlatlandExternalViewEmbedder::Reset() { diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index 360e068699d..b7019be4f5b 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -165,7 +165,10 @@ class FlatlandExternalViewEmbedder final fuchsia::ui::composition::ContentId viewport_id; ViewMutators mutators; SkSize size = SkSize::MakeEmpty(); - fit::callback pending_create_viewport_callback; + SkRect pending_occlusion_hint = SkRect::MakeEmpty(); + SkRect occlusion_hint = SkRect::MakeEmpty(); + fit::callback + pending_create_viewport_callback; }; struct FlatlandLayer { diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h index 3572f9cd127..74ae2da2803 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h @@ -28,6 +28,12 @@ inline bool operator==(const fuchsia::math::SizeU& a, return a.width == b.width && a.height == b.height; } +inline bool operator==(const fuchsia::math::Inset& a, + const fuchsia::math::Inset& b) { + return a.top == b.top && a.left == b.left && a.right == b.right && + a.bottom == b.bottom; +} + inline bool operator==(const fuchsia::math::Vec& a, const fuchsia::math::Vec& b) { return a.x == b.x && a.y == b.y; @@ -129,6 +135,7 @@ struct FakeViewport { bool operator==(const FakeViewport& other) const; constexpr static fuchsia::math::SizeU kDefaultViewportLogicalSize{}; + constexpr static fuchsia::math::Inset kDefaultViewportInset{}; fuchsia::ui::composition::ContentId id{kInvalidContentId}; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc index a387ba2c1ef..38c4454683b 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc @@ -187,14 +187,19 @@ Matcher IsImageProperties( } Matcher IsViewportProperties( - const fuchsia::math::SizeU& logical_size) { + const fuchsia::math::SizeU& logical_size, + const fuchsia::math::Inset& inset) { return AllOf( Property("has_logical_size", &fuchsia::ui::composition::ViewportProperties::has_logical_size, true), Property("logical_size", &fuchsia::ui::composition::ViewportProperties::logical_size, - logical_size)); + logical_size), + Property("has_inset", + &fuchsia::ui::composition::ViewportProperties::has_inset, true), + Property("inset", &fuchsia::ui::composition::ViewportProperties::inset, + inset)); } Matcher IsEmptyGraph() { @@ -250,17 +255,18 @@ Matcher> IsImageLayer( Matcher> IsViewportLayer( const fuchsia::ui::views::ViewCreationToken& view_token, const fuchsia::math::SizeU& view_logical_size, + const fuchsia::math::Inset& view_inset, const fuchsia::math::Vec& view_transform) { - return Pointee( - FieldsAre(/* id */ _, view_transform, - /*clip_bounds*/ _, FakeTransform::kDefaultOrientation, - /*children*/ IsEmpty(), - /*content*/ - Pointee(VariantWith(FieldsAre( - /* id */ _, IsViewportProperties(view_logical_size), - /* viewport_token */ GetKoids(view_token).second, - /* child_view_watcher */ _))), - /*num_hit_regions*/ 0)); + return Pointee(FieldsAre( + /* id */ _, view_transform, + /*clip_bounds*/ _, FakeTransform::kDefaultOrientation, + /*children*/ IsEmpty(), + /*content*/ + Pointee(VariantWith(FieldsAre( + /* id */ _, IsViewportProperties(view_logical_size, view_inset), + /* viewport_token */ GetKoids(view_token).second, + /* child_view_watcher */ _))), + /*num_hit_regions*/ 0)); } fuchsia::ui::composition::OnNextFrameBeginValues WithPresentCredits( @@ -517,8 +523,17 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) { child_view_id, []() {}, [](fuchsia::ui::composition::ContentId, fuchsia::ui::composition::ChildViewWatcherHandle) {}); + const SkRect child_view_occlusion_hint = SkRect::MakeLTRB(1, 2, 3, 4); + const fuchsia::math::Inset child_view_inset{ + static_cast(child_view_occlusion_hint.top()), + static_cast(child_view_occlusion_hint.right()), + static_cast(child_view_occlusion_hint.bottom()), + static_cast(child_view_occlusion_hint.left())}; + external_view_embedder.SetViewProperties( + child_view_id, child_view_occlusion_hint, /*hit_testable=*/false, + /*focusable=*/false); - // Draw the scene. The scene graph shouldn't change yet. + // Draw the scene. The scene graph shouldn't change yet. const SkISize frame_size_signed = SkISize::Make(512, 512); const fuchsia::math::SizeU frame_size{ static_cast(frame_size_signed.width()), @@ -558,22 +573,24 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) { loop().RunUntilIdle(); EXPECT_THAT( fake_flatland().graph(), - IsFlutterGraph( - parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ - {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0}), - IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); + IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, + view_ref, /*layers*/ + {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), + IsViewportLayer(child_view_token, child_view_size, + child_view_inset, {0, 0}), + IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); // Destroy the view. The scene graph shouldn't change yet. external_view_embedder.DestroyView( child_view_id, [](fuchsia::ui::composition::ContentId) {}); EXPECT_THAT( fake_flatland().graph(), - IsFlutterGraph( - parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ - {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0}), - IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); + IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, + view_ref, /*layers*/ + {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), + IsViewportLayer(child_view_token, child_view_size, + child_view_inset, {0, 0}), + IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); // Draw another frame without the view. The scene graph shouldn't change yet. DrawSimpleFrame( @@ -590,11 +607,12 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) { }); EXPECT_THAT( fake_flatland().graph(), - IsFlutterGraph( - parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ - {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0}), - IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); + IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, + view_ref, /*layers*/ + {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), + IsViewportLayer(child_view_token, child_view_size, + child_view_inset, {0, 0}), + IsImageLayer(frame_size, kUpperLayerBlendMode, 1)})); // Pump the message loop. The scene updates should propagate to flatland. loop().RunUntilIdle(); @@ -682,7 +700,8 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) { IsFlutterGraph( parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0})})); + IsViewportLayer(child_view_token, child_view_size, + FakeViewport::kDefaultViewportInset, {0, 0})})); // Destroy the view. The scene graph shouldn't change yet. external_view_embedder.DestroyView( @@ -692,7 +711,8 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) { IsFlutterGraph( parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0})})); + IsViewportLayer(child_view_token, child_view_size, + FakeViewport::kDefaultViewportInset, {0, 0})})); // Draw another frame without the view. The scene graph shouldn't change yet. DrawSimpleFrame( @@ -712,7 +732,8 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) { IsFlutterGraph( parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/ {IsImageLayer(frame_size, kFirstLayerBlendMode, 1), - IsViewportLayer(child_view_token, child_view_size, {0, 0})})); + IsViewportLayer(child_view_token, child_view_size, + FakeViewport::kDefaultViewportInset, {0, 0})})); // Pump the message loop. The scene updates should propagate to flatland. loop().RunUntilIdle();