Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

MultiView changes for dart:ui #42493

Merged
merged 5 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion lib/ui/fixtures/ui_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,37 @@ void hooksTests() async {
expectEquals(x.countryCode, y.countryCode);
});

await test('PlatformDispatcher.view getter returns view with provided ID', () {
const int viewId = 123456789;
_callHook(
'_updateWindowMetrics',
21,
viewId, // window Id
1.0, // devicePixelRatio
800.0, // width
600.0, // height
50.0, // paddingTop
0.0, // paddingRight
40.0, // paddingBottom
0.0, // paddingLeft
0.0, // insetTop
0.0, // insetRight
0.0, // insetBottom
0.0, // insetLeft
0.0, // systemGestureInsetTop
0.0, // systemGestureInsetRight
0.0, // systemGestureInsetBottom
0.0, // systemGestureInsetLeft
22.0, // physicalTouchSlop
<double>[], // display features bounds
<int>[], // display features types
<int>[], // display features states
0, // Display ID
);

expectEquals(PlatformDispatcher.instance.view(id: viewId)?.viewId, viewId);
});

await test('View padding/insets/viewPadding/systemGestureInsets', () {
_callHook(
'_updateWindowMetrics',
Expand Down Expand Up @@ -602,7 +633,7 @@ void hooksTests() async {
expectEquals(window.systemGestureInsets.bottom, 44.0);
});

await test('Window physical touch slop', () {
await test('Window physical touch slop', () {
_callHook(
'_updateWindowMetrics',
21,
Expand Down Expand Up @@ -816,6 +847,25 @@ void hooksTests() async {
expectEquals(action, 4);
});

await test('onSemanticsActionEvent preserves callback zone', () {
late Zone innerZone;
late Zone runZone;
late SemanticsActionEvent action;

runZoned(() {
innerZone = Zone.current;
PlatformDispatcher.instance.onSemanticsActionEvent = (SemanticsActionEvent actionEvent) {
runZone = Zone.current;
action = actionEvent;
};
});

_callHook('_dispatchSemanticsAction', 3, 1234, 4, null);
expectIdentical(runZone, innerZone);
expectEquals(action.nodeId, 1234);
expectEquals(action.type.index, 4);
});

await test('onPlatformMessage preserves callback zone', () {
late Zone innerZone;
late Zone runZone;
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void _updateDisplays(

@pragma('vm:entry-point')
void _updateWindowMetrics(
Object id,
int id,
double devicePixelRatio,
double width,
double height,
Expand Down
86 changes: 84 additions & 2 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ typedef PointerDataPacketCallback = void Function(PointerDataPacket packet);
typedef KeyDataCallback = bool Function(KeyData data);

/// Signature for [PlatformDispatcher.onSemanticsAction].
// TODO(goderbauer): Deprecate/remove this when the framework has migrated to SemanticsActionEventCallback.
typedef SemanticsActionCallback = void Function(int nodeId, SemanticsAction action, ByteData? args);

/// Signature for [PlatformDispatcher.onSemanticsActionEvent].
typedef SemanticsActionEventCallback = void Function(SemanticsActionEvent action);

/// Signature for responses to platform messages.
///
/// Used as a parameter to [PlatformDispatcher.sendPlatformMessage] and
Expand Down Expand Up @@ -174,7 +178,11 @@ class PlatformDispatcher {
///
/// If any of their configurations change, [onMetricsChanged] will be called.
Iterable<FlutterView> get views => _views.values;
final Map<Object, FlutterView> _views = <Object, FlutterView>{};
final Map<int, FlutterView> _views = <int, FlutterView>{};

/// Returns the [FlutterView] with the provided ID if one exists, or null
/// otherwise.
FlutterView? view({required int id}) => _views[id];

// A map of opaque platform view identifiers to view configurations.
final Map<Object, _ViewConfiguration> _viewConfigurations = <Object, _ViewConfiguration>{};
Expand Down Expand Up @@ -250,7 +258,7 @@ class PlatformDispatcher {
//
// Updates the metrics of the window with the given id.
void _updateWindowMetrics(
Object id,
int id,
double devicePixelRatio,
double width,
double height,
Expand Down Expand Up @@ -436,6 +444,7 @@ class PlatformDispatcher {
for (int i = 0; i < length; ++i) {
int offset = i * _kPointerDataFieldCount;
data.add(PointerData(
// TODO(goderbauer): Wire up viewId.
embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian),
timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)),
change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
Expand Down Expand Up @@ -1150,6 +1159,7 @@ class PlatformDispatcher {
///
/// The framework invokes this callback in the same zone in which the
/// callback was set.
// TODO(goderbauer): Deprecate/remove this when the framework has migrated to onSemanticsActionEvent.
SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction;
SemanticsActionCallback? _onSemanticsAction;
Zone _onSemanticsActionZone = Zone.root;
Expand All @@ -1158,6 +1168,22 @@ class PlatformDispatcher {
_onSemanticsActionZone = Zone.current;
}

/// A callback that is invoked whenever the user requests an action to be
/// performed on a semantics node.
///
/// This callback is used when the user expresses the action they wish to
/// perform based on the semantics node supplied by updateSemantics.
///
/// The framework invokes this callback in the same zone in which the
/// callback was set.
SemanticsActionEventCallback? get onSemanticsActionEvent => _onSemanticsActionEvent;
SemanticsActionEventCallback? _onSemanticsActionEvent;
Zone _onSemanticsActionEventZone = Zone.root;
set onSemanticsActionEvent(SemanticsActionEventCallback? callback) {
_onSemanticsActionEvent = callback;
_onSemanticsActionEventZone = Zone.current;
}

// Called from the engine via hooks.dart.
void _updateFrameData(int frameNumber) {
final FrameData previous = _frameData;
Expand Down Expand Up @@ -1190,6 +1216,16 @@ class PlatformDispatcher {
SemanticsAction.fromIndex(action)!,
args,
);
_invoke1<SemanticsActionEvent>(
onSemanticsActionEvent,
_onSemanticsActionEventZone,
SemanticsActionEvent(
type: SemanticsAction.fromIndex(action)!,
nodeId: nodeId,
viewId: 0, // TODO(goderbauer): Wire up the real view ID.
arguments: args,
),
);
}

ErrorCallback? _onError;
Expand Down Expand Up @@ -2350,3 +2386,49 @@ enum DartPerformanceMode {
/// frequently performing work.
memory,
}

/// An event to request a [SemanticsAction] of [type] to be performed on the
/// [SemanticsNode] identified by [nodeId] owned by the [FlutterView] identified
/// by [viewId].
///
/// Used by [SemanticsBinding.performSemanticsAction].
class SemanticsActionEvent {
/// Creates a [SemanticsActionEvent].
const SemanticsActionEvent({
required this.type,
required this.viewId,
required this.nodeId,
this.arguments,
});

/// The type of action to be performed.
final SemanticsAction type;

/// The id of the [FlutterView] the [SemanticsNode] identified by [nodeId] is
/// associated with.
final int viewId;

/// The id of the [SemanticsNode] on which the action is to be performed.
final int nodeId;

/// Optional arguments for the action.
final Object? arguments;

static const Object _noArgumentPlaceholder = Object();

/// Create a clone of the [SemanticsActionEvent] but with provided parameters
/// replaced.
SemanticsActionEvent copyWith({
SemanticsAction? type,
int? viewId,
int? nodeId,
Object? arguments = _noArgumentPlaceholder,
}) {
return SemanticsActionEvent(
type: type ?? this.type,
viewId: viewId ?? this.viewId,
nodeId: nodeId ?? this.nodeId,
arguments: arguments == _noArgumentPlaceholder ? this.arguments : arguments,
);
}
}
14 changes: 10 additions & 4 deletions lib/ui/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ enum PointerSignalKind {
class PointerData {
/// Creates an object that represents the state of a pointer.
const PointerData({
this.viewId = 0,
this.embedderId = 0,
this.timeStamp = Duration.zero,
this.change = PointerChange.cancel,
Expand Down Expand Up @@ -178,11 +179,16 @@ class PointerData {
this.rotation = 0.0,
});

/// Unique identifier that ties the [PointerEvent] to embedder event created it.
/// The ID of the [FlutterView] this [PointerEvent] originated from.
final int viewId;

/// Unique identifier that ties the [PointerEvent] to the embedder
/// event that created it.
/// it.
///
/// No two pointer events can have the same [embedderId]. This is different from
/// [pointerIdentifier] - used for hit-testing, whereas [embedderId] is used to
/// identify the platform event.
/// No two pointer events can have the same [embedderId]. This is different
/// from [pointerIdentifier] - used for hit-testing, whereas [embedderId] is
/// used to identify the platform event.
final int embedderId;

/// Time of event dispatch, relative to an arbitrary timeline.
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class FlutterView {
FlutterView._(this.viewId, this.platformDispatcher);

/// The opaque ID for this view.
final Object viewId;
final int viewId;

/// The platform dispatcher that this view is registered with, and gets its
/// information from.
Expand Down
36 changes: 36 additions & 0 deletions lib/web_ui/lib/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ typedef TimingsCallback = void Function(List<FrameTiming> timings);
typedef PointerDataPacketCallback = void Function(PointerDataPacket packet);
typedef KeyDataCallback = bool Function(KeyData data);
typedef SemanticsActionCallback = void Function(int nodeId, SemanticsAction action, ByteData? args);
typedef SemanticsActionEventCallback = void Function(SemanticsActionEvent action);
typedef PlatformMessageResponseCallback = void Function(ByteData? data);
typedef PlatformMessageCallback = void Function(
String name, ByteData? data, PlatformMessageResponseCallback? callback);
Expand All @@ -33,6 +34,8 @@ abstract class PlatformDispatcher {

Iterable<FlutterView> get views;

FlutterView? view({required int id});

FlutterView? get implicitView;

VoidCallback? get onMetricsChanged;
Expand Down Expand Up @@ -135,6 +138,9 @@ abstract class PlatformDispatcher {
SemanticsActionCallback? get onSemanticsAction;
set onSemanticsAction(SemanticsActionCallback? callback);

SemanticsActionEventCallback? get onSemanticsActionEvent;
set onSemanticsActionEvent(SemanticsActionEventCallback? callback);

ErrorCallback? get onError;
set onError(ErrorCallback? callback);

Expand Down Expand Up @@ -493,3 +499,33 @@ enum DartPerformanceMode {
throughput,
memory,
}

class SemanticsActionEvent {
const SemanticsActionEvent({
required this.type,
required this.viewId,
required this.nodeId,
this.arguments,
});

final SemanticsAction type;
final int viewId;
final int nodeId;
final Object? arguments;

static const Object _noArgumentPlaceholder = Object();

SemanticsActionEvent copyWith({
SemanticsAction? type,
int? viewId,
int? nodeId,
Object? arguments = _noArgumentPlaceholder,
}) {
return SemanticsActionEvent(
type: type ?? this.type,
viewId: viewId ?? this.viewId,
nodeId: nodeId ?? this.nodeId,
arguments: arguments == _noArgumentPlaceholder ? this.arguments : arguments,
);
}
}
2 changes: 2 additions & 0 deletions lib/web_ui/lib/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum PointerSignalKind {

class PointerData {
const PointerData({
this.viewId = 0,
this.embedderId = 0,
this.timeStamp = Duration.zero,
this.change = PointerChange.cancel,
Expand Down Expand Up @@ -72,6 +73,7 @@ class PointerData {
this.scale = 0.0,
this.rotation = 0.0,
});
final int viewId;
final int embedderId;
final Duration timeStamp;
final PointerChange change;
Expand Down
33 changes: 32 additions & 1 deletion lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
/// The current list of windows.
@override
Iterable<ui.FlutterView> get views => viewData.values;
final Map<Object, ui.FlutterView> viewData = <Object, ui.FlutterView>{};
final Map<int, ui.FlutterView> viewData = <int, ui.FlutterView>{};

/// Returns the [FlutterView] with the provided ID if one exists, or null
/// otherwise.
@override
ui.FlutterView? view({required int id}) => viewData[id];

/// A map of opaque platform window identifiers to window configurations.
///
Expand Down Expand Up @@ -1206,12 +1211,38 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_onSemanticsActionZone = Zone.current;
}

/// A callback that is invoked whenever the user requests an action to be
/// performed on a semantics node.
///
/// This callback is used when the user expresses the action they wish to
/// perform based on the semantics node supplied by updateSemantics.
///
/// The framework invokes this callback in the same zone in which the
/// callback was set.
@override
ui.SemanticsActionEventCallback? get onSemanticsActionEvent => _onSemanticsActionEvent;
ui.SemanticsActionEventCallback? _onSemanticsActionEvent;
Zone _onSemanticsActionEventZone = Zone.root;
@override
set onSemanticsActionEvent(ui.SemanticsActionEventCallback? callback) {
_onSemanticsActionEvent = callback;
_onSemanticsActionEventZone = Zone.current;
}

/// Engine code should use this method instead of the callback directly.
/// Otherwise zones won't work properly.
void invokeOnSemanticsAction(
int nodeId, ui.SemanticsAction action, ByteData? args) {
invoke3<int, ui.SemanticsAction, ByteData?>(
_onSemanticsAction, _onSemanticsActionZone, nodeId, action, args);
invoke1<ui.SemanticsActionEvent>(
_onSemanticsActionEvent, _onSemanticsActionEventZone, ui.SemanticsActionEvent(
type: action,
nodeId: nodeId,
viewId: 0, // TODO(goderbauer): Wire up the real view ID.
arguments: args,
),
);
}

// TODO(dnfield): make this work on web.
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow {
}

@override
final Object viewId;
final int viewId;

@override
final ui.PlatformDispatcher platformDispatcher;
Expand Down
Loading