Detect scrolling in treviews and optimize drawing

Detecting if the treeview is scrolling we can improve the performance of
the CellRendererIcon by avoiding unneeded calculations.

The example scrollingdetector.py shows how this can be used.  The
changes are backward compatible.

Signed-off-by: Manuel Quiñones <manuel.por.aca@gmail.com>
Signed-off-by: Gonzalo Odiard <godiard@sugarlabs.org>
This commit is contained in:
Gonzalo Odiard 2014-05-07 09:20:14 -03:00
parent 862176de38
commit 5626a6c182
4 changed files with 201 additions and 42 deletions

View File

@ -0,0 +1,58 @@
import os
import time
from gi.repository import Gtk
from sugar3.graphics import style
from sugar3.graphics.icon import CellRendererIcon
from sugar3.graphics.xocolor import XoColor
from sugar3.graphics.scrollingdetector import ScrollingDetector
import common
test = common.Test()
test.show()
model = Gtk.ListStore(str)
data_dir = os.getenv('GTK_DATA_PREFIX', '/usr/')
iconlist = os.listdir(os.path.join(data_dir,
'share/icons/sugar/scalable/actions/'))
print "Displaying %s icons" % len(iconlist)
for icon in iconlist:
icon = os.path.basename(icon)
icon = icon[:icon.find('.')]
model.append([icon])
scrolled = Gtk.ScrolledWindow()
scrolled.set_size_request(800, 800)
treeview = Gtk.TreeView()
treeview.set_model(model)
scrolled.add(treeview)
test.pack_start(scrolled, True, True, 0)
test.show_all()
col = Gtk.TreeViewColumn()
treeview.append_column(col)
xo_color = XoColor('#FF0000,#00FF00')
cell_icon = CellRendererIcon(treeview)
cell_icon.props.width = style.GRID_CELL_SIZE
cell_icon.props.height = style.GRID_CELL_SIZE
cell_icon.props.size = style.STANDARD_ICON_SIZE
cell_icon.props.xo_color = xo_color
col.pack_start(cell_icon, expand=False)
col.add_attribute(cell_icon, 'icon-name', 0)
cell_text = Gtk.CellRendererText()
col.pack_start(cell_text, expand=True)
col.add_attribute(cell_text, 'text', 0)
detector = ScrollingDetector(scrolled)
detector.connect_treeview(treeview)
if __name__ == '__main__':
time_ini = time.time()
common.main(test)

View File

@ -18,6 +18,7 @@ sugar_PYTHON = \
panel.py \
radiopalette.py \
radiotoolbutton.py \
scrollingdetector.py \
style.py \
toggletoolbutton.py \
toolbarbox.py \

View File

