Let's revisit the pattern we are using for processing mouse events as they apply to our cells. So far we have a complete pattern for mirror cell hints that appear as the user moves the mouse/cursor over mirror cells. The event is initially detected in the LaserGame morph.
mouseMoveWhileButtonUp: evt forMorph: aSketchMorph
| cell renderer pixelPositionWithinBoard |
cell := self cellForEvent: evt.
renderer := CellRenderer rendererFor: cell grid: self grid form: self boardForm.
pixelPositionWithinBoard := self boardRelativePositionFor: evt.
renderer showPositionHintFromWithinBoardOffset: pixelPositionWithinBoard.
We determine which cell is under the cursor. Then we figure out which CellRenderer applies to that cell and ask it to supply the "hint" graphic at a board relative position. In our case, the MirrorCellRenderer handles the hint request.
| cellPosn offsetWithinCell regionClass arrow offset arrowAndOffset |
cellPosn := self offsetWithinGridForm.
offsetWithinCell := aPoint - cellPosn.
regionClass := CellClickRegion clickRegionForPoint: offsetWithinCell.
arrowAndOffset := regionClass scaledHintArrowAndOffsetFromWithinCell: offsetWithinCell.
arrowAndOffset isNil ifTrue: [^self].
arrow := arrowAndOffset value.
offset := arrowAndOffset key.
offset := self offsetWithinGridForm + offset.
displayOn: self targetForm
clippingBox: self targetForm computeBoundingBox
rule: Form oldPaint
fillColor: Color gray.
The MirrorCellRenderer calculates the cell relative position for the cursor. Using that, it determines which CellClickRegion should have the request and asks that click regioin for the arrow to draw. It then draws the correct arrow. Note, the click region suppied the arrow but the renderer interacts with the gameboard. The renderer knows about the gameboard form and the specific cell involved.
Note that the click-regions also know how to differentiate between inside region and outside region clicks to decide which hint arrow to use. Here is the code for the outside click region. This is a class method for the CellClickRegionOutside class.
| arrow tinyArrow offset rotateRegion |
rotateRegion := self rotateRegionForPoint: aPoint.
arrow := rotateRegion arrowForm.
tinyArrow := arrow scaledToSize: CellRenderer cellExtent.
offset := 0.
The inside click region does something similar. This class method is on the CellClickRegionInside class.
| pushRegion arrow tinyArrow offset |
pushRegion := self pushRegionForPoint: aPoint.
arrow := pushRegion arrowForm.
tinyArrow := arrow scaledToSize: CellRenderer cellExtent - 2.
offset := 5.
A combination of renderer and click-region hierarchies were used to resolve the "case statement" decision logic for processing the event in the correct place. There's very little ifTrue/ifFalse logic in the code.