Re: dialog enhancements



On 25 Mar 2000, Havoc Pennington wrote:

> > 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.
> 
> I think you're getting stuck on the word "position". Perhaps I should
> s/position/action_id/ and you would be happy. ;-) They are just unique
> integers whose exact value doesn't matter much. The only difference is
> with your action_id the user can specify the ID. Of course the
> disadvantage is that the user _must_ specify the ID. 
> 
> So if we add a convenience API, such as a varargs list of buttons
> labels, it would make sense to automatically generate the action_id
> for those buttons, otherwise it isn't so convenient.

in my book, having the user specify the id counts as a definite plus,
especially since it won't change further on due to widget repositioning
or other circumstances.

> > 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
> 
> But who cares, this situation never happens. Who is going to hide a
> dialog button? That would be broken. 
> 
> > 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.
> 
> Inserting/removing buttons dynamically does not happen in any real
> application. In a GUI builder yes, but it doesn't matter there.
> 
> > 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
> 
> Sure, but this is also not something you need to do 99% of the
> time. ;-) 
> 
> Don't spend too much time thinking about the corner cases - remember
> that anyone can make their own dialogs from scratch with GtkWindow if
> they want. This whole widget is purely a convenience
> thing. Convenience things that aren't convenient are just bloat.

i'm not pondering about corner cases here. if we continue to present
a broken dialog API using "remember that anyone can make their own
dialogs from scratch with GtkWindow" as justification, where's the
point in providing it in the first place?
a sane dialog API that's supposed to be widely used should attempt
to cover most needs that arise when creating dialogs, which is why
i outlined a bunch of general requirements in my first response.
of course we can't cover virtually _any_ need that might come up
for people creating dialogs, but we should definitely provide
something usable, say beyond modal dialogs.

> However I will grant you your points about position, since an
> action_id will work just as well as you suggest.
> 
> > 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.
> >
> 
> People don't like pointers because you can't map them to actions
> easily.
> 
> For example:
>  
> GtkWidget* clicked_button;
> 
> clicked_button = gtk_dialog_run(dialog);
> 
> /* Now what? How do I know what button I have? */
> 
> Basically you have to set_data() an integer ID or a string ID or
> something so you know what to do with this button. The pointer by
> itself is useless here. Thus the position or action_id is very nice to
> have.

yes, i'm not arguing against ids in general, but it's a bad idea to
use them as widget references. rather, they need to be an abstract
identifier for the action choosen by the user, that is you should
also be able to have different buttons come with the same id (e.g.
Close most often should have the same effect as delete_event, and
foe some dialogs you may even want to have Ok have the same id, but
cause side effects by additional signal handlers).

> > 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.
> 
> In this case the varargs are just saving you typing. 
> 
> Compare:
>  dialog = gtk_dialog_new("Title", parent, "OK", "Cancel", NULL);
> 
> and:
>  dialog = gtk_dialog_new("Title", parent);
>  gtk_dialog_append_button(dialog, "OK");
>  gtk_dialog_append_button(dialog, "Cancel");
> 
> or worse:
>  dialog = gtk_dialog_new("Title", parent);
>  gtk_dialog_append_widget(dialog,
>                           gtk_button_new_with_label("OK"));
>  gtk_dialog_append_widget(dialog,
>                           gtk_button_new_with_label("Cancel"));
> 
> 98% of dialogs have only buttons in the action area, so this last one
> is very gratuitous pain. 

i understand that you simply want to make the common case convenient here,
however, that's an interface addition, the underlying mechanism, i.e.
gtk_dialog_append_widget() still has to be exposed in the API.
that way i can stuff customized widgets into the dialog (e.g. a button
containing a GnomeForest) and have the basic functionality available
for bindings.
the one thing i don't like about your varargs interface:

dialog = gtk_dialog_new("Title", parent, "OK", "Cancel", NULL);

is that i don't see how that's supposed to scale to stock widgets,
do you want to do string comparisions there, or recommend
gtk_dialog_append_widget() usage? since ideally we'd use stock
widgets for most things, and with GNOME even have things like
"[x] Buttons show icons" configurable, the varargs extension of
gtk_dialog_new() to auto-create buttons from strings is supposed
to be a dead interface really fast.
a better solution to this may be to have a usable stock interface
(sketched) like:

typedef enum
{
  GTK_STOCK_NONE = 0,
  GTK_STOCK_CLOSE,
  GTK_STOCK_OK,
  GTK_STOCK_CANCEL,
  GTK_STOCK_EDIT,
  GTK_STOCK_DELETE,
  [...]
} GtkStockType;
GtkWidget* gtk_stock_create_widget (GtkStockActionType stock_type);

and have a varargs intrface like:
dialog = gtk_dialog_new ("Title", parent,
                         gtk_stock_create_widget (GTK_STOCK_OK),
                         gtk_stock_create_widget (GTK_STOCK_CANCEL),
                         NULL);
