Re: dialog enhancements



On 24 Mar 2000, Havoc Pennington wrote:

> 
> Hi,
> 
> Trying to make GtkDialog a little less useless than it currently is,
> while retaining backward compatibility.

ok, i've spent quite some time composing this email, looking at your header,
GnomeDialog and other things, and tried to provide constructve criticism.
but this also lead me to point out some general mistakes in other people's
code/APIs, so please don't take my comments personally ;)


> Here's what I added:
>  - changed the action_area to be a button box

urg, button box is one of the widgets with the worst packing behaviours
i've come across, for gtk proper, we should better stay HBox and let gnome
libraries override that if required.

>  - buttons can be referred to by position in the box, as with 
>    GnomeDialog

this is an inherently bad idea, and it is what makes usage of quite
some widgets, such as the option menu and several gnome ones very ugly
and tedious.
1) "position" in itself is dubious, this can be the position within the
   the containers children list, or the "position" of the widget within
   its siblings on the screen. this is not necessarily the same, think
   of hidden siblings
2) a position is an extremely dynamic thing, it changes when siblings
   are inserted/removed, and may be totaly bogus when the user interface
   doesn't reflect the order in which children got added.
   e.g. this will be the case if you create a dialog and tell it to
   have a STOCK_CLOSE and STOCK_OK button, and the actual layout depends
   on some policy setting in libgnomeui or some GUI builder resource file
   (think GLE, with which i can change order of your dialog buttons during
   runtime any way i like)
3) a position is a relative reference only applicable within context,
   that is, you can only make use of it, only if you also have the context
   (dialog) pointer around that this position referes to, i.e. you can't
   pass it in to some signal handler that can then pass it on into
   gtk_widget_set_sensitive() or similar functions
this is why Gtk+ throughout most of its interface only exports widget
pointers. you can use them irregardless of context, and they remain
valid upon alterations of other things, such as dynamic layout changes.
some people seem to have taken a dislike for pointers in general,
which is probably why they prefer half-backed more-natural-looking?
widget references, such as integers. but fact is, this is still C we
code in, and there is only one authoritative reference to a thing
in C, and that is its memory adress, read: pointer.

>  - can auto-create buttons by passing a NULL-terminated varargs 
>    list of button labels; as with GnomeDialog, eventually these
>    labels could also be stock button IDs

i pretty dislike GnomeDialog's varargs interfaces, and your's isn't
much better to be honest. for one, varargs don't necessarily make
things easier for people, or we'd have more applications actually
using the gtk_widget_new() and gtk_widget_set() interfaces of gtk.
for another, if you decide to take the route of offering a varargs
interface, that can be much more powerfull than simply being
constrained to a NULL terminated string list, as an example, here's
a dialog widget from BEAST that creates buttons on and connects
signals on the fly:

  adialog = bst_adialog_new (NULL, dialog_p, GTK_WIDGET (shell),
                             BST_ADIALOG_POPUP_POS | BST_ADIALOG_MODAL,
                             "title", "BEAST: Procedure",
                             "object_signal_after::hide", shell_modal_selection_done, shell,
                             "data_choice::Execute", shell_hide_on_demand, shell,
                             "data_choice::Execute", bst_procedure_shell_execute, shell,
                             "default_choice::Execute", NULL, NULL,
                             "choice::Close", gtk_toplevel_hide, NULL,
                             NULL);

amongst other things that really aren't interesting for the moment,
this function call creates a dialog that comes with an "Execute"
button, having two functions connected to it, and a "Close" button,
that also has a signal connection. additionally, "Execute" is made
the default choice of the buttons.

on the stock idea, while that is in principle good, the actuall
implementation looks pretty ad-hoc. we've had lots of such good
ideas for gtk over the last few years, bunch of them got discarded
because they were to specialized or hackish, but others (often
due to owen's poking), evolved from a good initial idea with an
ad-hoc implementation to a new subsystem, which is extensible and
generically useful.
stock items are a candidate for this, but we better figure out an
extensible system first to deal with stock pixmaps, widgets,
buttons, internationalizable labels, menu items etc. and user
preference settings of somesuch, that GNOME can incrementally
integrate with, before exposing "stock concept" as defines in
some widget header file.

>  - gtk_dialog_run() function to block waiting for modal dialog
>  - button_clicked signal on the dialog so you don't have to 
>    connect to each button separately

this seems like a good idea, just pass in the button pointer as
well, and not some hosed position integer ;) an action_id associated
with that button might also be good for convenience.

