From 25a772420e9e6c66fb2abb6741e4e9bff79a3b29 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 22 Jun 2024 08:52:39 +0800 Subject: [PATCH] feat: optimize middlewares and renderer --- .../src/middleware/layout-selector/config.ts | 8 +- .../src/middleware/layout-selector/index.ts | 279 ++++++++++------ .../src/middleware/layout-selector/types.ts | 11 +- .../src/middleware/layout-selector/util.ts | 23 +- .../core/src/middleware/scroller/config.ts | 2 + .../core/src/middleware/scroller/index.ts | 32 +- .../core/src/middleware/scroller/types.ts | 4 +- packages/core/src/middleware/scroller/util.ts | 25 +- .../core/src/middleware/selector/index.ts | 8 +- .../core/src/middleware/selector/types.ts | 4 + .../core/src/middleware/text-editor/index.ts | 5 +- packages/renderer/src/draw/box.ts | 2 +- packages/renderer/src/draw/text.ts | 300 +++++++++--------- packages/types/src/lib/data.ts | 9 +- 14 files changed, 437 insertions(+), 275 deletions(-) diff --git a/packages/core/src/middleware/layout-selector/config.ts b/packages/core/src/middleware/layout-selector/config.ts index b5b2af2..b8ef3b3 100644 --- a/packages/core/src/middleware/layout-selector/config.ts +++ b/packages/core/src/middleware/layout-selector/config.ts @@ -1,8 +1,12 @@ export const key = 'LAYOUT_SELECT'; // export const keyHoverElement = Symbol(`${key}_hoverElementSize`); -export const keyLayoutActionType = Symbol(`${key}_layoutActionType`); // 'hover' | 'resize' | null = null; +export const keyLayoutActionType = Symbol(`${key}_layoutActionType`); // 'resize' | null = null; export const keyLayoutControlType = Symbol(`${key}_layoutControlType`); // ControlType | null; export const keyLayoutController = Symbol(`${key}_layoutController`); // ElementSizeController | null = null; +export const keyLayoutIsHover = Symbol(`${key}_layoutIsHover`); // boolean | null +export const keyLayoutIsSelected = Symbol(`${key}_layoutIsSelected`); // boolean | null -export const selectColor = '#1973ba'; +// export const selectColor = '#1973ba'; +export const selectColor = '#b331c9'; export const disableColor = '#5b5959b5'; +export const controllerSize = 10; diff --git a/packages/core/src/middleware/layout-selector/index.ts b/packages/core/src/middleware/layout-selector/index.ts index 534efbb..861edd5 100644 --- a/packages/core/src/middleware/layout-selector/index.ts +++ b/packages/core/src/middleware/layout-selector/index.ts @@ -1,60 +1,48 @@ import type { BoardMiddleware, ElementSize, Point } from '@idraw/types'; -import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot } from '@idraw/util'; +import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot, isViewPointInElementSize, calcViewElementSize } from '@idraw/util'; import type { LayoutSelectorSharedStorage, ControlType } from './types'; -import { keyLayoutActionType, keyLayoutController, keyLayoutControlType } from './config'; -import { keyActionType as keyElementActionType, middlewareEventSelectClear } from '../selector'; -import { drawLayoutController } from './util'; +import { keyLayoutActionType, keyLayoutController, keyLayoutControlType, keyLayoutIsHover, keyLayoutIsSelected, controllerSize } from './config'; +import { keyActionType as keyElementActionType, keyHoverElement, middlewareEventSelectClear } from '../selector'; +import { drawLayoutController, drawLayoutHover } from './util'; import { eventChange } from '../../config'; +export { keyLayoutIsSelected }; + export const MiddlewareLayoutSelector: BoardMiddleware = (opts) => { const { sharer, boardContent, calculator, viewer, eventHub } = opts; const { overlayContext } = boardContent; let prevPoint: Point | null = null; + let prevIsHover: boolean | null = null; + let prevIsSelected: boolean | null = null; + let isBusy: boolean | null = null; const clear = () => { prevPoint = null; sharer.setSharedStorage(keyLayoutActionType, null); sharer.setSharedStorage(keyLayoutControlType, null); sharer.setSharedStorage(keyLayoutController, null); + sharer.setSharedStorage(keyLayoutIsHover, null); + sharer.setSharedStorage(keyLayoutIsSelected, null); + prevIsHover = null; + prevIsSelected = null; + isBusy = null; }; - const isInElementAction = () => { - const elementType = sharer.getSharedStorage(keyElementActionType); - if (elementType) { + const isInElementHover = () => { + const hoverElement = sharer.getSharedStorage(keyHoverElement); + if (hoverElement) { + clear(); return true; } return false; }; - const isDisbaledControl = (controlType: ControlType) => { - const data = sharer.getActiveStorage('data'); - if (data?.layout?.operations) { - const operations = data.layout.operations; - if (controlType === 'left' && operations.disabledLeft === true) { - return true; - } - if (controlType === 'top' && operations.disabledTop === true) { - return true; - } - if (controlType === 'right' && operations.disabledRight === true) { - return true; - } - if (controlType === 'bottom' && operations.disabledBottom === true) { - return true; - } - if (controlType === 'top-left' && operations.disabledTopLeft === true) { - return true; - } - if (controlType === 'top-right' && operations.disabledTopRight === true) { - return true; - } - if (controlType === 'bottom-left' && operations.disabledBottomLeft === true) { - return true; - } - if (controlType === 'bottom-right' && operations.disabledBottomRight === true) { - return true; - } + const isInElementAction = () => { + const elementActionType = sharer.getSharedStorage(keyElementActionType); + if (elementActionType && elementActionType !== 'area') { + clear(); + return true; } return false; }; @@ -68,6 +56,25 @@ export const MiddlewareLayoutSelector: BoardMiddleware { + const size = getLayoutSize(); + if (size) { + const { x, y, w, h } = size; + const viewScaleInfo = sharer.getActiveViewScaleInfo(); + const viewSize = calcViewElementSize( + { + x: x - controllerSize / 2, + y: y - controllerSize / 2, + w: w + controllerSize, + h: h + controllerSize + }, + { viewScaleInfo } + ); + return isViewPointInElementSize(p, viewSize); + } + return false; + }; + const resetController = () => { const viewScaleInfo = sharer.getActiveViewScaleInfo(); const size: ElementSize | null = getLayoutSize(); @@ -105,6 +112,17 @@ export const MiddlewareLayoutSelector: BoardMiddleware { + if (isBusy === true) { + return; + } + eventHub.trigger('cursor', { + type: controlType ? `resize-${controlType}` : controlType, + groupQueue: [], + element: getLayoutSize() + }); + }; + return { name: '@middleware/layout-selector', use: () => { @@ -112,100 +130,160 @@ export const MiddlewareLayoutSelector: BoardMiddleware { + if (isBusy === true) { + return; + } if (isInElementAction()) { return; } - const prevLayoutActionType = sharer.getSharedStorage(keyLayoutActionType); - - const data = sharer.getActiveStorage('data'); - if (data?.layout && prevLayoutActionType !== 'resize') { - resetController(); - const layoutControlType = resetControlType(e); - if (layoutControlType) { - sharer.setSharedStorage(keyLayoutActionType, 'hover'); - if (!isDisbaledControl(layoutControlType)) { - eventHub.trigger('cursor', { - type: `resize-${layoutControlType}`, - groupQueue: [], - element: getLayoutSize() - }); - } + if (isInElementHover()) { + return; + } + if (isInLayout(e.point)) { + sharer.setSharedStorage(keyLayoutIsHover, true); + } else { + sharer.setSharedStorage(keyLayoutIsHover, false); + if (prevIsHover === true) { viewer.drawFrame(); - } else { - sharer.setSharedStorage(keyLayoutActionType, null); + prevIsHover = false; } } - if (['hover', 'resize'].includes(sharer.getSharedStorage(keyLayoutActionType) as string)) { - return false; + + if (sharer.getSharedStorage(keyLayoutIsSelected) === true) { + const prevLayoutActionType = sharer.getSharedStorage(keyLayoutActionType); + const data = sharer.getActiveStorage('data'); + if (data?.layout) { + if (prevLayoutActionType !== 'resize') { + resetController(); + const layoutControlType = resetControlType(e); + + if (layoutControlType) { + updateCursor(layoutControlType); + } else { + updateCursor(); + sharer.setSharedStorage(keyLayoutActionType, null); + } + } else { + const layoutControlType = resetControlType(e); + updateCursor(layoutControlType); + } + } + return; } - if (prevLayoutActionType === 'hover' && !sharer.getSharedStorage(keyLayoutActionType)) { + + if (sharer.getSharedStorage(keyLayoutIsHover) && !prevIsHover) { viewer.drawFrame(); } + prevIsHover = sharer.getSharedStorage(keyLayoutIsHover); }, + pointStart: (e) => { if (isInElementAction()) { return; } + + if (isInLayout(e.point)) { + sharer.setSharedStorage(keyLayoutIsSelected, true); + } else { + if (prevIsSelected === true) { + clear(); + viewer.drawFrame(); + } + sharer.setSharedStorage(keyLayoutIsSelected, false); + } + resetController(); const layoutControlType = resetControlType(e); prevPoint = e.point; + if (layoutControlType) { - if (isDisbaledControl(layoutControlType)) { - return; - } sharer.setSharedStorage(keyLayoutActionType, 'resize'); - return false; } - const layoutActionType = sharer.getSharedStorage(keyLayoutActionType); - if (['hover', 'resize'].includes(layoutActionType as string)) { - return false; + + if (sharer.getSharedStorage(keyLayoutIsSelected) === true && !prevIsSelected) { + viewer.drawFrame(); } + prevIsSelected = sharer.getSharedStorage(keyLayoutIsSelected); }, pointMove: (e) => { - if (isInElementAction()) { - return; + if (!sharer.getSharedStorage(keyLayoutIsSelected)) { + if (isInElementAction()) { + return; + } } const layoutActionType = sharer.getSharedStorage(keyLayoutActionType); const layoutControlType = sharer.getSharedStorage(keyLayoutControlType); const data = sharer.getActiveStorage('data'); - if (layoutControlType && isDisbaledControl(layoutControlType)) { - return; - } if (layoutActionType === 'resize' && layoutControlType && data?.layout) { if (prevPoint) { + isBusy = true; const scale = sharer.getActiveStorage('scale'); - const moveX = (e.point.x - prevPoint.x) / scale; - const moveY = (e.point.y - prevPoint.y) / scale; - const { x, y, w, h } = data.layout; - + const viewMoveX = e.point.x - prevPoint.x; + const viewMoveY = e.point.y - prevPoint.y; + const moveX = viewMoveX / scale; + const moveY = viewMoveY / scale; + const { x, y, w, h, operations = {} } = data.layout; + const { position = 'absolute' } = operations; if (layoutControlType === 'top') { - data.layout.y = calculator.toGridNum(y + moveY); - data.layout.h = calculator.toGridNum(h - moveY); + if (position === 'relative') { + data.layout.h = calculator.toGridNum(h - moveY); + viewer.scroll({ moveY: viewMoveY }); + } else { + data.layout.y = calculator.toGridNum(y + moveY); + data.layout.h = calculator.toGridNum(h - moveY); + } } else if (layoutControlType === 'right') { data.layout.w = calculator.toGridNum(w + moveX); } else if (layoutControlType === 'bottom') { data.layout.h = calculator.toGridNum(h + moveY); } else if (layoutControlType === 'left') { - data.layout.x = calculator.toGridNum(x + moveX); - data.layout.w = calculator.toGridNum(w - moveX); + if (position === 'relative') { + data.layout.w = calculator.toGridNum(w - moveX); + viewer.scroll({ moveX: viewMoveX }); + } else { + data.layout.x = calculator.toGridNum(x + moveX); + data.layout.w = calculator.toGridNum(w - moveX); + } } else if (layoutControlType === 'top-left') { - data.layout.x = calculator.toGridNum(x + moveX); - data.layout.y = calculator.toGridNum(y + moveY); - data.layout.w = calculator.toGridNum(w - moveX); - data.layout.h = calculator.toGridNum(h - moveY); + if (position === 'relative') { + data.layout.w = calculator.toGridNum(w - moveX); + data.layout.h = calculator.toGridNum(h - moveY); + viewer.scroll({ moveX: viewMoveX, moveY: viewMoveY }); + } else { + data.layout.x = calculator.toGridNum(x + moveX); + data.layout.y = calculator.toGridNum(y + moveY); + data.layout.w = calculator.toGridNum(w - moveX); + data.layout.h = calculator.toGridNum(h - moveY); + } } else if (layoutControlType === 'top-right') { - data.layout.y = calculator.toGridNum(y + moveY); - data.layout.w = calculator.toGridNum(w + moveX); - data.layout.h = calculator.toGridNum(h - moveY); + if (position === 'relative') { + viewer.scroll({ + moveY: viewMoveY + }); + data.layout.w = calculator.toGridNum(w + moveX); + data.layout.h = calculator.toGridNum(h - moveY); + } else { + data.layout.y = calculator.toGridNum(y + moveY); + data.layout.w = calculator.toGridNum(w + moveX); + data.layout.h = calculator.toGridNum(h - moveY); + } } else if (layoutControlType === 'bottom-right') { data.layout.w = calculator.toGridNum(w + moveX); data.layout.h = calculator.toGridNum(h + moveY); } else if (layoutControlType === 'bottom-left') { - data.layout.x = calculator.toGridNum(x + moveX); - data.layout.w = calculator.toGridNum(w - moveX); - data.layout.h = calculator.toGridNum(h + moveY); + if (position === 'relative') { + viewer.scroll({ + moveX: viewMoveX + }); + data.layout.w = calculator.toGridNum(w - moveX); + data.layout.h = calculator.toGridNum(h + moveY); + } else { + data.layout.x = calculator.toGridNum(x + moveX); + data.layout.w = calculator.toGridNum(w - moveX); + data.layout.h = calculator.toGridNum(h + moveY); + } } } prevPoint = e.point; @@ -215,35 +293,44 @@ export const MiddlewareLayoutSelector: BoardMiddleware { + isBusy = false; const layoutActionType = sharer.getSharedStorage(keyLayoutActionType); const layoutControlType = sharer.getSharedStorage(keyLayoutControlType); const data = sharer.getActiveStorage('data'); - if (data && layoutActionType === 'resize' && layoutControlType && !isDisbaledControl(layoutControlType)) { + if (data && layoutActionType === 'resize' && layoutControlType) { eventHub.trigger(eventChange, { type: 'changeLayout', data }); } - - clear(); }, beforeDrawFrame: ({ snapshot }) => { + if (isInElementAction()) { + return; + } + const { sharedStore, activeStore } = snapshot; const layoutActionType = sharedStore[keyLayoutActionType]; - const layoutControlType = sharedStore[keyLayoutControlType]; + const layoutIsHover = sharedStore[keyLayoutIsHover]; + const layoutIsSelected = sharedStore[keyLayoutIsSelected]; - if (activeStore.data?.layout && layoutActionType && layoutControlType) { - if (['hover', 'resize'].includes(layoutActionType)) { - const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot); - const { x, y, w, h } = activeStore.data.layout; - const size = { x, y, w, h }; - const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize: 10 }); + if (activeStore.data?.layout) { + const { x, y, w, h } = activeStore.data.layout; + const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot); + const size = { x, y, w, h }; + const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize }); + if (layoutIsHover === true) { + const viewSize = calcViewElementSize(size, { viewScaleInfo }); + drawLayoutHover(overlayContext, { layoutSize: viewSize }); + } + + if ((layoutActionType && ['resize'].includes(layoutActionType)) || layoutIsSelected === true) { drawLayoutController(overlayContext, { controller, operations: activeStore.data.layout.operations || {} }); } } diff --git a/packages/core/src/middleware/layout-selector/types.ts b/packages/core/src/middleware/layout-selector/types.ts index 7247f90..71e60d4 100644 --- a/packages/core/src/middleware/layout-selector/types.ts +++ b/packages/core/src/middleware/layout-selector/types.ts @@ -1,9 +1,9 @@ -import type { LayoutSizeController } from '@idraw/types'; -import { keyLayoutActionType, keyLayoutControlType, keyLayoutController } from './config'; -import { keyActionType as keyElementActionType } from '../selector'; +import type { LayoutSizeController, Element } from '@idraw/types'; +import { keyLayoutActionType, keyLayoutControlType, keyLayoutController, keyLayoutIsHover, keyLayoutIsSelected } from './config'; +import { keyActionType as keyElementActionType, keyHoverElement } from '../selector'; import type { ActionType as ElementActionType } from '../selector'; -export type ActionType = 'hover' | 'resize' | null; +export type ActionType = 'resize' | null; export type ControlType = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; @@ -12,4 +12,7 @@ export type LayoutSelectorSharedStorage = { [keyLayoutControlType]: ControlType | null; [keyLayoutController]: LayoutSizeController | null; [keyElementActionType]: ElementActionType | null; + [keyHoverElement]: Element | null; + [keyLayoutIsHover]: boolean | null; + [keyLayoutIsSelected]: boolean | null; }; diff --git a/packages/core/src/middleware/layout-selector/util.ts b/packages/core/src/middleware/layout-selector/util.ts index 19a1714..b33cc6f 100644 --- a/packages/core/src/middleware/layout-selector/util.ts +++ b/packages/core/src/middleware/layout-selector/util.ts @@ -1,4 +1,4 @@ -import type { ViewContext2D, LayoutSizeController, DataLayout, ViewRectVertexes, PointSize } from '@idraw/types'; +import type { ViewContext2D, LayoutSizeController, DataLayout, ViewRectVertexes, PointSize, ElementSize } from '@idraw/types'; import { selectColor, disableColor } from './config'; function drawControllerBox(ctx: ViewContext2D, boxVertexes: ViewRectVertexes) { @@ -109,3 +109,24 @@ export function drawLayoutController( drawControllerBox(ctx, bottomLeft.vertexes); } } + +export function drawLayoutHover( + ctx: ViewContext2D, + opts: { + layoutSize: ElementSize; + } +) { + const { layoutSize } = opts; + const { x, y, w, h } = layoutSize; + ctx.setLineDash([]); + ctx.strokeStyle = selectColor; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + w, y); + ctx.lineTo(x + w, y + h); + ctx.lineTo(x, y + h); + ctx.lineTo(x, y); + ctx.closePath(); + ctx.stroke(); +} diff --git a/packages/core/src/middleware/scroller/config.ts b/packages/core/src/middleware/scroller/config.ts index 6aa1c8d..d73fc25 100644 --- a/packages/core/src/middleware/scroller/config.ts +++ b/packages/core/src/middleware/scroller/config.ts @@ -1,6 +1,8 @@ export const key = 'SCROLL'; export const keyXThumbRect = Symbol(`${key}_xThumbRect`); export const keyYThumbRect = Symbol(`${key}_yThumbRect`); +export const keyHoverXThumbRect = Symbol(`${key}_hoverXThumbRect`); +export const keyHoverYThumbRect = Symbol(`${key}_hoverYThumbRect`); export const keyPrevPoint = Symbol(`${key}_prevPoint`); export const keyActivePoint = Symbol(`${key}_activePoint`); export const keyActiveThumbType = Symbol(`${key}_activeThumbType`); diff --git a/packages/core/src/middleware/scroller/index.ts b/packages/core/src/middleware/scroller/index.ts index ae854e9..9ebe297 100644 --- a/packages/core/src/middleware/scroller/index.ts +++ b/packages/core/src/middleware/scroller/index.ts @@ -1,20 +1,24 @@ import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelEvent } from '@idraw/types'; import { drawScroller, isPointInScrollThumb } from './util'; // import type { ScrollbarThumbType } from './util'; -import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config'; +import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType, keyHoverXThumbRect, keyHoverYThumbRect } from './config'; import type { DeepScrollerSharedStorage } from './types'; export const MiddlewareScroller: BoardMiddleware = (opts) => { - const { viewer, boardContent, sharer } = opts; + const { viewer, boardContent, sharer, eventHub } = opts; const { overlayContext } = boardContent; sharer.setSharedStorage(keyXThumbRect, null); // null | ElementSize sharer.setSharedStorage(keyYThumbRect, null); // null | ElementSize + let isBusy: boolean = false; // viewer.drawFrame(); const clear = () => { sharer.setSharedStorage(keyPrevPoint, null); // null | Point; sharer.setSharedStorage(keyActivePoint, null); // null | Point; sharer.setSharedStorage(keyActiveThumbType, null); // null | 'X' | 'Y' + sharer.setSharedStorage(keyHoverXThumbRect, null); // null | boolean + sharer.setSharedStorage(keyHoverYThumbRect, null); // null | boolean + isBusy = false; }; clear(); @@ -64,10 +68,33 @@ export const MiddlewareScroller: BoardMiddleware = (o viewer.drawFrame(); }, + hover: (e: PointWatcherEvent) => { + if (isBusy === true) { + return false; + } + const { point } = e; + const thumbType = getThumbType(point); + if (thumbType === 'X' || thumbType === 'Y') { + if (thumbType === 'X') { + sharer.setSharedStorage(keyHoverXThumbRect, true); + sharer.setSharedStorage(keyHoverYThumbRect, false); + } else { + sharer.setSharedStorage(keyHoverXThumbRect, false); + sharer.setSharedStorage(keyHoverYThumbRect, true); + } + eventHub.trigger('cursor', { type: 'default' }); + return false; + } + + sharer.setSharedStorage(keyHoverXThumbRect, false); + sharer.setSharedStorage(keyHoverYThumbRect, false); + }, + pointStart: (e: PointWatcherEvent) => { const { point } = e; const thumbType = getThumbType(point); if (thumbType === 'X' || thumbType === 'Y') { + isBusy = true; sharer.setSharedStorage(keyActiveThumbType, thumbType); sharer.setSharedStorage(keyPrevPoint, point); return false; @@ -88,6 +115,7 @@ export const MiddlewareScroller: BoardMiddleware = (o } }, pointEnd: () => { + isBusy = false; const activeThumbType = sharer.getSharedStorage(keyActiveThumbType); clear(); if (activeThumbType === 'X' || activeThumbType === 'Y') { diff --git a/packages/core/src/middleware/scroller/types.ts b/packages/core/src/middleware/scroller/types.ts index a0059aa..721d794 100644 --- a/packages/core/src/middleware/scroller/types.ts +++ b/packages/core/src/middleware/scroller/types.ts @@ -1,9 +1,11 @@ import type { Point, ElementSize } from '@idraw/types'; -import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config'; +import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType, keyHoverXThumbRect, keyHoverYThumbRect } from './config'; export type DeepScrollerSharedStorage = { [keyXThumbRect]: null | ElementSize; [keyYThumbRect]: null | ElementSize; + [keyHoverXThumbRect]: boolean | null; + [keyHoverYThumbRect]: boolean | null; [keyPrevPoint]: null | Point; [keyActivePoint]: null | Point; [keyActiveThumbType]: null | 'X' | 'Y'; diff --git a/packages/core/src/middleware/scroller/util.ts b/packages/core/src/middleware/scroller/util.ts index e45450c..03b262f 100644 --- a/packages/core/src/middleware/scroller/util.ts +++ b/packages/core/src/middleware/scroller/util.ts @@ -1,6 +1,6 @@ import type { Point, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo, ViewContext2D, ElementSize } from '@idraw/types'; import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util'; -import { keyActivePoint, keyActiveThumbType, keyPrevPoint, keyXThumbRect, keyYThumbRect } from './config'; +import { keyActivePoint, keyActiveThumbType, keyPrevPoint, keyXThumbRect, keyYThumbRect, keyHoverXThumbRect, keyHoverYThumbRect } from './config'; const minScrollerWidth = 12; const scrollerLineWidth = 16; @@ -10,7 +10,8 @@ export type ScrollbarThumbType = 'X' | 'Y'; const scrollConfig = { width: minScrollerWidth, - thumbColor: '#000000AA', + thumbColor: '#0000008A', + thumbHoverColor: '#000000EE', scrollBarColor: '#FFFFFF60', showScrollBar: false }; @@ -51,6 +52,8 @@ interface ScrollInfo { activeThumbType: ScrollbarThumbType | null; xThumbRect: ElementSize | null; yThumbRect: ElementSize | null; + hoverXThumb: boolean | null; + hoverYThumb: boolean | null; } function getScrollInfoFromSnapshot(snapshot: BoardViewerFrameSnapshot): ScrollInfo { const { sharedStore } = snapshot; @@ -59,12 +62,15 @@ function getScrollInfoFromSnapshot(snapshot: BoardViewerFrameSnapshot): ScrollIn prevPoint: sharedStore[keyPrevPoint] || null, activeThumbType: sharedStore[keyActiveThumbType] || null, xThumbRect: sharedStore[keyXThumbRect] || null, - yThumbRect: sharedStore[keyYThumbRect] || null + yThumbRect: sharedStore[keyYThumbRect] || null, + hoverXThumb: sharedStore[keyHoverXThumbRect], + hoverYThumb: sharedStore[keyHoverYThumbRect] }; return info; } -function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo) { +function calcScrollerInfo(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; hoverXThumb: boolean | null; hoverYThumb: boolean | null }) { + const { viewScaleInfo, viewSizeInfo, hoverXThumb, hoverYThumb } = opts; const { width, height } = viewSizeInfo; const { offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; const sliderMinSize = scrollerLineWidth * 2.5; @@ -125,7 +131,8 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn ySize, translateY, translateX, - thumbColor: scrollConfig.thumbColor, + xThumbColor: hoverXThumb ? scrollConfig.thumbHoverColor : scrollConfig.thumbColor, + yThumbColor: hoverYThumb ? scrollConfig.thumbHoverColor : scrollConfig.thumbColor, scrollBarColor: scrollConfig.scrollBarColor, xThumbRect, yThumbRect @@ -197,9 +204,9 @@ function drawScrollerThumb( function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; scrollInfo: ScrollInfo }) { const ctx = overlayContext; const { viewScaleInfo, viewSizeInfo, scrollInfo } = opts; - const { activeThumbType, prevPoint, activePoint } = scrollInfo; + const { activeThumbType, prevPoint, activePoint, hoverXThumb, hoverYThumb } = scrollInfo; const { width, height } = viewSizeInfo; - const wrapper = calcScrollerInfo(viewScaleInfo, viewSizeInfo); + const wrapper = calcScrollerInfo({ viewScaleInfo, viewSizeInfo, hoverXThumb, hoverYThumb }); let xThumbRect: ElementSize = { ...wrapper.xThumbRect }; let yThumbRect: ElementSize = { ...wrapper.yThumbRect }; @@ -225,7 +232,7 @@ function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo: axis: 'X', ...xThumbRect, r: wrapper.lineSize / 2, - color: wrapper.thumbColor + color: wrapper.xThumbColor }); // y-bar @@ -240,7 +247,7 @@ function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo: axis: 'Y', ...yThumbRect, r: wrapper.lineSize / 2, - color: wrapper.thumbColor + color: wrapper.yThumbColor }); ctx.globalAlpha = 1; diff --git a/packages/core/src/middleware/selector/index.ts b/packages/core/src/middleware/selector/index.ts index 18fc082..09a15b3 100644 --- a/packages/core/src/middleware/selector/index.ts +++ b/packages/core/src/middleware/selector/index.ts @@ -75,8 +75,9 @@ import { import { calcReferenceInfo } from './reference'; import { middlewareEventTextEdit } from '../text-editor'; import { eventChange } from '../../config'; +import { keyLayoutIsSelected } from '../layout-selector'; -export { keySelectedElementList, keyActionType, keyResizeType, keyGroupQueue }; +export { keySelectedElementList, keyHoverElement, keyActionType, keyResizeType, keyGroupQueue }; export type { DeepSelectorSharedStorage, ActionType }; export { middlewareEventSelect, middlewareEventSelectClear, middlewareEventSelectInGroup, middlewareEventSnapToGrid }; @@ -237,10 +238,15 @@ export const MiddlewareSelector: BoardMiddleware { + const layoutIsSelected = sharer.getSharedStorage(keyLayoutIsSelected); + const resizeType = sharer.getSharedStorage(keyResizeType); const actionType = sharer.getSharedStorage(keyActionType); const groupQueue = sharer.getSharedStorage(keyGroupQueue); const triggerCursor = (target: PointTarget) => { + if (layoutIsSelected === true) { + return; + } const cursor: string | null = target.type; if (inBusyMode === null) { eventHub.trigger('cursor', { diff --git a/packages/core/src/middleware/selector/types.ts b/packages/core/src/middleware/selector/types.ts index 3c23b7f..13502be 100644 --- a/packages/core/src/middleware/selector/types.ts +++ b/packages/core/src/middleware/selector/types.ts @@ -40,6 +40,7 @@ import { keyDebugStartHorizontal, keyDebugStartVertical } from './config'; +import { keyLayoutIsSelected } from '../layout-selector'; export { Data, @@ -112,4 +113,7 @@ export type DeepSelectorSharedStorage = { [keyDebugEndVertical]: PointSize | null; [keyDebugStartHorizontal]: PointSize | null; [keyDebugStartVertical]: PointSize | null; + + // layout-selector + [keyLayoutIsSelected]: boolean | null; }; diff --git a/packages/core/src/middleware/text-editor/index.ts b/packages/core/src/middleware/text-editor/index.ts index dceb45d..0164a22 100644 --- a/packages/core/src/middleware/text-editor/index.ts +++ b/packages/core/src/middleware/text-editor/index.ts @@ -174,9 +174,8 @@ export const MiddlewareTextEditor: BoardMiddleware void; + renderContent?: () => void; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; parentOpacity: number; diff --git a/packages/renderer/src/draw/text.ts b/packages/renderer/src/draw/text.ts index a0f1eed..a18036f 100644 --- a/packages/renderer/src/draw/text.ts +++ b/packages/renderer/src/draw/text.ts @@ -1,7 +1,7 @@ import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types'; import { rotateElement, calcViewElementSize, enhanceFontFamliy } from '@idraw/util'; import { is, isColorStr, getDefaultElementDetailConfig } from '@idraw/util'; -import { drawBox } from './box'; +import { drawBox, drawBoxShadow } from './box'; const detailConfig = getDefaultElementDetailConfig(); @@ -20,172 +20,178 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem; const viewElem = { ...elem, ...{ x, y, w, h, angle } }; rotateElement(ctx, { x, y, w, h, angle }, () => { - drawBox(ctx, viewElem, { - originElem: elem, - calcElemSize: { x, y, w, h, angle }, + drawBoxShadow(ctx, viewElem, { viewScaleInfo, viewSizeInfo, - parentOpacity, renderContent: () => { - const detail: Element<'text'>['detail'] = { - ...detailConfig, - ...elem.detail - }; - const originFontSize = detail.fontSize || detailConfig.fontSize; - const fontSize = originFontSize * viewScaleInfo.scale; - - if (fontSize < 2) { - return; - } - - const originLineHeight = detail.lineHeight || originFontSize; - const lineHeight = originLineHeight * viewScaleInfo.scale; - - ctx.fillStyle = elem.detail.color || detailConfig.color; - ctx.textBaseline = 'top'; - ctx.$setFont({ - fontWeight: detail.fontWeight, - fontSize: fontSize, - fontFamily: enhanceFontFamliy(detail.fontFamily) + drawBox(ctx, viewElem, { + originElem: elem, + calcElemSize: { x, y, w, h, angle }, + viewScaleInfo, + viewSizeInfo, + parentOpacity }); - let detailText = detail.text.replace(/\r\n/gi, '\n'); - if (detail.textTransform === 'lowercase') { - detailText = detailText.toLowerCase(); - } else if (detail.textTransform === 'uppercase') { - detailText = detailText.toUpperCase(); - } + } + }); + { + const detail: Element<'text'>['detail'] = { + ...detailConfig, + ...elem.detail + }; + const originFontSize = detail.fontSize || detailConfig.fontSize; + const fontSize = originFontSize * viewScaleInfo.scale; - const fontHeight = lineHeight; - const detailTextList = detailText.split('\n'); - const lines: { text: string; width: number }[] = []; + if (fontSize < 2) { + return; + } - let lineNum = 0; - detailTextList.forEach((itemText: string, idx: number) => { - if (detail.minInlineSize === 'maxContent') { - lines.push({ - text: itemText, - width: ctx.$undoPixelRatio(ctx.measureText(itemText).width) + const originLineHeight = detail.lineHeight || originFontSize; + const lineHeight = originLineHeight * viewScaleInfo.scale; + + ctx.fillStyle = elem.detail.color || detailConfig.color; + ctx.textBaseline = 'top'; + ctx.$setFont({ + fontWeight: detail.fontWeight, + fontSize: fontSize, + fontFamily: enhanceFontFamliy(detail.fontFamily) + }); + let detailText = detail.text.replace(/\r\n/gi, '\n'); + if (detail.textTransform === 'lowercase') { + detailText = detailText.toLowerCase(); + } else if (detail.textTransform === 'uppercase') { + detailText = detailText.toUpperCase(); + } + + const fontHeight = lineHeight; + const detailTextList = detailText.split('\n'); + const lines: { text: string; width: number }[] = []; + + let lineNum = 0; + detailTextList.forEach((itemText: string, idx: number) => { + if (detail.minInlineSize === 'maxContent') { + lines.push({ + text: itemText, + width: ctx.$undoPixelRatio(ctx.measureText(itemText).width) + }); + } else { + let lineText = ''; + let splitStr = ''; + let tempStrList: string[] = itemText.split(splitStr); + if (detail.wordBreak === 'normal') { + const splitStr = ' '; + const wordList = itemText.split(splitStr); + tempStrList = []; + wordList.forEach((word: string, idx: number) => { + tempStrList.push(word); + if (idx < wordList.length - 1) { + tempStrList.push(splitStr); + } }); - } else { - let lineText = ''; - let splitStr = ''; - let tempStrList: string[] = itemText.split(splitStr); - if (detail.wordBreak === 'normal') { - const splitStr = ' '; - const wordList = itemText.split(splitStr); - tempStrList = []; - wordList.forEach((word: string, idx: number) => { - tempStrList.push(word); - if (idx < wordList.length - 1) { - tempStrList.push(splitStr); - } - }); - } + } - if (tempStrList.length === 1 && detail.overflow === 'visible') { - lines.push({ - text: tempStrList[0], - width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width) - }); - } else if (tempStrList.length > 0) { - for (let i = 0; i < tempStrList.length; i++) { - if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, viewScaleInfo.scale)) { - lineText += tempStrList[i] || ''; - } else { + if (tempStrList.length === 1 && detail.overflow === 'visible') { + lines.push({ + text: tempStrList[0], + width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width) + }); + } else if (tempStrList.length > 0) { + for (let i = 0; i < tempStrList.length; i++) { + if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, viewScaleInfo.scale)) { + lineText += tempStrList[i] || ''; + } else { + lines.push({ + text: lineText, + width: ctx.$undoPixelRatio(ctx.measureText(lineText).width) + }); + lineText = tempStrList[i] || ''; + lineNum++; + } + if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') { + break; + } + if (tempStrList.length - 1 === i) { + if ((lineNum + 1) * fontHeight <= h) { lines.push({ text: lineText, width: ctx.$undoPixelRatio(ctx.measureText(lineText).width) }); - lineText = tempStrList[i] || ''; - lineNum++; - } - if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') { + if (idx < detailTextList.length - 1) { + lineNum++; + } break; } - if (tempStrList.length - 1 === i) { - if ((lineNum + 1) * fontHeight <= h) { - lines.push({ - text: lineText, - width: ctx.$undoPixelRatio(ctx.measureText(lineText).width) - }); - if (idx < detailTextList.length - 1) { - lineNum++; - } - break; - } - } } - } else { - lines.push({ - text: '', - width: 0 - }); } - } - }); - - let startY = 0; - let eachLineStartY = 0; - if (fontHeight > fontSize) { - eachLineStartY = (fontHeight - fontSize) / 2; - } - if (lines.length * fontHeight < h) { - if (elem.detail.verticalAlign === 'top') { - startY = 0; - } else if (elem.detail.verticalAlign === 'bottom') { - startY += h - lines.length * fontHeight; } else { - // middle and default - startY += (h - lines.length * fontHeight) / 2; + lines.push({ + text: '', + width: 0 + }); } } + }); - // draw text lines - { - const _y = y + startY; - if (detail.textShadowColor !== undefined && isColorStr(detail.textShadowColor)) { - ctx.shadowColor = detail.textShadowColor; - } - if (detail.textShadowOffsetX !== undefined && is.number(detail.textShadowOffsetX)) { - ctx.shadowOffsetX = detail.textShadowOffsetX; - } - if (detail.textShadowOffsetY !== undefined && is.number(detail.textShadowOffsetY)) { - ctx.shadowOffsetY = detail.textShadowOffsetY; - } - if (detail.textShadowBlur !== undefined && is.number(detail.textShadowBlur)) { - ctx.shadowBlur = detail.textShadowBlur; - } - lines.forEach((line, i) => { - let _x = x; - if (detail.textAlign === 'center') { - _x = x + (w - line.width) / 2; - } else if (detail.textAlign === 'right') { - _x = x + (w - line.width); - } - ctx.fillText(line.text, _x, _y + fontHeight * i + eachLineStartY); - }); - } - - // // draw text stroke - // if (isColorStr(detail.strokeColor) && detail.strokeWidth !== undefined && detail.strokeWidth > 0) { - // const _y = y + startY; - // lines.forEach((line, i) => { - // let _x = x; - // if (detail.textAlign === 'center') { - // _x = x + (w - line.width) / 2; - // } else if (detail.textAlign === 'right') { - // _x = x + (w - line.width); - // } - // if (detail.strokeColor !== undefined) { - // ctx.strokeStyle = detail.strokeColor; - // } - // if (detail.strokeWidth !== undefined && detail.strokeWidth > 0) { - // ctx.lineWidth = detail.strokeWidth; - // } - // ctx.strokeText(line.text, _x, _y + fontHeight * i); - // }); - // } + let startY = 0; + let eachLineStartY = 0; + if (fontHeight > fontSize) { + eachLineStartY = (fontHeight - fontSize) / 2; } - }); + if (lines.length * fontHeight < h) { + if (elem.detail.verticalAlign === 'top') { + startY = 0; + } else if (elem.detail.verticalAlign === 'bottom') { + startY += h - lines.length * fontHeight; + } else { + // middle and default + startY += (h - lines.length * fontHeight) / 2; + } + } + + // draw text lines + { + const _y = y + startY; + if (detail.textShadowColor !== undefined && isColorStr(detail.textShadowColor)) { + ctx.shadowColor = detail.textShadowColor; + } + if (detail.textShadowOffsetX !== undefined && is.number(detail.textShadowOffsetX)) { + ctx.shadowOffsetX = detail.textShadowOffsetX; + } + if (detail.textShadowOffsetY !== undefined && is.number(detail.textShadowOffsetY)) { + ctx.shadowOffsetY = detail.textShadowOffsetY; + } + if (detail.textShadowBlur !== undefined && is.number(detail.textShadowBlur)) { + ctx.shadowBlur = detail.textShadowBlur; + } + lines.forEach((line, i) => { + let _x = x; + if (detail.textAlign === 'center') { + _x = x + (w - line.width) / 2; + } else if (detail.textAlign === 'right') { + _x = x + (w - line.width); + } + ctx.fillText(line.text, _x, _y + fontHeight * i + eachLineStartY); + }); + } + + // // draw text stroke + // if (isColorStr(detail.strokeColor) && detail.strokeWidth !== undefined && detail.strokeWidth > 0) { + // const _y = y + startY; + // lines.forEach((line, i) => { + // let _x = x; + // if (detail.textAlign === 'center') { + // _x = x + (w - line.width) / 2; + // } else if (detail.textAlign === 'right') { + // _x = x + (w - line.width); + // } + // if (detail.strokeColor !== undefined) { + // ctx.strokeStyle = detail.strokeColor; + // } + // if (detail.strokeWidth !== undefined && detail.strokeWidth > 0) { + // ctx.lineWidth = detail.strokeWidth; + // } + // ctx.strokeText(line.text, _x, _y + fontHeight * i); + // }); + // } + } }); } diff --git a/packages/types/src/lib/data.ts b/packages/types/src/lib/data.ts index 73f7580..40ae62a 100644 --- a/packages/types/src/lib/data.ts +++ b/packages/types/src/lib/data.ts @@ -6,14 +6,7 @@ export type DataLayout = Pick & { 'background' | 'borderWidth' | 'overflow' | 'borderColor' | 'borderDash' | 'borderRadius' | 'shadowBlur' | 'shadowColor' | 'shadowOffsetX' | 'shadowOffsetY' >; operations?: { - disabledLeft?: boolean; - disabledTop?: boolean; - disabledRight?: boolean; - disabledBottom?: boolean; - disabledTopLeft?: boolean; - disabledTopRight?: boolean; - disabledBottomLeft?: boolean; - disabledBottomRight?: boolean; + position?: 'absolute' | 'relative'; }; };