4
* Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved.
6
* TADS 3 Library - Status Line
8
* This module defines the framework for displaying the status line,
9
* which is the area conventionally displayed at the top of the screen
10
* showing information such as the current location, score (if scoring is
11
* used at all), and number of turns.
14
/* include the library header */
18
/* ------------------------------------------------------------------------ */
20
* in case the 'score' module isn't included, make sure we can refer to
21
* totalScore as a property
26
/* ------------------------------------------------------------------------ */
28
* The banner window for the status line.
30
statuslineBanner: BannerWindow
33
/* inherit the base handling */
37
* notify the statusLine object that it needs to refigure the
40
statusLine.statusDispMode = nil;
46
/* if we're already initialized, do nothing */
50
/* inherit the default handling (to set our 'inited_' flag) */
53
/* tell the status line to initialize its banner window */
54
statusLine.initBannerWindow(self);
59
/* ------------------------------------------------------------------------ */
61
* A special OutputStream for the <BANNER> tag contents. This is really
62
* just part of the main output stream, but we use a separate output
63
* stream object so that we have our own separate stream state variables
64
* (for paragraph breaking and so forth).
66
transient statusTagOutputStream: OutputStream
68
* We're really part of the main window's output stream as far as the
69
* underlying interpreter I/O system is concerned, so we have to
70
* coordinate with the main game window's input manager.
72
myInputManager = inputManager
74
/* we sit atop the system-level main console output stream */
77
/* write the text directly to the main output stream */
83
* A special OutputStream for the left half of the status line (the
84
* short description area) in text mode. We use a separate stream for
85
* this because we must write the text using the output mode switching
86
* for the status line.
88
* We only use this stream when we use the old-style text-mode status
89
* line interface, which explicitly separates the status line into a
90
* left part and a right part. When we have the banner API available in
91
* the interpreter, we'll use banners instead, since banners give us
92
* much more flexibility.
94
transient statusLeftOutputStream: OutputStream
95
/* we sit atop the system-level main console output stream */
98
/* write the text directly to the main output stream */
104
* A special OutputStream for the right half of the status line (the
105
* score/turn count area) in text mode. We use a separate stream for
106
* this because we have to write this text with the special
107
* statusRight() intrinsic in text mode.
109
* We only use this stream when we use the old-style text-mode status
110
* line interface, which explicitly separates the status line into a
111
* left part and a right part. When we have the banner API available in
112
* the interpreter, we'll use banners instead, since banners give us
113
* much more flexibility.
115
transient statusRightOutputStream: OutputStream
117
* Write from the stream. We simply buffer up text until we're
118
* asked to display the final data.
122
/* buffer the text */
127
* Flush the buffer. This writes whatever we've buffered up to the
128
* right half of the text-mode status line.
132
/* write the text to the system console */
135
/* we no longer have anything buffered */
139
/* our buffered text */
144
/* ------------------------------------------------------------------------ */
146
* Statusline modes. We have three different ways to display the
147
* statusline, depending on the level of support in the interpreter.
149
* StatusModeApi - use the banner API. This is preferred method, because
150
* it gives us uniform capabilities on text and graphical interpreters,
151
* and provides an output stream for the statusline that is fully
152
* independent on the main game window's output stream.
154
* StatusModeTag - use the <BANNER> tag. This is the method we must use
155
* for HTML-enabled interpreters that don't support the banner API. This
156
* gives us the full formatting capabilities of HTML, but isn't as good
157
* as StatusModeApi because we have to share our output stream with the
160
* StatusModeText - use the old-style dedicated statusline in a text-only
161
* interpreter. This is the least desirable method, because it gives us
162
* a rigid format for the statusline (exactly one line high, no control
163
* over colors, and with the strict left/right bifurcation). We'll only
164
* use this method if we're on a text-only interpreter that doesn't
165
* support the banner API.
167
enum StatusModeApi, StatusModeTag, StatusModeText;
169
/* ------------------------------------------------------------------------ */
171
* Status line - this is an abstract object that controls the status line
174
* We provide two main methods: showStatusHtml, which shows the status
175
* line in HTML format, and showStatusText, which shows the status line
176
* in plain text mode. To display the status line, we invoke one or the
177
* other of these methods, according to the current mode, to display the
178
* statusline. The default implementations of these methods generate the
179
* appropriate formatting codes for a statusline with a left part and a
180
* right part, calling showStatusLeft and showStatusRight, respectively,
181
* to display the text for the parts.
183
* Games can customize the statusline at two levels. At the simpler
184
* level, a game can modify showStatusLeft and/or showStatusRight to
185
* change the text displayed on the left and/or right of the statusline.
186
* Since these two methods are used regardless of the statusline style of
187
* the underlying interpreter, games don't have to worry about the
188
* different modes when overriding these.
190
* At the more complex level, a game can modify showStatusHtml and/or
191
* showStatusText. Modifying these routines provides complete control
192
* over the formatting of the entir status line. If a game wants to use
193
* something other than the traditional left/right display, it must
194
* modify these methods.
196
* This object is transient, because the statusline style is a function
197
* of the interpreter we're currently running on, and thus isn't suitable
198
* for saving persistently.
200
transient statusLine: object
202
* Show the status line, in HTML or text mode, as appropriate. By
203
* default, the library sets this up as a "prompt daemon," which
204
* means that this will be called automatically just before each
205
* command line is read.
211
/* if the status line isn't active, or there's no PC, skip this */
212
if (statusDispMode == nil || gPlayerChar == nil)
216
* showing the status line doesn't normally change any game
217
* state, so we can turn on the sense cache while generating the
220
libGlobal.enableSenseCache();
223
* Enter status-line mode. This will do whatever is required for
224
* our current status-line display style to prepare the output
225
* manager so that any text we display to the default output
226
* stream is displayed on the status line.
228
oldStr = beginStatusLine();
230
/* make sure we restore statusline mode before we're done */
234
* Generate a text or HTML status line, as appropriate. If
235
* we're in <BANNER> tag mode or banner API mode, use HTML to
236
* format the contents of the status line; if we're using the
237
* old-style text mode, use plain text, since the formatting
238
* is rigidly defined in this mode.
240
if (statusDispMode != StatusModeText)
242
/* show the HTML status line */
247
/* show the status line in plain text mode */
253
/* end status-line mode */
254
endStatusLine(oldStr);
256
/* turn off sense caching */
257
libGlobal.disableSenseCache();
261
/* prompt-daemon showing the status line */
262
showStatusLineDaemon()
264
/* show the status line as normal */
268
* Explicitly flush the status line if it's in a banner window.
269
* This will ensure that we'll redraw the status line on each
270
* turn if we're reading an input script, which is nice because
271
* it provides a visual indication that something's happening.
273
if (statusDispMode == StatusModeApi)
274
statuslineBanner.flushBanner();
278
* Show the status line in HTML format. Our default implementation
279
* shows the traditional two-part (left/right) status line, using
280
* showStatusLeft() and showStatusRight() to display the parts.
284
/* hyperlink the location name to a "look around" command */
285
"<a plain href='<<gLibMessages.commandLookAround>>'>";
287
/* show the left part of the status line */
290
/* set up for the score part on the right half */
291
"</a><tab align=right><a plain
292
href='<<gLibMessages.commandFullScore>>'>";
294
/* show the right part of the status line */
297
/* end the score link */
300
/* add the status-line exit list, if desired */
301
if (gPlayerChar.location != nil)
302
gPlayerChar.location.showStatuslineExits();
306
* Get the estimated HTML-style banner height, in lines of text.
307
* This is used to set the status line banner size for platforms
308
* where sizing to the exact height of the rendered contents isn't
311
* If showStatusHtml() is overridden to display more or fewer lines
312
* of text than the basic implementation here, then this routine must
313
* be overridden as well to reflect the new height.
315
getEstimatedHeightHtml()
320
* we need one line for the basic display (the location name and
325
/* add in the estimated height of the exits display, if appropriate */
326
if (gPlayerChar.location != nil)
327
ht += gPlayerChar.location.getStatuslineExitsHeight();
329
/* return the result */
334
* Show the statusline in text mode. Our default implementation
335
* shows the traditional two-part (left/right) status line, using
336
* showStatusLeft() and showStatusRight() to display the parts.
340
/* show the left part of the display */
343
/* switch to the right-side status stream */
344
outputManager.setOutputStream(statusRightOutputStream);
346
/* show the right-half text */
349
/* flush the right-side stream */
350
statusRightOutputStream.flushStream();
354
* Show the left part of a standard left/right statusline. By
355
* default, we'll show the player character's location, by calling
356
* statusName() on the PC's immediate container.
362
/* get the player character actor */
367
/* show the actor's location's status name */
368
if (actor != nil && actor.location != nil)
369
actor.location.statusName(actor);
375
* Show the right part of a standard left/right statusline. By
376
* default, we'll show the current score, a slash, and the number of
383
/* if there's a score object, show the score */
384
if ((s = libGlobal.scoreObj) != nil)
386
/* show the score and the number of turns so far */
387
"<.statusscore><<s.totalScore>>/<<
388
libGlobal.totalTurns>><./statusscore>";
393
* Set up the status line's color scheme. This is called each time
394
* we redraw the status line to set the background and text colors.
395
* We simply show a <BODY> tag that selects the parameterized colors
396
* STATUSBG and STATUSTEXT. (These are called "parameterized" colors
397
* because they don't select specific colors, but rather select
398
* whatever colors the interpreter wishes to use for the status line.
399
* In many cases, the interpreter lets the user select these colors
400
* via a Preferences dialog.)
404
/* set up the interpreter's standard status line colors */
405
"<body bgcolor=statusbg text=statustext>";
409
* Begin status-line mode. This sets up the output manager so that
410
* text written to the default output stream is displayed on the
411
* status line. Returns the original output stream.
417
/* check what kind of statusline display we're using */
418
switch(statusDispMode)
422
* We have a banner API window. Start by clearing the
423
* window, so we can completely replace everything in it.
425
statuslineBanner.clearWindow();
428
* If the platform doesn't support size-to-contents, then set
429
* the height to our best estimate for the size.
431
* If we do support size-to-contents, we'll set the height to
432
* the exact rendered size when we're done, so we don't need
433
* to worry about setting an estimate; indicate this to the
434
* interpreter by setting the is-advisory flag to true.
436
statuslineBanner.setSize(getEstimatedHeightHtml(),
437
BannerSizeAbsolute, true);
439
/* switch to the banner's output stream */
440
oldStr = statuslineBanner.setOutputStream();
442
/* set up the statusline color in the window */
450
* We're using <BANNER> tags. Switch to our statusline
453
oldStr = outputManager.setOutputStream(statusTagOutputStream);
455
/* set up the <BANNER> tag */
456
"<banner id=StatusLine height=previous border>";
458
/* set up the color scheme */
465
/* flush the main window */
468
/* plain text mode - enter text status mode */
469
statusMode(StatModeStatus);
471
/* switch to the status-left output stream */
472
oldStr = outputManager.setOutputStream(statusLeftOutputStream);
478
/* return the original output stream */
482
/* end statusline display */
483
endStatusLine(oldStr)
485
/* restore the old default output stream */
486
outputManager.setOutputStream(oldStr);
488
/* check the type of statusline we're generating */
489
switch (statusDispMode)
492
/* banner API mode - end the last line */
493
statuslineBanner.writeToBanner('\n');
496
* Size the window to its current contents. This doesn't
497
* work everywhere - on a few platforms, it does nothing -
498
* but this will give us the optimal size where it's
499
* supported. On platforms that don't support this, it'll do
500
* nothing, which means we'll simply be left with the
501
* "advisory" size we established earlier.
503
statuslineBanner.sizeToContents();
507
/* HTML <BANNER> mode - end the <BANNER> tag */
508
statusTagOutputStream.writeToStream('</banner>');
512
/* plain text statusline - end status mode */
513
statusMode(StatModeNormal);
518
* Initialize the banner window, given the BannerWindow object
519
* representing the status line banner API window.
521
initBannerWindow(win)
524
* Try showing the banner API window. If that succeeds, use the
525
* banner API window, since it's the most portable and flexible
526
* way to show the status line. If we can't create the banner
527
* API window, it means we're on an interpreter that doesn't
528
* support the banner API, so fall back on one of the older, less
529
* flexible mechanisms; which older mechanism we choose depends
530
* on what kind of interpreter we're on.
532
* Since we create the status line banner during initialization
533
* and normally leave it as the first item in the display list at
534
* all times, we can attach to an existing status line banner
535
* window if there is one. This will avoid unnecessary redrawing
538
if (win.showBanner(nil, BannerFirst, nil, BannerTypeText,
539
BannerAlignTop, nil, nil,
540
BannerStyleBorder | BannerStyleTabAlign))
543
* we successfully created the banner window - use the banner
544
* API to show the status line
546
statusDispMode = StatusModeApi;
548
else if (systemInfo(SysInfoInterpClass) == SysInfoIClassHTML)
551
* We failed to create a banner API window, and we're running
552
* on a full HTML interpreter, so use <BANNER> tags to
553
* produce the status line.
555
statusDispMode = StatusModeTag;
560
* We failed to create a banner API window, and we're running
561
* on a text-only interpreter - use the old-style
562
* fixed-format status line mechanism.
564
statusDispMode = StatusModeText;
569
* The status mode we're using. If this is nil, it means we haven't