diff --git a/packages/editor/src/lib/components/default-components/CanvasShapeIndicators.tsx b/packages/editor/src/lib/components/default-components/CanvasShapeIndicators.tsx index 266b69da9680..d197671e1b47 100644 --- a/packages/editor/src/lib/components/default-components/CanvasShapeIndicators.tsx +++ b/packages/editor/src/lib/components/default-components/CanvasShapeIndicators.tsx @@ -16,6 +16,8 @@ interface CollaboratorIndicatorData { interface RenderData { idsToDisplay: Set + hoveredOnlyId: TLShapeId | null + isBrushing: boolean renderingShapeIds: Set hintingShapeIds: TLShapeId[] collaboratorIndicators: CollaboratorIndicatorData[] @@ -51,6 +53,8 @@ function collaboratorIndicatorsEqual( function renderDataEqual(a: RenderData, b: RenderData): boolean { return ( + a.hoveredOnlyId === b.hoveredOnlyId && + a.isBrushing === b.isBrushing && setsEqual(a.idsToDisplay, b.idsToDisplay) && setsEqual(a.renderingShapeIds, b.renderingShapeIds) && arraysEqual(a.hintingShapeIds, b.hintingShapeIds) && @@ -154,21 +158,26 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() { const instanceState = editor.getInstanceState() const isChangingStyle = instanceState.isChangingStyle const isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape') - const isInSelectState = editor.isInAny( - 'select.brushing', - 'select.scribble_brushing', - 'select.pointing_shape', - 'select.pointing_selection', - 'select.pointing_handle' - ) + const isBrushing = editor.isInAny('select.brushing', 'select.scribble_brushing') + const isInSelectState = + isBrushing || + editor.isInAny( + 'select.pointing_shape', + 'select.pointing_selection', + 'select.pointing_handle' + ) + let hoveredOnlyId: TLShapeId | null = null if (!isChangingStyle && (isIdleOrEditing || isInSelectState)) { for (const id of editor.getSelectedShapeIds()) { idsToDisplay.add(id) } if (isIdleOrEditing && instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) { const hovered = editor.getHoveredShapeId() - if (hovered) idsToDisplay.add(hovered) + if (hovered) { + if (!idsToDisplay.has(hovered)) hoveredOnlyId = hovered + idsToDisplay.add(hovered) + } } } @@ -201,6 +210,8 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() { return { idsToDisplay, + hoveredOnlyId, + isBrushing, renderingShapeIds, hintingShapeIds, collaboratorIndicators, @@ -219,7 +230,7 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() { const ctx = canvas.getContext('2d') if (!ctx) return - const { idsToDisplay, renderingShapeIds, hintingShapeIds, collaboratorIndicators } = + const { idsToDisplay, hoveredOnlyId, isBrushing, renderingShapeIds, hintingShapeIds, collaboratorIndicators } = $renderData.get() const { w, h } = editor.getViewportScreenBounds() @@ -269,9 +280,16 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() { // Draw selected/hovered indicators (1.5px stroke) ctx.lineWidth = 1.5 / zoom + ctx.globalAlpha = isBrushing ? 0.6 : 1.0 for (const shapeId of idsToDisplay) { + if (shapeId === hoveredOnlyId) continue renderShapeIndicator(ctx, editor, shapeId, renderingShapeIds) } + if (hoveredOnlyId) { + ctx.globalAlpha = 0.6 + renderShapeIndicator(ctx, editor, hoveredOnlyId, renderingShapeIds) + } + ctx.globalAlpha = 1.0 // Draw hinted indicators with a thicker stroke (2.5px) if (hintingShapeIds.length > 0) { diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index cec4046f81d2..e5c144d0b44a 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -5336,11 +5336,13 @@ export class Editor extends EventEmitter { // If the hit is within the frame's outer margin, then select the frame const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside) + const isGrid = (shape as any).type === 'grid' if ( - hitFrameInside + !isGrid && + (hitFrameInside ? (distance > 0 && distance <= outerMargin) || (distance <= 0 && distance > -innerMargin) - : distance > 0 && distance <= outerMargin + : distance > 0 && distance <= outerMargin) ) { return inMarginClosestToEdgeHit || shape } @@ -5355,7 +5357,7 @@ export class Editor extends EventEmitter { return ( inMarginClosestToEdgeHit || inHollowSmallestAreaHit || - (hitFrameInside ? shape : undefined) + (hitFrameInside || isGrid ? shape : undefined) ) } continue diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts index 79446240077d..75c90a96572f 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts @@ -1,4 +1,4 @@ -import { StateNode, TLPointerEventInfo, TLShape } from '@tldraw/editor' +import { Group2d, StateNode, TLPointerEventInfo, TLShape } from '@tldraw/editor' import { isOverArrowLabel } from '../../../shapes/arrow/arrowLabel' import { getTextLabels } from '../../../utils/shapes/shapes' @@ -11,6 +11,7 @@ export class PointingShape extends StateNode { didCtrlOnEnter = false didSelectOnEnter = false + didHitGridLabel = false override onEnter(info: TLPointerEventInfo & { target: 'shape' }) { const selectedShapeIds = this.editor.getSelectedShapeIds() @@ -22,6 +23,19 @@ export class PointingShape extends StateNode { this.hitShape = info.shape this.isDoubleClick = false this.didCtrlOnEnter = accelKey + this.didHitGridLabel = false + if ((info.shape as any).type === 'grid') { + const geometry = this.editor.getShapeGeometry(info.shape) + if (geometry instanceof Group2d) { + const pointInShapeSpace = this.editor.getPointInShapeSpace(info.shape, currentPagePoint) + for (const child of geometry.children) { + if (child.isLabel && child.isPointInBounds(pointInShapeSpace)) { + this.didHitGridLabel = true + break + } + } + } + } const outermostSelectingShape = this.editor.getOutermostSelectableShape(info.shape) const selectedAncestor = this.editor.findShapeAncestor(outermostSelectingShape, (parent) => selectedShapeIds.includes(parent.id) @@ -217,6 +231,9 @@ export class PointingShape extends StateNode { if (this.didCtrlOnEnter) { this.parent.transition('brushing', info) + } else if (this.didSelectOnEnter && (this.hitShape as any).type === 'grid' && !this.didHitGridLabel) { + this.editor.setSelectedShapes([]) + this.parent.transition('brushing', { ...info, target: 'canvas' as const }) } else { this.startTranslating(info) }