Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mobile selection #2559

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
91797ec
feat: init basic mobile features
wzhudev Jun 7, 2024
9719d1f
fix: rollback desktop changes
wzhudev Jun 7, 2024
a0702ce
feat: demonstrate removing canvas features
wzhudev Jun 7, 2024
afd0598
feat: enable filter rendering
wzhudev Jun 7, 2024
12a3d7f
feat: support formula over web worker
wzhudev Jun 11, 2024
a137899
feat: add mobile header
wzhudev Jun 11, 2024
19be9fd
feat: enable data validation
wzhudev Jun 11, 2024
c26bff6
feat: enable conditional formatting
wzhudev Jun 11, 2024
bce2a36
feat: add mobile worksheet
wzhudev Jun 11, 2024
2d92507
feat: add mobile jsdoc
wzhudev Jun 11, 2024
0433526
feat: add scroll into view
wzhudev Jun 11, 2024
23863ca
feat: mobile context menu
wzhudev Jun 11, 2024
0cf29f5
fix: change header style
wzhudev Jun 11, 2024
fb772fd
feat: context menu ui
wzhudev Jun 13, 2024
cda2931
chore: code style
wzhudev Jun 14, 2024
256d5c1
docs: update render unit drawing
wzhudev Jun 17, 2024
617c03c
Merge remote-tracking branch 'origin/dev' into feat/mobile-poc
wzhudev Jun 17, 2024
69acd64
feat(sheets-ui): optimize ref-range for hyper-link and thread-comment…
weird94 Jun 7, 2024
23865ce
fix: draw dashrect in visible area
lumixraku Jun 3, 2024
aa41b90
feat: mobile selection
lumixraku Jun 11, 2024
978edf8
chore: better shape style
lumixraku Jun 21, 2024
1f2a572
fix: popup position when scrolled
lumixraku Jun 21, 2024
1985536
chore: add rangetype when update control
lumixraku Jun 21, 2024
9df2f6c
chore: move dash logic into dashedrect
lumixraku Jun 19, 2024
cb2fa8d
test: change selection shape Y when scrolling
lumixraku Jun 26, 2024
f152d32
feat: use scrollInfo$ to control scrolling
lumixraku Jun 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: draw dashrect in visible area
chore: rm useless
  • Loading branch information
lumixraku committed Jun 20, 2024
commit 23865ce43f1e71cb5ccaea85e096ad9158ff8bfa
40 changes: 25 additions & 15 deletions packages/core/src/shared/rectangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import type { IRange } from '../types/interfaces/i-range';
import type { IRange, IRectLTRB } from '../types/interfaces/i-range';
import { AbsoluteRefType, RANGE_TYPE } from '../types/interfaces/i-range';
import type { Nullable } from './types';

Expand Down Expand Up @@ -314,20 +314,7 @@ export class Rectangle {
return result;
}