usable as:
switch (gtk_dialog_run (dialog))
{
case GTK_STOCK_OK:
  g_print ("Ok, doing stuff\n");
  break;
default: /* catches GTK_STOCK_CANCEL and GTK_STOCK_NONE */
  g_print ("dialog abortion\n");
  break;
}

> > 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:
> > 
> 
> Yes, but that is too complicated to be a convenience interface. No one
> would ever use it (much as they don't use gtk_widget_new).

right, i didn't propose that interface, i pasted it as an example of
how powerfull varargs _can_ actually be.

> > > /* 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).
> >
> 
> People use it to force a certain return value from gtk_dialog_run(),
> basically.
> 
> You could even call it gtk_dialog_quit_run(dialog, return_code); 

i still don't get it, where would you call this from?
is this for modal dialogs?

> > 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).
> >
> 
> I think you want to make some assumptions in the convenience interface.
> 
> 1) All dialogs have buttons on the bottom; if they don't, they are
>    probably a toolbox or something. for example, layers&channels is 
>    not a dialog in my opinion, it is just a small auxiliary window.

well, by now the l&c dialog handles layers for all image windows,
but had that not been the case, it'd be a good example of where
the new dialog interface could be very convenient to use.
you basically build your dialog's contents, and then do

GtkWidget *contents = l_and_c_contents_create ();

image->lc_dialog = gtk_dialog_new (image->window, /* parent */
                                   "layers&channels",
                                   content,
                                   GTK_DIALOG_DESTROY_ON_HIDE,
                                   &image->lc_dialog,
                                   gtk_stock_create_widget (GTK_STOCK_CLOSE),
                                   NULL);
gtk_widget_show (image->lc_dialog);

(this assumes that widget's added through gtk_dialog_new() varargs
facility, will automatically pass hide_on_activate as TRUE into
the underlying gtk_dialog_add_action() call)

the gtk_dialog_new() takes care of dialog parentship, i.e.
lifetime maintainance, title setting and memory/pointer
maintainance. also it creates a stock button on the fly that
comes with/without an icon, according to the current user
preferences.
to add the content child, you can also have an extra function
call like in the API you proposed, i personally just think
this is more convenient, e.g. to extend on that:

dialog = gtk_dialog_new (NULL, /* parent */
                         "GIMP: Close Image",
                         gtk_stock_message_create (GTK_STOCK_QUERY,
                                                   "Image %s has been modified.\n"
                                                   "Really Close?",
                                                   image_name),
                         GTK_DIALOG_DESTROY_ON_HIDE |
                         GTK_DIALOG_MODAL |
                         GTK_DIALOG_POPUP_POS, /* popup at cursor pos */
                         NULL, /* dialog_p */
                         gtk_stock_create_widget (GTK_STOCK_CLOSE),
                         gtk_stock_create_widget (GTK_STOCK_CANCEL),
                         NULL);
switch (gtk_dialog_run (dialog))
{
case GTK_STOCK_CLOSE:
  destroy_image (image);
  break;
default: /* GTK_STOCK_CANCEL and GTK_STOCK_NONE */
  break;
}
/* dialog is *destroyed* at this point */
                         

>    People should use GtkWindow for persistent stuff like
>    layers&channels. Your GtkDialog interface should be for transient
>    dialogs that pop up for a short time, the user quickly clicks some
>    stuff, the dialog pops down.

that's just limiting yourself to mostly modal dialogs as shown
above, the same interface may be very usefull for context-bound
helper dialogs, e.g. a per-image l&c dialog.

> 2) The most common dialog by far is a simple message with an OK
>    button.  Thus the MessageDialog convenience object. 

that's fine, in may above example gtk_stock_message_create() cares
about GTK_STOCK_QUERY (you can have GTK_STOCK_ALERT, GTK_STOCK_YES_NO
etc... types) which may come with a big question mark icon etc...
sure you can provide an extra convenience function for this, but most
stuff of gtk_dialog_new() would have to be carried on, i.e. the
parent widget, the title, the flags and the dialog_p, since you
don't know whether that dialog belongs to another window, is persistent
etc...
and at that point i wonder what the point in ahving an extra function,
that just gets rid of the varargs, is...


> > in order to come up with a dialog API that is generically usable, we
> > have
> 
> Remember though: GtkWindow is the generic interface. GtkDialog is
> _only_ a convenience interface - that's its only reason for existing,
> at all. So, I think it's important to be as generic as possible, while
> primarily being convenient, instead of being as convenient as
> possible, while primarily being generic.

you don't need to remind me of that, of course you can do all sorts
of wired shit with GtkWindow, i'm just saying you shouldn't limit
GtkDialog to short-lived modal messages only ;)

> > - 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
> 
> I think GTK+ should automatically handle this, considering the
> transient parent, and perhaps honoring user preferences settings.

