Migrate cellrenderer to introspection and store editable

In order to limit issue with vfunc, it is better to follow the best practice
by using introspection without pygtkcompat.
Also vfunc may have issue with object created by it and returned, so it is
safer to keep a Python reference to on the Python class.

issue8110
review47811002
This commit is contained in:
Cédric Krier committed 2019-02-20 12:14:12 +01:00
1 parent 74eb7d4e94
commit 9180bed90c
5 files changed
+140 -135

No files matched your search

-2
View File
@@ -96,8 +96,6 @@ def get_active_text(self):
return model[active][index]
Gtk.ComboBox.get_active_text = get_active_text
Gtk.GenericCellRenderer.__gobject_init__ = Gtk.GenericCellRenderer.__init__
from gi.repository import Pango
+42 -48
View File
@@ -1,8 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import gtk
import gobject
import pango
from gi.repository import Gtk, Gdk, GObject, Pango
from .common import IconFactory
@@ -10,31 +8,31 @@ BUTTON_BORDER = 2
BUTTON_SPACING = 1
class CellRendererBinary(gtk.GenericCellRenderer):
class CellRendererBinary(Gtk.CellRenderer):
__gproperties__ = {
'visible': (gobject.TYPE_BOOLEAN, 'Visible', 'Visible', True,
gobject.ParamFlags.READWRITE),
'editable': (gobject.TYPE_BOOLEAN, 'Editable', 'Editable', False,
gobject.ParamFlags.READWRITE),
'size': (gobject.TYPE_STRING, 'Size', 'Size', '',
gobject.ParamFlags.READWRITE),
}
'visible': (GObject.TYPE_BOOLEAN, 'Visible', 'Visible', True,
GObject.ParamFlags.READWRITE),
'editable': (GObject.TYPE_BOOLEAN, 'Editable', 'Editable', False,
GObject.ParamFlags.READWRITE),
'size': (GObject.TYPE_STRING, 'Size', 'Size', '',
GObject.ParamFlags.READWRITE),
}
__gsignals__ = {
'select': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
'open': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
'save': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
'clear': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING,)),
}
'select': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING,)),
'open': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING,)),
'save': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING,)),
'clear': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING,)),
}
def __init__(self, use_filename):
self.__gobject_init__()
Gtk.CellRenderer.__init__(self)
self.visible = True
self.editable = False
self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
self.set_property('mode', Gtk.CellRendererMode.ACTIVATABLE)
self.use_filename = use_filename
self.images = {}
for key, icon in (
@@ -42,9 +40,8 @@ class CellRendererBinary(gtk.GenericCellRenderer):
('open', 'tryton-open'),
('save', 'tryton-save'),
('clear', 'tryton-clear')):
# hack to get gtk.gdk.Image from stock icon
img_sensitive = IconFactory.get_pixbuf(
icon, gtk.ICON_SIZE_SMALL_TOOLBAR)
icon, Gtk.IconSize.SMALL_TOOLBAR)
img_insensitive = img_sensitive.copy()
img_sensitive.saturate_and_pixelate(img_insensitive, 0, False)
width = img_sensitive.get_width()
@@ -75,18 +72,17 @@ class CellRendererBinary(gtk.GenericCellRenderer):
+ (2 * (BUTTON_BORDER + BUTTON_SPACING) * len(self.buttons))
- 2 * BUTTON_SPACING)
def on_get_size(self, widget, cell_area=None):
def do_get_size(self, widget, cell_area=None):
if cell_area is None:
return (0, 0, 30, 18)
else:
return (cell_area.x, cell_area.y,
cell_area.width, cell_area.height)
do_get_size = on_get_size
def on_activate(self, event, widget, path, background_area,
cell_area, flags):
def do_activate(
self, event, widget, path, background_area, cell_area, flags):
if event is None:
return True
return
button_width = self.button_width()
for index, button_name in enumerate(self.buttons):
@@ -104,15 +100,13 @@ class CellRendererBinary(gtk.GenericCellRenderer):
else:
button_name = None
if not self.visible or not button_name:
return True
if not self.editable and button_name in ('select', 'clear'):
return True
if not self.size and button_name in {'open', 'save'}:
return True
if event.type == gtk.gdk.BUTTON_PRESS:
self.emit(button_name, path)
return
elif not self.editable and button_name in ('select', 'clear'):
return
elif not self.size and button_name in {'open', 'save'}:
return
self.emit(button_name, path)
return True
do_activate = on_activate
def do_render(self, cr, widget, background_area, cell_area, flags):
if not self.visible:
@@ -137,15 +131,15 @@ class CellRendererBinary(gtk.GenericCellRenderer):
lwidth = w - button_width - padding.left - padding.right
if lwidth < 0:
lwidth = 0
layout.set_width(lwidth * pango.SCALE)
layout.set_ellipsize(pango.ELLIPSIZE_END)
layout.set_wrap(pango.WRAP_CHAR)
layout.set_alignment(pango.ALIGN_RIGHT)
layout.set_width(lwidth * Pango.SCALE)
layout.set_ellipsize(Pango.ELLIPSIZE_END)
layout.set_wrap(Pango.WRAP_CHAR)
layout.set_alignment(Pango.ALIGN_RIGHT)
if lwidth > 0:
lw, lh = layout.get_size() # Can not use get_pixel_extents
lw /= pango.SCALE
lh /= pango.SCALE
lw /= Pango.SCALE
lh /= Pango.SCALE
lx = x + padding.left
if self.buttons and self.buttons[0] == 'open':
@@ -154,7 +148,7 @@ class CellRendererBinary(gtk.GenericCellRenderer):
ly = y + padding.top + 0.5 * (
h - padding.top - padding.bottom - lh)
gtk.render_layout(context, cr, lx, ly, layout)
Gtk.render_layout(context, cr, lx, ly, layout)
for index, button_name in enumerate(self.buttons):
pxbf_sens, pxbf_insens, pxbf_width, pxbf_height = \
@@ -177,13 +171,13 @@ class CellRendererBinary(gtk.GenericCellRenderer):
by = cell_area.y
bw = pxbf_width + (2 * BUTTON_BORDER)
gtk.render_background(context, cr, bx, by, bw, h)
gtk.render_frame(context, cr, bx, by, bw, h)
Gtk.render_background(context, cr, bx, by, bw, h)
Gtk.render_frame(context, cr, bx, by, bw, h)
gtk.gdk.cairo_set_source_pixbuf(
Gdk.cairo_set_source_pixbuf(
cr, pixbuf, bx + BUTTON_BORDER, by + (h - pxbf_height) / 2)
cr.paint()
context.restore()
gobject.type_register(CellRendererBinary)
GObject.type_register(CellRendererBinary)
+32 -50
View File
@@ -1,31 +1,27 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import gtk
import gobject
import pango
from gi.repository import Gtk, GObject, Pango
class CellRendererButton(gtk.GenericCellRenderer):
# TODO Add keyboard editing
class CellRendererButton(Gtk.CellRenderer):
__gproperties__ = {
"text": (gobject.TYPE_STRING, None, "Text",
"Displayed text", gobject.ParamFlags.READWRITE),
'visible': (gobject.TYPE_INT, 'Visible',
'Visible', 0, 10, 0, gobject.ParamFlags.READWRITE),
'sensitive': (gobject.TYPE_INT, 'Sensitive',
'Sensitive', 0, 10, 0, gobject.ParamFlags.READWRITE),
}
"text": (GObject.TYPE_STRING, None, "Text",
"Displayed text", GObject.ParamFlags.READWRITE),
'visible': (GObject.TYPE_INT, 'Visible',
'Visible', 0, 10, 0, GObject.ParamFlags.READWRITE),
'sensitive': (GObject.TYPE_INT, 'Sensitive',
'Sensitive', 0, 10, 0, GObject.ParamFlags.READWRITE),
}
__gsignals__ = {
'clicked': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING, )),
}
'clicked': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING,)),
}
def __init__(self, text=""):
self.__gobject_init__()
Gtk.CellRenderer.__init__(self)
self.text = text
self.set_property('mode', gtk.CELL_RENDERER_MODE_EDITABLE)
self.clicking = False
self.set_property('mode', Gtk.CellRendererMode.EDITABLE)
self.visible = True
self.sensitive = True
@@ -39,15 +35,14 @@ class CellRendererButton(gtk.GenericCellRenderer):
if not self.visible:
return
state = gtk.StateFlags.NORMAL
if self.clicking and flags & gtk.CELL_RENDERER_SELECTED:
state = gtk.StateFlags.ACTIVE
elif not self.sensitive:
state = gtk.StateFlags.INSENSITIVE
if not self.sensitive:
state = Gtk.StateFlags.INSENSITIVE
else:
state = Gtk.StateFlags.NORMAL
context = widget.get_style_context()
context.save()
context.add_class('button')
context.add_class('text-button')
context.set_state(state)
xpad, ypad = self.get_padding()
@@ -56,18 +51,18 @@ class CellRendererButton(gtk.GenericCellRenderer):
w = cell_area.width - 2 * xpad
h = cell_area.height - 2 * ypad
gtk.render_background(context, cr, x, y, w, h)
gtk.render_frame(context, cr, x, y, w, h)
Gtk.render_background(context, cr, x, y, w, h)
Gtk.render_frame(context, cr, x, y, w, h)
padding = context.get_padding(state)
layout = widget.create_pango_layout(self.text)
layout.set_width((w - padding.left - padding.right) * pango.SCALE)
layout.set_ellipsize(pango.ELLIPSIZE_END)
layout.set_wrap(pango.WRAP_CHAR)
layout.set_width((w - padding.left - padding.right) * Pango.SCALE)
layout.set_ellipsize(Pango.EllipsizeMode.END)
layout.set_wrap(Pango.WrapMode.CHAR)
lw, lh = layout.get_size() # Can not use get_pixel_extents
lw /= pango.SCALE
lh /= pango.SCALE
lw /= Pango.SCALE
lh /= Pango.SCALE
if w < lw:
x = x + padding.left
else:
@@ -75,34 +70,21 @@ class CellRendererButton(gtk.GenericCellRenderer):
w - padding.left - padding.right - lw)
y = y + padding.top + 0.5 * (h - padding.top - padding.bottom - lh)
gtk.render_layout(context, cr, x, y, layout)
Gtk.render_layout(context, cr, x, y, layout)
context.restore()
def on_get_size(self, widget, cell_area=None):
def do_get_size(self, widget, cell_area=None):
if cell_area is None:
return (0, 0, 30, 18)
else:
return (cell_area.x, cell_area.y,
cell_area.width, cell_area.height)
do_get_size = on_get_size
def on_start_editing(self, event, widget, path, background_area,
cell_area, flags):
def do_start_editing(
self, event, widget, path, background_area, cell_area, flags):
if not self.visible or not self.sensitive:
return
if (event is None) or ((event.type == gtk.gdk.BUTTON_PRESS)
or (event.type == gtk.gdk.KEY_PRESS
and event.keyval == gtk.keysyms.space)):
self.clicking = True
widget.queue_draw()
while gtk.events_pending():
gtk.main_iteration()
self.emit("clicked", path)
self.emit("clicked", path)
def timeout(self, widget):
self.clicking = False
widget.queue_draw()
gobject.timeout_add(60, timeout, self, widget)
do_start_editing = on_start_editing
gobject.type_register(CellRendererButton)
GObject.type_register(CellRendererButton)
+11 -14
View File
@@ -1,24 +1,21 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import gtk
import gobject
from gi.repository import Gtk, GObject
class CellRendererClickablePixbuf(gtk.CellRendererPixbuf):
class CellRendererClickablePixbuf(Gtk.CellRendererPixbuf):
__gsignals__ = {
'clicked': (gobject.SignalFlags.RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_STRING, )),
'clicked': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING, )),
}
def __init__(self):
super(CellRendererClickablePixbuf, self).__init__()
self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
Gtk.CellRendererPixbuf.__init__(self)
self.set_property('mode', Gtk.CellRendererMode.ACTIVATABLE)
def do_activate(self, event, widget, path, background_area, cell_area,
flags):
if (event
and cell_area.x <= event.x <= cell_area.x + cell_area.width
and cell_area.y <= event.y <= cell_area.y + cell_area.height):
self.emit('clicked', path)
def do_activate(
self, event, widget, path, background_area, cell_area, flags):
self.emit('clicked', path)
gobject.type_register(CellRendererClickablePixbuf)
GObject.type_register(CellRendererClickablePixbuf)
+55 -21
View File
@@ -199,6 +199,8 @@ class CellRendererDate(gtk.CellRendererText):
def __init__(self):
self.__format = '%x'
self.__entry = None
self.__focus_out_id = 0
gtk.CellRendererText.__init__(self)
@@ -215,16 +217,34 @@ class CellRendererDate(gtk.CellRendererText):
def do_start_editing(
self, event, widget, path, background_area, cell_area, flags):
widget = add_operators(Date()) # TODO add_operators has option
widget.props.format = self.props.format
widget.props.value = self.props.text
if self.props.background_set:
widget.modify_base(gtk.STATE_NORMAL, self.props.background_gdk)
if self.props.foreground_set:
widget.modify_fg(gtk.STATE_NORMAL, self.props.foreground_gdk)
widget.grab_focus()
widget.show()
return widget
if not self.props.editable:
return
self.__entry = add_operators(Date()) # TODO add_operators has option
self.__entry.props.format = self.props.format
self.__entry.props.value = self.props.text
self.__entry.set_has_frame(False)
self.__entry.connect('editing-done', self.__editing_done)
self.__focus_out_id = self.__entry.connect(
'focus-out-event', self.__focus_out_event)
# XXX focus-out-event
self.__entry.show()
return self.__entry
def __editing_done(self, entry):
self.__entry = None
if self.__focus_out_id:
entry.disconnect(self.__focus_out_id)
canceled = entry.props.editing_canceled
self.stop_editing(canceled)
if canceled:
return
# TODO emit edited
def __focus_out_event(self, entry, event):
entry.props.editing_canceled = True
entry.editing_done()
entry.remove_widget()
return False
gobject.type_register(CellRendererDate)
@@ -339,7 +359,7 @@ class Time(gtk.ComboBoxEntry):
gobject.type_register(Time)
class CellRendererTime(gtk.CellRendererText):
class CellRendererTime(gtk.CellRendererCombo):
__gproperties__ = {
'format': (gobject.TYPE_STRING,
'%X',
@@ -350,6 +370,8 @@ class CellRendererTime(gtk.CellRendererText):
def __init__(self):
self.__format = '%X'
self.__combo = None
self.__focus_out_id = 0
gtk.CellRendererText.__init__(self)
@@ -368,16 +390,28 @@ class CellRendererTime(gtk.CellRendererText):
self, event, widget, path, background_area, cell_area, flags):
if not self.props.editable:
return
widget = add_operators(Time()) # TODO add_operators has option
widget.props.format = self.props.format
widget.props.value = self.props.text
if self.props.background_set:
widget.modify_base(gtk.STATE_NORMAL, self.props.background_gdk)
if self.props.foreground_set:
widget.modify_fg(gtk.STATE_NORMAL, self.props.foreground_gdk)
widget.grab_focus()
widget.show()
return widget
self.__combo = add_operators(Time()) # TODO add_operators has option
self.__combo.props.format = self.props.format
self.__combo.props.value = self.props.text
self.__combo.props.has_frame = False
self.__combo.connect('editing-done', self.__editing_done)
# TODO: connect to changed
self.__focus_out_id = self.__combo.connect(
'focus-out-event', self.__focus_out_event)
self.__combo.show()
return self.__combo
def __editing_done(self, combo):
self.__combo = None
canceled = combo.props.editing_canceled
self.stop_editing(canceled)
if canceled:
return
# TODO emit edited
def __focus_out_event(self, combo, event):
self.__editing_done(combo)
return False
gobject.type_register(CellRendererTime)