Beispiel #1
0
class TreeView(BoxLayout):
    """ Main middle col, parent selected n uncles left, child nodes on right """

    def __init__(self, **kwargs):
        super(TreeView, self).__init__(**kwargs)

        self.parent_and_uncles_list_adapter = ListAdapter(
            data=[El.get("TEXT") for El in parentMap[selectedParent]],
            selection_mode='single',
            allow_empty_selection=False,
            cls=ListItemButton)
        self.add_widget(ListView(adapter=self.parent_and_uncles_list_adapter))
        self.parent_and_uncles_list_adapter.get_view(selectedIndexList[-2]).trigger_action(duration=0)

        self.main_list_adapter = ListAdapter(
            data=[El.get("TEXT") for El in selectedParent],
            selection_mode='single',
            allow_empty_selection=False,
            cls=ListItemButton)
        self.add_widget(ListView(adapter=self.main_list_adapter))
        self.main_list_adapter.get_view(selectedIndexList[-1]).trigger_action(duration=0)

        child_list_adapter = ListAdapter(
            data=[El.get("TEXT") for El in selectedParent[selectedIndexList[-1]]],
            selection_mode='single',
            allow_empty_selection=True,
            cls=ListItemButton)
        self.add_widget(ListView(adapter=child_list_adapter))
    def __init__(self, settings, channel, **kwargs):
        super(ChannelSelectView, self).__init__(**kwargs)
        self.register_event_type('on_channel_selected')
        self.register_event_type('on_channel_cancel')
        data = []
        channel_list = self.ids.channelList

        available_channels = list(settings.runtimeChannels.get_active_channels().iterkeys())
        available_channels.sort()
        try:
            for available_channel in available_channels:
                data.append({'text': available_channel, 'is_selected': False})
                
            args_converter = lambda row_index, rec: {'text': rec['text'], 'size_hint_y': None, 'height': dp(50)}
    
            list_adapter = ListAdapter(data=data,
                               args_converter=args_converter,
                               cls=ChannelItemButton,
                               selection_mode='single',
                               allow_empty_selection=True)
    
            channel_list.adapter = list_adapter

            #select the current channel
            index = 0
            for item in list_adapter.data:
                if item['text'] == channel:
                    view = list_adapter.get_view(index)
                    view.trigger_action(duration=0) #duration=0 means make it an instant selection
                index += 1

            list_adapter.bind(on_selection_change=self.on_select)
            self.channel = channel
        except Exception as e:
            Logger.error("ChannelSelectView: Error initializing: " + str(e))
Beispiel #3
0
    def __init__(self, settings, channel, **kwargs):
        super(ChannelSelectView, self).__init__(**kwargs)
        self.register_event_type('on_channel_selected')
        self.register_event_type('on_channel_cancel')
        data = []
        channel_list = self.ids.channelList

        available_channels = list(
            settings.runtimeChannels.get_active_channels().iterkeys())
        available_channels.sort()
        try:
            for available_channel in available_channels:
                data.append({'text': available_channel, 'is_selected': False})

            args_converter = lambda row_index, rec: {
                'text': rec['text'],
                'size_hint_y': None,
                'height': dp(50)
            }

            list_adapter = ListAdapter(data=data,
                                       args_converter=args_converter,
                                       cls=ChannelItemButton,
                                       selection_mode='single',
                                       allow_empty_selection=True)

            channel_list.adapter = list_adapter

            #select the current channel
            index = 0
            for item in list_adapter.data:
                if item['text'] == channel:
                    view = list_adapter.get_view(index)
                    view.trigger_action(
                        duration=0
                    )  #duration=0 means make it an instant selection
                index += 1

            list_adapter.bind(on_selection_change=self.on_select)
            self.channel = channel
        except Exception as e:
            Logger.error("ChannelSelectView: Error initializing: " + str(e))