yes, that's for dialogs that you simply need the user to answer, but
i think we actually have thre common cases here:
1) a dialog that should be quickly answered, e.g. by pressing Return
   to activate a default Ok button. this we'd want to put aas good at
   the user's disposal as possible, thus GTK_WIN_POS_MOUSE (done by
   GTK_DIALOG_POPUP_POS flag)
2) a dialog that belongs to a parent window and should be placed
   centered/above that parent window (e.g. a font selector for a
   gnumeric cell) according to preference settings (i.e. might be
   GTK_WIN_POS_MOUSE instead of transient-centered)
3) a dialog that belongs to a parent window, bt may stay for a while,
   not covering up the parent window's contents (like a per-image
   l&c dialog), placement here is probably best left to the wm.


> > - lifetime specification; this is the decision of creating a dialog on demand
> >   and destroying it upon hiding, or keeping it around for later
> > reuse
> 
> GnomeDialog has this "hide instead of destroy" feature, but I can't
> actually think of a case where it's more convenient. Can you think of
> such a case?

sure, say the per-image dialog creation is pretty time-consuming, and
probably going to be popped up/down very often.
then you want your image's "Show L&C dialog" accelerator do:

static void
show_l_and_c_dialog (GimpImage *image)
{
  if (!image->lc_dialog)
    {
      GtkWidget *contents = l_and_c_contents_create ();

      image->lc_dialog = gtk_dialog_new (image->window, /* parent */
                                         "layers&channels",
                                         content,
                                         0, /* don't destroy on hide */
                                         &image->lc_dialog,
                                         gtk_stock_create_widget (GTK_STOCK_CLOSE),
                                         NULL);
    }
  gtk_widget_show (image->lc_dialog);
  gdk_widnow_raise (image->lc_dialog->window);
}

if it were pretty resource consuming, you'd probably just want the same
exact code, but additionally specify GTK_DIALOG_DESTROY_ON_HIDE. the nice
thing here is, since gtk_dialog_new() takes care of life-time and pointer
maintainance, you just have to specify that one flag and don't need to worry
about setting up hide/destroy handlers etc...


> > - 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
> 
> Good point, transients should perhaps be destroyed when their parent
> is destroyed.

that's not only for transients, this can be virtually any assistance dialog,
e.g.
- non-modal adjust-zoom-area dialog for images
- non-modal edit-properties
- modal (transient) execute procedure dialog (beast has this)
- modal (transient) "Really Close?" dialogs
- non-modal select-current-font dialog
just to name a few...

> > GtkWidget* gtk_aux_dialog_new (GtkWidget        *context_parent,
> >                                const gchar      *dialog_title
> >                                GtkWidget        *content_child,
> >                                GtkAuxDialogFlags flags,
> >                                GtkWidget       **aux_dialog_p);
> >
> 
> I really want varargs button labels on the end of this. ;-)
> Maybe with action IDs alternating with the labels.

look at my writings above, maybe i can persuade you to have generic
widget pointers for pluggable stock widgets there? ;)

> Then I can create a useful dialog in a single function call.
> 
> Otherwise, I have to call some other functions to add my buttons.
> 
> > void gtk_aux_dialog_add_action (GtkAuxDialog *aux_dialog,
> >                                 GtkWidget    *action_child,
> >                                 gboolean      hide_dialog_on_activate,
> >                                 gint          action_id);
> > 
> 
> Consider that to make action_id better than position, it needs to
> always be an enumeration. If it's just random integers, it's no better
> than position.
> 
> Writing an enumeration per dialog is pretty inconvenient.
> 
> One nice solution might be to provide some stock action_id values:
>  
> typedef enum {
>  GTK_ACTION_ACCEPT,
>  GTK_ACTION_REJECT
> } GtkActionsType;

good point!


> > /* 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.
> 
> gnome_dialog_run() actually makes the dialog modal for the duration of
> the run - I guess it is almost impossible to use this function sanely
> if the dialog is not modal.

you're probably right there, we should be able to get rid of the
GTK_DIALOG_MODAL flag this way.

> > 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.
> >
> 
> I think we should just have nice packing in GTK also - but we can
> virtualize the function too. 

i'm not saying we need GNOME to fix gtk's broken packing, instead
GNOME should take care of user prefs such as:
 o  center buttons
 O  spread buttons out
 o  left-adjust buttons
 o  right-adjust buttons
[x] expand buttons

(where "spread buttons out"+"expand buttons" is the original
default packing behaviour for gtk dialogs, that i personally
prefer, for quick access)

> > 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().
> > 
> 
> Here I think gtk_button_new_with_label() is too much typing for 
> dialog buttons. 

well, i actually propose usage of gtk_stock_widget_create(), which
is a bit less typing, but more importantly allowes us to scale with
future needs and allowes us to have the widgets configurable by
GNOME.

> 
> Havoc
> 

---
ciaoTJ



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