From 5626a6c182ba4a8fca3c750e38ea144264a813f2 Mon Sep 17 00:00:00 2001 From: Gonzalo Odiard Date: Wed, 7 May 2014 09:20:14 -0300 Subject: [PATCH 1/3] Detect scrolling in treviews and optimize drawing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Gonzalo Odiard --- examples/scrollingdetector.py | 58 ++++++++++++ src/sugar3/graphics/Makefile.am | 1 + src/sugar3/graphics/icon.py | 114 ++++++++++++++--------- src/sugar3/graphics/scrollingdetector.py | 72 ++++++++++++++ 4 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 examples/scrollingdetector.py create mode 100644 src/sugar3/graphics/scrollingdetector.py diff --git a/examples/scrollingdetector.py b/examples/scrollingdetector.py new file mode 100644 index 00000000..f6845223 --- /dev/null +++ b/examples/scrollingdetector.py @@ -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) diff --git a/src/sugar3/graphics/Makefile.am b/src/sugar3/graphics/Makefile.am index 344cc82d..0621750e 100644 --- a/src/sugar3/graphics/Makefile.am +++ b/src/sugar3/graphics/Makefile.am @@ -18,6 +18,7 @@ sugar_PYTHON = \ panel.py \ radiopalette.py \ radiotoolbutton.py \ + scrollingdetector.py \ style.py \ toggletoolbutton.py \ toolbarbox.py \ diff --git a/src/sugar3/graphics/icon.py b/src/sugar3/graphics/icon.py index fc798ee7..329b4b15 100644 --- a/src/sugar3/graphics/icon.py +++ b/src/sugar3/graphics/icon.py @@ -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") - - 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: diff --git a/src/sugar3/graphics/scrollingdetector.py b/src/sugar3/graphics/scrollingdetector.py new file mode 100644 index 00000000..b44d35f8 --- /dev/null +++ b/src/sugar3/graphics/scrollingdetector.py @@ -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) From 49365e132c3a35b51c1700b3be5d2364d0200d98 Mon Sep 17 00:00:00 2001 From: Gonzalo Odiard Date: Fri, 28 Nov 2014 08:45:22 -0300 Subject: [PATCH 2/3] New invoker, TreeViewInvoker This new invoker is able to handle all the palettes on a treeview, removing the need of one invoker per cell renderer (CellRendererInvoker). This simplifies the code and makes it more efficient. Also removes the logic from the CellRendererIcon, which should only care about drawing itself. [1] Is important to note than in Gtk3 a CellRenderer is not a GtkWidget then can't know when the mouse is over it or have the usual events used by other invokers, making the implementation CellRendererInvoker very complicate. This commit also removes the invoker of CellRendererIcon. The ScrollingDetector logic is simplified too, because now there are only one invoker instead of one by renderer and the example code updated. [1] https://developer.gnome.org/gtk3/stable/GtkCellRenderer.html --- examples/cellrenderericon.py | 2 +- examples/scrollingdetector.py | 17 ++- src/sugar3/graphics/icon.py | 56 +------- src/sugar3/graphics/palette.py | 8 +- src/sugar3/graphics/palettewindow.py | 165 +++++++++++++++++++++-- src/sugar3/graphics/scrollingdetector.py | 17 +-- 6 files changed, 179 insertions(+), 86 deletions(-) diff --git a/examples/cellrenderericon.py b/examples/cellrenderericon.py index 1f875520..e2802045 100644 --- a/examples/cellrenderericon.py +++ b/examples/cellrenderericon.py @@ -21,7 +21,7 @@ treeview.show() col = Gtk.TreeViewColumn() treeview.append_column(col) -cell_icon = CellRendererIcon(treeview) +cell_icon = CellRendererIcon() cell_icon.props.width = style.GRID_CELL_SIZE cell_icon.props.height = style.GRID_CELL_SIZE cell_icon.props.size = style.SMALL_ICON_SIZE diff --git a/examples/scrollingdetector.py b/examples/scrollingdetector.py index f6845223..8f7243a0 100644 --- a/examples/scrollingdetector.py +++ b/examples/scrollingdetector.py @@ -7,9 +7,18 @@ from sugar3.graphics import style from sugar3.graphics.icon import CellRendererIcon from sugar3.graphics.xocolor import XoColor from sugar3.graphics.scrollingdetector import ScrollingDetector +from sugar3.graphics.palettewindow import TreeViewInvoker import common +def _scroll_start_cb(event, treeview, invoker): + print "Scroll starts" + invoker.detach() + +def _scroll_end_cb(event, treeview, invoker): + print "Scroll ends" + invoker.attach_treeview(treeview) + test = common.Test() test.show() @@ -38,7 +47,7 @@ col = Gtk.TreeViewColumn() treeview.append_column(col) xo_color = XoColor('#FF0000,#00FF00') -cell_icon = CellRendererIcon(treeview) +cell_icon = CellRendererIcon() cell_icon.props.width = style.GRID_CELL_SIZE cell_icon.props.height = style.GRID_CELL_SIZE cell_icon.props.size = style.STANDARD_ICON_SIZE @@ -50,8 +59,12 @@ cell_text = Gtk.CellRendererText() col.pack_start(cell_text, expand=True) col.add_attribute(cell_text, 'text', 0) +invoker = TreeViewInvoker() +invoker.attach_treeview(treeview) + detector = ScrollingDetector(scrolled) -detector.connect_treeview(treeview) +detector.connect('scroll-start', _scroll_start_cb, treeview, invoker) +detector.connect('scroll-end', _scroll_end_cb, treeview, invoker) if __name__ == '__main__': time_ini = time.time() diff --git a/src/sugar3/graphics/icon.py b/src/sugar3/graphics/icon.py index 329b4b15..69e481a6 100644 --- a/src/sugar3/graphics/icon.py +++ b/src/sugar3/graphics/icon.py @@ -825,9 +825,8 @@ class CellRendererIcon(Gtk.CellRenderer): 'clicked': (GObject.SignalFlags.RUN_FIRST, None, [object]), } - def __init__(self, tree_view): - from sugar3.graphics.palette import CellRendererInvoker - + def __init__(self, treeview=None): + # treeview is not used anymore, is here just to not break the API self._buffer = _IconBuffer() self._buffer.cache = True self._xo_color = None @@ -836,19 +835,10 @@ class CellRendererIcon(Gtk.CellRenderer): self._prelit_fill_color = None self._prelit_stroke_color = None self._active_state = False - self._palette_invoker = CellRendererInvoker() self._cached_offsets = None Gtk.CellRenderer.__init__(self) - tree_view.connect('button-press-event', - self.__button_press_event_cb) - tree_view.connect('button-release-event', - self.__button_release_event_cb) - - self._palette_invoker.attach_cell_renderer(tree_view, self) - - self._tree_view = tree_view self._is_scrolling = False def connect_to_scroller(self, scrolled): @@ -856,34 +846,17 @@ class CellRendererIcon(Gtk.CellRenderer): 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() - - def __button_press_event_cb(self, widget, event): - if self._point_in_cell_renderer(widget, event.x, event.y): - self._active_state = True - - def __button_release_event_cb(self, widget, event): - self._active_state = False - def create_palette(self): return None - def get_palette_invoker(self): - return self._palette_invoker - - palette_invoker = GObject.property(type=object, getter=get_palette_invoker) - def set_file_name(self, value): if self._buffer.file_name != value: self._buffer.file_name = value @@ -982,31 +955,6 @@ class CellRendererIcon(Gtk.CellRenderer): flags): pass - def _point_in_cell_renderer(self, tree_view, x=None, y=None): - """Check if the point with coordinates x, y is inside this icon. - - If the x, y coordinates are not given, they are taken from the - pointer current position. - - """ - if x is None and y is None: - x, y = tree_view.get_pointer() - x, y = tree_view.convert_widget_to_bin_window_coords(x, y) - pos = tree_view.get_path_at_pos(int(x), int(y)) - if pos is None: - return False - - path_, column, x, y_ = pos - - for cell_renderer in column.get_cells(): - if cell_renderer == self: - cell_x, cell_width = column.cell_get_position(cell_renderer) - if x > cell_x and x < (cell_x + cell_width): - return True - return False - - return False - def do_render(self, cr, widget, background_area, cell_area, flags): if not self._is_scrolling: diff --git a/src/sugar3/graphics/palette.py b/src/sugar3/graphics/palette.py index e50ac392..c7092c23 100644 --- a/src/sugar3/graphics/palette.py +++ b/src/sugar3/graphics/palette.py @@ -38,13 +38,14 @@ from sugar3.graphics.palettewindow import PaletteWindow, \ from sugar3.graphics.palettemenu import PaletteMenuItem from sugar3.graphics.palettewindow import MouseSpeedDetector, Invoker, \ - WidgetInvoker, CursorInvoker, ToolInvoker, CellRendererInvoker + WidgetInvoker, CursorInvoker, ToolInvoker, TreeViewInvoker + assert MouseSpeedDetector assert Invoker assert WidgetInvoker assert CursorInvoker assert ToolInvoker -assert CellRendererInvoker +assert TreeViewInvoker class _HeaderItem(Gtk.MenuItem): @@ -418,7 +419,8 @@ class Palette(PaletteWindow): if self._palette_state == self.PRIMARY: self._secondary_box.show() - self._full_request = self._widget.size_request() + if self._widget is not None: + self._full_request = self._widget.size_request() if self._palette_state == self.PRIMARY: self._secondary_box.hide() diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py index 1b43b65c..a19f2f3a 100644 --- a/src/sugar3/graphics/palettewindow.py +++ b/src/sugar3/graphics/palettewindow.py @@ -35,6 +35,7 @@ from gi.repository import SugarGestures from sugar3.graphics import palettegroup from sugar3.graphics import animator from sugar3.graphics import style +from sugar3.graphics.icon import CellRendererIcon def _calculate_gap(a, b): @@ -223,12 +224,9 @@ class _PaletteMenuWidget(Gtk.Menu): x = event.x_root y = event.y_root - if type(self._invoker) is CellRendererInvoker: - in_invoker = self._invoker.point_in_cell_renderer(x, y) - else: - rect = self._invoker.get_rect() - in_invoker = x >= rect.x and x < (rect.x + rect.width) \ - and y >= rect.y and y < (rect.y + rect.height) + rect = self._invoker.get_rect() + in_invoker = x >= rect.x and x < (rect.x + rect.width) \ + and y >= rect.y and y < (rect.y + rect.height) if in_invoker != self._mouse_in_invoker: self._mouse_in_invoker = in_invoker @@ -238,12 +236,9 @@ class _PaletteMenuWidget(Gtk.Menu): x = event.x_root y = event.y_root - if type(self._invoker) is CellRendererInvoker: - in_invoker = self._invoker.point_in_cell_renderer(x, y) - else: - rect = self._invoker.get_rect() - in_invoker = x >= rect.x and x < (rect.x + rect.width) \ - and y >= rect.y and y < (rect.y + rect.height) + rect = self._invoker.get_rect() + in_invoker = x >= rect.x and x < (rect.x + rect.width) \ + and y >= rect.y and y < (rect.y + rect.height) if in_invoker: return True @@ -399,6 +394,7 @@ class _PaletteWindowWidget(Gtk.Window): def popup(self, invoker): if self.get_visible(): + logging.error('PaletteWindowWidget popup get_visible True') return self.connect('enter-notify-event', self.__enter_notify_event_cb) self.connect('leave-notify-event', self.__leave_notify_event_cb) @@ -618,6 +614,9 @@ class PaletteWindow(GObject.GObject): logging.error('Cannot update the palette position.') return + if self._widget is None: + return + req = self._widget.size_request() # on Gtk 3.10, menu at the bottom of the screen are resized # to not fall out, and report a wrong size. @@ -642,6 +641,8 @@ class PaletteWindow(GObject.GObject): return self._widget.size_request() def popup(self, immediate=False): + if self._widget is None: + return if self._invoker is not None: full_size_request = self.get_full_size_request() self._alignment = self._invoker.get_alignment(full_size_request) @@ -1529,3 +1530,143 @@ class CellRendererInvoker(Invoker): def get_default_position(self): return self.AT_CURSOR + + +class TreeViewInvoker(Invoker): + def __init__(self): + Invoker.__init__(self) + + self._tree_view = None + self._motion_hid = None + self._leave_hid = None + self._release_hid = None + self._long_pressed_hid = None + self._position_hint = self.AT_CURSOR + + self._long_pressed_controller = SugarGestures.LongPressController() + + self._mouse_detector = MouseSpeedDetector(200, 5) + + self._tree_view = None + self._path = None + self._column = None + + self.palette = None + + def attach_treeview(self, tree_view): + self._tree_view = tree_view + + self._motion_hid = tree_view.connect('motion-notify-event', + self.__motion_notify_event_cb) + self._enter_hid = tree_view.connect('enter-notify-event', + self.__enter_notify_event_cb) + self._leave_hid = tree_view.connect('leave-notify-event', + self.__leave_notify_event_cb) + self._release_hid = tree_view.connect('button-release-event', + self.__button_release_event_cb) + self._long_pressed_hid = self._long_pressed_controller.connect( + 'pressed', self.__long_pressed_event_cb, tree_view) + self._long_pressed_controller.attach( + tree_view, + SugarGestures.EventControllerFlags.NONE) + + self._mouse_detector.connect('motion-slow', self.__mouse_slow_cb) + self._mouse_detector.parent = tree_view + Invoker.attach(self, tree_view) + + def detach(self): + Invoker.detach(self) + self._tree_view.disconnect(self._motion_hid) + self._tree_view.disconnect(self._enter_hid) + self._tree_view.disconnect(self._leave_hid) + self._tree_view.disconnect(self._release_hid) + self._long_pressed_controller.detach(self._tree_view) + self._long_pressed_controller.disconnect(self._long_pressed_hid) + self._mouse_detector.disconnect_by_func(self.__mouse_slow_cb) + + def get_rect(self): + return self._tree_view.get_background_area(self._path, self._column) + + def get_toplevel(self): + return self._tree_view.get_toplevel() + + def __motion_notify_event_cb(self, widget, event): + try: + path, column, x_, y_ = self._tree_view.get_path_at_pos( + int(event.x), int(event.y)) + if path != self._path or column != self._column: + self._redraw_cell(self._path, self._column) + self._redraw_cell(path, column) + + self._path = path + self._column = column + + if self.palette is not None: + self.palette.popdown(immediate=True) + self.palette = None + + self._mouse_detector.start() + except TypeError: + # tree_view.get_path_at_pos() fail if x,y poition is over + # a empty area + pass + + def _redraw_cell(self, path, column): + area = self._tree_view.get_background_area(path, column) + x, y = \ + self._tree_view.convert_bin_window_to_widget_coords(area.x, area.y) + self._tree_view.queue_draw_area(x, y, area.width, area.height) + + def __enter_notify_event_cb(self, widget, event): + self._mouse_detector.start() + + def __leave_notify_event_cb(self, widget, event): + self._mouse_detector.stop() + + def __button_release_event_cb(self, widget, event): + x, y = int(event.x), int(event.y) + path, column, cell_x, cell_y = self._tree_view.get_path_at_pos(x, y) + self._path = path + self._column = column + if event.button == 1: + # left mouse button + if self.palette is not None: + self.palette.popdown(immediate=True) + # NOTE: we don't use columns with more than one cell + cellrenderer = column.get_cells()[0] + if cellrenderer is not None and \ + isinstance(cellrenderer, CellRendererIcon): + cellrenderer.emit('clicked', path) + # So the treeview receives it and knows a drag isn't going on + return False + if event.button == 3: + # right mouse button + self._mouse_detector.stop() + self._change_palette() + self.notify_right_click() + return True + else: + return False + + def __long_pressed_event_cb(self, controller, x, y, widget): + path, column, x_, y_ = self._tree_view.get_path_at_pos(x, y) + self._path = path + self._column = column + self._change_palette() + self.notify_right_click() + + def __mouse_slow_cb(self, widget): + self._mouse_detector.stop() + self._change_palette() + self.emit('mouse-enter') + + def _change_palette(self): + if hasattr(self._tree_view, 'create_palette'): + self.palette = self._tree_view.create_palette( + self._path, self._column) + else: + self.palette = None + + def notify_popdown(self): + Invoker.notify_popdown(self) + self.palette = None diff --git a/src/sugar3/graphics/scrollingdetector.py b/src/sugar3/graphics/scrollingdetector.py index b44d35f8..e56dda75 100644 --- a/src/sugar3/graphics/scrollingdetector.py +++ b/src/sugar3/graphics/scrollingdetector.py @@ -18,11 +18,12 @@ from gi.repository import GObject from gi.repository import GLib -from sugar3.graphics.icon import CellRendererIcon - class ScrollingDetector(GObject.GObject): """ + ScollingDetector emit signals when a ScrolledWindow starts and + finish scrolling. Other widets can use that information to + avoid do performance expensive operations. """ scroll_start_signal = GObject.Signal('scroll-start') @@ -58,15 +59,3 @@ class ScrollingDetector(GObject.GObject): 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) From 7a9510a187b428b2b0944803a96839e69c7c54b8 Mon Sep 17 00:00:00 2001 From: Gonzalo Odiard Date: Mon, 17 Nov 2014 06:19:15 -0300 Subject: [PATCH 3/3] Remove CellRendererInvoker This invoker is not used anymore, now is replaced by TreeViewInvoker. --- src/sugar3/graphics/icon.py | 3 + src/sugar3/graphics/palettewindow.py | 159 --------------------------- 2 files changed, 3 insertions(+), 159 deletions(-) diff --git a/src/sugar3/graphics/icon.py b/src/sugar3/graphics/icon.py index 69e481a6..43b78a75 100644 --- a/src/sugar3/graphics/icon.py +++ b/src/sugar3/graphics/icon.py @@ -827,6 +827,9 @@ class CellRendererIcon(Gtk.CellRenderer): def __init__(self, treeview=None): # treeview is not used anymore, is here just to not break the API + if treeview is not None: + logging.warning('CellRendererIcon: treeview parameter in ' + 'constructor is deprecated') self._buffer = _IconBuffer() self._buffer.cache = True self._xo_color = None diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py index a19f2f3a..ed96070c 100644 --- a/src/sugar3/graphics/palettewindow.py +++ b/src/sugar3/graphics/palettewindow.py @@ -1373,165 +1373,6 @@ class ToolInvoker(WidgetInvoker): self._widget.emit('clicked') -class CellRendererInvoker(Invoker): - - def __init__(self): - Invoker.__init__(self) - - self._position_hint = self.AT_CURSOR - self._tree_view = None - self._cell_renderer = None - self._motion_hid = None - self._leave_hid = None - self._release_hid = None - self._long_pressed_hid = None - self.path = None - - self._long_pressed_controller = SugarGestures.LongPressController() - - def attach_cell_renderer(self, tree_view, cell_renderer): - self._tree_view = tree_view - self._cell_renderer = cell_renderer - - self._motion_hid = tree_view.connect('motion-notify-event', - self.__motion_notify_event_cb) - self._leave_hid = tree_view.connect('leave-notify-event', - self.__leave_notify_event_cb) - self._release_hid = tree_view.connect('button-release-event', - self.__button_release_event_cb) - self._long_pressed_hid = self._long_pressed_controller.connect( - 'pressed', self.__long_pressed_event_cb, tree_view) - self._long_pressed_controller.attach( - tree_view, - SugarGestures.EventControllerFlags.NONE) - Invoker.attach(self, cell_renderer) - - def detach(self): - Invoker.detach(self) - self._tree_view.disconnect(self._motion_hid) - self._tree_view.disconnect(self._leave_hid) - self._tree_view.disconnect(self._release_hid) - self._long_pressed_controller.detach(self._tree_view) - self._long_pressed_controller.disconnect(self._long_pressed_hid) - - def get_rect(self): - allocation = self._tree_view.get_allocation() - window = self._tree_view.get_window() - if window is not None: - res, x, y = window.get_origin() - else: - logging.warning( - "Trying to position palette with invoker that's not realized.") - x = 0 - y = 0 - - rect = Gdk.Rectangle() - rect.x = x + allocation.x - rect.y = y + allocation.y - - rect.width = allocation.width - rect.height = allocation.height - - return rect - - def __motion_notify_event_cb(self, widget, event): - if event.window != widget.get_bin_window(): - return - if self.point_in_cell_renderer(event.x, event.y): - - tree_view = self._tree_view - path, column_, x_, y_ = tree_view.get_path_at_pos(int(event.x), - int(event.y)) - if path != self.path: - if self.path is not None: - self._redraw_path(self.path) - if path is not None: - self._redraw_path(path) - if self.palette is not None: - self.palette.popdown(immediate=True) - self.palette = None - self.path = path - - if event.get_source_device().get_source() == \ - Gdk.InputSource.TOUCHSCREEN: - return False - self.notify_mouse_enter() - else: - if self.path is not None: - self._redraw_path(self.path) - self.path = None - - if event.get_source_device().get_source() == \ - Gdk.InputSource.TOUCHSCREEN: - return False - self.notify_mouse_leave() - - def _redraw_path(self, path): - column = None - for column in self._tree_view.get_columns(): - if self._cell_renderer in column.get_cells(): - break - assert column is not None - area = self._tree_view.get_background_area(path, column) - x, y = \ - self._tree_view.convert_bin_window_to_widget_coords(area.x, area.y) - self._tree_view.queue_draw_area(x, y, area.width, area.height) - - def __leave_notify_event_cb(self, widget, event): - if event.mode == Gdk.CrossingMode.NORMAL: - self.notify_mouse_leave() - return False - - def __button_release_event_cb(self, widget, event): - if event.button == 1 and self.point_in_cell_renderer(event.x, - event.y): - tree_view = self._tree_view - path, column_, x_, y_ = tree_view.get_path_at_pos(int(event.x), - int(event.y)) - self._cell_renderer.emit('clicked', path) - # So the treeview receives it and knows a drag isn't going on - return False - if event.button == 3 and self.point_in_cell_renderer(event.x, - event.y): - self.notify_right_click() - return True - else: - return False - - def __long_pressed_event_cb(self, controller, x, y, widget): - if self.point_in_cell_renderer(x, y): - self.notify_right_click() - - def point_in_cell_renderer(self, event_x, event_y): - pos = self._tree_view.get_path_at_pos(int(event_x), int(event_y)) - if pos is None: - return False - - path_, column, x, y_ = pos - - for cell_renderer in column.get_cells(): - if cell_renderer == self._cell_renderer: - cell_x, cell_width = column.cell_get_position(cell_renderer) - if x > cell_x and x < (cell_x + cell_width): - return True - return False - - return False - - def get_toplevel(self): - return self._tree_view.get_toplevel() - - def notify_popup(self): - Invoker.notify_popup(self) - - def notify_popdown(self): - Invoker.notify_popdown(self) - self.palette = None - - def get_default_position(self): - return self.AT_CURSOR - - class TreeViewInvoker(Invoker): def __init__(self): Invoker.__init__(self)