Skip to content

Commit

Permalink
[visionOS] Add menu button to control scene dimming in fullscreen
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=260164
rdar://110675100

Reviewed by Wenson Hsieh.

In <video> fullscreen, "Auto Dimming" will be offered as an option alongside
the rest of the overflow controls menu. In element fullscreen, a new menu is
added to the top left to hold the new action.

* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:

Enable fullscreen scene dimming by default.

* Source/WebCore/en.lproj/Localizable.strings:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h:
* Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView fullScreenWindowSceneDimmingAction]):

Expose the action as a method on `WKWebView` so that the logic may be shared
between <video> and element fullscreen.

Use a `UIDeferredMenuElement` so that the action is initialized each time its
menu is presented, and the selected state is accurately reflected.

Use `UIMenuOptionsDisplayInline` in order to display a separator.

* Source/WebKit/UIProcess/ios/WKActionSheetAssistant.h:
* Source/WebKit/UIProcess/ios/WKActionSheetAssistant.mm:
(-[WKActionSheetAssistant showMediaControlsContextMenu:items:completionHandler:]):

Augment media controls context menu logic to add support for actions that are
completely handled on the UI process side.

* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView additionalMediaControlsContextMenuItemsForActionSheetAssistant:]):
* Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenViewController.h:
* Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenViewController.mm:

Add `_WKExtrinsicButtonDelegate` in order to know if/when a button is presenting
a menu. If a menu is visible, the auto-hide logic needs to be prevented.

(-[_WKExtrinsicButton contextMenuInteraction:willDisplayMenuForConfiguration:animator:]):
(-[_WKExtrinsicButton contextMenuInteraction:willEndForConfiguration:animator:]):
(-[WKFullScreenViewController initWithWebView:]):
(-[WKFullScreenViewController hideUI]):
(-[WKFullScreenViewController videoControlsManagerDidChange]):
(-[WKFullScreenViewController loadView]):
(-[WKFullScreenViewController _createButtonWithExtrinsicContentSize:]):
(-[WKFullScreenViewController _wkExtrinsicButtonWillDisplayMenu:]):

Cancel auto-hide when a menu is presented.

(-[WKFullScreenViewController _wkExtrinsicButtonWillDismissMenu:]):

Auto-hide UI when the menu is dismissed, and playback is active.

(-[WKFullScreenViewController setSceneDimmed:]): Deleted.
(-[WKFullScreenViewController _toggleDimmingAction:]): Deleted.
* Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.h:
* Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
(-[WKFullScreenWindowController enterFullScreen:]):
(-[WKFullScreenWindowController prefersSceneDimming]):
(-[WKFullScreenWindowController _performSpatialFullScreenTransition:completionHandler:]):

Update logic to ensure dimming state is restored when exiting fullscreen, if the
preference is toggled while in fullscreen.

(-[WKFullScreenWindowController toggleSceneDimming]):
(-[WKFullScreenWindowController _prefersSceneDimming]): Deleted.
(-[WKFullScreenWindowController toggleDimming]): Deleted.

Canonical link: https://commits.webkit.org/266892@main
  • Loading branch information
pxlcoder committed Aug 15, 2023
1 parent 5b076c9 commit c007fbf
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 54 deletions.
6 changes: 3 additions & 3 deletions Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2784,11 +2784,11 @@ FullscreenSceneDimmingEnabled:
condition: PLATFORM(VISION)
defaultValue:
WebKitLegacy:
default: false
default: true
WebKit:
default: false
default: true
WebCore:
default: false
default: true

GStreamerEnabled:
type: bool
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@
/* Menu item label for automatic track selection behavior. */
"Auto (Recommended) (text track)" = "Auto (Recommended)";

/* Menu item label to toggle automatic scene dimming in fullscreen (visionOS only). */
"Auto Dimming" = "Auto Dimming";

/* Automatically Resize context menu item */
"Automatically Resize" = "Automatically Resize";

Expand Down
5 changes: 4 additions & 1 deletion Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,10 @@ RetainPtr<NSError> nsErrorFromExceptionDetails(const WebCore::ExceptionDetails&)