class NewProjectDialog(BoxLayout):

    listview = ObjectProperty(None)
    ''':class:`~kivy.uix.listview.ListView` used for showing file paths.
       :data:`listview` is a :class:`~kivy.properties.ObjectProperty`
    '''

    select_button = ObjectProperty(None)
    ''':class:`~kivy.uix.button.Button` used to select the list item.
       :data:`select_button` is a :class:`~kivy.properties.ObjectProperty`
    '''

    cancel_button = ObjectProperty(None)
    ''':class:`~kivy.uix.button.Button` to cancel the dialog.
       :data:`cancel_button` is a :class:`~kivy.properties.ObjectProperty`
    '''

    adapter = ObjectProperty(None)
    ''':class:`~kivy.uix.listview.ListAdapter` used for selecting files.
       :data:`adapter` is a :class:`~kivy.properties.ObjectProperty`
    '''

    image = ObjectProperty(None)
    '''Type of :class:`~kivy.uix.image.Image` to display image of selected
       new template.
       :data:`image` is a :class:`~kivy.properties.ObjectProperty`
    '''

    list_parent = ObjectProperty(None)
    '''Parent of listview.
       :data:`list_parent` is a :class:`~kivy.properties.ObjectProperty`
    '''

    prev_selection = NumericProperty(0)
    '''to memorize the previous selection.
       :attr:`prev_selection` is a :class:
       `~kivy.properties.NumericProperty`, defaults to (0).
    '''

    __events__ = ('on_select', 'on_cancel')

    def __init__(self, **kwargs):
        super(NewProjectDialog, self).__init__(**kwargs)
        item_strings = list(NEW_PROJECTS.keys())
        item_strings.sort()
        self.adapter = ListAdapter(cls=Factory.DesignerListItemButton,
                                   data=item_strings,
                                   selection_mode='single',
                                   allow_empty_selection=False)
        self.adapter.check_for_empty_selection = self.check_for_empty_selection
        self.adapter.bind(on_selection_change=self.on_adapter_selection_change)
        self.listview = ListView(adapter=self.adapter)
        self.listview.size_hint = (0.5, 1)
        self.listview.pos_hint = {'top': 1}
        self.list_parent.add_widget(self.listview, 1)
        self.on_adapter_selection_change(self.adapter)

    def on_parent(self, *args):
        if self.parent:
            Window.bind(on_key_down=self._on_keyboard_down)
        else:
            Window.unbind(on_key_down=self._on_keyboard_down)

    def _on_keyboard_down(self, keyboard, key, codepoint,
                          text, modifier, *args):
        '''To detect which key is pressed
        '''
        if modifier:
            return False
        key_str = Keyboard.keycode_to_string(Window._system_keyboard, key)
        if key_str == 'up':
            v = self.adapter.get_view(self.prev_selection - 1)
            if v is not None:
                self.adapter.handle_selection(v)
                return True
        if key_str == 'down':
            v = self.adapter.get_view(self.prev_selection + 1)
            if v is not None:
                self.adapter.handle_selection(v)
                return True
        if key_str == 'enter':
            self.dispatch('on_select')
            return True

    def check_for_empty_selection(self, *args):
        if not self.adapter.allow_empty_selection:
            if len(self.adapter.selection) == 0:
                # Select the first item if we have it.
                v = self.adapter.get_view(self.prev_selection)
                if v is not None:
                    self.adapter.handle_selection(v)

    def on_adapter_selection_change(self, adapter):
        '''Event handler for 'on_selection_change' event of adapter.
        '''
        name = adapter.selection[0].text.lower() + '.png'
        name = name.replace(' and ', '_')
        image_source = join(NEW_TEMPLATE_IMAGE_PATH, name)
        _dir = get_kd_dir()
        image_source = join(_dir, image_source)
        parent = self.image.parent
        parent.remove_widget(self.image)
        self.image = Image(source=image_source)
        parent.add_widget(self.image)
        self.prev_selection = adapter.data.index(adapter.selection[0].text)

    def on_touch_down(self, touch):
        '''Used to determine where touch is down and to detect double
           tap.
        '''
        if touch.is_double_tap:
            self.dispatch('on_select')
        return super(NewProjectDialog, self).on_touch_down(touch)

    def on_select(self, *args):
        '''Default Event Handler for 'on_select' event
        '''
        pass

    def on_cancel(self, *args):
        '''Default Event Handler for 'on_cancel' event
        '''
        pass

    def on_select_button(self, *args):
        '''Event Handler for 'on_release' of select button.
        '''
        self.select_button.bind(on_press=partial(self.dispatch, 'on_select'))

    def on_cancel_button(self, *args):
        '''Event Handler for 'on_release' of cancel button.
        '''
        self.cancel_button.bind(on_press=partial(self.dispatch, 'on_cancel'))
