fix(engine-render): fix some issues when hiding the first row of merged cells (#5904)

This commit is contained in:
wpxp123456
2025-09-22 20:55:52 +08:00
committed by GitHub
parent 17cecf14d7
commit 39b4f8708d
6 changed files with 161 additions and 111 deletions
@@ -47,14 +47,19 @@ export class Custom extends SheetExtension {
if (!worksheet.getRowVisible(row) || !worksheet.getColVisible(col)) {
return;
}
let primaryWithCoord = skeleton.getCellWithCoordByIndex(row, col, false);
const { mergeInfo } = primaryWithCoord;
let cellData = worksheet.getCell(row, col);
if (primaryWithCoord.isMerged) {
cellData = worksheet.getCell(mergeInfo.startRow, mergeInfo.startColumn);
}
if (!cellData?.customRender) {
return;
}
let primaryWithCoord = skeleton.getCellWithCoordByIndex(row, col, false);
const { mergeInfo } = primaryWithCoord;
if (!this.isRenderDiffRangesByRow(mergeInfo.startRow, mergeInfo.endRow, diffRanges)) {
return true;
}
@@ -69,16 +74,7 @@ export class Custom extends SheetExtension {
}
if (primaryWithCoord.isMerged) {
const mainCell = {
row: mergeInfo.startRow,
col: mergeInfo.startColumn,
};
cellData = worksheet.getCell(mainCell.row, mainCell.col);
if (!cellData?.customRender) {
return;
}
primaryWithCoord = skeleton.getCellWithCoordByIndex(mainCell.row, mainCell.col, false);
primaryWithCoord = skeleton.getCellWithCoordByIndex(mergeInfo.startRow, mergeInfo.startColumn, false);
}
const renderInfo = {
@@ -28,6 +28,11 @@ export const defaultPlaceholderColor = '#000';
const EXTENSION_Z_INDEX = 34;
const stringifyRange = (range: IRange) => {
const { startRow, endRow, startColumn, endColumn } = range;
return `${startRow}-${endRow}-${startColumn}-${endColumn}`;
};
export class DataBar extends SheetExtension {
private _paddingRightAndLeft = 2;
private _paddingTopAndBottom = 2;
@@ -44,77 +49,88 @@ export class DataBar extends SheetExtension {
diffRanges: IRange[]
) {
const { worksheet } = spreadsheetSkeleton;
if (!worksheet) {
return false;
}
const mergeCellRendered = new Set<string>();
ctx.save();
// ctx.globalCompositeOperation = 'destination-over';
Range.foreach(spreadsheetSkeleton.rowColumnSegment, (row, col) => {
if (!worksheet.getRowVisible(row) || !worksheet.getColVisible(col)) {
return;
}
const cellData = worksheet.getCell(row, col) as IDataBarCellData;
if (cellData && cellData.dataBar) {
const { color, value, startPoint, isGradient } = cellData.dataBar;
const cellInfo = spreadsheetSkeleton.getCellWithCoordByIndex(row, col, false);
let { isMerged, isMergedMainCell, mergeInfo, startY, endY, startX, endX } = cellInfo;
if (isMerged) {
return;
}
if (isMergedMainCell) {
startY = mergeInfo.startY;
endY = mergeInfo.endY;
startX = mergeInfo.startX;
endX = mergeInfo.endX;
}
if (!this.isRenderDiffRangesByCell(mergeInfo, diffRanges)) {
return;
}
const borderWidth = endX - startX;
const borderHeight = (endY + FIX_ONE_PIXEL_BLUR_OFFSET) - startY;
const width = borderWidth - this._paddingRightAndLeft * 2;
const height = borderHeight - this._paddingTopAndBottom * 2;
if (value > 0) {
// Width less than 1, almost invisible
const dataBarWidth = Math.max(width * (1 - startPoint / 100) * value / 100, 1);
const x0 = startX + this._paddingRightAndLeft + (startPoint / 100) * width;
const y0 = startY + this._paddingTopAndBottom;
if (isGradient) {
const gradient = ctx.createLinearGradient(x0, y0, x0 + dataBarWidth, y0);
gradient.addColorStop(0, color);
gradient.addColorStop(1, 'rgb(255 255 255)');
ctx.fillStyle = gradient;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
} else {
ctx.fillStyle = color;
}
this._drawRectWithRoundedCorner(ctx, x0, y0, dataBarWidth, height, false, true, true, false);
if (isGradient) {
ctx.stroke();
}
const primaryWithCoord = spreadsheetSkeleton.getCellWithCoordByIndex(row, col, false);
const { isMerged, isMergedMainCell, mergeInfo } = primaryWithCoord;
let cellData = worksheet.getCell(row, col) as IDataBarCellData;
if (isMerged) {
cellData = worksheet.getCell(mergeInfo.startRow, mergeInfo.startColumn) as IDataBarCellData;
}
if (!cellData?.dataBar) {
return;
}
if (!this.isRenderDiffRangesByCell(mergeInfo, diffRanges)) {
return;
}
if (isMerged || isMergedMainCell) {
const rangeStr = stringifyRange(mergeInfo);
if (mergeCellRendered.has(rangeStr)) {
return;
}
mergeCellRendered.add(rangeStr);
}
const { color, value, startPoint, isGradient } = cellData.dataBar;
const { startX, endX, startY, endY } = (isMerged || isMergedMainCell) ? mergeInfo : primaryWithCoord;
const borderWidth = endX - startX;
const borderHeight = (endY + FIX_ONE_PIXEL_BLUR_OFFSET) - startY;
const width = borderWidth - this._paddingRightAndLeft * 2;
const height = borderHeight - this._paddingTopAndBottom * 2;
if (value > 0) {
// Width less than 1, almost invisible
const dataBarWidth = Math.max(width * (1 - startPoint / 100) * value / 100, 1);
const x0 = startX + this._paddingRightAndLeft + (startPoint / 100) * width;
const y0 = startY + this._paddingTopAndBottom;
if (isGradient) {
const gradient = ctx.createLinearGradient(x0, y0, x0 + dataBarWidth, y0);
gradient.addColorStop(0, color);
gradient.addColorStop(1, 'rgb(255 255 255)');
ctx.fillStyle = gradient;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
} else {
// Width less than 1, almost invisible
const dataBarWidth = Math.max(width * startPoint / 100 * Math.abs(value) / 100, 1);
const x0 = startX + this._paddingRightAndLeft + (startPoint / 100) * width - dataBarWidth;
const y0 = startY + this._paddingTopAndBottom;
if (isGradient) {
const gradient = ctx.createLinearGradient(x0, y0, x0 + dataBarWidth, y0);
gradient.addColorStop(0, 'rgb(255 255 255)');
gradient.addColorStop(1, color);
ctx.fillStyle = gradient;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
} else {
ctx.fillStyle = color;
}
ctx.fillStyle = color;
}
this._drawRectWithRoundedCorner(ctx, x0, y0, dataBarWidth, height, true, false, false, true);
if (isGradient) {
ctx.stroke();
}
this._drawRectWithRoundedCorner(ctx, x0, y0, dataBarWidth, height, false, true, true, false);
if (isGradient) {
ctx.stroke();
}
} else {
// Width less than 1, almost invisible
const dataBarWidth = Math.max(width * startPoint / 100 * Math.abs(value) / 100, 1);
const x0 = startX + this._paddingRightAndLeft + (startPoint / 100) * width - dataBarWidth;
const y0 = startY + this._paddingTopAndBottom;
if (isGradient) {
const gradient = ctx.createLinearGradient(x0, y0, x0 + dataBarWidth, y0);
gradient.addColorStop(0, 'rgb(255 255 255)');
gradient.addColorStop(1, color);
ctx.fillStyle = gradient;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
} else {
ctx.fillStyle = color;
}
this._drawRectWithRoundedCorner(ctx, x0, y0, dataBarWidth, height, true, false, false, true);
if (isGradient) {
ctx.stroke();
}
}
});
@@ -27,6 +27,11 @@ const EXTENSION_Z_INDEX = 35;
export const DEFAULT_WIDTH = 15;
export const DEFAULT_PADDING = 2;
const stringifyRange = (range: IRange) => {
const { startRow, endRow, startColumn, endColumn } = range;
return `${startRow}-${endRow}-${startColumn}-${endColumn}`;
};
export class ConditionalFormattingIcon extends SheetExtension {
private _paddingRightAndLeft = DEFAULT_PADDING;
@@ -52,45 +57,59 @@ export class ConditionalFormattingIcon extends SheetExtension {
if (!worksheet) {
return false;
}
const mergeCellRendered = new Set<string>();
ctx.save();
// ctx.globalCompositeOperation = 'destination-over';
Range.foreach(spreadsheetSkeleton.rowColumnSegment, (row, col) => {
if (!worksheet.getRowVisible(row) || !worksheet.getColVisible(col)) {
return;
}
const cellData = worksheet.getCell(row, col) as IIconSetCellData;
if (cellData?.iconSet) {
const { iconType, iconId } = cellData.iconSet;
if (iconType === EMPTY_ICON_TYPE) {
return;
}
const icon = this._imageMap.get(this._createKey(iconType, iconId));
if (!icon) {
return;
}
const cellInfo = spreadsheetSkeleton.getCellWithCoordByIndex(row, col, false);
let { isMerged, isMergedMainCell, mergeInfo, startY, endY, startX, endX } = cellInfo;
if (isMerged) {
return;
}
if (isMergedMainCell) {
startY = mergeInfo.startY;
endY = mergeInfo.endY;
startX = mergeInfo.startX;
endX = mergeInfo.endX;
}
if (!this.isRenderDiffRangesByCell(mergeInfo, diffRanges)) {
return;
}
const borderWidth = endX - startX;
const borderHeight = endY - startY;
if (this._width > borderHeight || this._width > borderWidth + this._paddingRightAndLeft * 2) {
return;
}
// Highly centered processing
const y = (borderHeight - this._width) / 2 + startY;
ctx.drawImage(icon, startX + this._paddingRightAndLeft, y, this._width, this._width);
const primaryWithCoord = spreadsheetSkeleton.getCellWithCoordByIndex(row, col, false);
const { isMerged, isMergedMainCell, mergeInfo } = primaryWithCoord;
let cellData = worksheet.getCell(row, col) as IIconSetCellData;
if (isMerged) {
cellData = worksheet.getCell(mergeInfo.startRow, mergeInfo.startColumn) as IIconSetCellData;
}
if (!cellData?.iconSet) {
return;
}
const { iconType, iconId } = cellData.iconSet;
if (iconType === EMPTY_ICON_TYPE) {
return;
}
const icon = this._imageMap.get(this._createKey(iconType, iconId));
if (!icon) {
return;
}
if (!this.isRenderDiffRangesByCell(mergeInfo, diffRanges)) {
return;
}
if (isMerged || isMergedMainCell) {
const rangeStr = stringifyRange(mergeInfo);
if (mergeCellRendered.has(rangeStr)) {
return;
}
mergeCellRendered.add(rangeStr);
}
const { startX, endX, startY, endY } = (isMerged || isMergedMainCell) ? mergeInfo : primaryWithCoord;
const borderWidth = endX - startX;
const borderHeight = endY - startY;
if (this._width > borderHeight || this._width > borderWidth + this._paddingRightAndLeft * 2) {
return;
}
// Highly centered processing
const y = (borderHeight - this._width) / 2 + startY;
ctx.drawImage(icon, startX + this._paddingRightAndLeft, y, this._width, this._width);
});
ctx.restore();
}
@@ -87,7 +87,9 @@ export class DropdownMultipleWidget implements IBaseDataValidationWidget {
const map = this._ensureMap(subUnitId);
const key = this._generateKey(row, col);
const rule = this._dataValidationModel.getRuleByLocation(info.unitId, info.subUnitId, row, col);
const _row = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo.startRow : row;
const _col = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo.startColumn : col;
const rule = this._dataValidationModel.getRuleByLocation(info.unitId, info.subUnitId, _row, _col);
if (!rule) {
return;
}
@@ -149,7 +149,9 @@ export class DropdownWidget implements IBaseDataValidationWidget {
drawWith(ctx: UniverRenderingContext2D, info: ICellRenderContext, skeleton: SpreadsheetSkeleton): void {
const { primaryWithCoord, row, col, style, data, subUnitId } = info;
const _cellBounding = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo : primaryWithCoord;
const rule = this._dataValidationModel.getRuleByLocation(info.unitId, info.subUnitId, row, col);
const _row = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo.startRow : row;
const _col = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo.startColumn : col;
const rule = this._dataValidationModel.getRuleByLocation(info.unitId, info.subUnitId, _row, _col);
if (!rule) {
return;
}
@@ -535,12 +535,27 @@ export class SheetFindModel extends FindModel {
const { startX, startY } = startPosition;
const { endX, endY } = endPosition;
const rowHidden = !worksheet.getRowRawVisible(startRow);
const columnHidden = !worksheet.getColVisible(startColumn);
let isAllRowHidden = true;
const inHiddenRange = rowHidden || columnHidden;
const width = columnHidden ? 2 : endX - startX;
const height = rowHidden ? 2 : endY - startY;
for (let row = startRow; row <= endRow; row++) {
if (worksheet.getRowRawVisible(row)) {
isAllRowHidden = false;
break;
}
}
let isAllColHidden = true;
for (let col = startColumn; col <= endColumn; col++) {
if (worksheet.getColVisible(col)) {
isAllColHidden = false;
break;
}
}
const inHiddenRange = isAllRowHidden || isAllColHidden;
const width = isAllColHidden ? 2 : endX - startX;
const height = isAllRowHidden ? 2 : endY - startY;
const props: ISheetFindReplaceHighlightShapeProps = {
left: startX,