Esanders has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/267446

Change subject: DesktopContext: Float the context when it goes beyond the 
viewport
......................................................................

DesktopContext: Float the context when it goes beyond the viewport

This is especially important when the focusable node is bigger than
the viewport (e.g. infoboxes).

This will also allow us to add a context for tables, which are often
bigger than the viewport.

Bug: T51922
Change-Id: I984b98bea5930bdd4e09705be039c357c7f664ca
---
M src/ui/contexts/ve.ui.DesktopContext.js
M src/ui/contexts/ve.ui.LinearContext.js
M src/ui/styles/ve.ui.DesktopContext.css
M src/ui/ve.ui.Surface.js
4 files changed, 130 insertions(+), 32 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/46/267446/1

diff --git a/src/ui/contexts/ve.ui.DesktopContext.js 
b/src/ui/contexts/ve.ui.DesktopContext.js
index 973b01c..cbbb8cc 100644
--- a/src/ui/contexts/ve.ui.DesktopContext.js
+++ b/src/ui/contexts/ve.ui.DesktopContext.js
@@ -20,8 +20,13 @@
 
        // Properties
        this.popup = new OO.ui.PopupWidget( { $container: this.surface.$element 
} );
+       this.position = null;
+       this.embeddable = null;
+       this.boundingRect = null;
        this.transitioning = null;
+       this.dimensions = null;
        this.suppressed = false;
+       this.onWindowScrollDebounced = ve.debounce( this.onWindowScroll.bind( 
this ) );
        this.onWindowResizeHandler = this.onPosition.bind( this );
        this.$window = $( this.getElementWindow() );
 
@@ -37,9 +42,12 @@
                select: 'onModelSelect'
        } );
        this.inspectors.connect( this, {
-               resize: 'setPopupSize'
+               resize: 'onInspectorResize'
        } );
-       this.$window.on( 'resize', this.onWindowResizeHandler );
+       this.$window.on( {
+               resize: this.onWindowResizeHandler,
+               scroll: this.onWindowScrollDebounced
+       } );
 
        // Initialization
        this.$element
@@ -136,7 +144,14 @@
 ve.ui.DesktopContext.prototype.onInspectorOpening = function () {
        ve.ui.DesktopContext.super.prototype.onInspectorOpening.apply( this, 
arguments );
        // Resize the popup before opening so the body height of the window is 
measured correctly
-       this.setPopupSize();
+       this.setPopupSizeAndPosition();
+};
+
+/**
+ * Handle inspector resize events
+ */
+ve.ui.DesktopContext.prototype.onInspectorResize = function () {
+       this.updateDimensionsDebounced();
 };
 
 /**
@@ -182,8 +197,10 @@
  * @inheritdoc
  */
 ve.ui.DesktopContext.prototype.updateDimensions = function () {
-       var startAndEndRects, position, embeddable, middle, boundingRect, rtl,
-               surface, startingSelection, currentSelection, isTableSelection, 
focusedNode;
+       var startAndEndRects, position, middle, boundingRect, rtl,
+               surface, startingSelection, currentSelection, isTableSelection, 
focusedNode,
+               $container = this.inspector ? this.inspector.$frame : 
this.$group,
+               embeddable = false;
 
        // Parent method
        ve.ui.DesktopContext.super.prototype.updateDimensions.call( this );
@@ -260,14 +277,29 @@
        }
 
        if ( position ) {
-               this.$element.css( { left: position.x, top: position.y } );
+               this.position = position;
        }
+       if ( boundingRect ) {
+               this.boundingRect = boundingRect;
+       }
+       this.embeddable = embeddable;
+       this.dimensions = {
+               width: $container.outerWidth( true ),
+               height: $container.outerHeight( true )
+       };
 
-       // HACK: setPopupSize() has to be called at the end because it reads 
this.popup.align,
-       // which we set directly in the code above
-       this.setPopupSize();
+       this.setPopupSizeAndPosition();
 
        return this;
