[Embedder] Wire view focus event and focus request (#163930)
This wires `PlatformDispatcher.onViewFocusChange` and `PlatformDispatches.requestViewFocusChange` through embedder API. *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I readand followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Matthew Kosarek <matt.kosarek@canonical.com> Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
This commit is contained in:
parent
289993ab83
commit
940fb3c8b1
@ -42555,6 +42555,8 @@ ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet.cc + ../../../flutter
|
|||||||
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/lib/ui/window/view_focus.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/lib/ui/window/view_focus.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js + ../../../flutter/LICENSE
|
||||||
@ -45471,6 +45473,8 @@ FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc
|
|||||||
FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h
|
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.cc
|
||||||
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h
|
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h
|
||||||
|
FILE: ../../../flutter/lib/ui/window/view_focus.cc
|
||||||
|
FILE: ../../../flutter/lib/ui/window/view_focus.h
|
||||||
FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
|
FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
|
||||||
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
|
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
|
||||||
FILE: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js
|
FILE: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js
|
||||||
|
@ -156,6 +156,8 @@ source_set("ui") {
|
|||||||
"window/pointer_data_packet.h",
|
"window/pointer_data_packet.h",
|
||||||
"window/pointer_data_packet_converter.cc",
|
"window/pointer_data_packet_converter.cc",
|
||||||
"window/pointer_data_packet_converter.h",
|
"window/pointer_data_packet_converter.h",
|
||||||
|
"window/view_focus.cc",
|
||||||
|
"window/view_focus.h",
|
||||||
"window/viewport_metrics.cc",
|
"window/viewport_metrics.cc",
|
||||||
"window/viewport_metrics.h",
|
"window/viewport_metrics.h",
|
||||||
]
|
]
|
||||||
|
@ -107,6 +107,7 @@ typedef CanvasPath Path;
|
|||||||
V(PlatformConfigurationNativeApi::GetRootIsolateToken) \
|
V(PlatformConfigurationNativeApi::GetRootIsolateToken) \
|
||||||
V(PlatformConfigurationNativeApi::RegisterBackgroundIsolate) \
|
V(PlatformConfigurationNativeApi::RegisterBackgroundIsolate) \
|
||||||
V(PlatformConfigurationNativeApi::SendPortPlatformMessage) \
|
V(PlatformConfigurationNativeApi::SendPortPlatformMessage) \
|
||||||
|
V(PlatformConfigurationNativeApi::RequestViewFocusChange) \
|
||||||
V(PlatformConfigurationNativeApi::SendChannelUpdate) \
|
V(PlatformConfigurationNativeApi::SendChannelUpdate) \
|
||||||
V(PlatformConfigurationNativeApi::GetScaledFontSize) \
|
V(PlatformConfigurationNativeApi::GetScaledFontSize) \
|
||||||
V(PlatformIsolateNativeApi::IsRunningOnPlatformThread) \
|
V(PlatformIsolateNativeApi::IsRunningOnPlatformThread) \
|
||||||
|
@ -58,6 +58,16 @@ void _removeView(int viewId) {
|
|||||||
PlatformDispatcher.instance._removeView(viewId);
|
PlatformDispatcher.instance._removeView(viewId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void _sendViewFocusEvent(int viewId, int viewFocusState, int viewFocusDirection) {
|
||||||
|
final ViewFocusEvent viewFocusEvent = ViewFocusEvent(
|
||||||
|
viewId: viewId,
|
||||||
|
state: ViewFocusState.values[viewFocusState],
|
||||||
|
direction: ViewFocusDirection.values[viewFocusDirection],
|
||||||
|
);
|
||||||
|
PlatformDispatcher.instance._sendViewFocusEvent(viewFocusEvent);
|
||||||
|
}
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
void _updateDisplays(
|
void _updateDisplays(
|
||||||
List<int> ids,
|
List<int> ids,
|
||||||
|
@ -301,6 +301,10 @@ class PlatformDispatcher {
|
|||||||
_invoke(onMetricsChanged, _onMetricsChangedZone);
|
_invoke(onMetricsChanged, _onMetricsChangedZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _sendViewFocusEvent(ViewFocusEvent event) {
|
||||||
|
_invoke1<ViewFocusEvent>(onViewFocusChange, _onViewFocusChangeZone, event);
|
||||||
|
}
|
||||||
|
|
||||||
// Called from the engine, via hooks.dart.
|
// Called from the engine, via hooks.dart.
|
||||||
//
|
//
|
||||||
// Updates the available displays.
|
// Updates the available displays.
|
||||||
@ -384,9 +388,14 @@ class PlatformDispatcher {
|
|||||||
required ViewFocusState state,
|
required ViewFocusState state,
|
||||||
required ViewFocusDirection direction,
|
required ViewFocusDirection direction,
|
||||||
}) {
|
}) {
|
||||||
// TODO(tugorez): implement this method. At the moment will be a no op call.
|
_requestViewFocusChange(viewId, state.index, direction.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Native<Void Function(Int64, Int64, Int64)>(
|
||||||
|
symbol: 'PlatformConfigurationNativeApi::RequestViewFocusChange',
|
||||||
|
)
|
||||||
|
external static void _requestViewFocusChange(int viewId, int state, int direction);
|
||||||
|
|
||||||
/// A callback invoked when any view begins a frame.
|
/// A callback invoked when any view begins a frame.
|
||||||
///
|
///
|
||||||
/// A callback that is invoked to notify the application that it is an
|
/// A callback that is invoked to notify the application that it is an
|
||||||
|
@ -46,6 +46,9 @@ void PlatformConfiguration::DidCreateIsolate() {
|
|||||||
Dart_GetField(library, tonic::ToDart("_addView")));
|
Dart_GetField(library, tonic::ToDart("_addView")));
|
||||||
remove_view_.Set(tonic::DartState::Current(),
|
remove_view_.Set(tonic::DartState::Current(),
|
||||||
Dart_GetField(library, tonic::ToDart("_removeView")));
|
Dart_GetField(library, tonic::ToDart("_removeView")));
|
||||||
|
send_view_focus_event_.Set(
|
||||||
|
tonic::DartState::Current(),
|
||||||
|
Dart_GetField(library, tonic::ToDart("_sendViewFocusEvent")));
|
||||||
update_window_metrics_.Set(
|
update_window_metrics_.Set(
|
||||||
tonic::DartState::Current(),
|
tonic::DartState::Current(),
|
||||||
Dart_GetField(library, tonic::ToDart("_updateWindowMetrics")));
|
Dart_GetField(library, tonic::ToDart("_updateWindowMetrics")));
|
||||||
@ -149,6 +152,22 @@ bool PlatformConfiguration::RemoveView(int64_t view_id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlatformConfiguration::SendFocusEvent(const ViewFocusEvent& event) {
|
||||||
|
std::shared_ptr<tonic::DartState> dart_state =
|
||||||
|
remove_view_.dart_state().lock();
|
||||||
|
if (!dart_state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tonic::DartState::Scope scope(dart_state);
|
||||||
|
tonic::CheckAndHandleError(tonic::DartInvoke(
|
||||||
|
send_view_focus_event_.Get(), {
|
||||||
|
tonic::ToDart(event.view_id()),
|
||||||
|
tonic::ToDart(event.state()),
|
||||||
|
tonic::ToDart(event.direction()),
|
||||||
|
}));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool PlatformConfiguration::UpdateViewMetrics(
|
bool PlatformConfiguration::UpdateViewMetrics(
|
||||||
int64_t view_id,
|
int64_t view_id,
|
||||||
const ViewportMetrics& view_metrics) {
|
const ViewportMetrics& view_metrics) {
|
||||||
@ -552,6 +571,17 @@ Dart_Handle PlatformConfigurationNativeApi::SendPortPlatformMessage(
|
|||||||
return HandlePlatformMessage(dart_state, name, data_handle, response);
|
return HandlePlatformMessage(dart_state, name, data_handle, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlatformConfigurationNativeApi::RequestViewFocusChange(int64_t view_id,
|
||||||
|
int64_t state,
|
||||||
|
int64_t direction) {
|
||||||
|
ViewFocusChangeRequest request{view_id, //
|
||||||
|
static_cast<ViewFocusState>(state),
|
||||||
|
static_cast<ViewFocusDirection>(direction)};
|
||||||
|
UIDartState* dart_state = UIDartState::Current();
|
||||||
|
dart_state->platform_configuration()->client()->RequestViewFocusChange(
|
||||||
|
request);
|
||||||
|
}
|
||||||
|
|
||||||
void PlatformConfigurationNativeApi::RespondToPlatformMessage(
|
void PlatformConfigurationNativeApi::RespondToPlatformMessage(
|
||||||
int response_id,
|
int response_id,
|
||||||
const tonic::DartByteData& data) {
|
const tonic::DartByteData& data) {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "flutter/lib/ui/semantics/semantics_update.h"
|
#include "flutter/lib/ui/semantics/semantics_update.h"
|
||||||
#include "flutter/lib/ui/window/platform_message_response.h"
|
#include "flutter/lib/ui/window/platform_message_response.h"
|
||||||
#include "flutter/lib/ui/window/pointer_data_packet.h"
|
#include "flutter/lib/ui/window/pointer_data_packet.h"
|
||||||
|
#include "flutter/lib/ui/window/view_focus.h"
|
||||||
#include "flutter/lib/ui/window/viewport_metrics.h"
|
#include "flutter/lib/ui/window/viewport_metrics.h"
|
||||||
#include "flutter/shell/common/display.h"
|
#include "flutter/shell/common/display.h"
|
||||||
#include "fml/macros.h"
|
#include "fml/macros.h"
|
||||||
@ -257,6 +258,14 @@ class PlatformConfigurationClient {
|
|||||||
virtual double GetScaledFontSize(double unscaled_font_size,
|
virtual double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const = 0;
|
int configuration_id) const = 0;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/// @brief Notifies the client that the Flutter view focus state has
|
||||||
|
/// changed and the platform view should be updated.
|
||||||
|
///
|
||||||
|
/// @param[in] request The request to change the focus state of the view.
|
||||||
|
virtual void RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) = 0;
|
||||||
|
|
||||||
virtual std::shared_ptr<PlatformIsolateManager>
|
virtual std::shared_ptr<PlatformIsolateManager>
|
||||||
GetPlatformIsolateManager() = 0;
|
GetPlatformIsolateManager() = 0;
|
||||||
|
|
||||||
@ -338,6 +347,15 @@ class PlatformConfiguration final {
|
|||||||
///
|
///
|
||||||
bool RemoveView(int64_t view_id);
|
bool RemoveView(int64_t view_id);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// @brief Notify the isolate that the focus state of a native view has
|
||||||
|
/// changed.
|
||||||
|
///
|
||||||
|
/// @param[in] event The focus event describing the change.
|
||||||
|
///
|
||||||
|
/// @return Whether the focus event was sent.
|
||||||
|
bool SendFocusEvent(const ViewFocusEvent& event);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
/// @brief Update the view metrics for the specified view.
|
/// @brief Update the view metrics for the specified view.
|
||||||
///
|
///
|
||||||
@ -529,6 +547,7 @@ class PlatformConfiguration final {
|
|||||||
tonic::DartPersistentValue on_error_;
|
tonic::DartPersistentValue on_error_;
|
||||||
tonic::DartPersistentValue add_view_;
|
tonic::DartPersistentValue add_view_;
|
||||||
tonic::DartPersistentValue remove_view_;
|
tonic::DartPersistentValue remove_view_;
|
||||||
|
tonic::DartPersistentValue send_view_focus_event_;
|
||||||
tonic::DartPersistentValue update_window_metrics_;
|
tonic::DartPersistentValue update_window_metrics_;
|
||||||
tonic::DartPersistentValue update_displays_;
|
tonic::DartPersistentValue update_displays_;
|
||||||
tonic::DartPersistentValue update_locales_;
|
tonic::DartPersistentValue update_locales_;
|
||||||
@ -617,6 +636,10 @@ class PlatformConfigurationNativeApi {
|
|||||||
|
|
||||||
static void SendChannelUpdate(const std::string& name, bool listening);
|
static void SendChannelUpdate(const std::string& name, bool listening);
|
||||||
|
|
||||||
|
static void RequestViewFocusChange(int64_t view_id,
|
||||||
|
int64_t state,
|
||||||
|
int64_t direction);
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
/// @brief Requests the Dart VM to adjusts the GC heuristics based on
|
/// @brief Requests the Dart VM to adjusts the GC heuristics based on
|
||||||
/// the requested `performance_mode`. Returns the old performance
|
/// the requested `performance_mode`. Returns the old performance
|
||||||
|
23
engine/src/flutter/lib/ui/window/view_focus.cc
Normal file
23
engine/src/flutter/lib/ui/window/view_focus.cc
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 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/view_focus.h"
|
||||||
|
|
||||||
|
namespace flutter {
|
||||||
|
ViewFocusChangeRequest::ViewFocusChangeRequest(int64_t view_id,
|
||||||
|
ViewFocusState state,
|
||||||
|
ViewFocusDirection direction)
|
||||||
|
: view_id_(view_id), state_(state), direction_(direction) {}
|
||||||
|
|
||||||
|
int64_t ViewFocusChangeRequest::view_id() const {
|
||||||
|
return view_id_;
|
||||||
|
}
|
||||||
|
ViewFocusState ViewFocusChangeRequest::state() const {
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
ViewFocusDirection ViewFocusChangeRequest::direction() const {
|
||||||
|
return direction_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace flutter
|
69
engine/src/flutter/lib/ui/window/view_focus.h
Normal file
69
engine/src/flutter/lib/ui/window/view_focus.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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_VIEW_FOCUS_H_
|
||||||
|
#define FLUTTER_LIB_UI_WINDOW_VIEW_FOCUS_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace flutter {
|
||||||
|
|
||||||
|
// Focus state of a View.
|
||||||
|
// Must match ViewFocusState in ui/platform_dispatcher.dart.
|
||||||
|
enum class ViewFocusState : int64_t {
|
||||||
|
kUnfocused = 0,
|
||||||
|
kFocused,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents the direction of which the focus transitioned over
|
||||||
|
// a FlutterView.
|
||||||
|
// Must match ViewFocusDirection in ui/platform_dispatcher.dart.
|
||||||
|
enum class ViewFocusDirection : int64_t {
|
||||||
|
kUndefined = 0,
|
||||||
|
kForward,
|
||||||
|
kBackward,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Event sent by the embedder to the engine indicating that native view focus
|
||||||
|
// state has changed.
|
||||||
|
class ViewFocusEvent {
|
||||||
|
public:
|
||||||
|
ViewFocusEvent(int64_t view_id,
|
||||||
|
ViewFocusState state,
|
||||||
|
ViewFocusDirection direction)
|
||||||
|
: view_id_(view_id), state_(state), direction_(direction) {}
|
||||||
|
|
||||||
|
int64_t view_id() const { return view_id_; }
|
||||||
|
ViewFocusState state() const { return state_; }
|
||||||
|
ViewFocusDirection direction() const { return direction_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t view_id_;
|
||||||
|
ViewFocusState state_;
|
||||||
|
ViewFocusDirection direction_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Request sent by the engine to the embedder indicating that the FlutterView
|
||||||
|
// focus state has changed and the native view should be updated.
|
||||||
|
class ViewFocusChangeRequest {
|
||||||
|
public:
|
||||||
|
ViewFocusChangeRequest(int64_t view_id,
|
||||||
|
ViewFocusState state,
|
||||||
|
ViewFocusDirection direction);
|
||||||
|
|
||||||
|
int64_t view_id() const;
|
||||||
|
ViewFocusState state() const;
|
||||||
|
ViewFocusDirection direction() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ViewFocusChangeRequest() = delete;
|
||||||
|
|
||||||
|
int64_t view_id_ = 0;
|
||||||
|
ViewFocusState state_ = ViewFocusState::kUnfocused;
|
||||||
|
ViewFocusDirection direction_ = ViewFocusDirection::kUndefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace flutter
|
||||||
|
|
||||||
|
#endif // FLUTTER_LIB_UI_WINDOW_VIEW_FOCUS_H_
|
@ -735,6 +735,7 @@ class FakePlatformConfigurationClient : public PlatformConfigurationClient {
|
|||||||
int configuration_id) const override {
|
int configuration_id) const override {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
void RequestViewFocusChange(const ViewFocusChangeRequest& request) override {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DartIsolateTest, PlatformIsolateCreationAndShutdown) {
|
TEST_F(DartIsolateTest, PlatformIsolateCreationAndShutdown) {
|
||||||
|
@ -209,6 +209,14 @@ bool RuntimeController::RemoveView(int64_t view_id) {
|
|||||||
return platform_configuration->RemoveView(view_id);
|
return platform_configuration->RemoveView(view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RuntimeController::SendViewFocusEvent(const ViewFocusEvent& event) {
|
||||||
|
auto* platform_configuration = GetPlatformConfigurationIfAvailable();
|
||||||
|
if (!platform_configuration) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return platform_configuration->SendFocusEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
bool RuntimeController::ViewExists(int64_t view_id) const {
|
bool RuntimeController::ViewExists(int64_t view_id) const {
|
||||||
return platform_data_.viewport_metrics_for_views.count(view_id) != 0;
|
return platform_data_.viewport_metrics_for_views.count(view_id) != 0;
|
||||||
}
|
}
|
||||||
@ -649,6 +657,11 @@ double RuntimeController::GetScaledFontSize(double unscaled_font_size,
|
|||||||
return client_.GetScaledFontSize(unscaled_font_size, configuration_id);
|
return client_.GetScaledFontSize(unscaled_font_size, configuration_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeController::RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) {
|
||||||
|
client_.RequestViewFocusChange(request);
|
||||||
|
}
|
||||||
|
|
||||||
void RuntimeController::ShutdownPlatformIsolates() {
|
void RuntimeController::ShutdownPlatformIsolates() {
|
||||||
platform_isolate_manager_->ShutdownPlatformIsolates();
|
platform_isolate_manager_->ShutdownPlatformIsolates();
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "flutter/lib/ui/window/platform_configuration.h"
|
#include "flutter/lib/ui/window/platform_configuration.h"
|
||||||
#include "flutter/lib/ui/window/pointer_data_packet.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/pointer_data_packet_converter.h"
|
||||||
|
#include "flutter/lib/ui/window/view_focus.h"
|
||||||
#include "flutter/runtime/dart_vm.h"
|
#include "flutter/runtime/dart_vm.h"
|
||||||
#include "flutter/runtime/platform_data.h"
|
#include "flutter/runtime/platform_data.h"
|
||||||
#include "flutter/runtime/platform_isolate_manager.h"
|
#include "flutter/runtime/platform_isolate_manager.h"
|
||||||
@ -224,6 +225,13 @@ class RuntimeController : public PlatformConfigurationClient,
|
|||||||
/// cancelled and the return value is always false.
|
/// cancelled and the return value is always false.
|
||||||
bool RemoveView(int64_t view_id);
|
bool RemoveView(int64_t view_id);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// @brief Notify the isolate that the focus state of a native view has
|
||||||
|
/// changed.
|
||||||
|
///
|
||||||
|
/// @param[in] event The focus event describing the change.
|
||||||
|
bool SendViewFocusEvent(const ViewFocusEvent& event);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
/// @brief Forward the specified viewport metrics to the running isolate.
|
/// @brief Forward the specified viewport metrics to the running isolate.
|
||||||
/// If the isolate is not running, these metrics will be saved and
|
/// If the isolate is not running, these metrics will be saved and
|
||||||
@ -785,6 +793,9 @@ class RuntimeController : public PlatformConfigurationClient,
|
|||||||
double GetScaledFontSize(double unscaled_font_size,
|
double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const override;
|
int configuration_id) const override;
|
||||||
|
|
||||||
|
// |PlatformConfigurationClient|
|
||||||
|
void RequestViewFocusChange(const ViewFocusChangeRequest& request) override;
|
||||||
|
|
||||||
FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
|
FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "flutter/lib/ui/semantics/semantics_node.h"
|
#include "flutter/lib/ui/semantics/semantics_node.h"
|
||||||
#include "flutter/lib/ui/text/font_collection.h"
|
#include "flutter/lib/ui/text/font_collection.h"
|
||||||
#include "flutter/lib/ui/window/platform_message.h"
|
#include "flutter/lib/ui/window/platform_message.h"
|
||||||
|
#include "flutter/lib/ui/window/view_focus.h"
|
||||||
#include "flutter/shell/common/platform_message_handler.h"
|
#include "flutter/shell/common/platform_message_handler.h"
|
||||||
#include "third_party/dart/runtime/include/dart_api.h"
|
#include "third_party/dart/runtime/include/dart_api.h"
|
||||||
|
|
||||||
@ -62,6 +63,9 @@ class RuntimeDelegate {
|
|||||||
virtual double GetScaledFontSize(double unscaled_font_size,
|
virtual double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const = 0;
|
int configuration_id) const = 0;
|
||||||
|
|
||||||
|
virtual void RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~RuntimeDelegate();
|
virtual ~RuntimeDelegate();
|
||||||
};
|
};
|
||||||
|
@ -312,6 +312,10 @@ bool Engine::RemoveView(int64_t view_id) {
|
|||||||
return runtime_controller_->RemoveView(view_id);
|
return runtime_controller_->RemoveView(view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Engine::SendViewFocusEvent(const ViewFocusEvent& event) {
|
||||||
|
return runtime_controller_->SendViewFocusEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::SetViewportMetrics(int64_t view_id,
|
void Engine::SetViewportMetrics(int64_t view_id,
|
||||||
const ViewportMetrics& metrics) {
|
const ViewportMetrics& metrics) {
|
||||||
runtime_controller_->SetViewportMetrics(view_id, metrics);
|
runtime_controller_->SetViewportMetrics(view_id, metrics);
|
||||||
@ -522,6 +526,10 @@ double Engine::GetScaledFontSize(double unscaled_font_size,
|
|||||||
return delegate_.GetScaledFontSize(unscaled_font_size, configuration_id);
|
return delegate_.GetScaledFontSize(unscaled_font_size, configuration_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Engine::RequestViewFocusChange(const ViewFocusChangeRequest& request) {
|
||||||
|
delegate_.RequestViewFocusChange(request);
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::SetNeedsReportTimings(bool needs_reporting) {
|
void Engine::SetNeedsReportTimings(bool needs_reporting) {
|
||||||
delegate_.SetNeedsReportTimings(needs_reporting);
|
delegate_.SetNeedsReportTimings(needs_reporting);
|
||||||
}
|
}
|
||||||
|
@ -323,6 +323,14 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
|
|||||||
///
|
///
|
||||||
virtual double GetScaledFontSize(double unscaled_font_size,
|
virtual double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const = 0;
|
int configuration_id) const = 0;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/// @brief Notifies the client that the Flutter view focus state has
|
||||||
|
/// changed and the platform view should be updated.
|
||||||
|
///
|
||||||
|
/// @param[in] request The request to change the focus state of the view.
|
||||||
|
virtual void RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@ -747,6 +755,13 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
|
|||||||
///
|
///
|
||||||
bool RemoveView(int64_t view_id);
|
bool RemoveView(int64_t view_id);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// @brief Notify the Flutter application that the focus state of a
|
||||||
|
/// native view has changed.
|
||||||
|
///
|
||||||
|
/// @param[in] event The focus event describing the change.
|
||||||
|
bool SendViewFocusEvent(const ViewFocusEvent& event);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
/// @brief Updates the viewport metrics for a view. The viewport metrics
|
/// @brief Updates the viewport metrics for a view. The viewport metrics
|
||||||
/// detail the size of the rendering viewport in texels as well as
|
/// detail the size of the rendering viewport in texels as well as
|
||||||
@ -1024,6 +1039,9 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
|
|||||||
double GetScaledFontSize(double unscaled_font_size,
|
double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const override;
|
int configuration_id) const override;
|
||||||
|
|
||||||
|
// |RuntimeDelegate|
|
||||||
|
void RequestViewFocusChange(const ViewFocusChangeRequest& request) override;
|
||||||
|
|
||||||
void SetNeedsReportTimings(bool value) override;
|
void SetNeedsReportTimings(bool value) override;
|
||||||
|
|
||||||
bool HandleLifecyclePlatformMessage(PlatformMessage* message);
|
bool HandleLifecyclePlatformMessage(PlatformMessage* message);
|
||||||
|
@ -82,6 +82,10 @@ class MockDelegate : public Engine::Delegate {
|
|||||||
GetScaledFontSize,
|
GetScaledFontSize,
|
||||||
(double font_size, int configuration_id),
|
(double font_size, int configuration_id),
|
||||||
(const, override));
|
(const, override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
RequestViewFocusChange,
|
||||||
|
(const ViewFocusChangeRequest&),
|
||||||
|
(override));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockAnimatorDelegate : public Animator::Delegate {
|
class MockAnimatorDelegate : public Animator::Delegate {
|
||||||
|
@ -90,6 +90,10 @@ class MockDelegate : public Engine::Delegate {
|
|||||||
GetScaledFontSize,
|
GetScaledFontSize,
|
||||||
(double font_size, int configuration_id),
|
(double font_size, int configuration_id),
|
||||||
(const, override));
|
(const, override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
RequestViewFocusChange,
|
||||||
|
(const ViewFocusChangeRequest&),
|
||||||
|
(override));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockResponse : public PlatformMessageResponse {
|
class MockResponse : public PlatformMessageResponse {
|
||||||
@ -137,6 +141,10 @@ class MockRuntimeDelegate : public RuntimeDelegate {
|
|||||||
GetScaledFontSize,
|
GetScaledFontSize,
|
||||||
(double font_size, int configuration_id),
|
(double font_size, int configuration_id),
|
||||||
(const, override));
|
(const, override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
RequestViewFocusChange,
|
||||||
|
(const ViewFocusChangeRequest&),
|
||||||
|
(override));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockRuntimeController : public RuntimeController {
|
class MockRuntimeController : public RuntimeController {
|
||||||
|
@ -632,3 +632,11 @@ void testDispatchEvents() {
|
|||||||
notifyNative();
|
notifyNative();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void testSendViewFocusEvent() {
|
||||||
|
PlatformDispatcher.instance.onViewFocusChange = (ViewFocusEvent event) {
|
||||||
|
notifyMessage('${event.viewId} ${event.state} ${event.direction}');
|
||||||
|
};
|
||||||
|
notifyNative();
|
||||||
|
}
|
||||||
|
@ -97,6 +97,10 @@ void PlatformView::RemoveView(int64_t view_id, RemoveViewCallback callback) {
|
|||||||
delegate_.OnPlatformViewRemoveView(view_id, std::move(callback));
|
delegate_.OnPlatformViewRemoveView(view_id, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlatformView::SendViewFocusEvent(const ViewFocusEvent& event) {
|
||||||
|
delegate_.OnPlatformViewSendViewFocusEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
sk_sp<GrDirectContext> PlatformView::CreateResourceContext() const {
|
sk_sp<GrDirectContext> PlatformView::CreateResourceContext() const {
|
||||||
FML_DLOG(WARNING) << "This platform does not set up the resource "
|
FML_DLOG(WARNING) << "This platform does not set up the resource "
|
||||||
"context on the IO thread for async texture uploads.";
|
"context on the IO thread for async texture uploads.";
|
||||||
@ -219,4 +223,9 @@ double PlatformView::GetScaledFontSize(double unscaled_font_size,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlatformView::RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) {
|
||||||
|
// No-op by default.
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace flutter
|
} // namespace flutter
|
||||||
|
@ -121,6 +121,12 @@ class PlatformView {
|
|||||||
virtual void OnPlatformViewRemoveView(int64_t view_id,
|
virtual void OnPlatformViewRemoveView(int64_t view_id,
|
||||||
RemoveViewCallback callback) = 0;
|
RemoveViewCallback callback) = 0;
|
||||||
|
|
||||||
|
/// @brief Notify the delegate that platform view focus state has changed.
|
||||||
|
///
|
||||||
|
/// @param[in] event The focus event describing the change.
|
||||||
|
virtual void OnPlatformViewSendViewFocusEvent(
|
||||||
|
const ViewFocusEvent& event) = 0;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
/// @brief Notifies the delegate that the specified callback needs to
|
/// @brief Notifies the delegate that the specified callback needs to
|
||||||
/// be invoked after the rasterizer is done rendering the next
|
/// be invoked after the rasterizer is done rendering the next
|
||||||
@ -605,6 +611,8 @@ class PlatformView {
|
|||||||
///
|
///
|
||||||
void RemoveView(int64_t view_id, RemoveViewCallback callback);
|
void RemoveView(int64_t view_id, RemoveViewCallback callback);
|
||||||
|
|
||||||
|
void SendViewFocusEvent(const ViewFocusEvent& event);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
/// @brief Used by the shell to obtain a Skia GPU context that is capable
|
/// @brief Used by the shell to obtain a Skia GPU context that is capable
|
||||||
/// of operating on the IO thread. The context must be in the same
|
/// of operating on the IO thread. The context must be in the same
|
||||||
@ -954,6 +962,15 @@ class PlatformView {
|
|||||||
virtual double GetScaledFontSize(double unscaled_font_size,
|
virtual double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const;
|
int configuration_id) const;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/// @brief Notifies the client that the Flutter view focus state has
|
||||||
|
/// changed and the platform view should be updated.
|
||||||
|
///
|
||||||
|
/// Called on platform thread.
|
||||||
|
///
|
||||||
|
/// @param[in] request The request to change the focus state of the view.
|
||||||
|
virtual void RequestViewFocusChange(const ViewFocusChangeRequest& request);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// This is the only method called on the raster task runner.
|
// This is the only method called on the raster task runner.
|
||||||
virtual std::unique_ptr<Surface> CreateRenderingSurface();
|
virtual std::unique_ptr<Surface> CreateRenderingSurface();
|
||||||
|
@ -1550,6 +1550,18 @@ double Shell::GetScaledFontSize(double unscaled_font_size,
|
|||||||
configuration_id);
|
configuration_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shell::RequestViewFocusChange(const ViewFocusChangeRequest& request) {
|
||||||
|
FML_DCHECK(is_set_up_);
|
||||||
|
|
||||||
|
fml::TaskRunner::RunNowOrPostTask(
|
||||||
|
task_runners_.GetPlatformTaskRunner(),
|
||||||
|
[view = platform_view_->GetWeakPtr(), request] {
|
||||||
|
if (view) {
|
||||||
|
view->RequestViewFocusChange(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Shell::ReportTimings() {
|
void Shell::ReportTimings() {
|
||||||
FML_DCHECK(is_set_up_);
|
FML_DCHECK(is_set_up_);
|
||||||
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
||||||
@ -2102,6 +2114,20 @@ void Shell::OnPlatformViewRemoveView(int64_t view_id,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shell::OnPlatformViewSendViewFocusEvent(const ViewFocusEvent& event) {
|
||||||
|
TRACE_EVENT0("flutter", "Shell:: OnPlatformViewSendViewFocusEvent");
|
||||||
|
FML_DCHECK(is_set_up_);
|
||||||
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
||||||
|
|
||||||
|
task_runners_.GetUITaskRunner()->RunNowOrPostTask(
|
||||||
|
task_runners_.GetUITaskRunner(),
|
||||||
|
[engine = engine_->GetWeakPtr(), event = event] {
|
||||||
|
if (engine) {
|
||||||
|
engine->SendViewFocusEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Rasterizer::Screenshot Shell::Screenshot(
|
Rasterizer::Screenshot Shell::Screenshot(
|
||||||
Rasterizer::ScreenshotType screenshot_type,
|
Rasterizer::ScreenshotType screenshot_type,
|
||||||
bool base64_encode) {
|
bool base64_encode) {
|
||||||
|
@ -588,6 +588,9 @@ class Shell final : public PlatformView::Delegate,
|
|||||||
void OnPlatformViewRemoveView(int64_t view_id,
|
void OnPlatformViewRemoveView(int64_t view_id,
|
||||||
RemoveViewCallback callback) override;
|
RemoveViewCallback callback) override;
|
||||||
|
|
||||||
|
// |PlatformView::Delegate|
|
||||||
|
void OnPlatformViewSendViewFocusEvent(const ViewFocusEvent& event) override;
|
||||||
|
|
||||||
// |PlatformView::Delegate|
|
// |PlatformView::Delegate|
|
||||||
void OnPlatformViewSetViewportMetrics(
|
void OnPlatformViewSetViewportMetrics(
|
||||||
int64_t view_id,
|
int64_t view_id,
|
||||||
@ -702,6 +705,9 @@ class Shell final : public PlatformView::Delegate,
|
|||||||
double GetScaledFontSize(double unscaled_font_size,
|
double GetScaledFontSize(double unscaled_font_size,
|
||||||
int configuration_id) const override;
|
int configuration_id) const override;
|
||||||
|
|
||||||
|
// |Engine::Delegate|
|
||||||
|
void RequestViewFocusChange(const ViewFocusChangeRequest& request) override;
|
||||||
|
|
||||||
// |Rasterizer::Delegate|
|
// |Rasterizer::Delegate|
|
||||||
void OnFrameRasterized(const FrameTiming&) override;
|
void OnFrameRasterized(const FrameTiming&) override;
|
||||||
|
|
||||||
|
@ -116,6 +116,11 @@ class MockPlatformViewDelegate : public PlatformView::Delegate {
|
|||||||
(int64_t view_id, RemoveViewCallback callback),
|
(int64_t view_id, RemoveViewCallback callback),
|
||||||
(override));
|
(override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
OnPlatformViewSendViewFocusEvent,
|
||||||
|
(const ViewFocusEvent& event),
|
||||||
|
(override));
|
||||||
|
|
||||||
MOCK_METHOD(void,
|
MOCK_METHOD(void,
|
||||||
OnPlatformViewSetNextFrameCallback,
|
OnPlatformViewSetNextFrameCallback,
|
||||||
(const fml::closure& closure),
|
(const fml::closure& closure),
|
||||||
@ -4952,6 +4957,56 @@ TEST_F(ShellTest, WillLogWarningWhenImpellerIsOptedOut) {
|
|||||||
DestroyShell(std::move(shell), task_runners);
|
DestroyShell(std::move(shell), task_runners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ShellTest, SendViewFocusEvent) {
|
||||||
|
Settings settings = CreateSettingsForFixture();
|
||||||
|
TaskRunners task_runners = GetTaskRunnersForFixture();
|
||||||
|
fml::AutoResetWaitableEvent latch;
|
||||||
|
std::string last_event;
|
||||||
|
|
||||||
|
AddNativeCallback(
|
||||||
|
"NotifyNative",
|
||||||
|
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
|
||||||
|
|
||||||
|
AddNativeCallback("NotifyMessage",
|
||||||
|
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
|
||||||
|
const auto message_from_dart =
|
||||||
|
tonic::DartConverter<std::string>::FromDart(
|
||||||
|
Dart_GetNativeArgument(args, 0));
|
||||||
|
last_event = message_from_dart;
|
||||||
|
latch.Signal();
|
||||||
|
}));
|
||||||
|
fml::AutoResetWaitableEvent check_latch;
|
||||||
|
|
||||||
|
std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
|
||||||
|
ASSERT_TRUE(shell->IsSetup());
|
||||||
|
|
||||||
|
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||||
|
|
||||||
|
configuration.SetEntrypoint("testSendViewFocusEvent");
|
||||||
|
RunEngine(shell.get(), std::move(configuration));
|
||||||
|
latch.Wait();
|
||||||
|
latch.Reset();
|
||||||
|
|
||||||
|
PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
|
||||||
|
shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
|
||||||
|
1, ViewFocusState::kFocused, ViewFocusDirection::kUndefined));
|
||||||
|
});
|
||||||
|
latch.Wait();
|
||||||
|
ASSERT_EQ(last_event,
|
||||||
|
"1 ViewFocusState.focused ViewFocusDirection.undefined");
|
||||||
|
|
||||||
|
latch.Reset();
|
||||||
|
PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
|
||||||
|
shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
|
||||||
|
2, ViewFocusState::kUnfocused, ViewFocusDirection::kBackward));
|
||||||
|
});
|
||||||
|
latch.Wait();
|
||||||
|
ASSERT_EQ(last_event,
|
||||||
|
"2 ViewFocusState.unfocused ViewFocusDirection.backward");
|
||||||
|
|
||||||
|
DestroyShell(std::move(shell), task_runners);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace flutter
|
} // namespace flutter
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class FakeDelegate : public PlatformView::Delegate {
|
|||||||
const ViewportMetrics& viewport_metrics,
|
const ViewportMetrics& viewport_metrics,
|
||||||
AddViewCallback callback) override {}
|
AddViewCallback callback) override {}
|
||||||
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
||||||
|
void OnPlatformViewSendViewFocusEvent(const ViewFocusEvent& event) override {}
|
||||||
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
||||||
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
||||||
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
||||||
|
@ -263,6 +263,7 @@ class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::De
|
|||||||
const ViewportMetrics& viewport_metrics,
|
const ViewportMetrics& viewport_metrics,
|
||||||
AddViewCallback callback) override {}
|
AddViewCallback callback) override {}
|
||||||
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
||||||
|
void OnPlatformViewSendViewFocusEvent(const ViewFocusEvent& event) override {};
|
||||||
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
||||||
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
||||||
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
||||||
|
@ -75,6 +75,7 @@ class MockDelegate : public PlatformView::Delegate {
|
|||||||
const ViewportMetrics& viewport_metrics,
|
const ViewportMetrics& viewport_metrics,
|
||||||
AddViewCallback callback) override {}
|
AddViewCallback callback) override {}
|
||||||
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
|
||||||
|
void OnPlatformViewSendViewFocusEvent(const ViewFocusEvent& event) override {};
|
||||||
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
|
||||||
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
|
||||||
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
|
||||||
|
@ -2226,6 +2226,24 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flutter::PlatformViewEmbedder::ViewFocusChangeRequestCallback
|
||||||
|
view_focus_change_request_callback = nullptr;
|
||||||
|
if (SAFE_ACCESS(args, view_focus_change_request_callback, nullptr) !=
|
||||||
|
nullptr) {
|
||||||
|
view_focus_change_request_callback =
|
||||||
|
[ptr = args->view_focus_change_request_callback,
|
||||||
|
user_data](const flutter::ViewFocusChangeRequest& request) {
|
||||||
|
FlutterViewFocusChangeRequest embedder_request{
|
||||||
|
.struct_size = sizeof(FlutterViewFocusChangeRequest),
|
||||||
|
.view_id = request.view_id(),
|
||||||
|
.state = static_cast<FlutterViewFocusState>(request.state()),
|
||||||
|
.direction =
|
||||||
|
static_cast<FlutterViewFocusDirection>(request.direction()),
|
||||||
|
};
|
||||||
|
ptr(&embedder_request, user_data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
auto external_view_embedder_result = InferExternalViewEmbedderFromArgs(
|
auto external_view_embedder_result = InferExternalViewEmbedderFromArgs(
|
||||||
SAFE_ACCESS(args, compositor, nullptr), settings.enable_impeller);
|
SAFE_ACCESS(args, compositor, nullptr), settings.enable_impeller);
|
||||||
if (external_view_embedder_result.second) {
|
if (external_view_embedder_result.second) {
|
||||||
@ -2241,6 +2259,7 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
|
|||||||
compute_platform_resolved_locale_callback, //
|
compute_platform_resolved_locale_callback, //
|
||||||
on_pre_engine_restart_callback, //
|
on_pre_engine_restart_callback, //
|
||||||
channel_update_callback, //
|
channel_update_callback, //
|
||||||
|
view_focus_change_request_callback, //
|
||||||
};
|
};
|
||||||
|
|
||||||
auto on_create_platform_view = InferPlatformViewCreationCallback(
|
auto on_create_platform_view = InferPlatformViewCreationCallback(
|
||||||
@ -2556,6 +2575,38 @@ FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
|
|||||||
return kSuccess;
|
return kSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlutterEngineResult FlutterEngineSendViewFocusEvent(
|
||||||
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
||||||
|
const FlutterViewFocusEvent* event) {
|
||||||
|
if (!engine) {
|
||||||
|
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
|
||||||
|
}
|
||||||
|
if (!event) {
|
||||||
|
return LOG_EMBEDDER_ERROR(kInvalidArguments,
|
||||||
|
"View focus event must not be null.");
|
||||||
|
}
|
||||||
|
// The engine must be running to focus a view.
|
||||||
|
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
|
||||||
|
if (!embedder_engine->IsValid()) {
|
||||||
|
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!STRUCT_HAS_MEMBER(event, direction)) {
|
||||||
|
return LOG_EMBEDDER_ERROR(kInvalidArguments,
|
||||||
|
"The event struct has invalid size.");
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter::ViewFocusEvent flutter_event(
|
||||||
|
event->view_id, //
|
||||||
|
static_cast<flutter::ViewFocusState>(event->state),
|
||||||
|
static_cast<flutter::ViewFocusDirection>(event->direction));
|
||||||
|
|
||||||
|
embedder_engine->GetShell().GetPlatformView()->SendViewFocusEvent(
|
||||||
|
flutter_event);
|
||||||
|
|
||||||
|
return kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
FLUTTER_EXPORT
|
FLUTTER_EXPORT
|
||||||
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
|
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
|
||||||
engine) {
|
engine) {
|
||||||
@ -3657,6 +3708,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses(
|
|||||||
SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback);
|
SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback);
|
||||||
SET_PROC(AddView, FlutterEngineAddView);
|
SET_PROC(AddView, FlutterEngineAddView);
|
||||||
SET_PROC(RemoveView, FlutterEngineRemoveView);
|
SET_PROC(RemoveView, FlutterEngineRemoveView);
|
||||||
|
SET_PROC(SendViewFocusEvent, FlutterEngineSendViewFocusEvent);
|
||||||
#undef SET_PROC
|
#undef SET_PROC
|
||||||
|
|
||||||
return kSuccess;
|
return kSuccess;
|
||||||
|
@ -1060,6 +1060,74 @@ typedef struct {
|
|||||||
FlutterRemoveViewCallback remove_view_callback;
|
FlutterRemoveViewCallback remove_view_callback;
|
||||||
} FlutterRemoveViewInfo;
|
} FlutterRemoveViewInfo;
|
||||||
|
|
||||||
|
/// Represents the direction in which the focus transitioned across
|
||||||
|
/// [FlutterView]s.
|
||||||
|
typedef enum {
|
||||||
|
/// Indicates the focus transition did not have a direction.
|
||||||
|
///
|
||||||
|
/// This is typically associated with focus being programmatically requested
|
||||||
|
/// or when focus is lost.
|
||||||
|
kUndefined,
|
||||||
|
|
||||||
|
/// Indicates the focus transition was performed in a forward direction.
|
||||||
|
///
|
||||||
|
/// This is typically result of the user pressing tab.
|
||||||
|
kForward,
|
||||||
|
|
||||||
|
/// Indicates the focus transition was performed in a backward direction.
|
||||||
|
///
|
||||||
|
/// This is typically result of the user pressing shift + tab.
|
||||||
|
kBackward,
|
||||||
|
} FlutterViewFocusDirection;
|
||||||
|
|
||||||
|
/// Represents the focus state of a given [FlutterView].
|
||||||
|
typedef enum {
|
||||||
|
/// Specifies that a view does not have platform focus.
|
||||||
|
kUnfocused,
|
||||||
|
|
||||||
|
/// Specifies that a view has platform focus.
|
||||||
|
kFocused,
|
||||||
|
} FlutterViewFocusState;
|
||||||
|
|
||||||
|
/// A view focus event is sent to the engine by the embedder when a native view
|
||||||
|
/// focus state has changed.
|
||||||
|
///
|
||||||
|
/// Passed through FlutterEngineSendViewFocusEvent.
|
||||||
|
typedef struct {
|
||||||
|
/// The size of this struct.
|
||||||
|
/// Must be sizeof(FlutterViewFocusEvent).
|
||||||
|
size_t struct_size;
|
||||||
|
|
||||||
|
/// The identifier of the view that received the focus event.
|
||||||
|
FlutterViewId view_id;
|
||||||
|
|
||||||
|
/// The focus state of the view.
|
||||||
|
FlutterViewFocusState state;
|
||||||
|
|
||||||
|
/// The direction in which the focus transitioned across [FlutterView]s.
|
||||||
|
FlutterViewFocusDirection direction;
|
||||||
|
} FlutterViewFocusEvent;
|
||||||
|
|
||||||
|
/// A FlutterViewFocusChangeRequest is sent by the engine to the embedder when
|
||||||
|
/// when a FlutterView focus state has changed and native view focus
|
||||||
|
/// needs to be updated.
|
||||||
|
///
|
||||||
|
/// Received in FlutterProjectArgs.view_focus_change_request_callback.
|
||||||
|
typedef struct {
|
||||||
|
/// The size of this struct.
|
||||||
|
/// Must be sizeof(FlutterViewFocusChangeRequest).
|
||||||
|
size_t struct_size;
|
||||||
|
|
||||||
|
/// The identifier of the view that received the focus event.
|
||||||
|
FlutterViewId view_id;
|
||||||
|
|
||||||
|
/// The focus state of the view.
|
||||||
|
FlutterViewFocusState state;
|
||||||
|
|
||||||
|
/// The direction in which the focus transitioned across [FlutterView]s.
|
||||||
|
FlutterViewFocusDirection direction;
|
||||||
|
} FlutterViewFocusChangeRequest;
|
||||||
|
|
||||||
/// The phase of the pointer event.
|
/// The phase of the pointer event.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kCancel,
|
kCancel,
|
||||||
@ -1644,6 +1712,10 @@ typedef void (*FlutterChannelUpdateCallback)(
|
|||||||
const FlutterChannelUpdate* /* channel update */,
|
const FlutterChannelUpdate* /* channel update */,
|
||||||
void* /* user data */);
|
void* /* user data */);
|
||||||
|
|
||||||
|
typedef void (*FlutterViewFocusChangeRequestCallback)(
|
||||||
|
const FlutterViewFocusChangeRequest* /* request */,
|
||||||
|
void* /* user data */);
|
||||||
|
|
||||||
typedef struct _FlutterTaskRunner* FlutterTaskRunner;
|
typedef struct _FlutterTaskRunner* FlutterTaskRunner;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -2553,6 +2625,12 @@ typedef struct {
|
|||||||
/// being registered on the framework side. The callback is invoked from
|
/// being registered on the framework side. The callback is invoked from
|
||||||
/// a task posted to the platform thread.
|
/// a task posted to the platform thread.
|
||||||
FlutterChannelUpdateCallback channel_update_callback;
|
FlutterChannelUpdateCallback channel_update_callback;
|
||||||
|
|
||||||
|
/// The callback invoked by the engine when FlutterView focus state has
|
||||||
|
/// changed. The embedder can use this callback to request focus change for
|
||||||
|
/// the native view. The callback is invoked from a task posted to the
|
||||||
|
/// platform thread.
|
||||||
|
FlutterViewFocusChangeRequestCallback view_focus_change_request_callback;
|
||||||
} FlutterProjectArgs;
|
} FlutterProjectArgs;
|
||||||
|
|
||||||
#ifndef FLUTTER_ENGINE_NO_PROTOTYPES
|
#ifndef FLUTTER_ENGINE_NO_PROTOTYPES
|
||||||
@ -2757,6 +2835,16 @@ FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
|
|||||||
engine,
|
engine,
|
||||||
const FlutterRemoveViewInfo* info);
|
const FlutterRemoveViewInfo* info);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/// @brief Notifies the engine that platform view focus state has changed.
|
||||||
|
///
|
||||||
|
/// @param[in] engine A running engine instance
|
||||||
|
/// @param[in] event The focus event data describing the change.
|
||||||
|
FLUTTER_EXPORT
|
||||||
|
FlutterEngineResult FlutterEngineSendViewFocusEvent(
|
||||||
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
||||||
|
const FlutterViewFocusEvent* event);
|
||||||
|
|
||||||
FLUTTER_EXPORT
|
FLUTTER_EXPORT
|
||||||
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
|
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
|
||||||
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
||||||
@ -3433,6 +3521,9 @@ typedef FlutterEngineResult (*FlutterEngineAddViewFnPtr)(
|
|||||||
typedef FlutterEngineResult (*FlutterEngineRemoveViewFnPtr)(
|
typedef FlutterEngineResult (*FlutterEngineRemoveViewFnPtr)(
|
||||||
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
||||||
const FlutterRemoveViewInfo* info);
|
const FlutterRemoveViewInfo* info);
|
||||||
|
typedef FlutterEngineResult (*FlutterEngineSendViewFocusEventFnPtr)(
|
||||||
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
||||||
|
const FlutterViewFocusEvent* event);
|
||||||
|
|
||||||
/// Function-pointer-based versions of the APIs above.
|
/// Function-pointer-based versions of the APIs above.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -3481,6 +3572,7 @@ typedef struct {
|
|||||||
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback;
|
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback;
|
||||||
FlutterEngineAddViewFnPtr AddView;
|
FlutterEngineAddViewFnPtr AddView;
|
||||||
FlutterEngineRemoveViewFnPtr RemoveView;
|
FlutterEngineRemoveViewFnPtr RemoveView;
|
||||||
|
FlutterEngineSendViewFocusEventFnPtr SendViewFocusEvent;
|
||||||
} FlutterEngineProcTable;
|
} FlutterEngineProcTable;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -1600,3 +1600,30 @@ Future<void> render_impeller_image_snapshot_test() async {
|
|||||||
final bool result = (pixel & 0xFF) == color.alpha && ((pixel >> 8) & 0xFF) == color.blue;
|
final bool result = (pixel & 0xFF) == color.alpha && ((pixel >> 8) & 0xFF) == color.blue;
|
||||||
notifyBoolValue(result);
|
notifyBoolValue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void testSendViewFocusEvent() {
|
||||||
|
PlatformDispatcher.instance.onViewFocusChange = (ViewFocusEvent event) {
|
||||||
|
notifyStringValue('${event.viewId} ${event.state} ${event.direction}');
|
||||||
|
};
|
||||||
|
signalNativeTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void testSendViewFocusChangeRequest() {
|
||||||
|
PlatformDispatcher.instance.requestViewFocusChange(
|
||||||
|
viewId: 1,
|
||||||
|
state: ViewFocusState.unfocused,
|
||||||
|
direction: ViewFocusDirection.undefined,
|
||||||
|
);
|
||||||
|
PlatformDispatcher.instance.requestViewFocusChange(
|
||||||
|
viewId: 2,
|
||||||
|
state: ViewFocusState.focused,
|
||||||
|
direction: ViewFocusDirection.forward,
|
||||||
|
);
|
||||||
|
PlatformDispatcher.instance.requestViewFocusChange(
|
||||||
|
viewId: 3,
|
||||||
|
state: ViewFocusState.focused,
|
||||||
|
direction: ViewFocusDirection.backward,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -207,6 +207,13 @@ void PlatformViewEmbedder::SendChannelUpdate(const std::string& name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlatformViewEmbedder::RequestViewFocusChange(
|
||||||
|
const ViewFocusChangeRequest& request) {
|
||||||
|
if (platform_dispatch_table_.view_focus_change_request_callback != nullptr) {
|
||||||
|
platform_dispatch_table_.view_focus_change_request_callback(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<PlatformMessageHandler>
|
std::shared_ptr<PlatformMessageHandler>
|
||||||
PlatformViewEmbedder::GetPlatformMessageHandler() const {
|
PlatformViewEmbedder::GetPlatformMessageHandler() const {
|
||||||
return platform_message_handler_;
|
return platform_message_handler_;
|
||||||
|
@ -45,6 +45,8 @@ class PlatformViewEmbedder final : public PlatformView {
|
|||||||
const std::vector<std::string>& supported_locale_data)>;
|
const std::vector<std::string>& supported_locale_data)>;
|
||||||
using OnPreEngineRestartCallback = std::function<void()>;
|
using OnPreEngineRestartCallback = std::function<void()>;
|
||||||
using ChanneUpdateCallback = std::function<void(const std::string&, bool)>;
|
using ChanneUpdateCallback = std::function<void(const std::string&, bool)>;
|
||||||
|
using ViewFocusChangeRequestCallback =
|
||||||
|
std::function<void(const ViewFocusChangeRequest&)>;
|
||||||
|
|
||||||
struct PlatformDispatchTable {
|
struct PlatformDispatchTable {
|
||||||
UpdateSemanticsCallback update_semantics_callback; // optional
|
UpdateSemanticsCallback update_semantics_callback; // optional
|
||||||
@ -55,6 +57,8 @@ class PlatformViewEmbedder final : public PlatformView {
|
|||||||
compute_platform_resolved_locale_callback;
|
compute_platform_resolved_locale_callback;
|
||||||
OnPreEngineRestartCallback on_pre_engine_restart_callback; // optional
|
OnPreEngineRestartCallback on_pre_engine_restart_callback; // optional
|
||||||
ChanneUpdateCallback on_channel_update; // optional
|
ChanneUpdateCallback on_channel_update; // optional
|
||||||
|
ViewFocusChangeRequestCallback
|
||||||
|
view_focus_change_request_callback; // optional
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a platform view that sets up a software rasterizer.
|
// Create a platform view that sets up a software rasterizer.
|
||||||
@ -142,6 +146,9 @@ class PlatformViewEmbedder final : public PlatformView {
|
|||||||
// |PlatformView|
|
// |PlatformView|
|
||||||
void SendChannelUpdate(const std::string& name, bool listening) override;
|
void SendChannelUpdate(const std::string& name, bool listening) override;
|
||||||
|
|
||||||
|
// |PlatformView|
|
||||||
|
void RequestViewFocusChange(const ViewFocusChangeRequest& request) override;
|
||||||
|
|
||||||
FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder);
|
FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@ class MockDelegate : public PlatformView::Delegate {
|
|||||||
OnPlatformViewRemoveView,
|
OnPlatformViewRemoveView,
|
||||||
(int64_t view_id, RemoveViewCallback callback),
|
(int64_t view_id, RemoveViewCallback callback),
|
||||||
(override));
|
(override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
OnPlatformViewSendViewFocusEvent,
|
||||||
|
(const ViewFocusEvent& event),
|
||||||
|
(override));
|
||||||
MOCK_METHOD(void,
|
MOCK_METHOD(void,
|
||||||
OnPlatformViewSetNextFrameCallback,
|
OnPlatformViewSetNextFrameCallback,
|
||||||
(const fml::closure& closure),
|
(const fml::closure& closure),
|
||||||
|
@ -37,6 +37,7 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
|
|||||||
SetLogMessageCallbackHook();
|
SetLogMessageCallbackHook();
|
||||||
SetLocalizationCallbackHooks();
|
SetLocalizationCallbackHooks();
|
||||||
SetChannelUpdateCallbackHook();
|
SetChannelUpdateCallbackHook();
|
||||||
|
SetViewFocusChangeRequestHook();
|
||||||
AddCommandLineArgument("--disable-vm-service");
|
AddCommandLineArgument("--disable-vm-service");
|
||||||
|
|
||||||
if (preference == InitializationPreference::kSnapshotsInitialize ||
|
if (preference == InitializationPreference::kSnapshotsInitialize ||
|
||||||
@ -112,6 +113,11 @@ void EmbedderConfigBuilder::SetChannelUpdateCallbackHook() {
|
|||||||
context_.GetChannelUpdateCallbackHook();
|
context_.GetChannelUpdateCallbackHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmbedderConfigBuilder::SetViewFocusChangeRequestHook() {
|
||||||
|
project_args_.view_focus_change_request_callback =
|
||||||
|
context_.GetViewFocusChangeRequestCallbackHook();
|
||||||
|
}
|
||||||
|
|
||||||
void EmbedderConfigBuilder::SetLogTag(std::string tag) {
|
void EmbedderConfigBuilder::SetLogTag(std::string tag) {
|
||||||
log_tag_ = std::move(tag);
|
log_tag_ = std::move(tag);
|
||||||
project_args_.log_tag = log_tag_.c_str();
|
project_args_.log_tag = log_tag_.c_str();
|
||||||
@ -194,6 +200,11 @@ void EmbedderConfigBuilder::SetPlatformMessageCallback(
|
|||||||
context_.SetPlatformMessageCallback(callback);
|
context_.SetPlatformMessageCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmbedderConfigBuilder::SetViewFocusChangeRequestCallback(
|
||||||
|
const std::function<void(const FlutterViewFocusChangeRequest*)>& callback) {
|
||||||
|
context_.SetViewFocusChangeRequestCallback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
void EmbedderConfigBuilder::SetCompositor(bool avoid_backing_store_cache,
|
void EmbedderConfigBuilder::SetCompositor(bool avoid_backing_store_cache,
|
||||||
bool use_present_layers_callback) {
|
bool use_present_layers_callback) {
|
||||||
context_.SetupCompositor();
|
context_.SetupCompositor();
|
||||||
|
@ -59,6 +59,8 @@ class EmbedderConfigBuilder {
|
|||||||
|
|
||||||
void SetChannelUpdateCallbackHook();
|
void SetChannelUpdateCallbackHook();
|
||||||
|
|
||||||
|
void SetViewFocusChangeRequestHook();
|
||||||
|
|
||||||
// Used to set a custom log tag.
|
// Used to set a custom log tag.
|
||||||
void SetLogTag(std::string tag);
|
void SetLogTag(std::string tag);
|
||||||
|
|
||||||
@ -81,6 +83,10 @@ class EmbedderConfigBuilder {
|
|||||||
void SetPlatformMessageCallback(
|
void SetPlatformMessageCallback(
|
||||||
const std::function<void(const FlutterPlatformMessage*)>& callback);
|
const std::function<void(const FlutterPlatformMessage*)>& callback);
|
||||||
|
|
||||||
|
void SetViewFocusChangeRequestCallback(
|
||||||
|
const std::function<void(const FlutterViewFocusChangeRequest*)>&
|
||||||
|
callback);
|
||||||
|
|
||||||
void SetCompositor(bool avoid_backing_store_cache = false,
|
void SetCompositor(bool avoid_backing_store_cache = false,
|
||||||
bool use_present_layers_callback = false);
|
bool use_present_layers_callback = false);
|
||||||
|
|
||||||
@ -101,6 +107,9 @@ class EmbedderConfigBuilder {
|
|||||||
// text context vis `SetVsyncCallback`.
|
// text context vis `SetVsyncCallback`.
|
||||||
void SetupVsyncCallback();
|
void SetupVsyncCallback();
|
||||||
|
|
||||||
|
void SetViewFocusChangeRequestCallback(
|
||||||
|
const FlutterViewFocusChangeRequestCallback& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EmbedderTestContext& context_;
|
EmbedderTestContext& context_;
|
||||||
FlutterProjectArgs project_args_ = {};
|
FlutterProjectArgs project_args_ = {};
|
||||||
|
@ -156,6 +156,11 @@ void EmbedderTestContext::SetChannelUpdateCallback(
|
|||||||
channel_update_callback_ = callback;
|
channel_update_callback_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmbedderTestContext::SetViewFocusChangeRequestCallback(
|
||||||
|
const ViewFocusChangeRequestCallback& callback) {
|
||||||
|
view_focus_change_request_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
void EmbedderTestContext::PlatformMessageCallback(
|
void EmbedderTestContext::PlatformMessageCallback(
|
||||||
const FlutterPlatformMessage* message) {
|
const FlutterPlatformMessage* message) {
|
||||||
if (platform_message_callback_) {
|
if (platform_message_callback_) {
|
||||||
@ -255,6 +260,16 @@ EmbedderTestContext::GetChannelUpdateCallbackHook() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlutterViewFocusChangeRequestCallback
|
||||||
|
EmbedderTestContext::GetViewFocusChangeRequestCallbackHook() {
|
||||||
|
return [](const FlutterViewFocusChangeRequest* request, void* user_data) {
|
||||||
|
auto context = reinterpret_cast<EmbedderTestContext*>(user_data);
|
||||||
|
if (context->view_focus_change_request_callback_) {
|
||||||
|
context->view_focus_change_request_callback_(request);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
FlutterTransformation EmbedderTestContext::GetRootSurfaceTransformation() {
|
FlutterTransformation EmbedderTestContext::GetRootSurfaceTransformation() {
|
||||||
return FlutterTransformationMake(root_surface_transformation_);
|
return FlutterTransformationMake(root_surface_transformation_);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,8 @@ using SemanticsActionCallback =
|
|||||||
using LogMessageCallback =
|
using LogMessageCallback =
|
||||||
std::function<void(const char* tag, const char* message)>;
|
std::function<void(const char* tag, const char* message)>;
|
||||||
using ChannelUpdateCallback = std::function<void(const FlutterChannelUpdate*)>;
|
using ChannelUpdateCallback = std::function<void(const FlutterChannelUpdate*)>;
|
||||||
|
using ViewFocusChangeRequestCallback =
|
||||||
|
std::function<void(const FlutterViewFocusChangeRequest*)>;
|
||||||
|
|
||||||
struct AOTDataDeleter {
|
struct AOTDataDeleter {
|
||||||
void operator()(FlutterEngineAOTData aot_data) {
|
void operator()(FlutterEngineAOTData aot_data) {
|
||||||
@ -94,6 +96,9 @@ class EmbedderTestContext {
|
|||||||
|
|
||||||
void SetChannelUpdateCallback(const ChannelUpdateCallback& callback);
|
void SetChannelUpdateCallback(const ChannelUpdateCallback& callback);
|
||||||
|
|
||||||
|
void SetViewFocusChangeRequestCallback(
|
||||||
|
const ViewFocusChangeRequestCallback& callback);
|
||||||
|
|
||||||
std::future<sk_sp<SkImage>> GetNextSceneImage();
|
std::future<sk_sp<SkImage>> GetNextSceneImage();
|
||||||
|
|
||||||
EmbedderTestCompositor& GetCompositor();
|
EmbedderTestCompositor& GetCompositor();
|
||||||
@ -133,6 +138,7 @@ class EmbedderTestContext {
|
|||||||
SemanticsNodeCallback update_semantics_node_callback_;
|
SemanticsNodeCallback update_semantics_node_callback_;
|
||||||
SemanticsActionCallback update_semantics_custom_action_callback_;
|
SemanticsActionCallback update_semantics_custom_action_callback_;
|
||||||
ChannelUpdateCallback channel_update_callback_;
|
ChannelUpdateCallback channel_update_callback_;
|
||||||
|
ViewFocusChangeRequestCallback view_focus_change_request_callback_;
|
||||||
std::function<void(const FlutterPlatformMessage*)> platform_message_callback_;
|
std::function<void(const FlutterPlatformMessage*)> platform_message_callback_;
|
||||||
LogMessageCallback log_message_callback_;
|
LogMessageCallback log_message_callback_;
|
||||||
std::unique_ptr<EmbedderTestCompositor> compositor_;
|
std::unique_ptr<EmbedderTestCompositor> compositor_;
|
||||||
@ -158,6 +164,8 @@ class EmbedderTestContext {
|
|||||||
|
|
||||||
FlutterChannelUpdateCallback GetChannelUpdateCallbackHook();
|
FlutterChannelUpdateCallback GetChannelUpdateCallbackHook();
|
||||||
|
|
||||||
|
FlutterViewFocusChangeRequestCallback GetViewFocusChangeRequestCallbackHook();
|
||||||
|
|
||||||
void SetupAOTMappingsIfNecessary();
|
void SetupAOTMappingsIfNecessary();
|
||||||
|
|
||||||
void SetupAOTDataIfNecessary();
|
void SetupAOTDataIfNecessary();
|
||||||
|
@ -2045,6 +2045,124 @@ TEST_F(EmbedderTest, CanRenderMultipleViews) {
|
|||||||
latch123.Wait();
|
latch123.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(const FlutterViewFocusChangeRequest& lhs,
|
||||||
|
const FlutterViewFocusChangeRequest& rhs) {
|
||||||
|
return lhs.view_id == rhs.view_id && lhs.state == rhs.state &&
|
||||||
|
lhs.direction == rhs.direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmbedderTest, SendsViewFocusChangeRequest) {
|
||||||
|
auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
|
||||||
|
auto platform_task_runner = CreateNewThread("test_platform_thread");
|
||||||
|
UniqueEngine engine;
|
||||||
|
static std::mutex engine_mutex;
|
||||||
|
EmbedderTestTaskRunner test_platform_task_runner(
|
||||||
|
platform_task_runner, [&](FlutterTask task) {
|
||||||
|
std::scoped_lock lock(engine_mutex);
|
||||||
|
if (!engine.is_valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FlutterEngineRunTask(engine.get(), &task);
|
||||||
|
});
|
||||||
|
fml::CountDownLatch latch(3);
|
||||||
|
std::vector<FlutterViewFocusChangeRequest> received_requests;
|
||||||
|
platform_task_runner->PostTask([&]() {
|
||||||
|
EmbedderConfigBuilder builder(context);
|
||||||
|
builder.SetSurface(SkISize::Make(1, 1));
|
||||||
|
builder.SetDartEntrypoint("testSendViewFocusChangeRequest");
|
||||||
|
const auto platform_task_runner_description =
|
||||||
|
test_platform_task_runner.GetFlutterTaskRunnerDescription();
|
||||||
|
builder.SetPlatformTaskRunner(&platform_task_runner_description);
|
||||||
|
builder.SetViewFocusChangeRequestCallback(
|
||||||
|
[&](const FlutterViewFocusChangeRequest* request) {
|
||||||
|
EXPECT_TRUE(platform_task_runner->RunsTasksOnCurrentThread());
|
||||||
|
received_requests.push_back(*request);
|
||||||
|
latch.CountDown();
|
||||||
|
});
|
||||||
|
engine = builder.LaunchEngine();
|
||||||
|
ASSERT_TRUE(engine.is_valid());
|
||||||
|
});
|
||||||
|
latch.Wait();
|
||||||
|
|
||||||
|
std::vector<FlutterViewFocusChangeRequest> expected_requests{
|
||||||
|
{.view_id = 1, .state = kUnfocused, .direction = kUndefined},
|
||||||
|
{.view_id = 2, .state = kFocused, .direction = kForward},
|
||||||
|
{.view_id = 3, .state = kFocused, .direction = kBackward},
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_EQ(received_requests.size(), expected_requests.size());
|
||||||
|
for (size_t i = 0; i < received_requests.size(); ++i) {
|
||||||
|
ASSERT_TRUE(received_requests[i] == expected_requests[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fml::AutoResetWaitableEvent kill_latch;
|
||||||
|
platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
|
||||||
|
std::scoped_lock lock(engine_mutex);
|
||||||
|
engine.reset();
|
||||||
|
|
||||||
|
// There may still be pending tasks on the platform thread that were queued
|
||||||
|
// by the test_task_runner. Signal the latch after these tasks have been
|
||||||
|
// consumed.
|
||||||
|
platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
|
||||||
|
}));
|
||||||
|
kill_latch.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmbedderTest, CanSendViewFocusEvent) {
|
||||||
|
auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
|
||||||
|
EmbedderConfigBuilder builder(context);
|
||||||
|
builder.SetSurface(SkISize::Make(1, 1));
|
||||||
|
builder.SetDartEntrypoint("testSendViewFocusEvent");
|
||||||
|
|
||||||
|
fml::AutoResetWaitableEvent latch;
|
||||||
|
std::string last_event;
|
||||||
|
|
||||||
|
context.AddNativeCallback(
|
||||||
|
"SignalNativeTest",
|
||||||
|
CREATE_NATIVE_ENTRY(
|
||||||
|
[&latch](Dart_NativeArguments args) { latch.Signal(); }));
|
||||||
|
context.AddNativeCallback("NotifyStringValue",
|
||||||
|
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
|
||||||
|
const auto message_from_dart =
|
||||||
|
tonic::DartConverter<std::string>::FromDart(
|
||||||
|
Dart_GetNativeArgument(args, 0));
|
||||||
|
last_event = message_from_dart;
|
||||||
|
latch.Signal();
|
||||||
|
}));
|
||||||
|
|
||||||
|
auto engine = builder.LaunchEngine();
|
||||||
|
ASSERT_TRUE(engine.is_valid());
|
||||||
|
// Wait until the focus change handler is attached.
|
||||||
|
latch.Wait();
|
||||||
|
latch.Reset();
|
||||||
|
|
||||||
|
FlutterViewFocusEvent event1{
|
||||||
|
.struct_size = sizeof(FlutterViewFocusEvent),
|
||||||
|
.view_id = 1,
|
||||||
|
.state = kFocused,
|
||||||
|
.direction = kUndefined,
|
||||||
|
};
|
||||||
|
FlutterEngineResult result =
|
||||||
|
FlutterEngineSendViewFocusEvent(engine.get(), &event1);
|
||||||
|
ASSERT_EQ(result, kSuccess);
|
||||||
|
latch.Wait();
|
||||||
|
ASSERT_EQ(last_event,
|
||||||
|
"1 ViewFocusState.focused ViewFocusDirection.undefined");
|
||||||
|
|
||||||
|
FlutterViewFocusEvent event2{
|
||||||
|
.struct_size = sizeof(FlutterViewFocusEvent),
|
||||||
|
.view_id = 2,
|
||||||
|
.state = kUnfocused,
|
||||||
|
.direction = kBackward,
|
||||||
|
};
|
||||||
|
latch.Reset();
|
||||||
|
result = FlutterEngineSendViewFocusEvent(engine.get(), &event2);
|
||||||
|
ASSERT_EQ(result, kSuccess);
|
||||||
|
latch.Wait();
|
||||||
|
ASSERT_EQ(last_event,
|
||||||
|
"2 ViewFocusState.unfocused ViewFocusDirection.backward");
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/// Test that the backing store is created with the correct view ID, is used
|
/// Test that the backing store is created with the correct view ID, is used
|
||||||
/// for the correct view, and is cached according to their views.
|
/// for the correct view, and is cached according to their views.
|
||||||
|
@ -157,6 +157,9 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate {
|
|||||||
void UpdateAssetResolverByType(
|
void UpdateAssetResolverByType(
|
||||||
std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
|
std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
|
||||||
flutter::AssetResolver::AssetResolverType type) {}
|
flutter::AssetResolver::AssetResolverType type) {}
|
||||||
|
// |flutter::PlatformView::Delegate|
|
||||||
|
void OnPlatformViewSendViewFocusEvent(const flutter::ViewFocusEvent& event) {
|
||||||
|
};
|
||||||
|
|
||||||
flutter::Surface* surface() const { return surface_.get(); }
|
flutter::Surface* surface() const { return surface_.get(); }
|
||||||
flutter::PlatformMessage* message() const { return message_.get(); }
|
flutter::PlatformMessage* message() const { return message_.get(); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user