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