From 37c2a39928c4c7d4aca3676302e1897d811fda42 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 7 Aug 2012 07:48:52 +0200 Subject: [PATCH 1/3] textinput: clear cache and force refresh on gl context reloading. closes #494 --- kivy/graphics/context.pxd | 1 + kivy/graphics/context.pyx | 32 ++++++++++++++++++++++++++------ kivy/uix/textinput.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/kivy/graphics/context.pxd b/kivy/graphics/context.pxd index cbc8a2e4f..805e8f5e0 100644 --- a/kivy/graphics/context.pxd +++ b/kivy/graphics/context.pxd @@ -6,6 +6,7 @@ from kivy.graphics.fbo cimport Fbo cdef class Context: cdef list observers + cdef list observers_before cdef list l_texture cdef list l_canvas cdef list l_vbo diff --git a/kivy/graphics/context.pyx b/kivy/graphics/context.pyx index 1a841f413..3a82a8c33 100644 --- a/kivy/graphics/context.pyx +++ b/kivy/graphics/context.pyx @@ -33,6 +33,7 @@ cdef class Context: def __init__(self): self.observers = [] + self.observers_before = [] self.l_texture = [] self.l_canvas = [] self.l_vbo = [] @@ -96,23 +97,34 @@ cdef class Context: self.lr_fbo.append((fbo.buffer_id, fbo.depthbuffer_id)) self.trigger_gl_dealloc() - def add_reload_observer(self, callback): + def add_reload_observer(self, callback, before=False): '''Add a callback to be called after the whole graphics context have been reloaded. This is where you can reupload your custom data in GPU. :Parameters: `callback`: func(context) -> return None The first parameter will be the context itself - ''' - self.observers.append(WeakMethod(callback)) + `before`: boolean, default to False + If True, the callback will be executed before the whole + reloading processus. Use it if you want to clear your cache for + example. - def remove_reload_observer(self, callback): + .. versionchanged:: 1.4.0 + `before` parameter added. + ''' + if before: + self.observers_before.append(WeakMethod(callback)) + else: + self.observers.append(WeakMethod(callback)) + + def remove_reload_observer(self, callback, before=False): '''Remove a callback from the observer list, previously added by :func:`add_reload_observer`. ''' - for cb in self.observers[:]: + lst = self.observers_before if before else self.observers + for cb in lst[:]: if cb.is_dead() or cb() is callback: - self.observers.remove(cb) + lst.remove(cb) continue def reload(self): @@ -122,6 +134,14 @@ cdef class Context: cdef Shader shader cdef Canvas canvas + # call reload observers that want to do something after a whole gpu + # reloading. + for callback in self.observers_before[:]: + if callback.is_dead(): + self.observers_before.remove(callback) + continue + callback()(self) + image_objects = Cache._objects['kv.image'] Cache.remove('kv.image') Cache.remove('kv.shader') diff --git a/kivy/uix/textinput.py b/kivy/uix/textinput.py index 541349cf4..d1ce4dcdc 100644 --- a/kivy/uix/textinput.py +++ b/kivy/uix/textinput.py @@ -105,6 +105,8 @@ __all__ = ('TextInput', ) import sys +from os import environ +from weakref import ref from functools import partial from kivy.logger import Logger from kivy.utils import boundary @@ -126,6 +128,26 @@ FL_IS_NEWLINE = 0x01 # late binding Clipboard = None +# for reloading, we need to keep a list of textinput to retrigger the rendering +_textinput_list = [] + + +# register an observer to clear the textinput cache when OpenGL will reload +if 'KIVY_DOC' not in environ: + + def _textinput_clear_cache(*l): + Cache.remove('textinput.label') + for wr in _textinput_list[:]: + textinput = wr() + if textinput is None: + _textinput_list.remove(wr) + else: + textinput._trigger_refresh_text() + + from kivy.graphics.context import get_context + get_context().add_reload_observer(_textinput_clear_cache, True) + + class TextInputCutCopyPaste(Bubble): # Internal class used for showing the little bubble popup when @@ -224,6 +246,10 @@ class TextInput(Widget): self._trigger_refresh_line_options() self._trigger_refresh_text() + # when the gl context is reloaded, trigger the text rendering again. + _textinput_list.append(ref(self, TextInput._reload_remove_observer)) + + def on_text_validate(self): pass @@ -700,6 +726,13 @@ class TextInput(Widget): # # Private # + + @staticmethod + def _reload_remove_observer(wr): + # called when the textinput is deleted + if wr in _textinput_list: + _textinput_list.remove(wr) + def on_focus(self, instance, value, *largs): win = self._win if not win: @@ -1550,6 +1583,7 @@ class TextInput(Widget): 10. ''' + if __name__ == '__main__': from kivy.app import App from kivy.uix.boxlayout import BoxLayout From ed4218047cd0fe195142bfef86068fe6aea17b96 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 7 Aug 2012 07:50:46 +0200 Subject: [PATCH 2/3] text: force shorten to return unicode string --- kivy/core/text/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kivy/core/text/__init__.py b/kivy/core/text/__init__.py index e45493f04..fc3155c67 100644 --- a/kivy/core/text/__init__.py +++ b/kivy/core/text/__init__.py @@ -208,11 +208,11 @@ class LabelBase(object): if segment - margin > 5: segment -= margin - return '{0}...{1}'.format(text[:segment].strip(), + return u'{0}...{1}'.format(text[:segment].strip(), text[-segment:].strip()) else: segment = max_letters - 3 # length of '...' - return '{0}...'.format(text[:segment].strip()) + return u'{0}...'.format(text[:segment].strip()) def render(self, real=False): '''Return a tuple(width, height) to create the image From 435443c61d42a531bd770b6776c4c9597340483d Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 7 Aug 2012 08:03:10 +0200 Subject: [PATCH 3/3] scrollview: avoid all the touch outside the view. Because the drawing is outside the view is removed, it make no sense to dispatch touch. We was actually having an issue cause of that with the filebrowser: touching outside the view on a possible element was catched and used by the filebrowser. --- kivy/uix/scrollview.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/kivy/uix/scrollview.py b/kivy/uix/scrollview.py index 8a0e311e3..3f9d7502d 100644 --- a/kivy/uix/scrollview.py +++ b/kivy/uix/scrollview.py @@ -198,8 +198,8 @@ class ScrollView(StencilView): if widget is self._viewport: self._viewport = None - def _get_uid(self): - return 'sv.%d' % id(self) + def _get_uid(self, prefix='sv'): + return '{0}.{1}'.format(prefix, self.uid) def _change_touch_mode(self, *largs): if not self._touch: @@ -287,10 +287,11 @@ class ScrollView(StencilView): return False def on_touch_down(self, touch): + if not self.collide_point(*touch.pos): + touch.ud[self._get_uid('svavoid')] = True + return if self._touch: return super(ScrollView, self).on_touch_down(touch) - if not self.collide_point(*touch.pos): - return # support scrolling ! if self._viewport and 'button' in touch.profile and \ touch.button.startswith('scroll'): @@ -323,6 +324,8 @@ class ScrollView(StencilView): return True def on_touch_move(self, touch): + if self._get_uid('svavoid') in touch.ud: + return if self._touch is not touch: super(ScrollView, self).on_touch_move(touch) return self._get_uid() in touch.ud @@ -370,6 +373,10 @@ class ScrollView(StencilView): # never ungrabed, cause their on_touch_up will be never called. # base.py: the me.grab_list[:] => it's a copy, and we are already # iterate on it. + + if self._get_uid('svavoid') in touch.ud: + return + if 'button' in touch.profile and not touch.button.startswith('scroll'): self._scroll_y_mouse = self.scroll_y @@ -385,7 +392,7 @@ class ScrollView(StencilView): elif self.auto_scroll: self._do_animation(touch) else: - if self._touch is not touch: + if self._touch is not touch and self.uid not in touch.ud: super(ScrollView, self).on_touch_up(touch) # if we do mouse scrolling, always accept it