#if ENABLE(FULLSCREEN_API) && PLATFORM(IOS_FAMILY)
@interface WKWebView (FullScreenAPI_Internal)
-(WKFullScreenWindowController *)fullScreenWindowController;
- (WKFullScreenWindowController *)fullScreenWindowController;
#if PLATFORM(VISION)
- (UIMenu *)fullScreenWindowSceneDimmingAction;
#endif
@end
#endif

Expand Down
28 changes: 28 additions & 0 deletions Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4219,6 +4219,34 @@ - (WKFullScreenWindowController *)fullScreenWindowController
return _fullScreenWindowController.get();
}

#if PLATFORM(VISION)

- (UIMenu *)fullScreenWindowSceneDimmingAction
{
UIDeferredMenuElement *deferredMenu = [UIDeferredMenuElement elementWithUncachedProvider:[weakSelf = WeakObjCPtr<WKWebView>(self)] (void (^completion)(NSArray<UIMenuElement *> *)) {
auto strongSelf = weakSelf.get();
if (!strongSelf) {
completion(@[ ]);
return;
}

UIAction *dimmingAction = [UIAction actionWithTitle:WEB_UI_STRING("Auto Dimming", "Menu item label to toggle automatic scene dimming in fullscreen") image:[UIImage _systemImageNamed:@"circle.lefthalf.dotted.inset.half.filled"] identifier:nil handler:[weakSelf] (UIAction *) {
auto strongSelf = weakSelf.get();
if (!strongSelf)
return;

[strongSelf->_fullScreenWindowController toggleSceneDimming];
}];
dimmingAction.state = [strongSelf->_fullScreenWindowController prefersSceneDimming] ? UIMenuElementStateOn : UIMenuElementStateOff;

completion(@[ dimmingAction ]);
}];

return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[ deferredMenu ]];
}

#endif // PLATFORM(VISION)

@end

#endif // ENABLE(FULLSCREEN_API)
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/UIProcess/ios/WKActionSheetAssistant.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ typedef NS_ENUM(NSInteger, _WKElementActionType);
- (BOOL)_allowAnimationControls;
- (void)_actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action onElements:(Vector<WebCore::ElementContext>&&)elements;
#endif
- (NSArray<UIMenuElement *> *)additionalMediaControlsContextMenuItemsForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
@end

#if USE(UICONTEXTMENU)
Expand Down
11 changes: 9 additions & 2 deletions Source/WebKit/UIProcess/ios/WKActionSheetAssistant.mm
Original file line number Diff line number Diff line change
Expand Up @@ -869,12 +869,19 @@ - (void)showMediaControlsContextMenu:(WebCore::FloatRect&&)targetFrame items:(Ve
} else
itemsToPresent = WTFMove(items);

if (![_view window] || itemsToPresent.isEmpty()) {
NSArray<UIMenuElement *> *additionalItems = nil;
if ([_delegate respondsToSelector:@selector(additionalMediaControlsContextMenuItemsForActionSheetAssistant:)])
additionalItems = [_delegate additionalMediaControlsContextMenuItemsForActionSheetAssistant:self];

if (![_view window] || (itemsToPresent.isEmpty() && !additionalItems.count)) {
completionHandler(WebCore::MediaControlsContextMenuItem::invalidID);
return;
}

_mediaControlsContextMenu = [UIMenu menuWithTitle:WTFMove(menuTitle) children:[self _uiMenuElementsForMediaControlContextMenuItems:WTFMove(itemsToPresent)]];
NSArray<UIMenuElement *> *menuItems = [self _uiMenuElementsForMediaControlContextMenuItems:WTFMove(itemsToPresent)];
menuItems = [menuItems arrayByAddingObjectsFromArray:additionalItems];

_mediaControlsContextMenu = [UIMenu menuWithTitle:WTFMove(menuTitle) children:menuItems];
_mediaControlsContextMenuTargetFrame = WTFMove(targetFrame);
_mediaControlsContextMenuCallback = WTFMove(completionHandler);

Expand Down
9 changes: 9 additions & 0 deletions Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8548,6 +8548,15 @@ - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant getAlternateURL
completion(nil, nil);
}

