diff --git a/src/features/cseMachine/CseMachineConfig.ts b/src/features/cseMachine/CseMachineConfig.ts index d722459043..db1789bbc7 100644 --- a/src/features/cseMachine/CseMachineConfig.ts +++ b/src/features/cseMachine/CseMachineConfig.ts @@ -66,6 +66,7 @@ export const Config = Object.freeze({ PrintStrokeColor: '#777', PrintStrokeColorFaded: '#ccc', ArrowHighlightedColor: '#ffffff', + ArrowDeadHighlightedColor: '#787777', // Colors of different states ActiveColor: '#030fff', @@ -73,6 +74,7 @@ export const Config = Object.freeze({ DangerColor: '#ff1744', PrintDangerColor: '#f44336', HoverColor: '#25c225', + HoverDeadColor: '#127a12', PrintHoverColor: '#0dbf0d', // Colors for text hover background diff --git a/src/features/cseMachine/CseMachineTypes.ts b/src/features/cseMachine/CseMachineTypes.ts index 7f0e4d672e..9841ec0e2b 100644 --- a/src/features/cseMachine/CseMachineTypes.ts +++ b/src/features/cseMachine/CseMachineTypes.ts @@ -40,6 +40,8 @@ export interface IVisible extends Drawable { height(): number; ref?: React.RefObject; + setArrowSourceHighlightedStyle?(): void; + setArrowSourceNormalStyle?(): void; } /** unassigned is internally represented as a symbol */ diff --git a/src/features/cseMachine/components/ArrayUnit.tsx b/src/features/cseMachine/components/ArrayUnit.tsx index 400b2e1399..07ca195d15 100644 --- a/src/features/cseMachine/components/ArrayUnit.tsx +++ b/src/features/cseMachine/components/ArrayUnit.tsx @@ -60,6 +60,22 @@ export class ArrayUnit extends Visible { if (!CseMachine.getPrintableMode()) this.indexRef.current?.hide(); } + setArrowSourceHighlightedStyle(): void { + if (this.parent.isLive()) { + this.ref.current?.stroke(Config.HoverColor); + } else { + this.ref.current?.stroke(Config.HoverDeadColor); + } + } + + setArrowSourceNormalStyle(): void { + this.ref.current?.stroke( + this.parent.isReferenced() && this.parent.isEnclosingFrameLive() + ? defaultStrokeColor() + : fadedStrokeColor() + ); + } + draw(): React.ReactNode { if (this.isDrawn()) return null; this._isDrawn = true; diff --git a/src/features/cseMachine/components/ControlItemComponent.tsx b/src/features/cseMachine/components/ControlItemComponent.tsx index b693b7cb12..cc1bb37255 100644 --- a/src/features/cseMachine/components/ControlItemComponent.tsx +++ b/src/features/cseMachine/components/ControlItemComponent.tsx @@ -92,6 +92,16 @@ export class ControlItemComponent extends Visible implements IHoverable { this.ref.current.zIndex(this.zIndex); }; + setArrowSourceHighlightedStyle(): void { + this.tag?.stroke(Config.HoverColor); + this.secItem?.fill(Config.HoverColor); + } + + setArrowSourceNormalStyle(): void { + this.tag?.stroke(this.topItem ? defaultActiveColor() : defaultStrokeColor()); + this.secItem?.fill(defaultTextColor()); + } + destroy() { this.ref.current.destroyChildren(); } diff --git a/src/features/cseMachine/components/Frame.tsx b/src/features/cseMachine/components/Frame.tsx index 89d06788e5..1fba618933 100644 --- a/src/features/cseMachine/components/Frame.tsx +++ b/src/features/cseMachine/components/Frame.tsx @@ -1,3 +1,4 @@ +import { Rect as KonvaRect } from 'konva/lib/shapes/Rect'; import React from 'react'; import { Group, Rect } from 'react-konva'; @@ -59,6 +60,7 @@ export class Frame extends Visible implements IHoverable { readonly bindings: Binding[] = []; /** name of this frame to display */ private _name!: Text; // removed readonly to allow reassignment for fixed layout + private readonly rectRef = React.createRef(); /** the level in which this frame resides */ readonly level: Level | undefined; /** environment associated with this frame */ @@ -260,6 +262,26 @@ export class Frame extends Visible implements IHoverable { onMouseLeave = () => {}; + setArrowSourceHighlightedStyle(): void { + if (this.isLive) { + this.rectRef.current?.stroke(Config.HoverColor); + } else { + this.rectRef.current?.stroke(Config.HoverDeadColor); + } + this.name.setArrowSourceHighlightedStyle(); + } + + setArrowSourceNormalStyle(): void { + this.rectRef.current?.stroke( + CseMachine.getCurrentEnvId() === this.environment?.id + ? defaultActiveColor() + : this.isLive + ? defaultStrokeColor() + : fadedStrokeColor() + ); + this.name.setArrowSourceNormalStyle(); + } + draw(): React.ReactNode { return ( @@ -267,6 +289,7 @@ export class Frame extends Visible implements IHoverable { { + if (shape instanceof Konva.Shape) { + if (shape.attrs.stroke) { + shape.stroke(color); + } + if (shape.attrs.fill) { + shape.fill(color); + } + } + }); + } public ref: RefObject = React.createRef(); + protected get tag() { + return this.ref.current?.getChildren?.()[0]; + } + protected get secItem() { + return this.ref.current?.getChildren?.()[1]; + } + setArrowSourceHighlightedStyle(): void {} + setArrowSourceNormalStyle(): void {} abstract draw(key?: number): React.ReactNode; } diff --git a/src/features/cseMachine/components/arrows/ArrowSelection.ts b/src/features/cseMachine/components/arrows/ArrowSelection.ts index 7ca26b5e3f..bbbc7172fe 100644 --- a/src/features/cseMachine/components/arrows/ArrowSelection.ts +++ b/src/features/cseMachine/components/arrows/ArrowSelection.ts @@ -16,6 +16,7 @@ class ArrowSelectionManager { clearSelection(): GenericArrow | null { const oldArrow = this.selectedArrow; this.selectedArrow = null; + oldArrow?.setNormalStyle(); return oldArrow; } diff --git a/src/features/cseMachine/components/arrows/GenericArrow.tsx b/src/features/cseMachine/components/arrows/GenericArrow.tsx index 97c885e066..fad79db90e 100644 --- a/src/features/cseMachine/components/arrows/GenericArrow.tsx +++ b/src/features/cseMachine/components/arrows/GenericArrow.tsx @@ -146,7 +146,7 @@ export class GenericArrow * Subclasses can override this to provide custom hover colors. */ protected getHighlightedColor(): string { - return Config.ArrowHighlightedColor; + return this.isLive ? Config.ArrowHighlightedColor : Config.ArrowDeadHighlightedColor; } onMouseEnter = (e: KonvaEventObject) => { @@ -189,8 +189,9 @@ export class GenericArrow this.arrowHeadRef.current.pointerWidth(Config.ArrowHoveredHeadSize); this.arrowHeadRef.current.pointerLength(Config.ArrowHoveredHeadSize); } + this.source.setArrowSourceHighlightedStyle?.(); + this.target?.setArrowSourceHighlightedStyle?.(); } - public setNormalStyle() { const color = this.isLive ? defaultStrokeColor() : fadedStrokeColor(); if (this.pathRef.current) { @@ -202,6 +203,8 @@ export class GenericArrow this.arrowHeadRef.current.pointerWidth(Config.ArrowHeadSize); this.arrowHeadRef.current.pointerLength(Config.ArrowHeadSize); } + this.source.setArrowSourceNormalStyle?.(); + this.target?.setArrowSourceNormalStyle?.(); } onClick = (e: KonvaEventObject) => { @@ -209,12 +212,7 @@ export class GenericArrow // Toggle selection - clear first, then select if it wasn't already selected const wasSelected = this.isSelected(); - const oldArrow = arrowSelection.clearSelection(); - - // Update old arrow's visual state - if (oldArrow && oldArrow !== this) { - oldArrow.setNormalStyle(); - } + arrowSelection.clearSelection(); if (!wasSelected) { this.select(); diff --git a/src/features/cseMachine/components/values/ArrayValue.tsx b/src/features/cseMachine/components/values/ArrayValue.tsx index cb2de77a14..95aac8285e 100644 --- a/src/features/cseMachine/components/values/ArrayValue.tsx +++ b/src/features/cseMachine/components/values/ArrayValue.tsx @@ -109,6 +109,14 @@ export class ArrayValue extends Value implements IHoverable { } } + setArrowSourceHighlightedStyle(): void { + this.units.forEach(unit => unit.setArrowSourceHighlightedStyle()); + } + + setArrowSourceNormalStyle(): void { + this.units.forEach(unit => unit.setArrowSourceNormalStyle()); + } + isEnclosingFrameLive(): boolean { const id = (this.data as any).id; return id ? Layout.liveObjectIDs.has(id) : false; diff --git a/src/features/cseMachine/components/values/ContValue.tsx b/src/features/cseMachine/components/values/ContValue.tsx index 9deadcdcad..83d0adf228 100644 --- a/src/features/cseMachine/components/values/ContValue.tsx +++ b/src/features/cseMachine/components/values/ContValue.tsx @@ -128,6 +128,19 @@ export class ContValue extends Value implements IHoverable { this.labelRef.current?.hide(); }; + setArrowSourceHighlightedStyle(): void { + if (this.isLive()) { + this.setShapesStyle(Config.HoverColor); + } else { + this.setShapesStyle(Config.HoverDeadColor); + } + } + + setArrowSourceNormalStyle(): void { + const strokeColor = this.isLive() ? defaultStrokeColor() : fadedStrokeColor(); + this.setShapesStyle(strokeColor); + } + draw(): React.ReactNode { if (this.enclosingFrame) { this._arrow = new ArrowFromFn(this).to(this.enclosingFrame) as ArrowFromFn; diff --git a/src/features/cseMachine/components/values/FnValue.tsx b/src/features/cseMachine/components/values/FnValue.tsx index e854a57d53..8d8845850a 100644 --- a/src/features/cseMachine/components/values/FnValue.tsx +++ b/src/features/cseMachine/components/values/FnValue.tsx @@ -169,6 +169,19 @@ export class FnValue extends Value implements IHoverable { currentTarget.getLayer()?.batchDraw(); }; + setArrowSourceHighlightedStyle(): void { + if (this.isLive()) { + this.setShapesStyle(Config.HoverColor); + } else { + this.setShapesStyle(Config.HoverDeadColor); + } + } + + setArrowSourceNormalStyle(): void { + const strokeColor = this.isLive() ? defaultStrokeColor() : fadedStrokeColor(); + this.setShapesStyle(strokeColor); + } + isLive(): boolean { const id = (this.data as any).id; return id ? Layout.liveObjectIDs.has(id) : false; diff --git a/src/features/cseMachine/components/values/GlobalFnValue.tsx b/src/features/cseMachine/components/values/GlobalFnValue.tsx index 66cc1c9064..1ab1d296d5 100644 --- a/src/features/cseMachine/components/values/GlobalFnValue.tsx +++ b/src/features/cseMachine/components/values/GlobalFnValue.tsx @@ -142,6 +142,19 @@ export class GlobalFnValue extends Value implements IHoverable { currentTarget.getLayer()?.batchDraw(); }; + setArrowSourceHighlightedStyle(): void { + if (this.isReferenced()) { + this.setShapesStyle(Config.HoverColor); + } else { + this.setShapesStyle(Config.HoverDeadColor); + } + } + + setArrowSourceNormalStyle(): void { + const strokeColor = this.isReferenced() ? defaultStrokeColor() : fadedStrokeColor(); + this.setShapesStyle(strokeColor); + } + draw(): React.ReactNode { this._isDrawn = true; if (Layout.globalEnvNode.frame) {