Bug 163444 - [Modern Media Controls] macOS inline controls
Summary: [Modern Media Controls] macOS inline controls
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Media (show other bugs)
Version: Safari 10
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Antoine Quint
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-10-14 08:04 PDT by Antoine Quint
Modified: 2016-10-15 02:18 PDT (History)
3 users (show)

See Also:


Attachments
Patch (85.40 KB, patch)
2016-10-14 08:10 PDT, Antoine Quint
no flags Details | Formatted Diff | Diff
Patch for landing (85.35 KB, patch)
2016-10-15 01:43 PDT, Antoine Quint
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Antoine Quint 2016-10-14 08:04:40 PDT
This task covers the creation of a class to instantiate the media controls for inline media playback on macOS.
Comment 1 Antoine Quint 2016-10-14 08:04:53 PDT
<rdar://problem/27989473>
Comment 2 Antoine Quint 2016-10-14 08:10:33 PDT
Created attachment 291633 [details]
Patch
Comment 3 Dean Jackson 2016-10-14 16:58:14 PDT
Comment on attachment 291633 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=291633&action=review

> Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:47
> +        this._leftContainer = new ButtonsContainer({
> +            buttons: [this.playPauseButton, this.skipBackButton],
> +            cssClassName: "left",
> +            padding: 24,
> +            margin: 24
> +        });
> +
> +        this._rightContainer = new ButtonsContainer({
> +            buttons: [this.muteButton, this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton],
> +            cssClassName: "right",
> +            padding: 24,
> +            margin: 24
> +        });

Should we be calling these left and right? What about RTL mode?

Maybe call them Playback and Settings, or something like that.

> Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:101
> +        // Reset dropped buttons.
> +        this._rightContainer.buttons.concat(this._leftContainer.buttons).forEach(button => delete button.dropped);
> +
> +        this._leftContainer.layout();
> +        this._rightContainer.layout();
> +
> +        this.timeControl.width = this.width - this._leftContainer.width - this._rightContainer.width;
> +
> +        if (this.timeControl.isSufficientlyWide) {
> +            this.controlsBar.insertBefore(this.timeControl, this._rightContainer);
> +            this.timeControl.x = this._leftContainer.width;
> +        } else {
> +            this.timeControl.remove();
> +            // Since we don't have enough space to display the scrubber, we may also not have
> +            // enough space to display all buttons in the left and right containers, so gradually drop them.
> +            for (let control of [this.airplayButton, this.pipButton, this.tracksButton, this.muteButton, this.skipBackButton, this.fullscreenButton]) {
> +                // Nothing left to do if the combined container widths is shorter than the available width.
> +                if (this._leftContainer.width + this._rightContainer.width < this.width)
> +                    break;
> +
> +                // If the control was already not participating in layout, we can skip it.
> +                if (!control.visible)
> +                    continue;
> +
> +                // This control must now be dropped.
> +                control.dropped = true;
> +
> +                this._leftContainer.layout();
> +                this._rightContainer.layout();
> +            }
> +        }

I don't really understand how this works. We start by deleting all buttons that are dropped.

But I don't understand the "delete button.dropped" line (72). Isn't button.dropped just a boolean?
Did you mean to do .filter(button => button.dropped).forEach(button => delete button)

Then we go on to the loop over the left-side controls. If we need to drop the control, we set dropped to true, and call layout again. Why do you need to layout the left side after this? And won't it trigger this layout function to run again, since the children may have changed sizes? If so, aren't you doing more work than necessary?

> Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:104
> +        this._volumeSliderContainer.x = this._rightContainer.x + this.muteButton.x;

What if muteButton has been dropped?