- (NSArray<UIMenuElement *> *)additionalMediaControlsContextMenuItemsForActionSheetAssistant:(WKActionSheetAssistant *)assistant
{
#if PLATFORM(VISION)
if (self.webView.fullscreenState == WKFullscreenStateInFullscreen)
return @[ [self.webView fullScreenWindowSceneDimmingAction] ];
#endif
return @[ ];
}

#if USE(UICONTEXTMENU)

- (UITargetedPreview *)createTargetedContextMenuHintForActionSheetAssistant:(WKActionSheetAssistant *)assistant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)requestExitFullScreen;
- (void)showUI;
- (void)hideUI;
#if PLATFORM(VISION)
- (void)toggleDimming;
#endif
@end

@interface WKFullScreenViewController : UIViewController
Expand All @@ -60,7 +57,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setSupportedOrientations:(UIInterfaceOrientationMask)supportedOrientations;
- (void)resetSupportedOrientations;
#if PLATFORM(VISION)
- (void)setSceneDimmed:(BOOL)dimmed;
- (void)hideCustomControls:(BOOL)hidden;
#endif
@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,20 @@ void setInterface(WebCore::PlaybackSessionInterfaceAVKit* interface)

#pragma mark - _WKExtrinsicButton

@class _WKExtrinsicButton;

@protocol _WKExtrinsicButtonDelegate <NSObject>
- (void)_wkExtrinsicButtonWillDisplayMenu:(_WKExtrinsicButton *)button;
- (void)_wkExtrinsicButtonWillDismissMenu:(_WKExtrinsicButton *)button;
@end

@interface _WKExtrinsicButton : UIButton
@property (assign, nonatomic) CGSize extrinsicContentSize;
@property (nonatomic, assign) CGSize extrinsicContentSize;
@property (nonatomic, weak) id<_WKExtrinsicButtonDelegate> delegate;
@end

@implementation _WKExtrinsicButton

- (void)setExtrinsicContentSize:(CGSize)size
{
_extrinsicContentSize = size;
Expand All @@ -107,6 +116,19 @@ - (CGSize)intrinsicContentSize
{
return _extrinsicContentSize;
}

- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willDisplayMenuForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionAnimating>)animator
{
[super contextMenuInteraction:interaction willDisplayMenuForConfiguration:configuration animator:animator];
[_delegate _wkExtrinsicButtonWillDisplayMenu:self];
}

- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willEndForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionAnimating>)animator
{
[super contextMenuInteraction:interaction willEndForConfiguration:configuration animator:animator];
[_delegate _wkExtrinsicButtonWillDismissMenu:self];
}

@end

#pragma mark - _WKInsetLabel
Expand All @@ -132,7 +154,7 @@ - (CGSize)intrinsicContentSize

#pragma mark - WKFullScreenViewController

@interface WKFullScreenViewController () <UIGestureRecognizerDelegate, UIToolbarDelegate>
@interface WKFullScreenViewController () <UIGestureRecognizerDelegate, UIToolbarDelegate, _WKExtrinsicButtonDelegate>
@property (weak, nonatomic) WKWebView *_webView; // Cannot be retained, see <rdar://problem/14884666>.
@property (readonly, nonatomic) WebKit::WebFullScreenManagerProxy* _manager;
@property (readonly, nonatomic) WebCore::FloatBoxExtent _effectiveFullscreenInsets;
Expand All @@ -158,8 +180,9 @@ @implementation WKFullScreenViewController {
WKFullScreenViewControllerPlaybackSessionModelClient _playbackClient;
CGFloat _nonZeroStatusBarHeight;
std::optional<UIInterfaceOrientationMask> _supportedOrientations;
BOOL _isShowingMenu;
#if PLATFORM(VISION)
RetainPtr<_WKExtrinsicButton> _dimmingButton;
RetainPtr<_WKExtrinsicButton> _moreActionsButton;
BOOL m_shouldHideCustomControls;
#endif
}
Expand All @@ -184,6 +207,7 @@ - (id)initWithWebView:(WKWebView *)webView

