Or will it be Mark IV?

[... I almost finished writing what I'd thought of last night when a
better idea occured to me.  Rather than lose/rewrite what I'd written I've
left it in here and Mark V can be found in the P.S. at the end ;-)
Some of the discussion on Mark IV is still relevent however ...]

After a bit of thought it occurred to me that my RBG extension was overly
complex and missed the point of having a BC in the first place.  Anyway to
cut a long story short I propose to generalise the BC by replacing the
okay_, apply_ and restore_ variables with a single:
        list<pair<int, list<Widget *> > > state_activation_map;

state_activation_map will simply map widgets which should be active to
states of the given Policy. This will allow considerable extension of
policies.  This will allow the creation of, for example John's
CitationPolicy, where he'll be able to specify various widgets that must
be active in different states.  Of course, this means we run into a couple
of problems or at least observations I make below:

1. Creating such a policy doesn't mean every port should use it but if
   they did how restricted would they be in what they do with it?

2. Ideally I'd like to derive new policies from existing ones but how will
   that affect the SMInput and State enums we currently use.  Can we
   simply overload the enums and expect everything to work?
        2a. I really don't want to have to add every conceivable state or
            state input to those defined already in ButtonPolicy but it
            may work out as the simplest way to extend policies.  I
            _think_ an alternative might be to keep the existing enums,
            create a new enum in each new policy that needs more states or
            inputs, eg. enum MoreStateInputs {SMI_FIRSTNEW=SMI_TOTAL, ...};
            but only pass ints as parameters into/out of BC/Policy. That
            should allow the use of enums for our sanity.
        2b. (2a) would affect the ability to switch to a new policy

3. removing the explicit okay_, apply_ and restore_ variables and
   shifting them into the state_activation_map removes the explicit
   handling in refresh() however it also means that clients would need
   to explicitly add the buttons to appropriate states _unless_ I make
   the setOK() et el calls more complicated so they add the buttons to the
   appropriate state lists specified by the policy.  This is in fact what
   I'm going to do because otherwise we lose the link between policy
   and button activation for these critical buttons.  It also means from a
   purely maintenance point of view that we don't have to write the
   explicit widget additions for each existing dialog and changing policy
   wouldn't cause our okay, apply and restore buttons to mismatch intended
   policy.
        3a. Also note that these functions (setOK() etc.) could be used
            to add multiple widgets that all need to be activated when
            that particular special button is enabled.  This then gives
            the possiblity to consider BC as having 4 special groups of
            buttons.  Since OK and Apply are usually done together you
            could create a policy that setOK for both OK and Apply and
            setApply for some other group.  Preferences sort of does this
            now with the Save button replacing the OK button and following
            a different policy to the Apply button.
        3b. Of course carried to its extreme we could try to allow the
            creation of multiple groups within a given policy and then
            have a setGroup(group, widget) function that makes adding
            widgets easier.  "group" above could just need to index into
            a group versus state array as seen in the last paragraph of
            this email.  ie. a list<pair<int, list<int> > >

4. All control buttons (including radio buttons) that influence which
   other widgets are active must have a corresponding state input and
   cause a transition to a new state within the policy.
        4a. This ties in with 2 above.
        4b. This means everyone needs to be a state machine designer or at
            at least understand the basics of what is happening.
        4c. control handling should still be separated from input checking
            however a dialog.control() callback will need to return a state
            machine input which maybe SMI_NOOP or some real transition.

5. Cancel/Close is the only button handled explicitly because of the
   desire to change the label at appropriate times.

6. Support for making RadioButtonGroups disabled should be doable just by
   adding the xforms widget group to setReadOnly().  This is just an
   observation not anything new.

7. Memory usage shouldn't be too bad because the number of widgets that
   are affected by state changes is likely to be small for most dialogs.

8. I'm starting to think that the state machine variables in the different
   policies should be in a separate struct or at least a mix-in class so
   derived polices can access and resize them as necessary.  Assuming
   derived policies could be made to work or are even needed (I think
   they are).


The ButtonController interface gets:
        void addWidget(int state, Widget * widget);

and I'd like to retain the setOK(), setApply() and setRestore() methods
as outlined in (3) above.

ButtonPolicy can probably do without the buttonStatus() and isReadOnly()
methods since both were only really for debugging and buttonStatus() won't
really be useable after the changes.

ButtonPolicy and its ilk would need to provide some way of either
iterating over the StateOutputs outputs_ or of returning a list<int> of
states that the okay, apply and restore are active in.  This is needed to
support BC::setOK()  etc.  Alternatively the StateOutputs outputs_
variable could be replaced by a list<pair<Button, list<int> > > to map
keys to states rather than the other way around.  I like the way things
are at the moment though because initialisation within the policy code
shows exactly which buttons are together in a given state.

Allan. (ARRae)

P.S.  An alternative that is just starting to occur to me is to just
define groups in the Policy and have the BC keep lists of Widgets in those
corresponding groups and activate groups as needed rather than having
(potentially) great big lists of widgets for each state.  Hmmm.  Yeah
makes a bit more sense.  That'd give something like:
(this is additions only -- deletions are commented)

ButtonPolicy {
        enum StandardStates {...};  // rename existing enum State?
        enum StandardSMInputs {...}; // and likewise here?
        enum StandardGroups { RESTORE, OK, APPLY, CANCEL };

        typedef int Group;
        typedef int States;
        typedef int SMInputs;
        typedef list< pair<Group, list<States> > > Groupings;
        // this replaces StateOutputs and is filled in by the policy

        /// Transition map of the state machine.
        typedef std::vector<States> StateArray;  // s/State/States/
        ///
        typedef std::vector<StateArray> StateMachine; // as before

        /// group in current state output?
        virtual bool groupStatus(Group) = 0;    // replaces buttonStatus()
};

ButtonController {
        void addToGroup(ButtonPolicy::Group, Widget *);

        typedef list< pair<ButtonPolicy::Group, list<Widget *> > > Groups;
        Groups groups_;
};

BC::refresh() would then just iterate through the groups_ checking if that
group should be activated or deactivated and iterate over the list of
Widgets accordingly -- this could even be optimised so unnecessary
de/activations didn't occur.  Note that a widget must only ever belong to
one group. Note that the Cancel/Close stuff is merged into a single group
and will still need to be handled explicitly since that button is
relabeled not de/activated.

Yeah that's more like it.  This should make it easier to derive one policy
from another also.

Thoughts?

Reply via email to