[comp.windows.interviews] MOdal Dialogs/ G++

steve@Pkg.Mcc.COM (Steve Madere) (06/06/91)

Here at MCC one of our programmers created
a new kind of dialog that does not allow any
other interactors to receive events while it
is "popped up".  We call this a Modal Dialog.

What we had to do was to take over the run
operation during display of the MD and refuse
to pass on any events occurring outside the
bounds of the local scene to their targets.

Modal Dialogs seem to be a standard part of
a GUI so I wonder how is it that all of you other
people out there accomplish this task?

Ours looks rather kludgey since it requires 
finding out what the boundary of the dialog box
is and then checking every event to see if it 
occurred within this box.

on an unrelated note.....

Does IV 3.0 work with g++ yet?  
(I know it's an old and tired question but
 I REALLY want to use IBuild and my only c++
 compiler is g++).

Thanks.

Steve Madere
steve@pkg.mcc.com

david@UUNET.UU.NET (06/06/91)

	I hacked this together a while ago.  Basically  we do the same thing.
You take over the event loop and continue reading events, passing on events that
occur within the dialog box, until some state has changed.  I use a ButtonState,
but obviously other answers exist. 

	Of course this is unique to the application, I can't grab events from
other apps.  I guess what I would have really liked is a way to inform the
world that such and such an interactor wants to grab focus and have the
world take care of it. 

	If you get any interesting responses to this would you mind passing
them on to me?

	david


David Rivas					david@lolita.ntlp.com
Northfield Trading L. P.			(303) 985-3366

neil@AKBAR.TELEOS.COM (06/06/91)

IV2.6
=====

    Here at MCC one of our programmers created
    a new kind of dialog that does not allow any
    other interactors to receive events while it
    is "popped up".  We call this a Modal Dialog.

    What we had to do was to take over the run
    operation during display of the MD and refuse
    to pass on any events occurring outside the
    bounds of the local scene to their targets.

I had the same problem.  In fact, with the non modal dialogs, it was
easy to invoke the same dialog box twice, leaving internal variables
in a bad state.  I came up with the same solution as you: checking the
bounding box of the Dialog and only passing on events inside.

However, there is a fatal flaw with that approach as well.  If the
window manager buries the dialog window (e.g. if you click on the boundary
of an overlapping window initially behind the dialog), the dialog box
will be partly or completely obscured, potentially by another window of the
same application.  Now an event read in the Dialog::Run for this other window
may be within the Dialog bounding box, but not destined for a subwindow
of the Dialog.  Same problem occurrs, and it is not as unlikely as it sounds!

My solution - actually check the target interactor for having the dialog
as an ancestor:

    /*
     * Loop reading events.
     */
    int v;
    do
    {
	Read(e);

	// Only handle events for interactors whose ancestors include this.
	for(Interactor *i = e.target; i; i = i->Parent())
	{
	    if(i == this)
	    {
		e.target->Handle(e);
		break;
	    }
	}

	state->GetValue(v);
    } while(v == 0);

Neil/.	    Neil@teleos.com

vlis@lurch.stanford.edu (John Vlissides) (06/08/91)

>        Here at MCC one of our programmers created
>        a new kind of dialog that does not allow any
>        other interactors to receive events while it
>        is "popped up".  We call this a Modal Dialog.
> 
>        What we had to do was to take over the run
>        operation during display of the MD and refuse
>        to pass on any events occurring outside the
>        bounds of the local scene to their targets.
> 
>    I had the same problem.  In fact, with the non modal dialogs, it was
>    easy to invoke the same dialog box twice, leaving internal variables
>    in a bad state.  I came up with the same solution as you: checking the
>    bounding box of the Dialog and only passing on events inside.
> 
>    However, there is a fatal flaw with that approach as well.  If the
>    window manager buries the dialog window (e.g. if you click on the boundary
>    of an overlapping window initially behind the dialog), the dialog box
>    will be partly or completely obscured, potentially by another window of the
>    same application.  Now an event read in the Dialog::Run for this other window
>    may be within the Dialog bounding box, but not destined for a subwindow
>    of the Dialog.  Same problem occurrs, and it is not as unlikely as it sounds!
> 
>    My solution - actually check the target interactor for having the dialog
>    as an ancestor:

The BasicDialog class in Unidraw (3.0) addresses this problem in a
similar manner by defining Forward and IsAChild members:

void BasicDialog::Forward (Event& e) {
    if (IsAChild(e.target)) {
        e.target->Handle(e);
    } else {
        Handle(e);
    }
}    

boolean BasicDialog::IsAChild (Interactor* i) {
    Scene* parent = i->Parent();

    while (parent != nil) {
        if (parent == this) {
            return true;
        }
        parent = parent->Parent();
    }
    return false;
}

Subclasses use Forward instead of Handle to forward events only to
interactors inside the dialog.  AcknowledgeDialog, for example, simply
posts a message that the user can dismiss:

class AcknowledgeDialog : public BasicDialog {
public:
    AcknowledgeDialog(const char* title, const char* subtitle = "");

    virtual void Acknowledge();
private:
    Interactor* Interior();
};

AcknowledgeDialog::AcknowledgeDialog (
    const char* title, const char* subtitle
) : BasicDialog(new ButtonState, title, subtitle) {
    Insert(Interior());
    input = new Sensor(noEvents);
    input->Catch(KeyEvent);
}    

void AcknowledgeDialog::Acknowledge () {
    Event e;
    int v = 0;

    state->SetValue(v);
    do {
        Read(e);
        if (e.eventType == KeyEvent) {
            state->SetValue(e.keystring[0]);
        } else {
            Forward(e);
        }
        state->GetValue(v);
    } while (v == 0);
}

Interactor* AcknowledgeDialog::Interior () {
    const int space = round(.5*cm);

    return new MarginFrame(
        new VBox(
            new HBox(_title, new HGlue),
            new HBox(_subtitle, new HGlue),
            new VGlue(space),
            new HBox(
                new HGlue,
                new PushButton("  OK  ", state, 1),
                new HGlue
            )
        ), space, space/2, 0
    );
}
--
John Vlissides
Computer Systems Lab
Stanford University
vlis@interviews.stanford.edu