class CompletionBubble(Bubble):

    list_view = ObjectProperty(None, allownone=True)
    '''(internal) Reference a ListView with a list of SuggestionItems
       :data:`list_view` is a :class:`~kivy.properties.ObjectProperty`
    '''

    adapter = ObjectProperty(None)
    '''(internal) Reference a ListView adapter
       :data:`adapter` is a :class:`~kivy.properties.ObjectProperty`
    '''

    __events__ = (
        'on_complete',
        'on_cancel',
    )

    def __init__(self, **kwargs):
        super(CompletionBubble, self).__init__(**kwargs)
        Window.bind(on_touch_down=self.on_window_touch_down)

    def on_window_touch_down(self, win, touch):
        '''Disable the completion if the user clicks anywhere
        '''
        if not self.collide_point(*touch.pos):
            self.dispatch('on_cancel')

    def _create_list_view(self, data):
        '''Create the ListAdapter
        '''
        self.adapter = ListAdapter(data=data,
                                   args_converter=self._args_converter,
                                   cls=SuggestionItem,
                                   selection_mode='single',
                                   allow_empty_selection=False)
        self.adapter.bind(on_selection_change=self.on_selection_change)
        self.list_view = CompletionListView(adapter=self.adapter)
        self.add_widget(self.list_view)

    def _args_converter(self, index, completion):
        return {
            'text': completion.name,
            'is_selected': False,
            'complete': completion.complete,
            'selected_by_touch': self.selected_by_touch
        }

    def selected_by_touch(self, item):
        self.dispatch('on_complete', item.complete)

    def show_completions(self, completions, force_scroll=False):
        '''Update the Completion ListView with completions
        '''
        if completions == []:
            fake_completion = type('obj', (object, ), {
                'name': 'No suggestions',
                'complete': ''
            })
            completions.append(fake_completion)
        Window.bind(on_key_down=self.on_key_down)
        if not self.list_view:
            self._create_list_view(completions)
        else:
            self.adapter.data = completions
        if force_scroll:
            self.list_view.scroll_to(0)

    def on_selection_change(self, *args):
        pass

    def _scroll_item(self, new_index):
        '''Update the scroll view position to display the new_index item
        '''
        item = self.adapter.get_view(new_index)
        if item:
            item.trigger_action(0)
            if new_index > 2 and new_index < len(self.adapter.data) - 1:
                self.list_view.scroll_to(new_index - 3)

    def on_key_down(self, instance, key, *args):
        '''Keyboard listener to grab key codes and interact with the
        Completion box
        '''
        selected_item = self.adapter.selection[0]
        selected_index = selected_item.index
        if self.list_view.scrolled:
            # recreate list view after mouse scroll due to the bug kivy/#3418
            self.remove_widget(self.list_view)
            self.list_view = None
            self.show_completions(self.adapter.data)
            return self.on_key_down(instance, key, args)

        if key == 273:
            # up
            if selected_index > 0:
                self._scroll_item(selected_index - 1)
            return True

        elif key == 274:
            # down
            if selected_index < len(self.adapter.data) - 1:
                self._scroll_item(selected_index + 1)
            return True

        elif key in [9, 13, 32]:
            # tab, enter or space
            self.dispatch('on_complete', selected_item.complete)
            return True

        else:
            # another key cancel the completion
            self.dispatch('on_cancel')
            return False

    def reposition(self, pos, line_height):
        '''Update the Bubble position. Try to display it in the best place of
        the screen
        '''
        win = Window
        self.x = pos[0] - self.width / 2
        self.y = pos[1] - self.height - line_height

        # fit in the screen horizontally
        if self.right > win.width:
            self.x = win.width - self.width
        if self.x < 0:
            self.x = 0

        # fit in the screen vertically
        if self.y < 0:
            diff = abs(self.y)
            # check if we can move it to top
            new_y = pos[1] + line_height
            if new_y + self.height < win.height:  # fit in the screen
                self.y = new_y
            else:  # doesnt fit on top neither on bottom. Check the best place
                new_diff = abs(new_y + self.height - win.height)
                if new_diff < diff:  # if we lose lest moving it to top
                    self.y = new_y

        # compare the desired position with the actual position
        x_relative = self.x - (pos[0] - self.width / 2)

        x_range = self.width / 4  # consider 25% as the range

        def _get_hpos():
            '''Compare the position of the widget with the parent
            to display the arrow in the correct position
            '''
            _pos = 'mid'
            if x_relative == 0:
                _pos = 'mid'
            elif x_relative < -x_range:
                _pos = 'right'
            elif x_relative > x_range:
                _pos = 'left'
            return _pos

        if self.y == pos[1] - self.height - line_height:
            self.arrow_pos = 'top_' + _get_hpos()
        else:
            self.arrow_pos = 'bottom_' + _get_hpos()

    def on_complete(self, *args):
        '''Dispatch a completion selection
        '''
        Window.unbind(on_key_down=self.on_key_down)

    def on_cancel(self, *args):
        '''Disable key listener on cancel
        '''
        Window.unbind(on_key_down=self.on_key_down)
