requires: {#Region. #OrderedTree. #Stream}. provides: {#SceneElement}. Graphics addPrototype: #SceneElement derivedFrom: {OrderedTree}. "SceneElements are the basis of the Slate graphical visual element system. They can be Morphs or Presentations or both, but those aspects are optional mixins. All of them have bounding extents and definite positions, as well as a growable sequence of sub-elements / children." SceneElement addSlot: #boundingRect. "The smallest Rectangle bounding the SceneElement." SceneElement addSlot: #pos. "The position on the parent coordinate system of the reference point for locating the SceneElement." se@(SceneElement traits) newAt: p under: pse forChildren: n "Create a new element at a position, specifying its parent and the number of children intended." [| newSE | newSE: se clone. newSE children: (se children newSize: n). "Children must be set first since position: notifies them of the alteration." newSE position: p. newSE treeParent: pse. newSE ]. se@(SceneElement traits) position [ se pos ]. se@(SceneElement traits) position: p "When positioning a scene element, call the update notifier and propagate the positioning to the children." "The update notifier #updateExtentFor: is the preferred extension hook." [ se pos: p. se updateExtentFor: p. se children do: [| :each | each position: p]. se ]. se@(SceneElement traits) updateExtentFor: p@(Point traits) "p is now included in or's area, so update it and it's ancestors' bounding Rectangles as needed. Answers the highest se updated." [ (se boundingRect contains: p) ifTrue: [se] ifFalse: [| br | br: (se boundingRect encompass: p). se boundingRect: br. se treeParent updateExtentFor: br] ]. se@(SceneElement traits) updateExtentFor: r@(Rectangle traits) "This handles changing of the bounding rectangle by re-positioning a child to a point, by performing a merge as necessary, and then propagating up the update if the merge was necessary." [ (se boundingRect contains: r) ifTrue: [se] ifFalse: [| br | br: (se boundingRect merge: r). se boundingRect: br. se treeParent updateExtentFor: br] ]. se@(SceneElement traits) updateExtentFor: ch@(SceneElement traits) "This can be overridden in the cases where necessary to handle different specialized regions used as bounds." [ se updateExtentFor: ch boundingRect ]. se@(SceneElement traits) replayOn: s@(Stream traits) in: r@(Region traits) "Display the output captured by se on s, and all of or's children, subject to the restricted area of r. TODO: restrict se to be replayed only on the stream that recorded it?" [ ]. se@(SceneElement traits) hitDetectionRect "Return or's effective size for hit-detection. Override this to provide, say, hysteresis effects." [ se boundingRect ]. se@(SceneElement traits) occludingSiblings "Return all siblings that occlude at least part of the node's region." [| c | c: se treeParent children. c copyFrom: (c find: se) + 1 to: c size - 1 ]. se@(SceneElement traits) contains: p@(Point traits) "The definitive answerer of whether p lies on the scene element. Override for different shapes." [ se boundingRect contains: p ]. se@(SceneElement traits) highlight: b@(Boolean traits) "TODO: implement this generic system of highlighting." [ b ifTrue: ["Draw the highlighting widget."] ifFalse: ["Erase the highlighting widget."] ]. se@(SceneElement traits) add: ch@(SceneElement traits) "Add a scene element as a child, set the parent back-link, and update extent." [ resend. se updateExtentFor: ch. se ]. se@(SceneElement traits) addBack: ch@(SceneElement traits) "Scene elements interpret the last in the ordering as the back of the visual stack or pile." [ se children addLast: ch. ch reparentTo: se. se updateExtentFor: ch. se ]. se@(SceneElement traits) addFront: ch@(SceneElement traits) "Scene elements interpret the first in the ordering as the front of the visual stack or pile." [ se children addFirst: ch. ch reparentTo: se. se updateExtentFor: ch. se ]. "TODO: Adjust SceneElement>>remove: to recompute the boundingRect?" se@(SceneElement traits) replace: ch@(SceneElement traits) with: ch2@(SceneElement traits) [ se replaceAll: ch with: ch2. ch2 reparentTo: se. ch2 ]. se@(SceneElement traits) resetBoundingRect "Sets the boundingRect to a rectangle of 0,0 0,0." [| newR | newR: (Rectangle origin: 0 , 0 corner: 0 , 0). se boundingRect: newR ]. se@(SceneElement traits) clear "Remove all children, and reset the parent back-links." [| sec | sec: se children. sec ifNotNil: [sec do: [| :child | child treeParent: Nil]]. se children: sec newEmpty. se resetBoundingRect. se ]. se@(SceneElement traits) onChildrenContaining: p@(Point traits) collect: block "The CLIM spec claims that reverseDo: is needed here, apparently since this deals with pointer queries." [| newC | "se children select: [| :each | each contains: p] collect: [| :each | block applyWith: each]" newC: se newEmpty. se children reverseDo: [| :each | (each contains: p) ifTrue: [newC add: (block applyWith: each)]] ]. se@(SceneElement traits) onChildrenContaining: r@(Rectangle traits) collect: block [ se children select: [| :each | each contains: r] collect: [| :each | block applyWith: each] ]. se@(SceneElement traits) drawOn: c [ ]. Graphics addPrototype: #OutputRecordingStream derivedFrom: {ReadWriteStream}. OutputRecordingStream addSlot: #recording valued: False. OutputRecordingStream addSlot: #drawing valued: False. OutputRecordingStream addSlot: #stack valued: Stack newEmpty. OutputRecordingStream addSlot: #viewport. "TODO: OutputRecordingStreams are an artifact of CLIM, but they need to be integrated into Canvasses." ors@(OutputRecordingStream traits) on: se "Creates a new stream with the output record as top-level history. Note that calling this on an existing stream will copy some attributes." [| newORS | newORS: ors clone. newORS stack: Stack newEmpty. newORS stack push: se. newORS collection: se. newORS ]. ors@(OutputRecordingStream traits) newOn: se "Creates a new stream with the output record as top-level history." [| newORS | newORS: ors clone. newORS stack: ors stack newEmpty. newORS stack push: se. newORS collection: se. newORS ]. ors@(OutputRecordingStream traits) enter: se [ (ors collection includes: se) ifFalse: [ors error: NotIncluded]. ors stack push: se ]. ors@(OutputRecordingStream traits) leave [ ors stack size = 1 ifTrue: [^ Nil]. ors stack pop ]. ors@(OutputRecordingStream traits) history "Returns the history se top-level output record." [ ors collection ]. ors@(OutputRecordingStream traits) contents "Answer the actual output record, since it is more structured than an ExtensibleArray. TODO: make this overriding unnecessary." [ ors collection ]. ors@(OutputRecordingStream traits) current "Return the output record that the stream currently has open for writing. This is the top of the output record stack." [ ors stack top ]. ors@(OutputRecordingStream traits) add: se [ ors current add: se ]. ors@(OutputRecordingStream traits) nextPut: se [ ors current add: se ]. ors@(OutputRecordingStream traits) replayOn: r "Replay the output history for records overlapping the given region." [ ors history replayOn: r ]. ors@(OutputRecordingStream traits) replay "Replay the output history for all records on the stream's viewport." [ ors replayOn: ors viewport ]. ors@(OutputRecordingStream traits) remove: se "Remove the record from the stream's output history, and ensure all the records that it overlapped are visible." [ (ors contents includes: se) ifFalse: [^ Nil]. ors contents remove: se. ors history replayOn: se. "TODO: improve the implementation's laziness." ]. Graphics addPrototype: #DisplayedSceneElement derivedFrom: {SceneElement}. "TODO: consult CLIM/Morphic to see if this is redundant."