> LayoutTests/media/modern-media-controls/macos-inline-media-controls/macos-inline-media-controls-buttons-styles.html:54
> +const buttonPositions = new Map([

I think this could just be an array.
Comment 4 Antoine Quint 2016-10-15 00:49:59 PDT
(In reply to comment #3)
> Comment on attachment 291633 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=291633&action=review
> 
> > Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:47
> > +        this._leftContainer = new ButtonsContainer({
> > +            buttons: [this.playPauseButton, this.skipBackButton],
> > +            cssClassName: "left",
> > +            padding: 24,
> > +            margin: 24
> > +        });
> > +
> > +        this._rightContainer = new ButtonsContainer({
> > +            buttons: [this.muteButton, this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton],
> > +            cssClassName: "right",
> > +            padding: 24,
> > +            margin: 24
> > +        });
> 
> Should we be calling these left and right? What about RTL mode?

RTL for media controls only affects the direction of the volume control in fullscreen. I think it's fine to keep these terms.

> > Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:101
> > +        // Reset dropped buttons.
> > +        this._rightContainer.buttons.concat(this._leftContainer.buttons).forEach(button => delete button.dropped);
> > +
> > +        this._leftContainer.layout();
> > +        this._rightContainer.layout();
> > +
> > +        this.timeControl.width = this.width - this._leftContainer.width - this._rightContainer.width;
> > +
> > +        if (this.timeControl.isSufficientlyWide) {
> > +            this.controlsBar.insertBefore(this.timeControl, this._rightContainer);
> > +            this.timeControl.x = this._leftContainer.width;
> > +        } else {
> > +            this.timeControl.remove();
> > +            // Since we don't have enough space to display the scrubber, we may also not have
> > +            // enough space to display all buttons in the left and right containers, so gradually drop them.
> > +            for (let control of [this.airplayButton, this.pipButton, this.tracksButton, this.muteButton, this.skipBackButton, this.fullscreenButton]) {
> > +                // Nothing left to do if the combined container widths is shorter than the available width.
> > +                if (this._leftContainer.width + this._rightContainer.width < this.width)
> > +                    break;
> > +
> > +                // If the control was already not participating in layout, we can skip it.
> > +                if (!control.visible)
> > +                    continue;
> > +
> > +                // This control must now be dropped.
> > +                control.dropped = true;
> > +
> > +                this._leftContainer.layout();
> > +                this._rightContainer.layout();
> > +            }
> > +        }
> 
> I don't really understand how this works. We start by deleting all buttons
> that are dropped.

We delete the `dropped` property on all buttons, so that their dropped status may be reconsidered for this new layout. The `dropped` property is an informal protocol of the ButtonsContainer class to distinguish buttons that may not be shown for a given layout width.

> But I don't understand the "delete button.dropped" line (72). Isn't
> button.dropped just a boolean?

It is, but it's not a property of Button, so we just remove it since it's only added for the purpose of layout.

> Did you mean to do .filter(button => button.dropped).forEach(button =>
> delete button)

No, see above.

> Then we go on to the loop over the left-side controls. If we need to drop
> the control, we set dropped to true, and call layout again. Why do you need
> to layout the left side after this? And won't it trigger this layout
> function to run again, since the children may have changed sizes? If so,
> aren't you doing more work than necessary?

Buttons that are candidates to be dropped happen to be contained in both the left and right containers. So, for each button, in the order that they may be dropped, we update the layout of both the left and right containers, to ensure that their `width` property is updated, and then recheck whether the combined width of the two button containers now fit in the width of the controls bar. Remember that calling layout() on the buttons container is only touching the LayoutNode tree and not the DOM, so it's a cheap operation, which lets us keep things simple.

One thing we could do is only relayout the buttons container in which the button we're processing is contained, but that would require a lookup on both containers to see where the button belongs.

> > Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js:104
> > +        this._volumeSliderContainer.x = this._rightContainer.x + this.muteButton.x;
> 
> What if muteButton has been dropped?

Then the volume slider position will be irrelevant because the volume slider will never appear since it's tied to the muteButton being hovered.

> > LayoutTests/media/modern-media-controls/macos-inline-media-controls/macos-inline-media-controls-buttons-styles.html:54
> > +const buttonPositions = new Map([
> 
> I think this could just be an array.

You're right, will change that.
Comment 5 Antoine Quint 2016-10-15 01:43:42 PDT
Created attachment 291711 [details]
Patch for landing
Comment 6 WebKit Commit Bot 2016-10-15 02:18:20 PDT
Comment on attachment 291711 [details]
Patch for landing

Clearing flags on attachment: 291711

Committed r207373: <http://trac.webkit.org/changeset/207373>
Comment 7 WebKit Commit Bot 2016-10-15 02:18:24 PDT
All reviewed patches have been landed.  Closing bug.