[r9373]: docs / trunk / oodguide / en-US / appendix03.xml Maximize Restore History

Download this file

appendix03.xml    289 lines (265 with data), 15.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "oodguide.ent">
%BOOK_ENTITIES;
]>
<!--#########################################################################
#
# Description: Open Object Rexx: ooDialog User Guide XML file
#
# Copyright (c) 2012-2013 Rexx Language Association. All rights reserved.
#
# This program and the accompanying materials are made available under
# the terms of the Common Public License v1.0 which accompanies this
# distribution. A copy is also available at the following address:
# http://www.oorexx.org/license.html
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the distribution.
#
# Neither the name of Rexx Language Association nor the names
# of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#########################################################################
-->
<!-- Appendix 03 - Testing Popups v01-00 07Jun12
An illustration of (a) how popups can be tested in stand-alone mode with a
single code base, and (b) how "child" dialogs can be visually offset from
the "parent" dialog that pops up the "child".
Both supported by examples in Exercises\Samples\Popups folder.
Changes:
v01-00 07Jun12: First version.
15Aug13: Corrected folder names after folder structure change.
-->
<appendix id="apx-satesting">
<title id="satesting.title">Testing Popups in Stand-Alone Mode</title>
<indexterm><primary>Testing</primary><secondary>Popups</secondary></indexterm>
<indexterm><primary>Popups</primary><secondary>Stand-alone Testing</secondary></indexterm>
<indexterm><primary>Standalone testing</primary></indexterm>
<para>This appendix discusses two separate aspects of "popup" dialogs. The first aspect is testing
popped-up dialogs in stand-alone mode - that is, without having to run the "parent" dialog
from which the popped-up dialog is launched. The second aspect is the issue of how a popped-up dialog can
be visually offset from its "parent" so that it does not obscure the parent. </para>
<section id="apx-satesting-sa"><title>Stand-Alone Testing</title>
<para>Consider four dialogs called Parent, Child, Grandchild, and GreatGrandChild.
Parent is the "application" - the dialog that opens first, and from which other
dialogs are directly or indirectly surfaced. Parent can thus be called the "root" dialog,
and is designed to run in "standalone" mode - that is, it is not surfaced by
some other dialog. It pops up Child dialogs, each of which may pop-up Grandchild
dialogs, which in turn may pop-up GreatGrandchild dialogs, and so on.
</para>
<para>When testing an application, there is often a need to test an individual dialog which, in
the application, is a "child" that's invoked by a "parent" dialog which issues <emphasis
role="italic">self~popUpAsChild(...)</emphasis> - rather than
<emphasis role="italic">self~execute(...)</emphasis> - to surface the child dialog.
In addition, popping-up requires the parent dialog to be specified as a method argument:
<emphasis role="italic">self~popUpAsChild(parent,...)</emphasis>.
</para>
<para>In the Order Management application, the
<computeroutput>OrderMgrView</computeroutput> class is the parent for all child dialogs.
The reason for using popupAsChild is so that, for example, a CustomerList can be closed
without automatically closing any Customer dialogs that might have been opened from it.
</para>
<para>Now, using the parent dialog (Order Manager in our case) as some sort of test-harness that
will eventually surface the child dialog to be tested can be time-consuming and irritating.
However, if a child dialog is started without the parent first being run, it must still be
able to invoke subordinate dialogs in the same way as if it were running as part of the full
application. Thus there is a need to enable individual child dialogs to be tested in
"stand-alone" mode, without using the parent dialog just to surface them, but invoking other
"subordinate" dialogs as if it was not being run stand-alone. In addition, a stand-alone
test of what is normally a "child" dialog will require the child to act as the parent of any
"grandchild" dialogs that it invokes, which means that the child must pass its own id to the
grandchild instead of passing the parent's.
</para>
<para>One approach to resolving this problem is to have two versions of each child dialog - one
using <emphasis role="italic">~execute(...)</emphasis> and one using <emphasis
role="italic">~popupAsChild(...)</emphasis>. This results in two code bases for each dialog -
which can quickly get out of sync. Not the best idea.
</para>
<para>An arguably better solution is to build each dialog so it can be run either individually
(stand-alone) or within the application. The file
<computeroutput>Popups.rex</computeroutput> in the folder
<computeroutput>Exercise06\Extras\Popups</computeroutput> shows a way of doing this, using dialogs
that are as simple as possible.
</para>
<para>The rules illustrated by the code in <computeroutput>Popups.rex</computeroutput> are as
follows, assuming an application consisting of a single Parent dialog that invokes one or
more Child dialogs, each of which may invoke one or more Grandchild dialogs, each of which
may invoke one or more GreatGrandChild dialogs. The child and grandchild dialogs are
"intermediate" dialogs. The GreatGrandChild dialog is a "leaf" dialog - that is, it does not
invoke any other dialog (except of course those integral to its own functioning such as an
About box or a data entry sub-dialog).
<programlisting>
<![CDATA[
The Parent Dialog:
(1) Is invoked from a Startup script with:
.ParentDialog~newInstance
(2) Provides an event handling method that sufaces a Child dialog:
::METHOD popup UNGUARDED
...
.ChildDialog~newInstance(self)
...
An Intermediate Dialog:
(1) For stand-alone testing is invoked from a Startup script with:
.AnIntermediateDlg~newInstance("SA")
(2) Provides the following methods (among others):
::METHOD newInstance CLASS
use arg rootDlg
...
dlg = self~new
dlg~activate(rootDlg)
::METHOD activate UNGUARDED
expose rootDlg
use arg rootDlg
...
if rootDlg = "SA" then do -- If standalone operation required
rootDlg = self -- To pass on to subordinates
self~execute("SHOWTOP")
end
else self~popupAsChild(rootDlg, "SHOWTOP")
::METHOD popup UNGUARDED -- An event handler method
expose rootDlg
.ASubordinateDlg~newInstance(rootDlg)
A Leaf Dialog:
(1) For standalone testing is invoked from a Startup script with:
.ALeafDlg~newInstance("SA")
(2) Provides the following methods (among others):
::METHOD newInstance CLASS
use arg rootDlg
...
dlg = self~new
dlg~activate(rootDlg)
::METHOD activate
use arg rootDlg
if rootDlg = "SA" then self~execute("SHOWTOP")
else self~popupAsChild(rootDlg, "SHOWTOP")
]]>
</programlisting>
</para>
<para>Try running the <computeroutput>Popups.rex</computeroutput> program without any parameters.
Note that as each "junior" dialog is created (by pressing the pushbutton in the
"senior" dialog) it completely obscures its parent. This is because all dialogs are coded to
surface in the center of the screen (by the style "<computeroutput>CENTER</computeroutput>
in the <emphasis role="italic">create</emphasis> method), and second all dialogs are the
same size. The next section illustrates a useful way to offset the subordinate dialogs so
that at least some part of the senior dialog is still visible. </para>
</section>
<section id="apx-satesting-offsets"><title>Visual Offsetting</title>
<para>The program <computeroutput>OffsetPopups.rex</computeroutput> in the
<computeroutput>exercises\Exercise06\Extras\Popups</computeroutput> folder is a copy of
<computeroutput>Popups.rex</computeroutput> with added code to handle dialog offsetting
(comments show where statements have been added or modified). If no parameters are provided,
the offset defaults to zero and the behavior is identical to that of
<computeroutput>Popups.rex</computeroutput>. Try running the program with an offset of 100
by entering <computeroutput>OffsetPopups 100</computeroutput> on a command prompt. You'll
see that popped-up dialogs are offset from the dialogs from which they're popped up, and do
not now obscure them. Entering <computeroutput>OffsetPopups ?</computeroutput> provides help.
</para>
<para>In <computeroutput>OffsetPopups.rex</computeroutput>, all classes are subclassed from a
<computeroutput>View</computeroutput> class (itself subclassed from
<computeroutput>UserDialog</computeroutput>) which has one class attribute and two methods,
<emphasis role="italic">getPopupPos</emphasis> and <emphasis role="italic">offset</emphasis>,
as follows:
<programlisting>
<![CDATA[
::CLASS View SUBCLASS UserDialog
::ATTRIBUTE offsetAmount CLASS PUBLIC
::METHOD getPopupPos
popupPos = self~getRealpos
offset = .View~offsetAmount
popupPos~incr(offset,offset)
return popupPos
::METHOD offset
use arg dlgPos
self~moveTo(dlgPos, 'SHOWWINDOW')
self~ensureVisible()
]]>
</programlisting>
The class attribute <emphasis role="italic">offsetAmount</emphasis> is set
at the start of the program, and defines the amount of space by which to offset a junior dialog.
The term "junior dialog" in this section
refers to a dialog that is popped up by a "senior dialog", and in the sample code refers
to any of the classes <computeroutput>Child</computeroutput>,
<computeroutput>GrandChild</computeroutput>, and<computeroutput>GreatGrandChild</computeroutput>.
"Senior dialog" refers to the dialog that pops up a junior dialog, and in the sample code
refers to any of the classes <computeroutput>Parent</computeroutput>, <computeroutput>Child</computeroutput>,
and <computeroutput>GrandChild</computeroutput>
</para>
<para>
In brief, the method <emphasis role="italic">getPopupPos</emphasis> is used by a senior dialog to
establish where it wants a junior dialog to pop up.
The junior dialog then uses the <emphasis role="italic">offset</emphasis> method to (a) move
itself to the desired position, and (b) to ensure that it is wholly visible on the screen
and not partly off the screen.
</para>
<para>In detail:
<itemizedlist>
<listitem><para><emphasis role="bold"><emphasis role="italic">getPopupPos</emphasis></emphasis> -
This method is used by the senior dialog to establish where on the screen
the junior dialog is to appear (relative to the senior dialog).
The first statement <emphasis role="italic">popupPos = self~getRealPos</emphasis> gets the
position of the senior dialog as a point object (see ooDialog Reference) whose
attributes are the point's <emphasis role="italic">x</emphasis> and <emphasis
role="italic">y</emphasis> screen coordinates (that is, the top-left corner of the
dialog). The point object is assigned to <emphasis role="italic">popupPos</emphasis>.
<indexterm><primary>Point object</primary></indexterm></para>
</listitem>
<listitem><para>
The second statement, <emphasis role="italic">offset = .View~offsetAmount</emphasis>
gets the offset amount stored in the class attribute.</para>
</listitem>
<listitem><para>Then the statement
<emphasis role="italic">popupPos~incr(offset,offset)</emphasis> increments
each coordinate of the <emphasis role="italic">popupPos</emphasis>
object by the amount defined by <emphasis role="italic">offset</emphasis>.
That is, <emphasis role="italic">popupPos</emphasis> is now the desired
new position of the junior dialog.</para>
</listitem>
<listitem><para>Finally, the desired junior dialog's position is returned, and
the senior dialog then passes it to the junior dialog when the
latter is created via its <emphasis role="italic">newInstance</emphasis> method.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem><para><emphasis role="bold"><emphasis role="italic">offset</emphasis></emphasis> -
This method is invoked from the the junior dialog's <emphasis role="italic">initDialog</emphasis>
method in order to move itself to the position
(<emphasis role="italic">dlgPos</emphasis>) defined by the senior dialog.
The first statement (<emphasis role="italic">self~moveTo(dlgPos,'SHOWWINDOW')</emphasis>)
moves the dialog. However, if the senior dialog is near the bottom or right-hand
edge of the screen, the junior dialog could surface half-off the screen in
the correct offset position. But half-off the screen is not particularly
friendly.So...</para>
</listitem>
<listitem><para>... the last statement (<emphasis role="italic">self~ensureVisible()</emphasis>)
ensures that the current dialog is wholly visible. However,
because it's so fast, you don't see this re-positioning. To see the re-positioning,
insert <emphasis role="italic">call sysSleep(2)</emphasis> just before the last
statement, run the program, and move the parent dialog to the bottom of the screen.
Then popup the child dialog. It appears half-off the screen, then after two seconds it
snaps up to a wholly-visible position. Neat.</para>
</listitem>
</itemizedlist>
</para>
</section>
</appendix>