> Then I did a GtkMessageDialog subclass, which simply packs a label,
> sets the dialog title, and eventually adds one of those little pixmaps
> representing the dialog type. Pretty much identical to Motif, Qt, and
> GNOME message boxes.

sets the pixmap, based on some GtkMessageType value i suppose?
this is exactly where you better want some general stock entity
to query certain message dialog type appearance from.

> Headers appended, I have implementation too though.

> 
> Havoc
> 

> GtkType    gtk_dialog_get_type                   (void);
> GtkWidget* gtk_dialog_new                        (void);
> /* Think of a better name ;-) */
> GtkWidget* gtk_dialog_new_improved               (const gchar *title,
>                                                   GtkWindow   *parent,
>                                                   const gchar *first_button_name,
>                                                   ...);
> void       gtk_dialog_append_button              (GtkDialog   *dialog,
>                                                   const gchar *button_name);
> void       gtk_dialog_append_buttons             (GtkDialog   *dialog,
>                                                   const gchar *first_button_name,
>                                                   ...);
> void       gtk_dialog_append_buttons_valist      (GtkDialog   *dialog,
>                                                   const gchar *first,
>                                                   va_list      list);

what's the va_list variant for? are there really going to be functions
that carry on the label list varargs thingy, but are too sophisticated
to call gtk_dialog_append_button() on their own then? ;)
this tastes like a bunch of the cruft which went into GnomeDialog,
that tries to provide some pretend-to-be-convenience functions for
language bindings, where language bindings better make sure to cook up
their language-specific convenience on their own and use normal gtk
interfaces to set things up.

> void       gtk_dialog_append_button_widget       (GtkDialog   *dialog,
>                                                   GtkWidget   *button);
> GtkWidget* gtk_dialog_get_button_widget          (GtkDialog   *dialog,
>                                                   gint         position);
> gint       gtk_dialog_get_button_widget_position (GtkDialog   *dialog,
>                                                   GtkWidget   *button);
> gint       gtk_dialog_run                        (GtkDialog   *dialog);
> 
> /* emit button_clicked signal */
> void       gtk_dialog_button_clicked             (GtkDialog   *dialog,
>                                                   gint         button);

what exactly is this usefull for? i'm not saying it is not usefully,
just wondering what people would use this for (since then we might
want to have gtk_aux_dialog_activate () function in the below as well).



creating dialogs is an often recurring task in application programming,
making it easy and convenient should definitely be adressed by gtk.
in general, we should try to help the application programmer out with
the more advanced/tedious problems and still provide flexibility in areas
were needs often fluctuate.