Beispiel #6
0
class NewProjectDialog(BoxLayout):

    listview = ObjectProperty(None)
    ''':class:`~kivy.uix.listview.ListView` used for showing file paths.
       :data:`listview` is a :class:`~kivy.properties.ObjectProperty`
    '''

    select_button = ObjectProperty(None)
    ''':class:`~kivy.uix.button.Button` used to select the list item.
       :data:`select_button` is a :class:`~kivy.properties.ObjectProperty`
    '''

    cancel_button = ObjectProperty(None)
    ''':class:`~kivy.uix.button.Button` to cancel the dialog.
       :data:`cancel_button` is a :class:`~kivy.properties.ObjectProperty`
    '''

    adapter = ObjectProperty(None)
    ''':class:`~kivy.uix.listview.ListAdapter` used for selecting files.
       :data:`adapter` is a :class:`~kivy.properties.ObjectProperty`
    '''

    image = ObjectProperty(None)
    '''Type of :class:`~kivy.uix.image.Image` to display image of selected
       new template.
       :data:`image` is a :class:`~kivy.properties.ObjectProperty`
    '''

    list_parent = ObjectProperty(None)
    '''Parent of listview.
       :data:`list_parent` is a :class:`~kivy.properties.ObjectProperty`
    '''

    prev_selection = NumericProperty(0)
    '''to memorize the previous selection.
       :attr:`prev_selection` is a :class:
       `~kivy.properties.NumericProperty`, defaults to (0).
    '''

    __events__ = ('on_select', 'on_cancel')

    def __init__(self, **kwargs):
        super(NewProjectDialog, self).__init__(**kwargs)
        item_strings = list(NEW_PROJECTS.keys())
        item_strings.sort()
        self.adapter = ListAdapter(cls=Factory.DesignerListItemButton,
                                   data=item_strings,
                                   selection_mode='single',
                                   allow_empty_selection=False)
        self.adapter.check_for_empty_selection = self.check_for_empty_selection
        self.adapter.bind(on_selection_change=self.on_adapter_selection_change)
        self.listview = ListView(adapter=self.adapter)
        self.listview.size_hint = (0.5, 1)
        self.listview.pos_hint = {'top': 1}
        self.list_parent.add_widget(self.listview, 1)
        self.on_adapter_selection_change(self.adapter)

    def on_parent(self, *args):
        if self.parent:
            Window.bind(on_key_down=self._on_keyboard_down)
        else:
            Window.unbind(on_key_down=self._on_keyboard_down)

    def _on_keyboard_down(self, keyboard, key, codepoint, text, modifier,
                          *args):
        '''To detect which key is pressed
        '''
        if modifier:
            return False
        key_str = Keyboard.keycode_to_string(Window._system_keyboard, key)
        if key_str == 'up':
            v = self.adapter.get_view(self.prev_selection - 1)
            if v is not None:
                self.adapter.handle_selection(v)
                return True
        if key_str == 'down':
            v = self.adapter.get_view(self.prev_selection + 1)
            if v is not None:
                self.adapter.handle_selection(v)
                return True
        if key_str == 'enter':
            self.dispatch('on_select')
            return True

    def check_for_empty_selection(self, *args):
        if not self.adapter.allow_empty_selection:
            if len(self.adapter.selection) == 0:
                # Select the first item if we have it.
                v = self.adapter.get_view(self.prev_selection)
                if v is not None:
                    self.adapter.handle_selection(v)

    def on_adapter_selection_change(self, adapter):
        '''Event handler for 'on_selection_change' event of adapter.
        '''
        name = adapter.selection[0].text.lower() + '.png'
        name = name.replace(' and ', '_')
        image_source = join(constants.NEW_TEMPLATE_IMAGE_PATH, name)
        image_source = join(get_kd_data_dir(), image_source)
        parent = self.image.parent
        parent.remove_widget(self.image)
        self.image = Image(source=image_source)
        parent.add_widget(self.image)
        self.prev_selection = adapter.data.index(adapter.selection[0].text)

    def on_touch_down(self, touch):
        '''Used to determine where touch is down and to detect double
           tap.
        '''
        if touch.is_double_tap:
            self.dispatch('on_select')
        return super(NewProjectDialog, self).on_touch_down(touch)

    def on_select(self, *args):
        '''Default Event Handler for 'on_select' event
        '''
        pass

    def on_cancel(self, *args):
        '''Default Event Handler for 'on_cancel' event
        '''
        pass

    def on_select_button(self, *args):
        '''Event Handler for 'on_release' of select button.
        '''
        self.select_button.bind(on_press=partial(self.dispatch, 'on_select'))

    def on_cancel_button(self, *args):
        '''Event Handler for 'on_release' of cancel button.
        '''
        self.cancel_button.bind(on_press=partial(self.dispatch, 'on_cancel'))
