Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ extern NSNotificationName const FlutterSemanticsUpdateNotification;
* Dart-related state and asynchronous tasks when navigating back and forth between a
* FlutterViewController and other `UIViewController`s.
*/
#ifdef __IPHONE_13_4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't need this, the presence of UIPointerInteractionDelegate will exist depending on the SDK used for compilation. We don't need to support old versions of Xcode.

FLUTTER_EXPORT
@interface FlutterViewController
: UIViewController <FlutterTextureRegistry, FlutterPluginRegistry, UIPointerInteractionDelegate>
#else
@interface FlutterViewController : UIViewController <FlutterTextureRegistry, FlutterPluginRegistry>
#endif

/**
* Initializes this FlutterViewController with the specified `FlutterEngine`.
Expand Down
106 changes: 105 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@
NSNotificationName const FlutterViewControllerShowHomeIndicator =
@"FlutterViewControllerShowHomeIndicator";

// Struct holding the mouse state. The engine doesn't keep track of which
// mouse buttons have been pressed, so it's the embedding's responsibility.
typedef struct MouseState {
// True if the last event sent to Flutter had at least one mouse button.
// pressed.
bool flutter_state_is_down = false;

// True if kAdd has been sent to Flutter. Used to determine whether
// to send a kAdd event before sending an incoming mouse event, since
// Flutter expects pointers to be added before events are sent for them.
bool flutter_state_is_added = false;

// Current coordinate of the mouse cursor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the units logical or real pixels?

CGPoint location = CGPointZero;

// Last reported translation for an in-flight pan gesture
CGPoint last_translation = CGPointZero;

// The currently pressed buttons, as represented in FlutterPointerEvent.
uint64_t buttons = 0;
} MouseState;

// This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the
// change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
// just a warning.
Expand Down Expand Up @@ -77,6 +99,11 @@ @implementation FlutterViewController {
// UIScrollView with height zero and a content offset so we can get those events. See also:
// https://github.com/flutter/flutter/issues/35050
fml::scoped_nsobject<UIScrollView> _scrollView;
#ifdef __IPHONE_13_4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't have to be ifdef'd. I think it's better to limit the amount of conditional compilation.

fml::scoped_nsobject<UIPointerInteraction> _pointerInteraction API_AVAILABLE(ios(13.4));
fml::scoped_nsobject<UIPanGestureRecognizer> _panGestureRecognizer API_AVAILABLE(ios(13.4));
MouseState _mouseState;
#endif
}

@synthesize displayingFlutterUI = _displayingFlutterUI;
Expand Down Expand Up @@ -528,6 +555,19 @@ - (void)viewDidLoad {
<< "FlutterViewController's view is loaded but is not attached to a FlutterEngine";
[_engine.get() attachView];

#ifdef __IPHONE_13_4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also remove this preprocessor guard.

if (@available(iOS 13.4, *)) {
_pointerInteraction.reset([[UIPointerInteraction alloc] initWithDelegate:self]);
[self.view addInteraction:_pointerInteraction];

_panGestureRecognizer.reset(
[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(scrollEvent:)]);
_panGestureRecognizer.get().allowedScrollTypesMask = UIScrollTypeMaskAll;
_panGestureRecognizer.get().allowedTouchTypes = @[ @(UITouchTypeIndirectPointer) ];
[_flutterView.get() addGestureRecognizer:_panGestureRecognizer.get()];
}
#endif

[super viewDidLoad];
}

Expand Down Expand Up @@ -685,8 +725,9 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state {
return flutter::PointerData::DeviceKind::kTouch;
case UITouchTypeStylus:
return flutter::PointerData::DeviceKind::kStylus;
case UITouchTypeIndirectPointer:
return flutter::PointerData::DeviceKind::kMouse;
default:
// TODO(53696): Handle the UITouchTypeIndirectPointer enum value.
FML_DLOG(INFO) << "Unhandled touch type: " << touch.type;
break;
}
Expand Down Expand Up @@ -1286,4 +1327,67 @@ - (BOOL)isPresentingViewController {
return self.presentedViewController != nil || self.isPresentingViewControllerAnimating;
}

#ifdef __IPHONE_13_4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too

- (flutter::PointerData)generatePointerDataForMouse {
const CGFloat scale = [UIScreen mainScreen].scale;

flutter::PointerData pointer_data;

pointer_data.Clear();

pointer_data.kind = flutter::PointerData::DeviceKind::kMouse;
pointer_data.change = _mouseState.flutter_state_is_added ? flutter::PointerData::Change::kAdd
: flutter::PointerData::Change::kHover;
pointer_data.pointer_identifier = reinterpret_cast<int64_t>(_pointerInteraction.get());

pointer_data.physical_x = _mouseState.location.x * scale;
pointer_data.physical_y = _mouseState.location.y * scale;

return pointer_data;
}

- (UIPointerRegion*)pointerInteraction:(UIPointerInteraction*)interaction
regionForRequest:(UIPointerRegionRequest*)request
defaultRegion:(UIPointerRegion*)defaultRegion API_AVAILABLE(ios(13.4)) {
if (request != nil) {
auto packet = std::make_unique<flutter::PointerDataPacket>(1);
_mouseState.location = request.location;

flutter::PointerData pointer_data = [self generatePointerDataForMouse];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A static C function for generatePointerDataForMouse would execute faster because of dynamic dispatch.


pointer_data.signal_kind = flutter::PointerData::SignalKind::kNone;
packet->SetPointerData(0, pointer_data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please annotate what "0" means by pulling it into a variable or with arg comments.


[_engine.get() dispatchPointerDataPacket:std::move(packet)];
}
return nil;
}

- (void)scrollEvent:(UIPanGestureRecognizer*)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
const CGFloat scale = [UIScreen mainScreen].scale;

auto packet = std::make_unique<flutter::PointerDataPacket>(1);

flutter::PointerData pointer_data = [self generatePointerDataForMouse];
pointer_data.signal_kind = flutter::PointerData::SignalKind::kScroll;
pointer_data.scroll_delta_x = (translation.x - _mouseState.last_translation.x) * scale;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: you are scaling these values twice, once when you receive them and once when they are used as "last_translation". You could avoid the multiplication by storing the scaled value.

pointer_data.scroll_delta_y = -(translation.y - _mouseState.last_translation.y) * scale;

// The translation reported by UIPanGestureRecognizer is the total translation
// generated by the pan gesture since the gesture began. We need to be able
// to keep track of the last translation value in order to generate the deltaX
// and deltaY coordinates for each subsequent scroll event.
if (recognizer.state != UIGestureRecognizerStateEnded) {
_mouseState.last_translation = translation;
} else {
_mouseState.last_translation = CGPointZero;
}

packet->SetPointerData(0, pointer_data);

[_engine.get() dispatchPointerDataPacket:std::move(packet)];
}
#endif

@end