title: Dialog Boxes
author: Tiglari

<u>Window Structure</u><br>
Dialog boxes are actually a special kind of 'floating window',
defined by the class quarkpy.qmacro.dialogbox.  Floating
windows are child windows of the application that float
around 'on top of' other windows, can be moved, and often
resized.  A floating window contains a further kind of
window, called a 'panel', whose purpose is to contain
and control the positioning of the windows that do the real
work, which are called '(screen) controls' (in jargon,
a QuArK panel is a sort of 'geometry manager').  Dialog
boxes put one screen control into their panel, an
extremely powerful creature called a 'dataform', which
makes automagical links between QObjects and areas of
the screen (Armin told me once that even he doesn't
remember everything about how they work).

So the basic structure of a dialog box is:

<code>
  Floating Window     ->  Main Panel   ->   DataForm
 (positions the whole   (positions the   (does the real
       thing)              conrols)          work)
</code>

(Main Panel because floating windows can have more than
once panel, as we will see eventually).  The qmacro.dialogbox
code sets all this up for you, but I think it's good to
know this much about it at the beginning.

<u>Defining a Dialog</u><br>
So down to the details.  We'll look at the MakeTexMultDlg
in plugins.maptagside, since it's very simple.  This
dialog asks for a number which the mapper is going to
use as a 'multiplier' to know how many times a texture
should be repeated when it's being wrapped along or around
a chain of faces.

A dialog is defined as a class, descended from either
quarkpy.qmacro.dialogbox, or some class descended therefrom (see
quarkpy.qeditor and quarkpy.dlgclasses for some examples of the
latter technique).  MakeTexMultDlg is based directly on dialogbox,
so it starts out like this:

<code>
class MakeTexMultDlg (quarkpy.qmacro .dialogbox):

    #
    # dialog layout
    #

    size = (265, 70)
    dfsep = 0.2        # separation at 20% between labels and edit boxes
    dlgflags = FWF_KEEPFOCUS
</code>

On the first line we see the standard Python syntax for
defining a class descended from another (the spacing before
the periods doesn't matter, some coders stick it in; others
leave it out); then after a comment comes an assortment of
attributes for the dialog. Size is obvious, 'dfsep' is a
dataform property.  Dataforms are organized in two columns,
labels on the left, and input controls on the right. The 'Txt'
attribute in a :form object says what to put in the label
column.  Dfsep gives the percentage of the total width of the
dataform that gets devoted to the label.  Then comes
'dlgflags', which are ultimately passed on to the floating
window, so they start with 'FWF_'.  For a list, see
quarkpy.qutils.py.  If you don't specify any dlgflags, the
dialogbox code gives you

<code>
  FWF_KEEPFOCUS | FWF_POPUPCLOSE
</code>

by default.

Next comes the 'dialog definition', dlgdef:

<code>
    dlgdef = """
        {
        Style = "15"
        Caption = "Texture Wrapping Multiplier"

        mult: =
        {
        Txt = "Multiplier"
        Typ = "EF1"
        Hint = "Needn't be an integer; if it's 0, no multiplier is set"
        }
        close:py = { }
    }
    """
</code>

This is just a form, represented in .qrk format.
The value to be assigned to dlgdef begins with triple double quotes,
and ends with them too, because it's a multi-line string.
Note that it can happily contain ordinary solo double quotes.
A detailed discription of the <a href="adv.intro.html#typ">Typ</a>
items that it uses is located in the <a href="adv.intro.html#typ">Guide to Typ's</a>
section of these docs.

A <b>very important</b> thing to know and remember about this is how to use a Python defined variable within this
tripple double quotes area.

First the variable, which can also be a stored setting, must be defind before and outside of the above area like this:
<code>
    NbrOfLines = "10"
</code>
Then that variable can be used within the tripple double quotes area like this:

<code>
    mesh_shader: = {Typ="M"
                    Rows = """ + chr(34) + NbrOfLines + chr(34) + """
                    Scrollbars="1"
                    Txt = "mesh shader"
                    Hint="Contains the full text of this skin texture's shader, if any."$0D
                         "This can be copied to a text file, changed and saved."
                   }