Beispiel #7
0
class PopupListView(FlatPopup):
    '''
    When a click on this button occur, a popup will be shown to pick a value.
    Arguments named popup_* will be passed down to the popup for customizations.

    To customize ListItemButton selected_color and deselected_color please use kv lang.
    '''

    item_row_height = NumericProperty(dp(40))
    '''Height of rows shown by the popup

    .. versionadded:: 1.0

    :attr:`item_row_height` is a :class:`~kivy.properties.NumericProperty`, default to 40.
    '''

    selected = ObjectProperty(None)
    '''Data selected.

    .. versionadded:: 1.0

    :attr:`selected` is a :class:`~kivy.properties.ObjectProperty`.
    '''

    selected = ObjectProperty(None)
    '''Index of selected data.

    .. versionadded:: 1.0

    :attr:`selected` is a :class:`~kivy.properties.ObjectProperty`.
    '''

    on_selection = ObjectProperty(None)
    '''Called whenever a value is selected, see 'on_selection_change' method code.

    .. versionadded:: 1.0

    :attr:`on_selection` is a :class:`~kivy.properties.ObjectProperty`.
    '''
    def __init__(self, list_data, **kargs):
        super(PopupListView, self).__init__(**kargs)
        self.list_data = self.build_list_data(list_data)
        self.content = self._build_list_view(kargs)

    def _build_list_view(self, kargs):
        self._list_view = ListView(adapter=self._build_adapter(),
                                   propagate_selection_to_data=True)
        return self._list_view

    def build_list_data(self, data):
        if len(data) > 0:
            if data[0].__class__ == str:
                l2dict = [{
                    'is_selected': False,
                    'rowid': 0,
                    'label': x
                } for x in data]
                return self.build_list_data(l2dict)
            else:
                result = data
                if 'is_selected' not in data[0].keys():
                    result = []
                    for x in data:
                        x['is_selected'] = False
                        result.append(x)
                return result
        return []

    def show_choices(self, *args):
        self.open()

    def adapter_converter(self):
        return lambda i, o : { \
            'is_selected'      : o['is_selected'], \
            'size_hint_y'      : None, \
            'height'           : self.item_row_height, \
            'text'             : o['label'], \
            'rowid'            : o['rowid'] \
        }

    def _build_adapter(self):
        self.list_adapter = ListAdapter(
            cls=ListItemButton,
            data=self.list_data,
            args_converter=self.adapter_converter(),
            selection_mode='single',
            allow_empty_selection=True)
        self.list_adapter.bind(on_selection_change=self.on_selection_change)
        return self.list_adapter

    def on_selection_change(self, adapter, *args):
        if (adapter.selection):
            self.selected_index = adapter.selection[0].index
            self.selected = self.list_data[self.selected_index]
            if self.on_selection:
                self.on_selection(self, self.selected, self.selected_index)
            self.dismiss()

    def select(self, i):
        self.list_adapter.get_view(i).trigger_action(duration=0)
