WindowGroup.st
changeset 90 b1f1d7fc96eb
parent 86 032006651226
child 103 6156d12a414d
--- a/WindowGroup.st	Mon Feb 06 01:38:04 1995 +0100
+++ b/WindowGroup.st	Mon Feb 06 01:40:27 1995 +0100
@@ -11,7 +11,8 @@
 "
 
 Object subclass:#WindowGroup
-	 instanceVariableNames:'views topViews myProcess mySensor isModal previousGroup'
+	 instanceVariableNames:'views topViews myProcess mySensor isModal previousGroup 
+				focusView focusSequence'
 	 classVariableNames:'ActiveGroup ScheduledWindowGroups LeaveSignal'
 	 poolDictionaries:''
 	 category:'Interface-Support'
@@ -21,7 +22,7 @@
 COPYRIGHT (c) 1993 by Claus Gittinger
 	      All Rights Reserved
 
-$Header: /cvs/stx/stx/libview/WindowGroup.st,v 1.16 1994-11-28 21:01:20 claus Exp $
+$Header: /cvs/stx/stx/libview/WindowGroup.st,v 1.17 1995-02-06 00:39:42 claus Exp $
 '!
 
 !WindowGroup class methodsFor:'documentation'!
@@ -42,7 +43,7 @@
 
 version
 "
-$Header: /cvs/stx/stx/libview/WindowGroup.st,v 1.16 1994-11-28 21:01:20 claus Exp $
+$Header: /cvs/stx/stx/libview/WindowGroup.st,v 1.17 1995-02-06 00:39:42 claus Exp $
 "
 !
 
@@ -190,7 +191,7 @@
     mySensor := aSensor
 ! !
 
-!WindowGroup methodsFor:'enumeration'!
+!WindowGroup methodsFor:'enumerating'!
 
 allViewsDo:aBlock
     "evaluate aBlock for all views & topviews in this group"
@@ -265,13 +266,40 @@
     "process a single event from either the damage- or user input queues.
      Debugger abort brings us back here."
 
-    |event|
+    |event ignore|
 
     self processExposeEvents.
     [mySensor hasEvents] whileTrue:[
 	event := mySensor nextEvent.
-	(views isNil and:[topViews isNil]) ifFalse:[
-	    event sendEvent.
+	event notNil ifTrue:[
+	    (views isNil and:[topViews isNil]) ifFalse:[
+
+		ignore := false.
+		event isKeyPressEvent ifTrue:[
+		    event key == #FocusNext ifTrue:[
+			self focusNext.
+			ignore := true
+		    ].
+		    event key == #FocusPrevious ifTrue:[
+			self focusPrevious.
+			ignore := true
+		    ].
+		].
+		"
+		 button events turn off explicit focus, and revert
+		 to implicit focus control
+		"
+		(focusView notNil
+		and:[event isButtonEvent]) ifTrue:[
+		    self focusView:nil
+		].
+
+		ignore ifFalse:[
+		    AbortSignal catch:[
+			event sendEventWithFocusOn:focusView.
+		    ]
+		]
+	    ]
 	].
     ]
 !
@@ -279,31 +307,43 @@
 processExposeEvents
     "process all expose events from the damage queue"
 
-    |event view rect oldActive|
+    |event view rect oldActive x y w h|
 
     oldActive := ActiveGroup.
