123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- import { Measure } from "../../measure";
- import { Container } from "../container";
- import { ValueAndUnit } from "../../valueAndUnit";
- import { Control } from "../control";
- /**
- * Class used to hold a the container for ScrollViewer
- * @hidden
- */
- export class _ScrollViewerWindow extends Container {
- public parentClientWidth: number;
- public parentClientHeight: number;
- private _freezeControls = false;
- private _parentMeasure: Measure;
- private _oldLeft: number | null;
- private _oldTop: number | null;
- public get freezeControls(): boolean {
- return this._freezeControls;
- }
- public set freezeControls(value: boolean) {
- if (this._freezeControls === value) {
- return;
- }
- if (!value) {
- this._restoreMeasures();
- }
- // trigger a full normal layout calculation to be sure all children have their measures up to date
- this._freezeControls = false;
- var textureSize = this.host.getSize();
- var renderWidth = textureSize.width;
- var renderHeight = textureSize.height;
- var context = this.host.getContext();
- var measure = new Measure(0, 0, renderWidth, renderHeight);
- this.host._numLayoutCalls = 0;
- this.host._rootContainer._layout(measure, context);
- // in freeze mode, prepare children measures accordingly
- if (value) {
- this._updateMeasures();
- if (this._useBuckets()) {
- this._makeBuckets();
- }
- }
- this._freezeControls = value;
- this.host.markAsDirty(); // redraw with the (new) current settings
- }
- private _bucketWidth: number = 0;
- private _bucketHeight: number = 0;
- private _buckets: { [key: number]: Array<Control> } = {};
- private _bucketLen: number;
- public get bucketWidth(): number {
- return this._bucketWidth;
- }
- public get bucketHeight(): number {
- return this._bucketHeight;
- }
- public setBucketSizes(width: number, height: number): void {
- this._bucketWidth = width;
- this._bucketHeight = height;
- if (this._useBuckets()) {
- if (this._freezeControls) {
- this._makeBuckets();
- }
- } else {
- this._buckets = {};
- }
- }
- private _useBuckets(): boolean {
- return this._bucketWidth > 0 && this._bucketHeight > 0;
- }
- private _makeBuckets(): void {
- this._buckets = {};
- this._bucketLen = Math.ceil(this.widthInPixels / this._bucketWidth);
- this._dispatchInBuckets(this._children);
- this._oldLeft = null;
- this._oldTop = null;
- }
- private _dispatchInBuckets(children: Control[]): void {
- for (let i = 0; i < children.length; ++i) {
- let child = children[i];
- let bStartX = Math.max(0, Math.floor((child._customData._origLeft - this._customData.origLeft) / this._bucketWidth)),
- bEndX = Math.floor((child._customData._origLeft - this._customData.origLeft + child._currentMeasure.width - 1) / this._bucketWidth),
- bStartY = Math.max(0, Math.floor((child._customData._origTop - this._customData.origTop) / this._bucketHeight)),
- bEndY = Math.floor((child._customData._origTop - this._customData.origTop + child._currentMeasure.height - 1) / this._bucketHeight);
- while (bStartY <= bEndY) {
- for (let x = bStartX; x <= bEndX; ++x) {
- let bucket = bStartY * this._bucketLen + x,
- lstc = this._buckets[bucket];
- if (!lstc) {
- lstc = [];
- this._buckets[bucket] = lstc;
- }
- lstc.push(child);
- }
- bStartY++;
- }
- if (child instanceof Container && child._children.length > 0) {
- this._dispatchInBuckets(child._children);
- }
- }
- }
- // reset left and top measures for the window and all its children
- private _updateMeasures(): void {
- let left = this.leftInPixels | 0,
- top = this.topInPixels | 0;
- this._measureForChildren.left -= left;
- this._measureForChildren.top -= top;
- this._currentMeasure.left -= left;
- this._currentMeasure.top -= top;
- this._customData.origLeftForChildren = this._measureForChildren.left;
- this._customData.origTopForChildren = this._measureForChildren.top;
- this._customData.origLeft = this._currentMeasure.left;
- this._customData.origTop = this._currentMeasure.top;
- this._updateChildrenMeasures(this._children, left, top);
- }
- private _updateChildrenMeasures(children: Control[], left: number, top: number): void {
- for (let i = 0; i < children.length; ++i) {
- let child = children[i];
- child._currentMeasure.left -= left;
- child._currentMeasure.top -= top;
- child._customData._origLeft = child._currentMeasure.left; // save the original left and top values for each child
- child._customData._origTop = child._currentMeasure.top;
- if (child instanceof Container && child._children.length > 0) {
- this._updateChildrenMeasures(child._children, left, top);
- }
- }
- }
- private _restoreMeasures(): void {
- let left = this.leftInPixels | 0,
- top = this.topInPixels | 0;
- this._measureForChildren.left = this._customData.origLeftForChildren + left;
- this._measureForChildren.top = this._customData.origTopForChildren + top;
- this._currentMeasure.left = this._customData.origLeft + left;
- this._currentMeasure.top = this._customData.origTop + top;
- }
- /**
- * Creates a new ScrollViewerWindow
- * @param name of ScrollViewerWindow
- */
- constructor(name?: string) {
- super(name);
- }
- protected _getTypeName(): string {
- return "ScrollViewerWindow";
- }
- /** @hidden */
- protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
- super._additionalProcessing(parentMeasure, context);
- this._parentMeasure = parentMeasure;
- this._measureForChildren.left = this._currentMeasure.left;
- this._measureForChildren.top = this._currentMeasure.top;
- this._measureForChildren.width = parentMeasure.width;
- this._measureForChildren.height = parentMeasure.height;
- }
- /** @hidden */
- public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
- if (this._freezeControls) {
- this.invalidateRect(); // will trigger a redraw of the window
- return false;
- }
- return super._layout(parentMeasure, context);
- }
- private _scrollChildren(children: Control[], left: number, top: number): void {
- for (let i = 0; i < children.length; ++i) {
- let child = children[i];
- child._currentMeasure.left = child._customData._origLeft + left;
- child._currentMeasure.top = child._customData._origTop + top;
- child._isClipped = false; // clipping will be handled by _draw and the call to _intersectsRect()
- if (child instanceof Container && child._children.length > 0) {
- this._scrollChildren(child._children, left, top);
- }
- }
- }
- private _scrollChildrenWithBuckets(left: number, top: number, scrollLeft: number, scrollTop: number): void {
- let bStartX = Math.max(0, Math.floor(-left / this._bucketWidth)),
- bEndX = Math.floor((-left + this._parentMeasure.width - 1) / this._bucketWidth),
- bStartY = Math.max(0, Math.floor(-top / this._bucketHeight)),
- bEndY = Math.floor((-top + this._parentMeasure.height - 1) / this._bucketHeight);
- while (bStartY <= bEndY) {
- for (let x = bStartX; x <= bEndX; ++x) {
- let bucket = bStartY * this._bucketLen + x,
- lstc = this._buckets[bucket];
- if (lstc) {
- for (let i = 0; i < lstc.length; ++i) {
- let child = lstc[i];
- child._currentMeasure.left = child._customData._origLeft + scrollLeft;
- child._currentMeasure.top = child._customData._origTop + scrollTop;
- child._isClipped = false; // clipping will be handled by _draw and the call to _intersectsRect()
- }
- }
- }
- bStartY++;
- }
- }
- /** @hidden */
- public _draw(context: CanvasRenderingContext2D, invalidatedRectangle?: Measure): void {
- if (!this._freezeControls) {
- super._draw(context, invalidatedRectangle);
- return;
- }
- this._localDraw(context);
- if (this.clipChildren) {
- this._clipForChildren(context);
- }
- let left = this.leftInPixels | 0,
- top = this.topInPixels | 0;
- if (this._useBuckets()) {
- if (this._oldLeft !== null && this._oldTop !== null) {
- this._scrollChildrenWithBuckets(this._oldLeft, this._oldTop, left, top);
- this._scrollChildrenWithBuckets(left, top, left, top);
- } else {
- this._scrollChildren(this._children, left, top);
- }
- } else {
- this._scrollChildren(this._children, left, top);
- }
- this._oldLeft = left;
- this._oldTop = top;
- for (var child of this._children) {
- if (!child._intersectsRect(this._parentMeasure)) {
- continue;
- }
- child._render(context, this._parentMeasure);
- }
- }
- protected _postMeasure(): void {
- if (this._freezeControls) {
- super._postMeasure();
- return;
- }
- var maxWidth = this.parentClientWidth;
- var maxHeight = this.parentClientHeight;
- for (var child of this.children) {
- if (!child.isVisible || child.notRenderable) {
- continue;
- }
- if (child.horizontalAlignment === Control.HORIZONTAL_ALIGNMENT_CENTER) {
- child._offsetLeft(this._currentMeasure.left - child._currentMeasure.left);
- }
- if (child.verticalAlignment === Control.VERTICAL_ALIGNMENT_CENTER) {
- child._offsetTop(this._currentMeasure.top - child._currentMeasure.top);
- }
- maxWidth = Math.max(maxWidth, child._currentMeasure.left - this._currentMeasure.left + child._currentMeasure.width + child.paddingRightInPixels);
- maxHeight = Math.max(maxHeight, child._currentMeasure.top - this._currentMeasure.top + child._currentMeasure.height + child.paddingBottomInPixels);
- }
- if (this._currentMeasure.width !== maxWidth) {
- this._width.updateInPlace(maxWidth, ValueAndUnit.UNITMODE_PIXEL);
- this._currentMeasure.width = maxWidth;
- this._rebuildLayout = true;
- this._isDirty = true;
- }
- if (this._currentMeasure.height !== maxHeight) {
- this._height.updateInPlace(maxHeight, ValueAndUnit.UNITMODE_PIXEL);
- this._currentMeasure.height = maxHeight;
- this._rebuildLayout = true;
- this._isDirty = true;
- }
- super._postMeasure();
- }
- }
|