POW -- draw focus indicators correctly


Subject: POW -- draw focus indicators correctly
From: Paul Rohr (paul@abisource.com)
Date: Wed Mar 29 2000 - 18:13:58 CST


The goal of this week's POW is to update the XP drawing code in AbiWord so
that we only draw focus indicators (such as selections and blinking cursors)
when appropriate. Currently, they're always drawn, whether that window has
the focus or not, and this leads to lots of visual annoyance and confusion.

Your mission, should you choose to accept it, is to make this Just Work.

scope
-----
This POW shouldn't require very much code, but those changes will be fairly
widely dispersed throughout the tree. In short, you'll need to:

  1. introduce a new XAP/XP mechanism for focus notification
  2. change the FV_View drawing logic to respond to those notifications
  3. drive that mechanism from platform code (preferably XAP)

Since few of us are expert on more than one platform, it's OK if you only
implement #3 for your favorite platform. However, you may want to drive
discussions on this list to make sure that the mechanisms you come up with
are likely to work for other platforms.

step 1
------
The first step is to introduce an XP mechanism to notify views about focus
changes. Ideally, we'd like to be able to use this mechanism for apps other
than AbiWord, so where possible, this should be implemented in XAP code.

Here's a proposed design, as outlined in an earlier post to this list:

  http://www.abisource.com/mailinglists/abiword-dev/00/March/0419.html

Assume that any AV_View can have one of the following focus states:

  AV_FOCUS_HERE /* in this view */
  AV_FOCUS_NEARBY /* on another window in this frame */
  AV_FOCUS_NONE /* not this frame, somewhere else */

However, since views can be recreated any time the zoom factor changes, this
state should actually be tracked in the XAP_Frame instead, and then
forwarded to the AV_View as needed.

For example, any time a frame's widgets really took or lost focus, they'd
notify the frame accordingly:

  pFrame->focusChanged(AV_FOCUS_NEARBY);

In turn, the frame could filter redundant changes and just pass the
interesting ones along to its views:

  pView->focusChanged(AV_FOCUS_NEARBY);

The AV_View implementation probably doesn't need to do much more than store
the new focus state, since all the real work will get done in an
app-specific subclass -- in our case, FV_View.

You'll also need to make sure that whenever we rezoom a view that it gets
renotified about the current focus state.

step 2
------
Change the FV_View drawing logic to honor the focus state as follows:

  AV_FOCUS_HERE -- draw selection or blinking cursor (if prefs allow)
  AV_FOCUS_NEARBY -- draw selection or unblinking cursor
  AV_FOCUS_NONE -- don't draw either

This may take a bit of fiddling to get everything working smoothly, but it
shouldn't be too bad.

Cursor blinking is done in response to timer callbacks, so to stop/start
that behavior you'll need to stop/start that timer, being sure to fix the
display first, so there aren't any leftover artifacts from the old state.

Selections should be easier, because they don't involve XORs or timers.

The tricky thing here will be to make sure that you get the transitions
right -- that moving between states doesn't leave any dirt on-screen.

Thus, to test your logic, you may want to temporarily add a keybinding and
edit method which allows you to interactively cycle focus states for a given
view on cue. Once everything's working smoothly, this testing harness can
easily be ripped out. In the mean time, though, you may want to #if DEBUG
guard this testing code, to keep from confusing users of production builds.

step 3
------
Hook this all up to the platform-specific mechanisms for detecting focus
changes. As far as an AbiWord frame is concerned, there currently aren't
many places where the focus can wind up.

  AV_FOCUS_HERE == the frame's top-level window

  AV_FOCUS_NEARBY == some other window in this frame's window hierarchy,
        such as a menu, a focusable toolbar control, or modal dialog

  AV_FOCUS_NONE == outside this frame entirely

Given what you know about how focus notifications get propagated on your
platform, it shouldn't be too hard to figure out where to detect those
changes so that you can call pFrame->focusChanged() appropriately.

hints
-----
1. UT_DEBUGMSG is your friend.

2. You're best off if you figure out how to call focusChanged() from the
minimum number of spots on your platform. One would be ideal, but I doubt
it can be done.

3. It may be easier to make hint #2 work if you warp focus away from
windows that never need it to a single common window. The work you did in
step 1 to ignore redundant changes should come in handy here.

4. To test your changes, you'll probably want to spend time doing things
like:

  - restacking windows so that cursors/selections are partially hidden
  - clicking on title bars to change focus
  - changing toolbar fields
  - pulling down menus
  - etc.

5. I've provided less guidance than usual about specific places in the code
that would need to be touched. If you're having trouble finding your way
around, by all means ask questions here on the list.

Enjoy!

Paul

PS: For more background on the whole POW / ZAP / SHAZAM concept, see the
following introduction:

  http://www.abisource.com/mailinglists/abiword-dev/99/September/0097.html



This archive was generated by hypermail 2b25 : Wed Mar 29 2000 - 18:08:24 CST