6

I use a Gtk.IconView widget in my app to select different types of elements. The way the IconView works is the following:

  • Do one click to select the item
  • Do an additional double-click to activate the item

I've always found it confusing that an additional double click is needed, when most button-like widgets in the desktop just require a single click. Now I've just had feedback from a couple of users that find it confusing (someone thought it was a bug that clicking on the button "did nothing"), so I've decided to look into how to change that behaviour.

Ideally, I'd like the IconView to behave as follows:

  • Hover to select an item (optional)
  • Single-click activates the item

Now I haven't found anything obvious in the widget's properties to make it behave like that. Does anyone know how or if that could be achieved?

Gtk.IconView in action

4 Answers 4

5

One solution (but only for the single click activation) is of cause to use the selection_changed signal of the IconView (because the IconView selection is single click based).

A sample that assumes a working on_item_activated signal would be:

def on_icon_view_selection_changed(self, widget):
        self.on_item_activated(widget, widget.get_selected_items()[0])
1
2

A GtkIconView is not an arrangement of button widgets into a table. It is basically a special case of GtkTreeView, with items laid out in a grid, rather than a list/tree. To do highlight of items on hover, I believe a patch to GTK+ would be required. A patch would also be required to add API to enable single-click vs. double-click to activate, however, this feature can be implemented on top of the existing widget API, by listening to the button_press_event signal on the widget, and acting accordingly.

4
  • Ok, thanks @dobey. So I guess I can leave out the hovering part, but looking at the single click and button-press-event, I'm not sure I can use that, either. If I understand it correctly, the problem with that is that when I click on an icon and intercept button-press-event, I won't be able to tell which one it is, as the item id of the underlying model won't be passed in the callback in the way it is for the on_iconview1_view_item_activated callback that I'm using right now. May 31, 2012 at 11:08
  • Right. You'll also have to use the API to query the IconView to get the cell over which the mouse click occurred. The button-press-event signal is a generic signal from GtkWidget, that most all (interactive/focusable) widgets will emit when you click a mouse button on them. You'll have to do all the hard work manually, which the activate code in GTK+ already does for double clicking, but do it for single click, and manually avoid double click conditions, in your code.
    – dobey
    May 31, 2012 at 11:32
  • Any directions as to how I can use the API to query the IconView to get the cell over which the mouse click occurred? I think if I can figure that one out, it will help me assessing if the rest of the manual work is worth the effort, or if I should just live with double click :) May 31, 2012 at 18:47
  • developer.gnome.org/gtk3/stable/… This will give you a GtkTreePath, which you can use to get an iter from the model on the view, and then go from there, the same as you do in the callback for the item-activated signal.
    – dobey
    May 31, 2012 at 20:19
1

You seem to explore the limitations of Gtk lately :-)

First of all, dobeys answer seems correct.

Tree- and List-Stores are a good choice for collections. Larger amounts of data which are identical in type but different in meta-content (e.g. a music collection where every piece has an artist, album, title, but the actual artist name, album name, title name changes).

The good thing about these widgets is that they are easily sortable, many items can be selected (e.g. in 'rubber-band' mode), and you need relatively few lines of code to add thousands of similar items.

Changing the way a TreeView or ListView behaves to clicks / hover etc on a per application level is a bad idea, because it messes with the way users expect your application to behave. You found that out yourself based on the reaction you describe in your question.

Your problem however is that users did not understand that your widget is a TreeView / ListView, instead they thought that they are a number of buttons. And that seems to make sense, based on your screenshot.

Buttons are meant to start an action (e.g. stop, play, pause) or communicate with the program (e.g. Ok, Cancel) or even act as a link to a different view (see LinkButton). They don't normally have an (abstract) object or item associated with it (e.g. ' "Sammy Davis Jr" by "Movits!" from the album "Out of my head" ').

So, while the not-configurable click behaviour is a shortcoming of gtk, it should anyway only be on a per-user global level.

What you want is probably a grid with Buttons. (Buttons can also contain icons).

4
  • Indeed, I seem to be hitting some of the limitations of the toolkit :-) I do love GTK, but lately I'm spending more time with it than on actual coding of features of the application. I did explore using a grid with buttons before I chose an Iconview, but I discarded it in favour of the built-in functionality for displaying the icons and their descriptions and most importantly for the integration with the associated Gtk.ListStore that I'm using to store metadata. May 31, 2012 at 8:26
  • I'm looking at the system settings (GNOME Control Center) dialog, and that seems to use single-click for a view that resembles an IconView. I migh have to explore what they'Re using later on. May 31, 2012 at 8:26
  • @DavidPlanella added a branch with a grid of buttons. You can try it out and see if you like it...
    – xubuntix
    Jun 1, 2012 at 17:28
  • Thanks! I got notified and saw the merge proposal, that's awesome. I've just reviewed one and I'll do this one during the weekend. Let's continue the discussion on the merge proposal Jun 1, 2012 at 22:19
1

Here's how I made my Iconview respond to pointer position to get a hover effect and single left-click and right-click actions.

You have to activate pointer-motion-notify for the widget, and then convert x,y position of pointer to the path of the iconview:

...
    self.iconview.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
    self.iconview.connect("item-activated", self.iv_icon_activated)
    self.iconview.connect("button-press-event", self.on_mouse_click)
    self.iconview.connect("motion-notify-event", self.on_pointer_motion)

def on_pointer_motion(self, widget, event):
    path= self.iconview.get_path_at_pos(event.x, event.y)
    if path !=None:
            self.iconview.select_path(path)
    #If we're outside of an item, deselect all items (turn off highlighting)
    if path == None:
        self.iconview.unselect_all()

def on_mouse_click(self,widget, event):
    if event.type == Gdk.EventType.BUTTON_PRESS:
        path=self.iconview.get_selected_items()[0]
       #if right click activate a pop-up menu
        if event.button == 3 and path != None:
            self.popup.popup(None, None, None, None, event.button, event.time)
       #if left click, activate the item to execute
        if event.button == 1 and path != None:
            self.iv_icon_activated(widget, path)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .