mirror of
https://github.com/google/blockly.git
synced 2026-01-04 23:50:12 +01:00
325 lines
9.5 KiB
JavaScript
325 lines
9.5 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2011 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Object representing a pair of scrollbars.
|
|
*/
|
|
'use strict';
|
|
|
|
/**
|
|
* Object representing a pair of scrollbars.
|
|
* @class
|
|
*/
|
|
goog.module('Blockly.ScrollbarPair');
|
|
|
|
const dom = goog.require('Blockly.utils.dom');
|
|
const eventUtils = goog.require('Blockly.Events.utils');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {Metrics} = goog.requireType('Blockly.utils.Metrics');
|
|
const {Scrollbar} = goog.require('Blockly.Scrollbar');
|
|
const {Svg} = goog.require('Blockly.utils.Svg');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg');
|
|
|
|
|
|
/**
|
|
* Class for a pair of scrollbars. Horizontal and vertical.
|
|
* @param {!WorkspaceSvg} workspace Workspace to bind the scrollbars to.
|
|
* @param {boolean=} addHorizontal Whether to add a horizontal scrollbar.
|
|
* Defaults to true.
|
|
* @param {boolean=} addVertical Whether to add a vertical scrollbar. Defaults
|
|
* to true.
|
|
* @param {string=} opt_class A class to be applied to these scrollbars.
|
|
* @param {number=} opt_margin The margin to apply to these scrollbars.
|
|
* @constructor
|
|
* @alias Blockly.ScrollbarPair
|
|
*/
|
|
const ScrollbarPair = function(
|
|
workspace, addHorizontal, addVertical, opt_class, opt_margin) {
|
|
/**
|
|
* The workspace this scrollbar pair is bound to.
|
|
* @type {!WorkspaceSvg}
|
|
* @private
|
|
*/
|
|
this.workspace_ = workspace;
|
|
|
|
addHorizontal = addHorizontal === undefined ? true : addHorizontal;
|
|
addVertical = addVertical === undefined ? true : addVertical;
|
|
const isPair = addHorizontal && addVertical;
|
|
|
|
if (addHorizontal) {
|
|
this.hScroll =
|
|
new Scrollbar(workspace, true, isPair, opt_class, opt_margin);
|
|
}
|
|
if (addVertical) {
|
|
this.vScroll =
|
|
new Scrollbar(workspace, false, isPair, opt_class, opt_margin);
|
|
}
|
|
|
|
if (isPair) {
|
|
this.corner_ = dom.createSvgElement(
|
|
Svg.RECT, {
|
|
'height': Scrollbar.scrollbarThickness,
|
|
'width': Scrollbar.scrollbarThickness,
|
|
'class': 'blocklyScrollbarBackground',
|
|
},
|
|
null);
|
|
dom.insertAfter(this.corner_, workspace.getBubbleCanvas());
|
|
}
|
|
|
|
/**
|
|
* Previously recorded metrics from the workspace.
|
|
* @type {?Metrics}
|
|
* @private
|
|
*/
|
|
this.oldHostMetrics_ = null;
|
|
};
|
|
|
|
/**
|
|
* Dispose of this pair of scrollbars.
|
|
* Unlink from all DOM elements to prevent memory leaks.
|
|
* @suppress {checkTypes}
|
|
*/
|
|
ScrollbarPair.prototype.dispose = function() {
|
|
dom.removeNode(this.corner_);
|
|
this.corner_ = null;
|
|
this.workspace_ = null;
|
|
this.oldHostMetrics_ = null;
|
|
if (this.hScroll) {
|
|
this.hScroll.dispose();
|
|
this.hScroll = null;
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.dispose();
|
|
this.vScroll = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Recalculate both of the scrollbars' locations and lengths.
|
|
* Also reposition the corner rectangle.
|
|
*/
|
|
ScrollbarPair.prototype.resize = function() {
|
|
// Look up the host metrics once, and use for both scrollbars.
|
|
const hostMetrics = this.workspace_.getMetrics();
|
|
if (!hostMetrics) {
|
|
// Host element is likely not visible.
|
|
return;
|
|
}
|
|
|
|
// Only change the scrollbars if there has been a change in metrics.
|
|
let resizeH = false;
|
|
let resizeV = false;
|
|
if (!this.oldHostMetrics_ ||
|
|
this.oldHostMetrics_.viewWidth !== hostMetrics.viewWidth ||
|
|
this.oldHostMetrics_.viewHeight !== hostMetrics.viewHeight ||
|
|
this.oldHostMetrics_.absoluteTop !== hostMetrics.absoluteTop ||
|
|
this.oldHostMetrics_.absoluteLeft !== hostMetrics.absoluteLeft) {
|
|
// The window has been resized or repositioned.
|
|
resizeH = true;
|
|
resizeV = true;
|
|
} else {
|
|
// Has the content been resized or moved?
|
|
if (!this.oldHostMetrics_ ||
|
|
this.oldHostMetrics_.scrollWidth !== hostMetrics.scrollWidth ||
|
|
this.oldHostMetrics_.viewLeft !== hostMetrics.viewLeft ||
|
|
this.oldHostMetrics_.scrollLeft !== hostMetrics.scrollLeft) {
|
|
resizeH = true;
|
|
}
|
|
if (!this.oldHostMetrics_ ||
|
|
this.oldHostMetrics_.scrollHeight !== hostMetrics.scrollHeight ||
|
|
this.oldHostMetrics_.viewTop !== hostMetrics.viewTop ||
|
|
this.oldHostMetrics_.scrollTop !== hostMetrics.scrollTop) {
|
|
resizeV = true;
|
|
}
|
|
}
|
|
|
|
if (resizeH || resizeV) {
|
|
try {
|
|
eventUtils.disable();
|
|
if (this.hScroll && resizeH) {
|
|
this.hScroll.resize(hostMetrics);
|
|
}
|
|
if (this.vScroll && resizeV) {
|
|
this.vScroll.resize(hostMetrics);
|
|
}
|
|
} finally {
|
|
eventUtils.enable();
|
|
}
|
|
this.workspace_.maybeFireViewportChangeEvent();
|
|
}
|
|
|
|
if (this.hScroll && this.vScroll) {
|
|
// Reposition the corner square.
|
|
if (!this.oldHostMetrics_ ||
|
|
this.oldHostMetrics_.viewWidth !== hostMetrics.viewWidth ||
|
|
this.oldHostMetrics_.absoluteLeft !== hostMetrics.absoluteLeft) {
|
|
this.corner_.setAttribute('x', this.vScroll.position.x);
|
|
}
|
|
if (!this.oldHostMetrics_ ||
|
|
this.oldHostMetrics_.viewHeight !== hostMetrics.viewHeight ||
|
|
this.oldHostMetrics_.absoluteTop !== hostMetrics.absoluteTop) {
|
|
this.corner_.setAttribute('y', this.hScroll.position.y);
|
|
}
|
|
}
|
|
|
|
// Cache the current metrics to potentially short-cut the next resize event.
|
|
this.oldHostMetrics_ = hostMetrics;
|
|
};
|
|
|
|
/**
|
|
* Returns whether scrolling horizontally is enabled.
|
|
* @return {boolean} True if horizontal scroll is enabled.
|
|
*/
|
|
ScrollbarPair.prototype.canScrollHorizontally = function() {
|
|
return !!this.hScroll;
|
|
};
|
|
|
|
/**
|
|
* Returns whether scrolling vertically is enabled.
|
|
* @return {boolean} True if vertical scroll is enabled.
|
|
*/
|
|
ScrollbarPair.prototype.canScrollVertically = function() {
|
|
return !!this.vScroll;
|
|
};
|
|
|
|
/**
|
|
* Record the origin of the workspace that the scrollbar is in, in pixels
|
|
* relative to the injection div origin. This is for times when the scrollbar is
|
|
* used in an object whose origin isn't the same as the main workspace
|
|
* (e.g. in a flyout.)
|
|
* @param {number} x The x coordinate of the scrollbar's origin, in CSS pixels.
|
|
* @param {number} y The y coordinate of the scrollbar's origin, in CSS pixels.
|
|
* @package
|
|
*/
|
|
ScrollbarPair.prototype.setOrigin = function(x, y) {
|
|
if (this.hScroll) {
|
|
this.hScroll.setOrigin(x, y);
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.setOrigin(x, y);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the handles of both scrollbars.
|
|
* @param {number} x The horizontal content displacement, relative to the view
|
|
* in pixels.
|
|
* @param {number} y The vertical content displacement, relative to the view in
|
|
* pixels.
|
|
* @param {boolean} updateMetrics Whether to update metrics on this set call.
|
|
* Defaults to true.
|
|
*/
|
|
ScrollbarPair.prototype.set = function(x, y, updateMetrics) {
|
|
// This function is equivalent to:
|
|
// this.hScroll.set(x);
|
|
// this.vScroll.set(y);
|
|
// However, that calls setMetrics twice which causes a chain of
|
|
// getAttribute->setAttribute->getAttribute resulting in an extra layout pass.
|
|
// Combining them speeds up rendering.
|
|
if (this.hScroll) {
|
|
this.hScroll.set(x, false);
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.set(y, false);
|
|
}
|
|
|
|
if (updateMetrics || updateMetrics === undefined) {
|
|
// Update metrics.
|
|
const xyRatio = {};
|
|
if (this.hScroll) {
|
|
xyRatio.x = this.hScroll.getRatio_();
|
|
}
|
|
if (this.vScroll) {
|
|
xyRatio.y = this.vScroll.getRatio_();
|
|
}
|
|
this.workspace_.setMetrics(xyRatio);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the handle of the horizontal scrollbar to be at a certain position in
|
|
* CSS pixels relative to its parents.
|
|
* @param {number} x Horizontal scroll value.
|
|
*/
|
|
ScrollbarPair.prototype.setX = function(x) {
|
|
if (this.hScroll) {
|
|
this.hScroll.set(x, true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the handle of the vertical scrollbar to be at a certain position in
|
|
* CSS pixels relative to its parents.
|
|
* @param {number} y Vertical scroll value.
|
|
*/
|
|
ScrollbarPair.prototype.setY = function(y) {
|
|
if (this.vScroll) {
|
|
this.vScroll.set(y, true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set whether this scrollbar's container is visible.
|
|
* @param {boolean} visible Whether the container is visible.
|
|
*/
|
|
ScrollbarPair.prototype.setContainerVisible = function(visible) {
|
|
if (this.hScroll) {
|
|
this.hScroll.setContainerVisible(visible);
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.setContainerVisible(visible);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* If any of the scrollbars are visible. Non-paired scrollbars may disappear
|
|
* when they aren't needed.
|
|
* @return {boolean} True if visible.
|
|
*/
|
|
ScrollbarPair.prototype.isVisible = function() {
|
|
let isVisible = false;
|
|
if (this.hScroll) {
|
|
isVisible = this.hScroll.isVisible();
|
|
}
|
|
if (this.vScroll) {
|
|
isVisible = isVisible || this.vScroll.isVisible();
|
|
}
|
|
return isVisible;
|
|
};
|
|
|
|
/**
|
|
* Recalculates the scrollbars' locations within their path and length.
|
|
* This should be called when the contents of the workspace have changed.
|
|
* @param {!Metrics} hostMetrics A data structure describing all
|
|
* the required dimensions, possibly fetched from the host object.
|
|
*/
|
|
ScrollbarPair.prototype.resizeContent = function(hostMetrics) {
|
|
if (this.hScroll) {
|
|
this.hScroll.resizeContentHorizontal(hostMetrics);
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.resizeContentVertical(hostMetrics);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Recalculates the scrollbars' locations on the screen and path length.
|
|
* This should be called when the layout or size of the window has changed.
|
|
* @param {!Metrics} hostMetrics A data structure describing all
|
|
* the required dimensions, possibly fetched from the host object.
|
|
*/
|
|
ScrollbarPair.prototype.resizeView = function(hostMetrics) {
|
|
if (this.hScroll) {
|
|
this.hScroll.resizeViewHorizontal(hostMetrics);
|
|
}
|
|
if (this.vScroll) {
|
|
this.vScroll.resizeViewVertical(hostMetrics);
|
|
}
|
|
};
|
|
|
|
exports.ScrollbarPair = ScrollbarPair;
|