+};
+
+/**
+ * Handle window scroll events
+ *
+ * @param {jQuery.Event} e Scroll event
+ */
+ve.ui.DesktopContext.prototype.onWindowScroll = function () {
+       this.setPopupSizeAndPosition( true );
 };
 
 /**
@@ -289,35 +321,91 @@
 };
 
 /**
- * Resize the popup to match the size of its contents (menu or inspector).
+ * Apply the popup's size and position, within the bounds of the viewport
+ *
+ * @param {boolean} [repositionOnly] Reposition the popup only
  */
-ve.ui.DesktopContext.prototype.setPopupSize = function () {
-       var surface = this.surface,
-               viewport = surface.getViewportDimensions(),
-               $container = this.inspector ? this.inspector.$frame : 
this.$group;
+ve.ui.DesktopContext.prototype.setPopupSizeAndPosition = function ( 
repositionOnly ) {
+       var floating, viewport,
+               margin = 10,
+               minimumVisibleHeight = 100,
+               surface = this.surface;
 
-       if ( !viewport ) {
+       if ( !this.isVisible() ) {
+               return;
+       }
+
+       viewport = surface.getViewportDimensions();
+
+       if ( !viewport || !this.dimensions ) {
                // viewport can be null if the surface is not attached
                return;
        }
 
-       // PopupWidget normally is clippable, suppress that to be able to 
resize and scroll it into view.
-       // Needs to be repeated before every call, as it resets itself when the 
popup is shown or hidden.
-       this.popup.toggleClipping( false );
+       if ( this.position ) {
+               floating =
+                       ( !this.embeddable && this.position.y + 
this.dimensions.height > viewport.bottom - margin ) ||
+                       ( this.embeddable && this.position.y < viewport.top + 
margin );
+               this.$element.toggleClass( 've-ui-desktopContext-floating', 
floating );
+               this.popup.toggleAnchor( !floating && !this.embeddable );
 
-       // We want to stop the popup from possibly being bigger than the 
viewport,
-       // as that can result in situations where it's impossible to reach parts
-       // of the popup. Limiting it to the window height would ignore toolbars
-       // and the find-replace dialog and suchlike. Therefore we set its max
-       // height to the surface's estimation of the actual viewport available 
to
-       // it. It's okay if the inspector goes off the edge of the viewport, so
-       // long as it's possible to scroll and get it all in view.
-       this.popup.setSize(
-               $container.outerWidth( true ),
-               Math.min( $container.outerHeight( true ), viewport.height )
-       );
+               if ( floating ) {
+                       if ( this.embeddable ) {
+                               if ( this.boundingRect.bottom - viewport.top - 
minimumVisibleHeight < this.dimensions.height + margin ) {
+                                       this.$element.toggleClass( 
've-ui-desktopContext-floating', false );
+                                       this.$element.css( {
+                                               left: this.position.x,
+                                               top: this.position.y + 
this.boundingRect.height - this.dimensions.height - minimumVisibleHeight,
+                                               bottom: ''
+                                       } );
+                               } else {
+                                       this.$element.css( {
+                                               left: this.position.x + 
viewport.left,
+                                               top: this.surface.toolbarHeight 
+ margin,
+                                               bottom: ''
+                                       } );
+                               }
+                       } else {
+                               if ( viewport.bottom - this.boundingRect.top - 
minimumVisibleHeight < this.dimensions.height + margin ) {
+                                       this.$element.toggleClass( 
've-ui-desktopContext-floating', false );
+                                       this.$element.css( {
+                                               left: this.position.x,
+                                               top: this.boundingRect.top + 
minimumVisibleHeight,
+                                               bottom: ''
+                                       } );
+                               } else {
+                                       this.$element.css( {
+                                               left: this.position.x + 
viewport.left,
+                                               top: '',
+                                               bottom: this.dimensions.height 
+ margin
+                                       } );
+                               }
+                       }
+               } else {
+                       this.$element.css( {
+                               left: this.position.x,
+                               top: this.position.y,
+                               bottom: ''
+                       } );
+               }
+       }
 
-       this.popup.scrollElementIntoView();
+       if ( !repositionOnly ) {
+               // PopupWidget normally is clippable, suppress that to be able 
to resize and scroll it into view.
+               // Needs to be repeated before every call, as it resets itself 
when the popup is shown or hidden.
+               this.popup.toggleClipping( false );
+
+               // We want to stop the popup from possibly being bigger than 
the viewport,
+               // as that can result in situations where it's impossible to 
reach parts
+               // of the popup. Limiting it to the window height would ignore 
toolbars
+               // and the find-replace dialog and suchlike. Therefore we set 
its max
+               // height to the surface's estimation of the actual viewport 
available to
+               // it. It's okay if the inspector goes off the edge of the 
viewport, so
+               // long as it's possible to scroll and get it all in view.
+               this.popup.setSize( this.dimensions.width, Math.min( 
this.dimensions.height, viewport.height ) );
+
+               this.popup.scrollElementIntoView();
+       }
 };
 
 /**
@@ -327,7 +415,11 @@
        // Disconnect
        this.surface.getView().disconnect( this );
        this.surface.getModel().disconnect( this );
-       this.$window.off( 'resize', this.onWindowResizeHandler );
+       this.inspectors.disconnect( this );
+       this.$window.off( {
+               resize: this.onWindowResizeHandler,
+               scroll: this.onWindowScrollDebounced
+       } );
 
        // Parent method
        return ve.ui.DesktopContext.super.prototype.destroy.call( this );
diff --git a/src/ui/contexts/ve.ui.LinearContext.js 
b/src/ui/contexts/ve.ui.LinearContext.js
index f476b5b..115ab13 100644
--- a/src/ui/contexts/ve.ui.LinearContext.js
+++ b/src/ui/contexts/ve.ui.LinearContext.js
@@ -310,6 +310,7 @@
  */
 ve.ui.LinearContext.prototype.destroy = function () {
        // Disconnect events
+       this.surface.getModel().disconnect( this );
        this.inspectors.disconnect( this );
 
        // Destroy inspectors WindowManager
diff --git a/src/ui/styles/ve.ui.DesktopContext.css 
b/src/ui/styles/ve.ui.DesktopContext.css
index 3df5ee1..bf5b657 100644
--- a/src/ui/styles/ve.ui.DesktopContext.css
+++ b/src/ui/styles/ve.ui.DesktopContext.css
@@ -38,6 +38,10 @@
        background-image: none;
 }
 
+.ve-ui-desktopContext-floating {
+       position: fixed;
+}
+
 .ve-ui-desktopContext > .oo-ui-popupWidget:not( .oo-ui-popupWidget-anchored ) 
.oo-ui-popupWidget-popup {
        margin-top: 0.25em;
 }
diff --git a/src/ui/ve.ui.Surface.js b/src/ui/ve.ui.Surface.js
index e0d8972..f2602e5 100644
--- a/src/ui/ve.ui.Surface.js
+++ b/src/ui/ve.ui.Surface.js
@@ -249,7 +249,7 @@
 /**
  * Get vertical measurements of the visible area of the surface viewport
  *
- * @return {Object|null} Object with top, bottom, and height properties. Null 
if the surface is not attached.
+ * @return {Object|null} Object with top, left, bottom, and height properties. 
Null if the surface is not attached.
  */
 ve.ui.Surface.prototype.getViewportDimensions = function () {
        var top, bottom,
@@ -264,6 +264,7 @@
 
        return {
                top: top,
+               left: rect.left,
                bottom: bottom,
                height: bottom - top
        };

-- 
To view, visit https://gerrit.wikimedia.org/r/267446
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I984b98bea5930bdd4e09705be039c357c7f664ca
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to