_playbackClient.setParent(self);
_valid = YES;
_isShowingMenu = NO;
#if PLATFORM(VISION)
m_shouldHideCustomControls = NO;
#endif
Expand Down Expand Up @@ -270,6 +294,10 @@ - (void)hideUI
{
ASSERT(_valid);
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideUI) object:nil];

if (_isShowingMenu)
return;

[UIView animateWithDuration:showHideAnimationDuration animations:^{
[[self delegate] hideUI];
if (_topConstraint)
Expand Down Expand Up @@ -342,7 +370,7 @@ - (void)videoControlsManagerDidChange
bool isDimmingEnabled = false;
if (auto page = [self._webView _page])
isDimmingEnabled = page->preferences().fullscreenSceneDimmingEnabled();
[_dimmingButton setHidden:m_shouldHideCustomControls || !isDimmingEnabled];
[_moreActionsButton setHidden:m_shouldHideCustomControls || !isDimmingEnabled];

isPiPEnabled = !m_shouldHideCustomControls && isPiPEnabled;
#endif
Expand Down Expand Up @@ -413,23 +441,6 @@ - (void)setPictureInPictureActive:(BOOL)active
[_pipButton setSelected:active];
}

#if PLATFORM(VISION)

- (void)setSceneDimmed:(BOOL)dimmed
{
ASSERT(_valid);

NSString *symbolName = nil;
if (dimmed)
symbolName = @"light.max";
else
symbolName = @"light.min";

[_dimmingButton setImage:[[UIImage systemImageNamed:symbolName] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
}

#endif // PLATFORM(VISION)

- (void)setAnimating:(BOOL)animating
{
ASSERT(_valid);
Expand Down Expand Up @@ -506,16 +517,18 @@ - (void)loadView
[_cancelButton setConfiguration:cancelButtonConfiguration];

#if PLATFORM(VISION)
_dimmingButton = [self _createButtonWithExtrinsicContentSize:buttonSize];
[_dimmingButton addTarget:self action:@selector(_toggleDimmingAction:) forControlEvents:UIControlEventTouchUpInside];
[_dimmingButton setConfiguration:cancelButtonConfiguration];
_moreActionsButton = [self _createButtonWithExtrinsicContentSize:buttonSize];
[_moreActionsButton setConfiguration:cancelButtonConfiguration];
[_moreActionsButton setMenu:self._webView.fullScreenWindowSceneDimmingAction];
[_moreActionsButton setShowsMenuAsPrimaryAction:YES];
[_moreActionsButton setImage:[[UIImage systemImageNamed:@"ellipsis"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
#endif

_stackView = adoptNS([[UIStackView alloc] init]);
[_stackView addArrangedSubview:_cancelButton.get()];
[_stackView addArrangedSubview:_pipButton.get()];
#if PLATFORM(VISION)
[_stackView addArrangedSubview:_dimmingButton.get()];
[_stackView addArrangedSubview:_moreActionsButton.get()];
#endif
[_stackView setSpacing:24.0];
} else {
Expand Down Expand Up @@ -710,16 +723,6 @@ - (void)_togglePiPAction:(id)sender
playbackSessionModel->togglePictureInPicture();
}

#if PLATFORM(VISION)

- (void)_toggleDimmingAction:(id)sender
{
ASSERT(_valid);
[[self delegate] toggleDimming];
}

#endif // PLATFORM(VISION)

- (void)_touchDetected:(id)sender
{
ASSERT(_valid);
Expand Down Expand Up @@ -788,6 +791,7 @@ - (void)_showPhishingAlert
- (_WKExtrinsicButton *)_createButtonWithExtrinsicContentSize:(CGSize)size
{
_WKExtrinsicButton *button = [_WKExtrinsicButton buttonWithType:UIButtonTypeSystem];
[button setDelegate:self];
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
[button setAdjustsImageWhenHighlighted:NO];
Expand All @@ -797,6 +801,20 @@ - (_WKExtrinsicButton *)_createButtonWithExtrinsicContentSize:(CGSize)size
return button;
}

- (void)_wkExtrinsicButtonWillDisplayMenu:(_WKExtrinsicButton *)button
{
_isShowingMenu = YES;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideUI) object:nil];
}

- (void)_wkExtrinsicButtonWillDismissMenu:(_WKExtrinsicButton *)button
{
_isShowingMenu = NO;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideUI) object:nil];
if (_playing)
[self performSelector:@selector(hideUI) withObject:nil afterDelay:autoHideDelay];
}

@end

#endif // ENABLE(FULLSCREEN_API) && PLATFORM(IOS_FAMILY)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
@interface WKFullScreenWindowController : NSObject <UIViewControllerTransitioningDelegate>
@property (readonly, retain, nonatomic) UIView *webViewPlaceholder;
@property (readonly, assign, nonatomic) BOOL isFullScreen;
#if PLATFORM(VISION)
@property (readonly, assign, nonatomic) BOOL prefersSceneDimming;
#endif

- (id)initWithWebView:(WKWebView *)webView;
- (void)enterFullScreen:(CGSize)videoDimensions;
Expand All @@ -46,6 +49,10 @@
- (void)webViewDidRemoveFromSuperviewWhileInFullscreen;
- (void)videoControlsManagerDidChange;

#if PLATFORM(VISION)
- (void)toggleSceneDimming;
#endif

@end

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,6 @@ - (void)enterFullScreen:(CGSize)videoDimensions
[_fullscreenViewController setDelegate:self];
_fullscreenViewController.get().view.frame = _rootViewController.get().view.bounds;
#if PLATFORM(VISION)
[_fullscreenViewController setSceneDimmed:[self _prefersSceneDimming]];
[_fullscreenViewController hideCustomControls:manager->isVideoElement()];
#endif
[self _updateLocationInfo];
Expand Down Expand Up @@ -1652,7 +1651,7 @@ - (BOOL)_sceneAspectRatioLockingEnabled
return YES;
}

- (BOOL)_prefersSceneDimming
- (BOOL)prefersSceneDimming
{
if (![self _sceneDimmingEnabled])
return NO;
Expand Down Expand Up @@ -1718,9 +1717,10 @@ - (void)_performSpatialFullScreenTransition:(BOOL)enter completionHandler:(Compl

inWindow.transform3D = CATransform3DTranslate(originalState.transform3D, 0, 0, kIncomingWindowZOffset);

if ([self _prefersSceneDimming]) {
MRUIStage *stage = UIApplication.sharedApplication.mrui_activeStage;
if (self.prefersSceneDimming
|| (!enter && stage.preferredDarkness != originalState.preferredDarkness)) {
[UIView animateWithDuration:kDarknessAnimationDuration animations:^{
MRUIStage *stage = UIApplication.sharedApplication.mrui_activeStage;
stage.preferredDarkness = enter ? MRUIDarknessPreferenceVeryDark : originalState.preferredDarkness;
} completion:nil];
}
Expand Down Expand Up @@ -1787,15 +1787,16 @@ - (void)_performSpatialFullScreenTransition:(BOOL)enter completionHandler:(Compl
} completion:completion.get()];
}

- (void)toggleDimming
- (void)toggleSceneDimming
{
BOOL updatedPrefersSceneDimming = ![self _prefersSceneDimming];
BOOL updatedPrefersSceneDimming = !self.prefersSceneDimming;

[[NSUserDefaults standardUserDefaults] setBool:updatedPrefersSceneDimming forKey:kPrefersFullScreenDimmingKey];
[_fullscreenViewController setSceneDimmed:updatedPrefersSceneDimming];

MRUIStage *stage = UIApplication.sharedApplication.mrui_activeStage;
stage.preferredDarkness = updatedPrefersSceneDimming ? MRUIDarknessPreferenceVeryDark : [_parentWindowState preferredDarkness];
if (self.isFullScreen) {
MRUIStage *stage = UIApplication.sharedApplication.mrui_activeStage;
stage.preferredDarkness = updatedPrefersSceneDimming ? MRUIDarknessPreferenceVeryDark : [_parentWindowState preferredDarkness];
}
}

#endif // PLATFORM(VISION)
Expand Down

0 comments on commit c007fbf

Please sign in to comment.