dialogs are used for a wide of variety things, some of them simply display
a message, others are used to make a yes/no or 1-out-of-many choice,
and they are also frequently used for short hand inputs (e.g. changing
a layer name in gimp's layers&channels dialog).

in order to come up with a dialog API that is generically usable, we have
to provide flexibility with regards to the dialogs contents, and with
regards to the actions that can be performed.

mostly from using/looking at other people's code, i figured that many
applications get the widget creation of their labels/entries etc. right,
what is often done in a wrong way though (or simply not paid
attention to), are:
- dialog modality; if a dialog has to be completed before the application
  can move on, it should be modal, i.e. make all other toplevels unresponsive
- title settings; it is very annoying to have a dialog popped up that comes
  with now title/only the application name as title (what is done if no title
  is set is window manager dependant)
- positioning; i.e. short lived dialogs that the user can quickly fill in or
  answer should be made easily accessible, e.g. by popping them up at the
  current mouse cursor position
- lifetime specification; this is the decision of creating a dialog on demand
  and destroying it upon hiding, or keeping it around for later reuse
- dialog parentship; a bunch of temporary dialogs belong to other longer-lived
  toplevels (e.g. the "Really close this window? Y/N" type of dialogs), and
  shouldn't stay around longer than the toplevel they refer to
- pointer/memory maintenance; some apps don't keep track of dialog invokation,
  which can lead to multiple dialogs of the same kind being popped up (e.g.
  the above "Really close this window? Y/N" type). some applications that do
  try to keep track get the destruction notification wrong (or don't bother),
  or attempt to use reference counting (which is utterly wrong for this case)

these are some points where we desperatly need improvements, application wise,
and thusly in the toolkit as well. your improved dialog adresses some of the
issues, but not all of them, and comes with a lot of cruft to fiddle with
the dialog's contents, an area where we'd better stay flexible. GnomeDialog
partly goes the same route, just that it's much worse in regards to the content
cruft. for BEAST, i've written an "auxiallary dialog" widget (simply because
GtkDialog and GnomeDialog just don't cut it), it adresses most of the above
issues, and doesn't limit actual content, but it's probably still not generic
enough for gtk proper.
so lets try to come up with a new API that covers the required needs, i'll
call this GtkAuxDialog for the moment:

typedef enum
{
  GTK_AUX_DIALOG_DESTROY_ON_HIDE	= (1 << 0),
  GTK_AUX_DIALOG_POPUP_POS		= (1 << 1), /* choose popup position */
  GTK_AUX_DIALOG_MODAL			= (1 << 2), /* shown dialog is modal */
} GtkAuxDialogFlags;

/* gtk_aux_dialog_new() creates an auxillary dialog
 * prperties:
 * - the dialog will always be hidden for delete-events
 *
 * context_parent
 *	this is the widget that this dialog belongs to, may be NULL
 *	the dialog is automatically destroyed with its context_parent
 *	if context_parent is a visible toplevel and GTK_AUX_DIALOG_POPUP_POS
 *	is set, GtkAuxDialog will be positioned over its parent
 * aux_dialog_p
 *	pointer to a widget pointer which will be set to NULL once the
 *	dialog is destroyed, may be NULL
 * dialog_title
 *	the dialog's window manager decoration title
 * content_child
 *	child widget containing the dialogs contents
 * flags
 *	any combination of GtkAuxDialogFlags to alter dialog behaviour
 */
GtkWidget* gtk_aux_dialog_new (GtkWidget        *context_parent,
                               const gchar      *dialog_title
                               GtkWidget        *content_child,
                               GtkAuxDialogFlags flags,
                               GtkWidget       **aux_dialog_p);

this provides a quick way to create new auxillary dialogs, while covering
pretty much all of the issues outlined above. not that this function doesn't
provide any content limitations to keep flexibility for the general case,
we can have some G_GNUC_PRINTF() style convenience function based on this one.

of course, we also want to provide convenient access to the action area while
maintaining flexibility, so we have a function to add widgets to the action
area, accompanied by an id to identify choices:

/* gtk_aux_dialog_add_action() adds action_child to the dialog's action
 * area, action_id is kept in the child's "action_id" object data and will
 * be returned from gtk_aux_dialog_run (). if hide_dialog_on_activate is
 * passed as TRUE, the dialog is automatically hidden when the child is
 * activated.
 */
void gtk_aux_dialog_add_action (GtkAuxDialog *aux_dialog,
                                GtkWidget    *action_child,
                                gboolean      hide_dialog_on_activate,
                                gint          action_id);

/* gtk_dialog_run() shows the dialog and enters a recursive main loop
 * to be executed until the dialog is hidden. the return value is the
 * action_id of the child that was lastly activated or 0 if none.
 * gtk_dialog_run() is recommended to be used only with
 * GTK_AUX_DIALOG_MODAL dialogs.
 */
gint gtk_aux_dialog_run (GtkAuxDialog *aux_dialog);


this is three functions, that cover the needs of most temporary dialogs,
extra setter functions can be provided to adress some of the arguemnts
passed into the above functions, but i guess alteration of those values
will be rarely needed.
a few notes on the implementation, gtk_aux_dialog_add_action() should
for instance be implemented by a signal of GtkAuxDialogClass, so GNOME
libraries can override behaviour to specify policy, e.g. by altering
packing of the action widgets.

on top of that foundation, we can then build convenience functions
which cover needs like automated printf() style content creation.
things like on-the-fly creation of sub-heirachies, such as a button
containing a label, better be implemented on a per container-basis,
here, e.g. gtkbutton.h, so they are then generically pluggable into
functions like container_add() or aux_dialog_add_action().

---
ciaoTJ



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]