-    ActiveGroup := self.
-    [mySensor notNil and:[mySensor hasDamage]] whileTrue:[
-	event := mySensor nextDamage.
-	event notNil ifTrue:[
-	    (views isNil and:[topViews isNil]) ifFalse:[
-		event isDamage ifTrue:[
-		    view := event view.
-		    rect := event rectangle.
-		    view shown ifTrue:[
-			view transformation notNil ifTrue:[
-			    view deviceExposeX:(rect left) y:(rect top) width:(rect width) height:(rect height)
-			] ifFalse:[
-			    view exposeX:(rect left) y:(rect top) width:(rect width) height:(rect height)
+    [
+	[mySensor notNil and:[mySensor hasDamage]] whileTrue:[
+	    ActiveGroup := self.
+	    event := mySensor nextDamage.
+	    event notNil ifTrue:[
+		(views isNil and:[topViews isNil]) ifFalse:[
+		    event isDamage ifTrue:[
+			view := event view.
+			rect := event rectangle.
+			view shown ifTrue:[
+			    rect := event rectangle.
+			    x := rect left.
+			    y := rect top.
+			    w := rect width.
+			    h := rect height.
+			    view transformation notNil ifTrue:[
+				view deviceExposeX:x y:y width:w height:h
+			    ] ifFalse:[
+				view exposeX:x y:y width:w height:h
+			    ]
 			]
+		    ] ifFalse:[
+			"
+			 mhmh - could we possibly arrive here ?
+			"
+			event sendEvent.
 		    ]
-		] ifFalse:[
-		    event sendEvent.
 		]
 	    ]
 	]
-    ].
-    ActiveGroup := oldActive
+    ] valueNowOrOnUnwindDo:[
+	ActiveGroup := oldActive.
+	oldActive := nil
+    ]
 !
 
 eventLoop
@@ -314,70 +354,80 @@
 !
 
 eventLoopWhile:aBlock
-    "wait-for and process events while aBlock evaluates to true."
-
-    |abortSignal|
+    "wait-for and process events. 
+     Stay in this loop while there are still any views to dispatch for,
+     and aBlock evaluates to true."
 
-    abortSignal := Object abortSignal.
-    "/ ScheduledWindowGroups add:self.
+    |oldActive|
 
-    (SignalSet with:LeaveSignal with:abortSignal)
-    handle:[:ex |
-	ex return
-    ] do:[
-	|p g oldActive mainGroup|
+    oldActive := ActiveGroup.
+"/     ScheduledWindowGroups add:self.
 
-	isModal ifTrue:[
-	    mainGroup := self mainGroup.
-	].
+    [
+	(SignalSet with:LeaveSignal with:AbortSignal)
+	handle:[:ex |
+"/            ActiveGroup := oldActive.
+"/            oldActive := nil.
+	    ex return
+	] do:[
+	    |p g mainGroup|
 
-	aBlock whileTrue:[ 
-	    (views isNil and:[topViews isNil]) ifTrue:[
-		"/ ScheduledWindowGroups remove:self ifAbsent:[].
-		myProcess notNil ifTrue:[
-		    p := myProcess.
-		    myProcess := nil.
-		    p terminate.
-		    "not reached - there is no life after death"
-		 ].
-		 "
-		  this is the end of a modal loop
-		  (not having a private process ...)
-		 "
-		 ^ self
+	    isModal ifTrue:[
+		mainGroup := self mainGroup.
 	    ].
-	    abortSignal handle:[:ex |
-		ex return
-	    ] do:[
-		"
-		 if modal, break out of the wait after some time
-		 to allow servicing update-events of the blocked
-		 windowgroup.
-		"
-		isModal ifTrue:[
-		    mySensor eventSemaphore waitWithTimeout:0.2.
-		] ifFalse:[
-		    Processor activeProcess setStateTo:#eventWait if:#active.
-		    mySensor eventSemaphore wait.
+
+	    aBlock whileTrue:[ 
+		(views isNil and:[topViews isNil]) ifTrue:[
+"/                     ScheduledWindowGroups remove:self ifAbsent:[].
+		    myProcess notNil ifTrue:[
+			p := myProcess.
+			myProcess := nil.
+			p terminate.
+			"not reached - there is no life after death"
+		     ].
+		     "
+		      this is the end of a modal loop
+		      (not having a private process ...)
+		     "
+		     ^ self
 		].
-		oldActive := ActiveGroup.
-		ActiveGroup := self.
-		self processEvent
-	    ].
-	    ActiveGroup := oldActive.
-	    oldActive := nil.
 
-	    "
-	     if modal, also check for redraw events in my maingroup
-	     (we arrive here after every event for myself or after the
-	      above timeout)
-	    "
-	    mainGroup notNil ifTrue:[
-		mainGroup processExposeEvents.
+		AbortSignal handle:[:ex |
+"/                    ActiveGroup := oldActive.
+"/                    oldActive := nil.
+		    ex return
+		] do:[
+		    "
+		     if modal, break out of the wait after some time
+		     to allow servicing update-events of the blocked
+		     windowgroup.
+		    "
+		    Processor activeProcess setStateTo:#eventWait if:#active.
+		    isModal ifTrue:[
+			mySensor eventSemaphore waitWithTimeout:0.2.
+		    ] ifFalse:[
+			mySensor eventSemaphore wait.
+		    ].
+		    ActiveGroup := self.
+		    self processEvent
+		].
+"/                ActiveGroup := oldActive.
+
+		"
+		 if modal, also check for redraw events in my maingroup
+		 (we arrive here after every event for myself or after the
+		  above timeout)
+		"
+		mainGroup notNil ifTrue:[
+		    mainGroup processExposeEvents.
+		]
 	    ]
-	]
+	].
+    ] valueNowOrOnUnwindDo:[
+"/     ScheduledWindowGroups remove:self ifAbsent:[].
+	ActiveGroup := oldActive.
+	oldActive := nil.
     ].
-    "/ ScheduledWindowGroups remove:self ifAbsent:[].
 !
 
 waitForExposeFor:aView
@@ -385,7 +435,9 @@
      To be used after a scroll"
 
     mySensor waitForExposeFor:aView.
-    self processExposeEvents
+    AbortSignal catch:[
+	self processExposeEvents
+    ]
 !
 
 leaveEventLoop
@@ -419,7 +471,6 @@
     previousGroup := nil.
     myProcess isNil ifTrue:[
 	isModal := false.
-	"/ ScheduledWindowGroups add:self.
 	myProcess := [
 	    topViews notNil ifTrue:[
 		topViews do:[:aView |
@@ -498,7 +549,7 @@
     ].
     views := nil.
     topViews := nil.
-    "/ ScheduledWindowGroups remove:self ifAbsent:[].
+"/     ScheduledWindowGroups remove:self ifAbsent:[].
 !
 
 shutdown
@@ -511,7 +562,7 @@
     myProcess notNil ifTrue:[
 	p := myProcess.
 	myProcess := nil.
-	"/ ScheduledWindowGroups remove:self ifAbsent:[].
+"/         ScheduledWindowGroups remove:self ifAbsent:[].
 	p terminate.
     ]
 ! !
@@ -544,12 +595,99 @@
     isModal := false.
 ! !
 
+!WindowGroup methodsFor:'focus control'!
+
+focusSequence:aSequenceableCollection
+    "define the focus sequence for focusNext/focusPrevious.
+     Focus is stepped in the order in which subviews occur in
+     the sequence"
+
+    focusSequence := aSequenceableCollection
+!
+
+focusView
+    "return the view which has the focus"
+
+    ^ focusView
+!
+
+focusView:aViewOrNil
+    "give focus to aViewOrNil"
+
+    focusView notNil ifTrue:[
+	focusView focusOut.
+    ].
+    focusView := aViewOrNil.
+    focusView notNil ifTrue:[
+	focusView focusIn
+    ].
+
+    "
+     |top v1 v2|
+
+     top := StandardSystemView new.
+     v1 := EditTextView origin:0.0@0.0 corner:1.0@0.5 in:top.
+     v2 := EditTextView origin:0.0@0.5 corner:1.0@1.0 in:top.
+     top open.
+     top windowGroup focusView:v1.
+    "
+!
+
+focusNext
+    "give focus to next view in focusSequence"
+
+    |index|
+
+    focusSequence isNil ifTrue:[^ self].
+    focusView notNil ifTrue:[
+	index := (focusSequence indexOf:focusView) + 1.
+	index > focusSequence size ifTrue:[index := 1].
+    ] ifFalse:[
+	index := 1.
+    ].
+    self focusView:(focusSequence at:index)
+
+    "
+     |top v1 v2|
+
+     top := StandardSystemView new.
+     v1 := EditTextView origin:0.0@0.0 corner:1.0@0.5 in:top.
+     v2 := EditTextView origin:0.0@0.5 corner:1.0@1.0 in:top.
+     top open.
+     top windowGroup focusSequence:(Array with:v1 with:v2).
+     top windowGroup focusOn:v1.
+     (Delay forSeconds:10) wait.
+     top windowGroup focusNext.
+    "
+!
+
+focusPrevious
+    "give focus to previous view in focusSequence"
+
+    |index|
+
+    focusSequence isNil ifTrue:[^ self].
+    focusView notNil ifTrue:[
+	index := (focusSequence indexOf:focusView) - 1.
+	index < 1 ifTrue:[index := focusSequence size].
+    ] ifFalse:[
+	index := focusSequence size.
+    ].
+    self focusView:(focusSequence at:index)
+! !
+
 !WindowGroup methodsFor:'printing'!
 
-printString
+printOn:aStream
     "return a printed representation;
      just for more user friendlyness, add name of process."
 
-    myProcess isNil ifTrue:[^ super printString].
-    ^ 'WindowGroup(' , myProcess nameOrId , ')'
+    myProcess isNil ifTrue:[
+	(previousGroup notNil and:[previousGroup process notNil]) ifTrue:[
+	    aStream nextPutAll:('WindowGroup(modal in ' , previousGroup process nameOrId , ')').
+	    ^ self.
+	].
+	^ super printOn:aStream
+    ].
+    aStream nextPutAll:('WindowGroup(' , myProcess nameOrId , ')')
 ! !