UNPKG

devexpress-richedit

Version:

DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.

99 lines (98 loc) 4.96 kB
import { Rectangle } from '@devexpress/utils/lib/geometry/rectangle'; import { IntervalAlgorithms } from '@devexpress/utils/lib/intervals/algorithms'; import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { Log } from '../../../rich-utils/debug/logger/base-logger/log'; import { LogObjToStr } from '../../../rich-utils/debug/logger/base-logger/log-obj-to-str'; import { LogSource } from '../../../rich-utils/debug/logger/base-logger/log-source'; import { RectangleUtils } from '../utils/rectangle-utils'; export class RowIntervalInfo extends FixedInterval { constructor(start, length, avaliableWidth = length) { super(start, length); this.avaliableWidth = avaliableWidth; } get busyWidth() { return this.length - this.avaliableWidth; } get startOfFreeSpace() { return this.start + this.busyWidth; } isConsiderBoxes() { return this.avaliableWidth != this.length; } } export class RowFormattingInfo { get isFloatingIntersectRow() { return !!this.intersectsObjects.length; } get lastNonEmptyInterval() { return this.intervals[this.lastNonEmptyIntervalIndex]; } constructor(minY, height, outerHorizontalRowContentBounds, boundsOfAnchoredOblectsOnThisColumn, tableCell) { this.intervals = []; this.busyIntervals = []; this.currIndex = 0; this.minY = minY; this.height = Math.max(1, height); this.outerHorizontalRowContentBounds = outerHorizontalRowContentBounds; this.boundsOfAnchoredOblectsOnThisColumn = boundsOfAnchoredOblectsOnThisColumn; this.tableCell = tableCell; this.lastNonEmptyIntervalIndex = 0; } get currInterval() { return this.intervals[this.currIndex]; } indexOfFreeInterval(width) { return ListUtils.indexBy(this.intervals, (curr) => curr.avaliableWidth >= width, this.currIndex); } indexOfIntervalContainsPositon(pos) { const ind = ListUtils.indexBy(this.intervals, (curr) => curr.start > pos || pos < curr.end, this.currIndex + 1); return ind < 0 ? this.intervals.length - 1 : ind; } calculate() { this.setIntersectObjects(); this.busyIntervals = IntervalAlgorithms.getMergedIntervals(ListUtils.map(this.intersectsObjects, (objBnds) => new FixedInterval(objBnds.x, objBnds.width)), true); const freeIntervals = IntervalAlgorithms.reflectIntervals(this.busyIntervals, this.outerHorizontalRowContentBounds); if (freeIntervals.length) { this.intervals = ListUtils.map(freeIntervals, (curr) => new RowIntervalInfo(curr.start, curr.length)); return; } this.resetMinY(ListUtils.min); Log.print(LogSource.RowFormatter, "RowFormattingInfo.calculate ", `minY:${this.minY}, height:${this.height}, currIndex: ${this.currIndex} intervals:\n${Log.join("\n", ListUtils.map(this.intervals, (curr) => LogObjToStr.fixedInterval(curr)))}`); } canIncrementHeightTo(newHeight) { const contentBounds = ListUtils.map(this.intervals, (c) => new Rectangle(c.start, this.minY, c.busyWidth, newHeight)); const intersectedObjects = this.calcIntersectObjects(newHeight); return !ListUtils.unsafeAnyOf(intersectedObjects, (ancBound) => ListUtils.unsafeAnyOf(contentBounds, (contentBound) => RectangleUtils.getNonCollapsedIntersection(contentBound, ancBound))); } findNextYPos() { this.resetMinY(ListUtils.min); } findNextYPosWhatHasNeededSpace(requiredWidth) { while (this.isFloatingIntersectRow && !ListUtils.anyOf(this.intervals, i => i.avaliableWidth >= requiredWidth)) this.resetMinY(ListUtils.min); this.currIndex = 0; } setIntersectObjects() { this.intersectsObjects = this.calcIntersectObjects(this.height); } calcIntersectObjects(height) { const initRect = new Rectangle(this.outerHorizontalRowContentBounds.start, this.minY, this.outerHorizontalRowContentBounds.length, height); return ListUtils.reducedMap(this.boundsOfAnchoredOblectsOnThisColumn, (objInfo) => { if (this.tableCell !== objInfo.cell) return null; const rect = Rectangle.getNonCollapsedIntersection(initRect, objInfo.bounds); if (!rect) return null; if (!objInfo.canPutTextAtLeft) { const right = rect.right; rect.x = initRect.x; rect.width = right - rect.x; } if (!objInfo.canPutTextAtRight) { rect.width = initRect.right - rect.x; } return rect; }); } resetMinY(getVal) { this.minY = getVal(this.intersectsObjects, a => a.bottom).bottom; this.calculate(); } }