@ -848,6 +848,24 @@ class CellRendererIcon(Gtk.CellRenderer):
self._palette_invoker.attach_cell_renderer(tree_view, self)
self._tree_view = tree_view
self._is_scrolling = False
def connect_to_scroller(self, scrolled):
scrolled.connect('scroll-start', self._scroll_start_cb)
scrolled.connect('scroll-end', self._scroll_end_cb)
def _scroll_start_cb(self, event):
self._palette_invoker.detach()
self._is_scrolling = True
def _scroll_end_cb(self, event):
self._palette_invoker.attach_cell_renderer(self._tree_view, self)
self._is_scrolling = False
def is_scrolling(self):
return self._is_scrolling
def __del__(self):
self._palette_invoker.detach()
@ -990,55 +1008,65 @@ class CellRendererIcon(Gtk.CellRenderer):
return False
def do_render(self, cr, widget, background_area, cell_area, flags):
context = widget.get_style_context()
context.save()
context.add_class("sugar-icon-cell")
if not self._is_scrolling:
def is_pointer_inside():
# widget is the treeview
x, y = widget.get_pointer()
x, y = widget.convert_widget_to_bin_window_coords(x, y)
return ((cell_area.x <= x <= cell_area.x + cell_area.width)
and (cell_area.y <= y <= cell_area.y + cell_area.height))
context = widget.get_style_context()
context.save()
context.add_class("sugar-icon-cell")
pointer_inside = is_pointer_inside()
def is_pointer_inside():
# widget is the treeview
x, y = widget.get_pointer()
x, y = widget.convert_widget_to_bin_window_coords(x, y)
return ((cell_area.x <= x <= cell_area.x + cell_area.width)
and
(cell_area.y <= y <= cell_area.y + cell_area.height))
# The context will have prelight state if the mouse pointer is
# in the entire row, but we want that state if the pointer is
# in this cell only:
if flags & Gtk.CellRendererState.PRELIT:
if pointer_inside:
if self._active_state:
context.set_state(Gtk.StateFlags.ACTIVE)
pointer_inside = is_pointer_inside()
# The context will have prelight state if the mouse pointer is
# in the entire row, but we want that state if the pointer is
# in this cell only:
if flags & Gtk.CellRendererState.PRELIT:
if pointer_inside:
if self._active_state:
context.set_state(Gtk.StateFlags.ACTIVE)
else:
context.set_state(Gtk.StateFlags.NORMAL)
Gtk.render_background(
context, cr, background_area.x, background_area.y,
background_area.width, background_area.height)
if self._xo_color is not None:
stroke_color = self._xo_color.get_stroke_color()
fill_color = self._xo_color.get_fill_color()
prelit_fill_color = None
prelit_stroke_color = None
else:
context.set_state(Gtk.StateFlags.NORMAL)
stroke_color = self._stroke_color
fill_color = self._fill_color
prelit_fill_color = self._prelit_fill_color
prelit_stroke_color = self._prelit_stroke_color
Gtk.render_background(
context, cr, background_area.x, background_area.y,
background_area.width, background_area.height)
has_prelit_colors = None not in [prelit_fill_color,
prelit_stroke_color]
if self._xo_color is not None:
stroke_color = self._xo_color.get_stroke_color()
fill_color = self._xo_color.get_fill_color()
prelit_fill_color = None
prelit_stroke_color = None
if flags & Gtk.CellRendererState.PRELIT and has_prelit_colors and \
pointer_inside:
self._buffer.fill_color = prelit_fill_color
self._buffer.stroke_color = prelit_stroke_color
else:
self._buffer.fill_color = fill_color
self._buffer.stroke_color = stroke_color
else:
stroke_color = self._stroke_color
fill_color = self._fill_color
prelit_fill_color = self._prelit_fill_color
prelit_stroke_color = self._prelit_stroke_color
has_prelit_colors = None not in [prelit_fill_color,
prelit_stroke_color]
if flags & Gtk.CellRendererState.PRELIT and has_prelit_colors and \
pointer_inside:
self._buffer.fill_color = prelit_fill_color
self._buffer.stroke_color = prelit_stroke_color
else:
self._buffer.fill_color = fill_color
self._buffer.stroke_color = stroke_color
if self._xo_color is not None:
self._buffer.fill_color = self._xo_color.get_fill_color()
self._buffer.stroke_color = self._xo_color.get_stroke_color()
else:
self._buffer.fill_color = self._fill_color
self._buffer.stroke_color = self._stroke_color
surface = self._buffer.get_surface()
if surface is None:

View File

@ -0,0 +1,72 @@
# Copyright (C) 2014, Sugarlabs
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
from gi.repository import GObject
from gi.repository import GLib
from sugar3.graphics.icon import CellRendererIcon
class ScrollingDetector(GObject.GObject):
"""
"""
scroll_start_signal = GObject.Signal('scroll-start')
scroll_end_signal = GObject.Signal('scroll-end')
def __init__(self, scrolled_window, timeout=100):
self._scrolled_window = scrolled_window
self._timeout = timeout
self.is_scrolling = False
self._prev_value = 0
self.connect_scrolled_window()
GObject.GObject.__init__(self)
def connect_scrolled_window(self):
adj = self._scrolled_window.get_vadjustment()
adj.connect('value-changed', self._value_changed_cb)
def _check_scroll_cb(self, adj):
if (adj.props.value == self._prev_value):
self.is_scrolling = False
self.scroll_end_signal.emit()
return False
self._prev_value = adj.props.value
return True
def _value_changed_cb(self, adj):
if (self.is_scrolling):
return
self.is_scrolling = True
self.scroll_start_signal.emit()
self._prev_value = adj.props.value
GLib.timeout_add(self._timeout, self._check_scroll_cb, adj)
def connect_treeview(self, treeview):
"""
treeview -- Gtk.TreeView: a TreeView with CellRendererIcons already
added.
This method need be executed after all the columns and renderers
were added to the treeview.
"""
for column in treeview.get_columns():
for cell_renderer in column.get_cells():
if isinstance(cell_renderer, CellRendererIcon):
cell_renderer.connect_to_scroller(self)