static hasIntersectionBetweenTwoBounds(
rect1: {
left: number;
top: number;
right: number;
bottom: number;
},
rect2: {
left: number;
top: number;
right: number;
bottom: number;
}
) {
static hasIntersectionBetweenTwoRect(rect1: IRectLTRB, rect2: IRectLTRB) {
if (
rect1.left > rect2.right || // rect1 在 rect2 右侧
rect1.right < rect2.left || // rect1 在 rect2 左侧
Expand All @@ -339,4 +326,27 @@ export class Rectangle {

return true;
}

static getIntersectionBetweenTwoRect(rect1: IRectLTRB, rect2: IRectLTRB) {
// 计算两个矩形的交集部分的坐标
const left = Math.max(rect1.left, rect2.left);
const right = Math.min(rect1.right, rect2.right);
const top = Math.max(rect1.top, rect2.top);
const bottom = Math.min(rect1.bottom, rect2.bottom);

// 如果交集部分的宽度或高度小于等于 0,说明两个矩形不相交
if (right <= left || bottom <= top) {
return null;
}

// 返回交集部分的矩形
return {
left,
right,
top,
bottom,
width: right - left,
height: bottom - top,
} as Required<IRectLTRB>;
}
}
9 changes: 9 additions & 0 deletions packages/core/src/types/interfaces/i-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,12 @@ export interface IOptionData {
* Option of copyTo function
*/
export interface ICopyToOptionsData extends IOptionData {}

export interface IRectLTRB {
left: number;
top: number;
right: number;
bottom: number;
width?: number;
height?: number;
}
2 changes: 1 addition & 1 deletion packages/engine-render/src/base-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ export abstract class BaseObject extends Disposable {
this._makeDirtyMix();
}

render(ctx: UniverRenderingContext, bounds?: IViewportInfo) {
render(ctx: UniverRenderingContext, bounds: IViewportInfo) {
/* abstract */
}

Expand Down
4 changes: 2 additions & 2 deletions packages/engine-render/src/basics/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ export class Transform {
this._m[1] *= sx;
this._m[2] *= sy;
this._m[3] *= sy;
this._m[4] *= sx;
this._m[5] *= sy;
// this._m[4] *= sx;
// this._m[5] *= sy;
return this;
}

Expand Down
4 changes: 0 additions & 4 deletions packages/engine-render/src/basics/vector2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -847,10 +847,6 @@ export interface IBoundRect {
}

export interface IBoundRectNoAngle {
/**
* 冻结区域相对 MainCanvas 的物理位置,
* left = n * colWidth + rowHeaderWidth
*/
left: number;
top: number;
right: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,6 @@ export class Font extends SheetExtension {
// return true;
// }

const cellData = worksheet.getCell(rowIndex, columnIndex) as ICellData & ISheetFontRenderExtension || {};
if (cellData.fontRenderExtension?.isSkip) {
return true;
}

// If the cell is overflowing, but the overflowRectangle has not been set,
// then overflowRectangle is set to undefined.
const overflowRectangle = overflowCache.getValue(rowIndex, columnIndex);
Expand All @@ -184,6 +179,11 @@ export class Font extends SheetExtension {
}
}

const cellData = worksheet.getCell(rowIndex, columnIndex) as ICellData & ISheetFontRenderExtension || {};
if (cellData.fontRenderExtension?.isSkip) {
return true;
}

ctx.save();
ctx.beginPath();

Expand Down
13 changes: 9 additions & 4 deletions packages/engine-render/src/components/sheets/spreadsheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export class Spreadsheet extends SheetComponent {
const extensions = this.getExtensionsByOrder();
// At this moment, ctx.transform is at topLeft of sheet content, cell(0, 0)
for (const extension of extensions) {
// const timeKey = `extension ${viewportInfo.viewPortKey}:${extension.constructor.name}`;
const _timeKey = `extension ${viewportInfo.viewportKey}:${extension.constructor.name}`;
extension.draw(ctx, parentScale, spreadsheetSkeleton, diffRanges, {
viewRanges,
checkOutOfViewBound: true,
Expand Down Expand Up @@ -377,9 +377,14 @@ export class Spreadsheet extends SheetComponent {

const { viewportKey } = viewportInfo;
// scene --> layer, getObjects --> viewport.render(object) --> spreadsheet
// zIndex 0 spreadsheet this.getObjectsByOrder() ---> [spreadsheet]
// zIndex 2 rowHeader & colHeader & freezeBorder this.getObjectsByOrder() ---> [SpreadsheetRowHeader, SpreadsheetColumnHeader, _Rect]
// zIndex 3 selection this.getObjectsByOrder() ---> [group]
// SHEET_COMPONENT_MAIN_LAYER_INDEX = 0;
// SHEET_COMPONENT_SELECTION_LAYER_INDEX = 1;
// SHEET_COMPONENT_HEADER_LAYER_INDEX = 10;
// SHEET_COMPONENT_HEADER_SELECTION_LAYER_INDEX = 11;
// ......
// SHEET_COMPONENT_MAIN_LAYER_INDEX spreadsheet this.getObjectsByOrder() ---> [spreadsheet]
// SHEET_COMPONENT_HEADER_LAYER_INDEX rowHeader & colHeader & freezeBorder this.getObjectsByOrder() ---> [SpreadsheetRowHeader, SpreadsheetColumnHeader, _Rect..., HeaderMenuResizeShape]
// SHEET_COMPONENT_HEADER_SELECTION_LAYER_INDEX selection this.getObjectsByOrder() ---> [_Rect, Group]

// SpreadsheetRowHeader SpreadsheetColumnHeader is not render by spreadsheet
if (this.sheetContentViewport().includes(viewportKey as SHEET_VIEWPORT_KEY)) {
Expand Down
9 changes: 6 additions & 3 deletions packages/engine-render/src/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,17 @@ export class Group extends BaseObject {
return this._objects;
}

override render(ctx: UniverRenderingContext, bounds?: IViewportInfo) {
override render(ctx: UniverRenderingContext, bounds: IViewportInfo) {
ctx.save();
const m = this.transform.getMatrix();
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
const objects = this.getObjectsByOrder();
for (const object of objects) {
object.render(ctx, this._transformBounds(bounds));

for (let i = 0; i < objects.length; i++) {
const object = objects[i];
object.render(ctx, bounds);
}

ctx.restore();
}

Expand Down
89 changes: 82 additions & 7 deletions packages/engine-render/src/shape/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,43 @@
* limitations under the License.
*/

import type { IKeyValue } from '@univerjs/core';
import { type IKeyValue, Rectangle } from '@univerjs/core';

import type { UniverRenderingContext } from '../context';
import type { IViewportInfo } from '../basics/vector2';
import type { IShapeProps } from './shape';
import { Shape } from './shape';

export interface IRectProps extends IShapeProps {
radius?: number;
startX?: number;
startY?: number;
endX?: number;
endY?: number;
}

export const RECT_OBJECT_ARRAY = ['radius'];

export class Rect<T extends IRectProps = IRectProps> extends Shape<T> {
private _radius: number = 0;

/**
* the X value of rect topleft position in scene (same cordinate as viewport)
*/
private _startX: number = 0;
/**
* the Y of rect topleft position in scene (same cordinate as viewport)
*/
private _startY: number = 0;
/**
* the X value of rect bottomright position in scene (same cordinate as viewport)
*/
private _endX: number = 0;
/**
* the Y value of rect bottomright position in scene (same cordinate as viewport)
*/
private _endY: number = 0;

constructor(key?: string, props?: T) {
super(key, props);
if (props?.radius) {
Expand All @@ -40,22 +62,56 @@ export class Rect<T extends IRectProps = IRectProps> extends Shape<T> {
return this._radius;
}

/**
* the X value of rect topleft position in scene (same cordinate as viewport)
*/
get startX() {
return this._startX;
}

/**
* the Y of rect topleft position in scene (same cordinate as viewport)
*/
get startY() {
return this._startY;
}

/**
* the X value of rect bottomright position in scene (same cordinate as viewport)
*/
get endX() {
return this._endX;
}

/**
* the Y value of rect bottomright position in scene (same cordinate as viewport)
*/
get endY() {
return this._endY;
}

static override drawWith(ctx: UniverRenderingContext, props: IRectProps | Rect) {
let { radius, width, height } = props;
let { radius, left, top, width, height } = props;

radius = radius ?? 0;
width = width ?? 30;
height = height ?? 30;
width = width ?? 0;
height = height ?? 0;
left = left ?? 0;
top = top ?? 0;

ctx.beginPath();

if (props.strokeDashArray) {
ctx.setLineDash(props.strokeDashArray);
} else {
// only dashrect needs top & left(which relative to topleft of viewport)
top = 0;
left = 0;
}

if (!radius) {
// simple rect - don't bother doing all that complicated maths stuff.
ctx.rect(0, 0, width, height);
ctx.rect(left, top, width, height);
} else {
let topLeft = 0;
let topRight = 0;
Expand Down Expand Up @@ -92,7 +148,26 @@ export class Rect<T extends IRectProps = IRectProps> extends Shape<T> {
};
}

protected override _draw(ctx: UniverRenderingContext) {
Rect.drawWith(ctx, this);
protected override _draw(ctx: UniverRenderingContext, viewportInfo?: IViewportInfo) {
const { radius, paintFirst, stroke, strokeWidth, fill, strokeScaleEnabled, fillRule, strokeLineCap, strokeDashOffset, strokeLineJoin, strokeMiterLimit, strokeDashArray } = this;
if (!strokeDashArray) {
Rect.drawWith(ctx, this);
} else {
const { startX, startY, endX, endY } = this;
const rect = { left: startX, top: startY, right: endX, bottom: endY };
let { left, top, right, bottom } = rect;
let width = right - left;
let height = bottom - top;
if (viewportInfo && Rectangle.hasIntersectionBetweenTwoRect(rect, viewportInfo.cacheBound)) {
const intersectRect = Rectangle.getIntersectionBetweenTwoRect(rect, viewportInfo.cacheBound)!;
left = intersectRect.left - startX;
top = intersectRect.top - startY;
right = intersectRect.right;
bottom = intersectRect.bottom;
width = intersectRect.width;
height = intersectRect.height;
}
Rect.drawWith(ctx, { ...{ radius, width, height, paintFirst, stroke, strokeWidth, fill, strokeScaleEnabled, fillRule, strokeLineCap, strokeDashOffset, strokeLineJoin, strokeMiterLimit, strokeDashArray }, ...{ width, height, left, top } });
}
}
}
4 changes: 2 additions & 2 deletions packages/engine-render/src/shape/shape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ export abstract class Shape<T extends IShapeProps> extends BaseObject {
const m = this.transform.getMatrix();
mainCtx.save();
mainCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
this._draw(mainCtx);
this._draw(mainCtx, bounds);
mainCtx.restore();
this.makeDirty(false);
return this;
Expand Down Expand Up @@ -374,7 +374,7 @@ export abstract class Shape<T extends IShapeProps> extends BaseObject {
};
}

protected _draw(ctx: UniverRenderingContext) {
protected _draw(ctx: UniverRenderingContext, bounds?: IViewportInfo) {
/** abstract */
}

Expand Down
25 changes: 14 additions & 11 deletions packages/engine-render/src/viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,14 +734,14 @@ export class Viewport {
}
const mainCtx = parentCtx || (this._scene.getEngine()?.getCanvas().getContext() as UniverRenderingContext);

// this._scene.transform --> [scale, 0, 0, scale, - viewportScrollX * scaleX, - viewportScrollY * scaleY]
// see transform.ts@multiply
const sceneTrans = this._scene.transform.clone();
sceneTrans.multiply(Transform.create([1, 0, 0, 1, -this.viewportScrollX || 0, -this.viewportScrollY || 0]));

// Logical translation & scaling, unrelated to dpr.
const tm = sceneTrans.getMatrix();
const scrollbarTM = this.getScrollBarTransForm().getMatrix();

mainCtx.save();
mainCtx.save();// At this time, mainCtx transform is (dpr, 0, 0, dpr, 0, 0)

if (this._renderClipState) {
mainCtx.beginPath();
Expand All @@ -752,12 +752,13 @@ export class Viewport {
mainCtx.clip();
}

// set scrolling state for mainCtx,
mainCtx.transform(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]);
const viewPortInfo = this._calcViewportInfo();

objects.forEach((o) => {
o.render(mainCtx, viewPortInfo);
});
for (let i = 0, length = objects.length; i < length; i++) {
objects[i].render(mainCtx, viewPortInfo);
}

this.markDirty(false);
this.markForceDirty(false);
Expand All @@ -770,7 +771,7 @@ export class Viewport {

if (this._scrollBar && isMaxLayer) {
mainCtx.save();

const scrollbarTM = this.getScrollBarTransForm().getMatrix();
mainCtx.transform(scrollbarTM[0], scrollbarTM[1], scrollbarTM[2], scrollbarTM[3], scrollbarTM[4], scrollbarTM[5]);
this._drawScrollbar(mainCtx);
mainCtx.restore();
Expand Down Expand Up @@ -1483,11 +1484,13 @@ export class Viewport {
right: Math.min(prevBound.right, currBound.right),
});
}
const expandX = this.bufferEdgeX;
const expandY = this.bufferEdgeY;
for (const bound of additionalAreas) {
bound.left = bound.left - this.bufferEdgeX;
bound.right = bound.right + this.bufferEdgeX;
bound.top = bound.top - this.bufferEdgeY;
bound.bottom = bound.bottom + this.bufferEdgeY;
bound.left = bound.left - expandX;
bound.right = bound.right + expandX;
bound.top = bound.top - expandY;
bound.bottom = bound.bottom + expandY;
}

return additionalAreas;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ export class SheetRenderController extends RxDisposable implements IRenderModule
const activeViewports = viewports.filter((vp) => vp.isActive && vp.cacheBound);
for (const vp of activeViewports) {
for (const b of dirtyBounds) {
if (Rectangle.hasIntersectionBetweenTwoBounds(vp.cacheBound!, b)) {
if (Rectangle.hasIntersectionBetweenTwoRect(vp.cacheBound!, b)) {
vp.markDirty(true);
}
}
Expand Down
Loading