</code>
The <g>chr(34)</g> items, which is for a single double quote, must be used like this to avoid confusion with the tripple double quotes.

It starts out with a style and a caption; the style is
a number obtained by adding these flags (from quarkpy.qutils):

<code>
  # "style" of ":form" objects (convert the numeric value into a string to assign to "style")
  GF_GRAY       = 1
  GF_EXTRASPACE = 2
  GF_NOICONS    = 4
  GF_NOBORDER   = 8
</code>

Then comes one field, a single floating point value called
mult:, and finally a button, of which more shortly.

There are a number of gotchas for forms:

<ol><li>
     The syntax is full of easy-to-screw up punctuation,
     and the error-messages tell you what line in the quoted
     string is wrong, rather than in the file, so you
     have to do some arithmetic to use the info (so I
     always start by dlgdefs by taking some other one that's
     already done as a basis, and modifying it).
<li>
     Buttons in forms are a horrible mess.  I don't have
     a deep enough understanding to know why Armin couldn't
     find one good implementation, but actually there are
     three crappy ones (2 by him, 1 by me), that work in
     different contexts.  This dialog box uses 'PyButtons',
     which are but into forms by lines like this:
<span class="doccode">
     close:py = { }
</span>
     (the content of the button is specified later)
<li>
      You can't put comments into these triple quote-defined
      forms, the function that reads them can't strip them
      out.
</ol>

Other than buttons, almost everything that works in an
entity form will work in a dialog box form, and vice versa,
I think (it would take a long time to test them all!!).
One other exceptions is checkboxes:  in entity forms,
you can have Typ = "X4", say to control the third bit
position, but in dialog boxes only Typ = "X" works,
this gives you an variable that's nonzero iff the
box is checked, zero otherwise.

So you can use most of the Typ types that you understand
to build dialog box forms, one that's rather useful for
making a bit of extra horizontal space is a separator like
this:

<code>
      sep: = {Typ="S" Txt=" "}
</code>

The Txt part has three formats you can use:
1) Txt="" gives a horizontal line separator
2) Txt=" " just separates with a blank area (note the space)
3) Txt="Your Label" will print the text in bold lettering

So next comes initialization of the dialog box object:

<code>
    #
    # __init__ initialize the object
    #

    def __init__(self, form, editor):

    #
    # General initialization of some local values
    #

        self.editor = editor
        self.sellist = self .editor .visualselection ()
</code>

This is the beginning of a Python 'constructor' a function
that makes an instance of a class (think of a class as a
template or factory for making objects; you call the
constructor as a function to make an instance of the object).

In Python, functions that get special treatment tend to start
and end with double underscores, as here with __init__.
To make this dialog box, you write:

<code>
   MakeTexMultDlg(quarkx.clickform, editor)
</code>

Python then calls the ReplaceTextureDlg class's
__init__ function to set up the dialog object.  The first
is by convention 'self', referring to the object (the rule
is that the first argument is the object; the convention
is to call it 'self').

The second argument 'form' is meant to be the parent window
of the dialog, quarkx.clickform sets it to be the last
window that got clicked in (e.g. the one you clicked in
to bring up the dialog).  The remaining arguments depend
solely on what we want the dialog to do, here we're
just passing 'editor'.

Next comes some real setup code:  we attach the editor and
its current visualselection() (the stuff that's in funny
colors due to being selected, not selected faces in a
selected poly) to the dialog as data members (this allows
other methods of the dialog class to act on them as desired).

Now comes the crucial step of 'creating the data source':

<code>
        #
        # Create and fill the data source
        #

        src = quarkx.newobj(":")   # make it
        src["mult"] = 0,           # fill it
        editor.texmult = self      # attach dialog to editor
        self.src = src             # attach src to dialog
</code>

The heart of a dialog box is its DataForm, which connects
stuff on the screen to a QObject.  src is the QObject
that the dialogbox will do this for.  So first we make it,
and then we fill it.

The comma after the 0 is not typo, but a gotcha: EFn Typ's
expect a Python tuple as their filler, and the comma
here is how you make a singleton tuple (I had a *lot* of
trouble before getting straight on that one!).  Finally
we want the editor to have some way of knowing what
multiplier we set; this is acheived in this case by
attaching the dialog to the editor, and then the source
to the dialog, so that multiplier can be gotten by
this line elsewhere in maptagside:

<code>
    multiplier, = editor.texmult.src["mult"]
</code>

Note the comma again, for pulling the solo value out of a
singleton tuple (actually if you're looking at maptagside from
QuArK511 or earlier, there's something a bit different done, since
this was my first dialog box and I knew less about what I was
doing).  editor.texmult.src["mult"] returns the value of the
specific "mult" for the src QObject of the editor, and by the
automagical powers of the dialogbox's dataform, this will be
whatever you currently have typed in there.

The next step is to call the code from
quarkpy.qmacro.dialogbox that actually puts all this together
and makes the stuff:

<code>
    #
    # Create the dialog form and the buttons
    #

        quarkpy.qmacro.dialogbox.__init__(self, form, src,
        close = quarkpy.qtoolbar.button(
            self.close,         # method called on push
            "close this box",   # hint
            ico_editor, 0,      # icon source, icon #
            "Close"))           # caption
</code>

We're here calling quarkpy.qmacro.dialogbox's own __init__
function, as an ordinary function, so we pass it's first
argument as 'self'. So it will set up the dataform, etc,
attaching more stuff to our dialog box instance.  We also
pass through the form argument, as well as the src we've
created.  And finally a list of buttons (here just one).
The field are explained; for each 'buttonname:py' in the
Dlgdef, we have to have buttonname = ... passed to the
initialization.  These equalities here are an aspect of
Python's very flexible parameter-passing mechanism, which
I won't explain here.

And finally, we have to define any methods used by the
dialog; here there's only one, onclose(), which the
quarkpy.dialogbox code causes to be executed when the
dialog closes for the purpose of cleaning things up.

<code>
    def onclose(self, dlg):
      requestmultiplier.state=qmenu.normal
      self.editor.texmult = None
      quarkpy.qmacro.dialogbox(self, close)
</code>

The onclose() method requires two arguments, the dialog 'self',
and its floating window 'dlg', and should also call the
qmacro.dialogbox.onclose method, so that the cleanup organized by
that occurs.  Here the additional work is done of restoring the
menu item  to normal, and detaching the dialog from the editor.
It's a simple example of a 'callback' function; as we'll see,
the dialog initialization code passes it to the floating window,
and then when the floating window shuts down, it runs this
function, 'calling back' into the module that provided it.

But we don't have to define a close() method, since that's
already taken care of in the qmacro.dialogbox code.

So finally, how do we make this dialog box?  That's done
by this little code:

<code>
def WrapMultClick(m):
  editor = mapeditor()
  if editor is None: return
  if requestmultiplier.state==qmenu.checked:
#    requestmultiplier.state=qmenu.normal
    editor.texmult.close(None)
  else:
    requestmultiplier.state=qmenu.checked
    MakeTexMultDlg(quarkx.clickform,editor)
</code>

This is a toggle; requestmultiplier is a menu item, if its
state is checked, the dialog's close function is called, which
unchecks the menu item (the earlier version just unchecked the
menu item but left the dialog floating; if you prefer that
behavior, move the comment #). But if it's normal (unchecked),
then it gets checked *and* the dialog is called up.  The menu
item can be defined after the function that refers to it
because the function gets called later (when a menu item is
clicked).

So there's a walk thru a small but complete box (tho a bit
unusual in its mode of operation).

A few more general points on the dialogs.  First, the dialogbox
definition sets up two callbacks, whereby things can be made
to happen when something is done to the window, plus the
close method:

<code>
    def datachange(self, df):
       # called when the data is changed

    def onclose(self, dlg):
       # called on closing

    def close(self)
       # effects closing
</code>

Datachange() doesn't do anything in qmacro.dialogbox, so
the original doesn't need to be called if you override it,
but close() and onclose() do do things, so should have their
originals called.

Second, the dialog boxes are inherently 'modeless'.  That
means that when you call one, the program doesn't wait for
you to enter a value before proceeding.  If you want to
fake modal behavior, by having something happen after you've
provided some values and closed the box, you have to pass
the action you want performed as a parameter to the dialog
box.  The texture positioning dialog in maptexpos.py
illustrates this and various other advanced techniques.