class InputScreen(GridLayout):

  # Create interface
  def __init__(self, **kwargs):
    self.controller = kwargs.pop('controller', None)
    super(InputScreen, self).__init__(**kwargs)
    self.cols = 2

    # Text input
    self.text = ShortcutTextInput(multiline=True, focus=True, keyboard_handler=self._on_keyboard_down)
    self.add_widget(self.text)
    self.text.bind(text=self._on_text)
    self.text.hint_text="Begin typing to test text prediction."

    # predictions
    obj_printer = lambda obj: "{} - {}".format(str(obj), self.current_prediction.get(obj, 0))
    args_converter = lambda row_index, obj: {'text': obj_printer(obj),
                                                   'size_hint_y': None,
                                                   'height': 25}

    self.prediction_adapter = ListAdapter(data=[],
                                               args_converter=args_converter,
                                               selection_mode='single',
                                               allow_empty_selection=False,
                                               cls=ListItemLabel)

    self.prediction_list = ListView(adapter=self.prediction_adapter)
    self.add_widget(self.prediction_list)

    self.current_prediction = FreqDist()

    # Record history
    self.previous_record = None
    self.in_word = False

  # Handle non-standard shortcuts
  # Mainly prediction selection and confirmation
  def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
    # print keycode
    if keycode[1] == 'tab':
      # Change selected prediction
      self.change_selection('shift' in modifiers)
      return True
    elif keycode[1] == 'up':
      self.change_selection(True)
      return True
    elif keycode[1] == 'down':
      self.change_selection(False)
      return True
    elif keycode[1] == 'enter':
      # Use the current prediction
      self._accept_prediction()
      return True
    elif keycode[1] == 'spacebar':
      # Record word
      self._accept_word(self._current_word())
      return False
    elif keycode[1] == 'backspace':
      # Remove previous word from record
      self.undo_word()
      return False
    else:
      # Default behaviour
      return  False  

  # Select a different prediction to use
  def change_selection(self, direction):  
    # Get current index
    selection = self.prediction_adapter.selection[0]
    index = self._selection_index(selection)

    # Deselect the current selection
    self._deselect_current()

    # whether to move back or forward
    if direction:
      index -= 1
    else:
      index += 1

    index = index % len(self.prediction_adapter.cached_views)

    # Select next
    self._select_at_index(index)

  # Get the index of the currently selected prediction
  def _selection_index(self, selection): 
    for i, view in self.prediction_adapter.cached_views.iteritems():
        if view == selection:
          return i

  # Deselect the currently selected prediction
  def _deselect_current(self):
    selection = self.prediction_adapter.selection[0]
    index = self._selection_index(selection)
    self.prediction_adapter.get_view(index).deselect()


  # Select the prediction with the given index
  def _select_at_index(self, index):
    view = self.prediction_adapter.get_view(index)
    view.select()
    self.prediction_adapter.selection[0] = view

  # Accept the selected prediction and insert it into the text field
  def _accept_prediction(self):
    offset = len(self._current_word())
    selection = self.prediction_adapter.selection[0]
    index = self._selection_index(selection)
    word = self.prediction_adapter.data[index]
    addition = word[offset:]
    self._accept_word(word)
    self.text.insert_text(addition + ' ')

  # Set that we are no longer within a word
  def _accept_word(self, word):
    self.in_word = False

  # Set that we are now in a word again after backspacing
  def undo_word(self):
    if self.previous_record is not None:
      if self.previous_record == self._current_word():
        self.previous_record = None
        self.in_word = True

  # Called when the text is changed to update the current predictions
  def _on_text(self, widget, text):
    # Record if we are typing a word
    if len(text) > 0 and text[-1] != ' ':
      self.in_word = True

    # Predict based on input
    prediction = self.controller.predict(str(text), self.in_word, MAX_PREDICTIONS)

    # Reset index if there is a prediction
    current_word = self._current_word()
    if len(current_word) > 0 and current_word not in prediction:
      prediction[current_word] = 0
    # populate list
    self.current_prediction = prediction
    self.prediction_adapter.data = self.controller.prediction_list(prediction)

    # Select a randomly select a weighted next word
    # rand = self.controller.weighted_random_prediction(prediction)
    # index = self.prediction_adapter.data.index(rand)
    # self._deselect_current()
    # self._select_at_index(index)

  # Returns the word currently being typed
  def _current_word(self):
    return self.text.text.split(' ')[-1]
class CompletionBubble(Bubble):

    list_view = ObjectProperty(None, allownone=True)
    '''(internal) Reference a ListView with a list of SuggestionItems
       :data:`list_view` is a :class:`~kivy.properties.ObjectProperty`
    '''

    adapter = ObjectProperty(None)
    '''(internal) Reference a ListView adapter
       :data:`adapter` is a :class:`~kivy.properties.ObjectProperty`
    '''

    __events__ = ('on_complete', 'on_cancel', )

    def __init__(self, **kwargs):
        super(CompletionBubble, self).__init__(**kwargs)
        Window.bind(on_touch_down=self.on_window_touch_down)

    def on_window_touch_down(self, win, touch):
        '''Disable the completion if the user clicks anywhere
        '''
        if not self.collide_point(*touch.pos):
            self.dispatch('on_cancel')

    def _create_list_view(self, data):
        '''Create the ListAdapter
        '''
        self.adapter = ListAdapter(
            data=data,
            args_converter=self._args_converter,
            cls=SuggestionItem,
            selection_mode='single',
            allow_empty_selection=False
        )
        self.adapter.bind(on_selection_change=self.on_selection_change)
        self.list_view = CompletionListView(adapter=self.adapter)
        self.add_widget(self.list_view)

    def _args_converter(self, index, completion):
        return {'text': completion.name,
                'is_selected': False,
                'complete': completion.complete,
                'selected_by_touch': self.selected_by_touch}

    def selected_by_touch(self, item):
        self.dispatch('on_complete', item.complete)

    def show_completions(self, completions, force_scroll=False):
        '''Update the Completion ListView with completions
        '''
        if completions == []:
            fake_completion = type('obj', (object,),
                                   {'name': 'No suggestions', 'complete': ''})
            completions.append(fake_completion)
        Window.bind(on_key_down=self.on_key_down)
        if not self.list_view:
            self._create_list_view(completions)
        else:
            self.adapter.data = completions
        if force_scroll:
            self.list_view.scroll_to(0)

    def on_selection_change(self, *args):
        pass

    def _scroll_item(self, new_index):
        '''Update the scroll view position to display the new_index item
        '''
        item = self.adapter.get_view(new_index)
        if item:
            item.trigger_action(0)
            if new_index > 2 and new_index < len(self.adapter.data) - 1:
                self.list_view.scroll_to(new_index - 3)

    def on_key_down(self, instance, key, *args):
        '''Keyboard listener to grab key codes and interact with the
        Completion box
        '''
        selected_item = self.adapter.selection[0]
        selected_index = selected_item.index
        if self.list_view.scrolled:
            # recreate list view after mouse scroll due to the bug kivy/#3418
            self.remove_widget(self.list_view)
            self.list_view = None
            self.show_completions(self.adapter.data)
            return self.on_key_down(instance, key, args)

        if key == 273:
            # up
            if selected_index > 0:
                self._scroll_item(selected_index - 1)
            return True

        elif key == 274:
            # down
            if selected_index < len(self.adapter.data) - 1:
                self._scroll_item(selected_index + 1)
            return True

        elif key in [9, 13, 32]:
            # tab, enter or space
            self.dispatch('on_complete', selected_item.complete)
            return True

        else:
            # another key cancel the completion
            self.dispatch('on_cancel')
            return False

    def reposition(self, pos, line_height):
        '''Update the Bubble position. Try to display it in the best place of
        the screen
        '''
        win = Window
        self.x = pos[0] - self.width / 2
        self.y = pos[1] - self.height - line_height

        # fit in the screen horizontally
        if self.right > win.width:
            self.x = win.width - self.width
        if self.x < 0:
            self.x = 0

        # fit in the screen vertically
        if self.y < 0:
            diff = abs(self.y)
            # check if we can move it to top
            new_y = pos[1] + line_height
            if new_y + self.height < win.height:  # fit in the screen
                self.y = new_y
            else:  # doesnt fit on top neither on bottom. Check the best place
                new_diff = abs(new_y + self.height - win.height)
                if new_diff < diff:  # if we lose lest moving it to top
                    self.y = new_y

        # compare the desired position with the actual position
        x_relative = self.x - (pos[0] - self.width / 2)

        x_range = self.width / 4  # consider 25% as the range

        def _get_hpos():
            '''Compare the position of the widget with the parent
            to display the arrow in the correct position
            '''
            _pos = 'mid'
            if x_relative == 0:
                _pos = 'mid'
            elif x_relative < -x_range:
                _pos = 'right'
            elif x_relative > x_range:
                _pos = 'left'
            return _pos

        if self.y == pos[1] - self.height - line_height:
            self.arrow_pos = 'top_' + _get_hpos()
        else:
            self.arrow_pos = 'bottom_' + _get_hpos()

    def on_complete(self, *args):
        '''Dispatch a completion selection
        '''
        Window.unbind(on_key_down=self.on_key_down)

    def on_cancel(self, *args):
        '''Disable key listener on cancel
        '''
        Window.unbind(on_key_down=self.on_key_down)