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:
1 parent
74eb7d4e94
commit
9180bed90c
5 files changed
+140
-135
No files matched your search
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user