Skip to content

Commit

Permalink
Detect scrolling in treviews and optimize drawing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
godiard committed May 8, 2014
1 parent 142f614 commit c45818c
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 43 deletions.
58 changes: 58 additions & 0 deletions examples/scrollingdetector.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions src/sugar3/graphics/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ sugar_PYTHON = \
panel.py \
radiopalette.py \
radiotoolbutton.py \
scrollingdetector.py \
style.py \
toggletoolbutton.py \
toolbarbox.py \
Expand Down
114 changes: 71 additions & 43 deletions src/sugar3/graphics/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,24 @@ def __init__(self, tree_view):

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()

Expand Down Expand Up @@ -973,55 +991,65 @@ def _point_in_cell_renderer(self, tree_view, x=None, y=None):
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")

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))

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)
if not self._is_scrolling:

context = widget.get_style_context()
context.save()
context.add_class("sugar-icon-cell")

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))

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)
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:
stroke_color = self._stroke_color
fill_color = self._fill_color
prelit_fill_color = self._prelit_fill_color
prelit_stroke_color = self._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
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]
has_prelit_colors = None not in [prelit_fill_color,
prelit_stroke_color]

if flags & Gtk.CellRendererState.PRELIT and has_prelit_colors and \
pointer_inside:
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
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:
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:
Expand Down
71 changes: 71 additions & 0 deletions src/sugar3/graphics/scrollingdetector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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.
"""
for column in treeview.get_columns():
for cell_renderer in column.get_cells():
if isinstance(cell_renderer, CellRendererIcon):
cell_renderer.connect_to_scroller(self)

0 comments on commit c45818c

Please sign in to comment.