Skip to content

Commit

Permalink
feat(macOS): preliminary support for writing tools (#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
knopp authored Sep 25, 2024
1 parent 5da2047 commit d066c0c
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 30 deletions.
13 changes: 13 additions & 0 deletions super_context_menu/lib/src/desktop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class DesktopContextMenuWidget extends StatelessWidget {
required this.menuProvider,
required this.contextMenuIsAllowed,
required this.menuWidgetBuilder,
this.writingToolsConfigurationProvider,
this.iconTheme,
});

Expand All @@ -156,6 +157,8 @@ class DesktopContextMenuWidget extends StatelessWidget {
/// on platform.
final IconThemeData? iconTheme;

final WritingToolsConfiguration? Function()?
writingToolsConfigurationProvider;
@override
Widget build(BuildContext context) {
return _ContextMenuDetector(
Expand Down Expand Up @@ -229,10 +232,20 @@ class DesktopContextMenuWidget extends StatelessWidget {
}
onMenuResolved(true);
onShowMenu.notify();
final writingToolsConfiguration =
writingToolsConfigurationProvider?.call();
raw.writingToolsSuggestionCallback =
writingToolsConfiguration?.onSuggestion;

final request = raw.DesktopContextMenuRequest(
iconTheme: serializationOptions.iconTheme,
position: globalPosition,
menu: handle,
writingToolsConfiguration: switch (writingToolsConfiguration) {
(WritingToolsConfiguration c) =>
raw.WritingToolsConfiguration(rect: c.rect, text: c.text),
_ => null,
},
fallback: () {
final completer = Completer<MenuResult>();
ContextMenuSession(
Expand Down
18 changes: 18 additions & 0 deletions super_context_menu/lib/src/menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class ContextMenuWidget extends StatelessWidget {
this.contextMenuIsAllowed = _defaultContextMenuIsAllowed,
MobileMenuWidgetBuilder? mobileMenuWidgetBuilder,
DesktopMenuWidgetBuilder? desktopMenuWidgetBuilder,
this.writingToolsConfigurationProvider,
}) : assert(previewBuilder == null || deferredPreviewBuilder == null,
'Cannot use both previewBuilder and deferredPreviewBuilder'),
mobileMenuWidgetBuilder =
Expand All @@ -81,6 +82,9 @@ class ContextMenuWidget extends StatelessWidget {
final MobileMenuWidgetBuilder mobileMenuWidgetBuilder;
final DesktopMenuWidgetBuilder desktopMenuWidgetBuilder;

final WritingToolsConfiguration? Function()?
writingToolsConfigurationProvider;

/// Base icon theme for menu icons. The size will be overridden depending
/// on platform.
final IconThemeData? iconTheme;
Expand Down Expand Up @@ -111,6 +115,8 @@ class ContextMenuWidget extends StatelessWidget {
contextMenuIsAllowed: contextMenuIsAllowed,
iconTheme: iconTheme,
menuWidgetBuilder: desktopMenuWidgetBuilder,
writingToolsConfigurationProvider:
writingToolsConfigurationProvider,
child: child!,
);
}
Expand All @@ -120,3 +126,15 @@ class ContextMenuWidget extends StatelessWidget {
}

bool _defaultContextMenuIsAllowed(Offset location) => true;

class WritingToolsConfiguration {
WritingToolsConfiguration({
required this.text,
required this.rect,
required this.onSuggestion,
});

final String text;
final Rect rect;
final ValueChanged<String> onSuggestion;
}
22 changes: 22 additions & 0 deletions super_native_extensions/lib/src/menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import 'mutex.dart';
import 'native/menu.dart' if (dart.library.js_interop) 'web/menu.dart';
import 'menu_flutter.dart';
import 'gesture/pointer_device_kind.dart';
import 'util.dart';
import 'widget_snapshot/widget_snapshot.dart';

abstract class MobileMenuDelegate {
void didPushSubmenu();
void hideMenu({required bool itemSelected});
}

void Function(String)? writingToolsSuggestionCallback;

typedef MobileMenuWidgetFactory = Widget Function(
BuildContext context,
Menu rootMenu,
Expand Down Expand Up @@ -96,17 +99,36 @@ class MenuSerializationOptions {
final double devicePixelRatio;
}

class WritingToolsConfiguration {
WritingToolsConfiguration({
required this.rect,
required this.text,
});

final Rect rect;
final String text;

Map<String, dynamic> serialize() {
return {
'rect': rect.serialize(),
'text': text,
};
}
}

class DesktopContextMenuRequest {
DesktopContextMenuRequest({
required this.menu,
required this.position,
required this.iconTheme,
required this.fallback,
required this.writingToolsConfiguration,
});

final MenuHandle menu;
final Offset position;
final IconThemeData iconTheme;
final WritingToolsConfiguration? writingToolsConfiguration;

// Passed to delegate when requesting Flutter desktop menu implementation.
final Future<MenuResult> Function() fallback;
Expand Down
6 changes: 6 additions & 0 deletions super_native_extensions/lib/src/native/menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ class MenuContextImpl extends MenuContext {
final res = await _channel.invokeMethod('showContextMenu', {
'menuHandle': (request.menu as NativeMenuHandle).handle,
'location': request.position.serialize(),
if (request.writingToolsConfiguration != null)
'writingToolsConfiguration':
request.writingToolsConfiguration!.serialize(),
}) as Map;
return MenuResult(
itemSelected: res['itemSelected'],
Expand Down Expand Up @@ -317,6 +320,9 @@ class MenuContextImpl extends MenuContext {
}
return {'elements': res};
}, () => {'elements': []});
} else if (call.method == 'sendWritingToolsReplacement') {
final text = call.arguments['text'] as String;
writingToolsSuggestionCallback?.call(text);
} else {
return null;
}
Expand Down
1 change: 1 addition & 0 deletions super_native_extensions/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ objc2-app-kit = { version = "0.2.2", features = [
"NSEvent",
"NSFilePromiseProvider",
"NSFilePromiseReceiver",
"NSGraphics",
"NSGraphicsContext",
"NSImage",
"NSImage",
Expand Down
15 changes: 15 additions & 0 deletions super_native_extensions/rust/src/api_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,30 @@ pub struct MenuConfiguration {
pub menu: Option<Rc<PlatformMenu>>,
}

/// macOS only
#[derive(TryFromValue)]
#[irondash(rename_all = "camelCase")]
pub struct WritingToolsConfiguration {
pub rect: Rect,
pub text: String,
}

#[derive(TryFromValue)]
#[irondash(rename_all = "camelCase")]
pub struct ShowContextMenuRequest {
pub menu_handle: i64,
pub location: Point,
pub writing_tools_configuration: Option<WritingToolsConfiguration>,
#[irondash(skip)]
pub menu: Option<Rc<PlatformMenu>>,
}

#[derive(IntoValue)]
#[irondash(rename_all = "camelCase")]
pub struct WritingToolsReplacementRequest {
pub text: String,
}

#[derive(IntoValue)]
#[irondash(rename_all = "camelCase")]
pub struct ShowContextMenuResponse {
Expand Down
13 changes: 8 additions & 5 deletions super_native_extensions/rust/src/darwin/macos/drag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,18 @@ impl PlatformDragContext {
}
}

pub unsafe fn synthesize_mouse_up_event(&self) {
pub unsafe fn synthesize_mouse_up_event(&self) -> Option<Id<NSEvent>> {
self.finish_scroll_events();

if let Some(event) = self.last_mouse_down_event.borrow().as_ref().cloned() {
if let Some(original_event) = self.last_mouse_down_event.borrow().as_ref().cloned() {
#[allow(non_upper_case_globals)]
let opposite = match event.r#type() {
let opposite = match original_event.r#type() {
NSEventType::LeftMouseDown => CGEventType::LeftMouseUp,
NSEventType::RightMouseDown => CGEventType::RightMouseUp,
_ => return,
_ => return None,
};

let event = event.CGEvent();
let event = original_event.CGEvent();
let event = CGEventCreateCopy(event);
CGEventSetType(event, opposite);

Expand All @@ -153,6 +153,9 @@ impl PlatformDragContext {
if let Some(window) = window {
window.sendEvent(&synthesized);
}
Some(original_event.clone())
} else {
None
}
}

Expand Down
Loading

0 comments on commit d066c0c

Please sign in to comment.