[go: nahoru, domu]

Skip to content

Commit

Permalink
MultiView changes for dart:ui (flutter#42493)
Browse files Browse the repository at this point in the history
Fixes flutter/flutter#124991.

Framework tests are failing because I need to do a pre-migration of `flutter_test` for the Object -> int change of viewId. That's in flutter/flutter#128092.
  • Loading branch information
goderbauer committed Jun 2, 2023
1 parent 8769e9c commit fce6978
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 29 deletions.
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

0 comments on commit fce6978

Please sign in to comment.