Search code examples
pygobject

Own drag icon with same color and font settings as the default drag icon in a Gtk.TreeView


The Gtk.TreeView implements a default drag icon. It use the background color of the TreeView, it's font and the complete row-content as string.

I want the same (background-color, font-face, font-size, font-color) but with a shorter string (only the second of three columns).

In the example below create my own cairo.Surface to create such an icon. But color and font is a problem. I don't know how to set them up or (much more important) to ask the TreeView or Gtk itself for the current color and font values.

How does the TreeView get this values?

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
import cairo

class MainWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="TreeView Drag and Drop")
        self.connect("delete-event", Gtk.main_quit)
        self.box = Gtk.Box()
        self.add(self.box)

        # "model" with dummy data
        self.store = Gtk.TreeStore(int, str, int)
        for i in range(5):
            self.store.append(None, [i, 'Item {}'.format(i), i]) # treeview
        self.tree = Gtk.TreeView(model=self.store)
        self.box.pack_start(self.tree, True, True, 0)

        # build columns
        colA = Gtk.TreeViewColumn('Col A', Gtk.CellRendererText(), text=0)
        self.tree.append_column(colA)
        colB = Gtk.TreeViewColumn('Col B', Gtk.CellRendererText(), text=1)
        self.tree.append_column(colB)
        colC = Gtk.TreeViewColumn('Col C', Gtk.CellRendererText(), text=2)
        self.tree.append_column(colC)

        # enable default drag and drop
        self.tree.set_reorderable(True)

        # DnD events
        self.tree.connect_after("drag-begin", self.drag_begin)

    def drag_begin(self, widget, context):
        model, path = widget.get_selection().get_selected_rows()
        text = model[path][1]

        # dummy surface/context
        surface = cairo.ImageSurface(cairo.Format.RGB24, 0, 0)
        cr = cairo.Context(surface)

        # calculate text size
        txtext = cr.text_extents(text)
        width = int(txtext.width)
        height = int(txtext.height)
        offset = 10

        # creal surface/context
        surface = cairo.ImageSurface(cairo.Format.RGB24,
                                     width + (offset*2),
                                     height + (offset*2))
        cr = cairo.Context(surface)
        cr.set_source_rgb(1, 1, 1) # text color: white
        cr.move_to(0+offset, height+offset)
        cr.show_text(text)

        # use the surface as drag icon
        Gtk.drag_set_icon_surface(context, surface)

win = MainWindow()
win.show_all()
Gtk.main()

What I tried (but not worked) was cairo.Surface.create_similar()',cairo.Surface.create_similar_image()andGtk.TreeView.create_row_drag_icon()`.


Solution

  • This answer is based on a foreign mailing list posting.

    The widget has a Gtk.StyleContext. A Pango.Layout is used to render the text based on the style informations in the Gtk.StyleContext.

    def drag_begin(self, widget, context):
        model, path = widget.get_selection().get_selected_rows()
        text = model[path][1]
    
        stylecontext = widget.get_style_context()
    
        # new pango layout
        pl = widget.create_pango_layout(text)
        ink_rec, log_rect = pl.get_pixel_extents()
        padding = 5 
    
        # create surface/context
        surface = cairo.ImageSurface(cairo.Format.RGB24,
                                     log_rect.width + (padding*2),
                                     log_rect.height + (padding*2))
        cr = cairo.Context(surface)
        Gtk.render_background(stylecontext, cr, 0, 0,
                              log_rect.width + (padding*2),
                              log_rect.height + (padding*2))
        Gtk.render_layout(stylecontext, cr, padding, padding, pl)
    
        # border
        line_width = cr.get_line_width()
        cr.rectangle(-1+line_width, -1+line_width,
                     log_rect.width+(padding*2)-line_width,
                     log_rect.height+(padding*2)-line_width)
        cr.stroke()
    
        # use the surface as drag icon
        Gtk.drag_set_icon_surface(context, surface)