Esempio n. 1
0
 def _get_keyboard(self):
     if not self._keyboard:
         self._keyboard = self.group['keyboard']
         if self._keyboard is None:
             self._keyboard = MTVKeyboard()
             self.group['keyboard'] = self._keyboard
     return self._keyboard
Esempio n. 2
0
 def _get_keyboard(self):
     if not self._keyboard:
         self._keyboard = self.group['keyboard']
         if self._keyboard is None:
             self._keyboard = MTVKeyboard()
             self.group['keyboard'] = self._keyboard
     return self._keyboard
Esempio n. 3
0
class MTTextInput(MTButton):
    '''
    A text input widget is a simple label widget that will pop up
    a virtual keyboard when touched any input of the virtual keyboard
    will then have effect on the TextInput widget.

    .. versionadded:: 0.5.1
        Support of scrolling textarea, controlled by `scroll` attribute. You can
        now scroll inside the text area without editing text.

    :Parameters:
        `keyboard`: MTVkeyboard object, default to None
            Use another MTVKeyboard than the default one
        `keyboard_to_root`: bool, defaults to False.
            Indicate whether the keyboard should be attached to the root
            window. If True, this will have the effect of the keyboard being
            raised above other widgets.
        `keyboard_type`: str, default to config.
            Configuration section is 'widgets', token 'keyboard_type'.
            Can be one of 'virtual' or 'real'. If real, the virtual keyboard
            will be not showed
        `password`: bool, default to False
            If True, the label will be showed with star
        `group`: str, default to random
            If the group is the same for multiple text input
            You can switch between them with TAB, and use the same keyboard.
        `switch`: bool, default to True
            If True, a switch button will be show to switch from real or virtual
            keyboard
        `scroll`: bool, default to True
            If True, the scrolling of a text area is allowed.
        `scroll_trigger`: int, default to 10
            If scrolling is detected, don't fire event to show/hide keyboard.
    :Events:
        `on_text_change` (text)
            Fired when the content of text input is changed
        `on_text_validate` ()
            Fired when the text is validate (when ENTER is hit on keyboard)
    '''
    _group_id = 0
    _group = {}

    def __init__(self, **kwargs):
        kwargs.setdefault('anchor_x', 'left')
        kwargs.setdefault('anchor_y', 'center')
        kwargs.setdefault('halign', 'left')
        kwargs.setdefault('keyboard', None)
        kwargs.setdefault('keyboard_to_root', False)
        kwargs.setdefault('group', None)
        kwargs.setdefault('switch', True)
        kwargs.setdefault('keyboard_type',
            pymt_config.get('widgets', 'keyboard_type'))

        super(MTTextInput, self).__init__(**kwargs)

        self.register_event_type('on_text_change')
        self.register_event_type('on_text_validate')

        self._scroll_x = 0
        self._can_deactive = True
        self._keyboard = kwargs.get('keyboard')
        self._is_active_input = False

        #: Boolean to activate the password mode on the textinput.
        #: If true, it will show star instead of real characters
        self.password = kwargs.get('password', False)

        #: Boolean to control the scrolling of the textinput content
        self.scroll = kwargs.get('scroll', True)

        #: If the scroll if more than scroll_trigger, no event will be
        #: dispatched, and no show/hide of keyboard.
        self.scroll_trigger = kwargs.get('scroll_trigger', 10)

        #: Keyboard type (virtual or real)
        self.keyboard_type = kwargs.get('keyboard_type')

        #: If True, the keyboard is added to root window, not to parent window.
        self.keyboard_to_root = kwargs.get('keyboard_to_root')

        # initialize group on random if nothing is set
        self._groupname = kwargs.get('group')
        if self._groupname is None:
            MTTextInput._group_id += 1
            self._groupname = 'uniqgroup%d' % MTTextInput._group_id
        # first time ? create the group
        if not self._groupname in self._group:
            self.group['keyboard'] = None
            self.group['widgets'] = []
        self.group['widgets'].append(self)

        # save original color for error
        self._notify_bg_color = self.style['bg-color']
        self._notify_bg_color_active = self.style['bg-color-active']
        self._notify_animation = None

        # switch button between vkeyboard or hardware
        self._switch = None
        if kwargs.get('switch'):
            self._switch = MTButton(
                label=kwargs.get('keyboard_type'), cls='switch-button',
                size=(60, 20), font_size=8,
                pos=(self.x + self.width - 60, self.y + self.height))

        self.interesting_keys = {8: 'backspace', 13: 'enter', 127: 'del',
                                 271: 'enter', 273: 'cursor_up', 274: 'cursor_down',
                                 275: 'cursor_right', 276: 'cursor_left',
                                 278: 'cursor_home', 279: 'cursor_end',
                                 280: 'cursor_pgup', 281: 'cursor_pgdown'}

    def on_resize(self, *largs):
        if hasattr(self, '_switch'):
            self._switch.pos = self.x + self.width - 60, self.y + self.height
        return super(MTTextInput, self).on_resize(*largs)

    def _get_keyboard(self):
        if not self._keyboard:
            self._keyboard = self.group['keyboard']
            if self._keyboard is None:
                self._keyboard = MTVKeyboard()
                self.group['keyboard'] = self._keyboard
        return self._keyboard
    def _set_keyboard(self, value):
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
        self._keyboard = value
    keyboard = property(_get_keyboard, _set_keyboard)

    def on_release(self, touch):
        if not self._can_deactive:
            return
        if self._is_active_input:
            self.hide_keyboard()
        else:
            self.show_keyboard()

    def _kbd_on_text_change(self, value):
        # reset scrolling when text is typed
        self._scroll_x = 0
        self.label = value
        self.dispatch_event('on_text_change', value)

    def _kbd_on_key_up(self, key, repeat=False):
        displayed_str, internal_str, internal_action, scale = key
        if internal_action is None:
            return
        elif internal_action == 'enter':
            self.hide_keyboard()
            self.dispatch_event('on_text_validate')
        elif internal_action == 'escape':
            self.hide_keyboard()

    def on_text_validate(self):
        pass

    def on_text_change(self, text):
        pass

    @property
    def group(self):
        '''Return information (keyboard/widget list) from the current
        group of the widget'''
        if not self._groupname in self._group:
            self._group[self._groupname] = {}
        return self._group[self._groupname]

    def notify_error(self):
        '''Call this function to make animation on background as an error
        '''
        error_color = self.style['bg-color-error']
        if self._notify_animation is not None:
            self._notify_animation.stop()
        self.style['bg-color'] = self._notify_bg_color
        self.style['bg-color-active'] = self._notify_bg_color_active
        self._notify_animation = self.do(Animation(
            style={'bg-color': error_color, 'bg-color-active': error_color},
            f=lambda x: 1 - AnimationAlpha.ease_in_out_quart(x)))

    def deactivate_group(self):
        '''Deactivate all widgets in the group'''
        for w in self.group['widgets']:
            w.hide_keyboard()

    def focus_next(self):
        '''Focus the next textinput in the list'''
        idx = (self.group['widgets'].index(self) + 1)
        idx = idx % len(self.group['widgets'])
        widget = self.group['widgets'][idx]
        widget.show_keyboard()

    def show_keyboard(self):
        '''Show the associed keyboard of this widget.'''
        if self._is_active_input:
            return
        self.deactivate_group()
        self._is_active_input = True

        # activate switch button if necessary
        if self._switch:
            self.add_widget(self._switch)

        # activate the real keyboard
        w = self.get_root_window()
        w.push_handlers(on_key_down=self._window_on_key_down,
                        on_key_up=self._window_on_key_up)

        # activate the virtual keyboard
        if self.keyboard_type == 'virtual':
            to_root = self.keyboard_to_root
            w = self.get_root_window() if to_root else self.get_parent_window()
            w.add_widget(self.keyboard)
        if self.keyboard is not None:
            self._keyboard.push_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
            self._keyboard.text = self.label

    def hide_keyboard(self):
        '''Hide the associed keyboard of this widget.'''
        if not self._is_active_input:
            return
        if self._switch:
            self.remove_widget(self._switch)
        parent = self.keyboard.parent
        if parent is not None:
            # If keyboard type is real, the keyboard is not attached to any
            # parent widget.
            parent.remove_widget(self.keyboard)
        w = self.get_root_window()
        w.remove_handlers(on_key_down=self._window_on_key_down,
                          on_key_up=self._window_on_key_up)
        self._is_active_input = False
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )

    def _window_on_key_down(self, key, scancode=None, unicode=None):
        try:
            # XXX Experimental, and work only with pygame.
            modifiers = getWindow().modifiers
            if scancode == 55 and 'ctrl' in modifiers:
                from pygame import SCRAP_TEXT
                from pygame import scrap
                scrap.init()
                text = scrap.get(SCRAP_TEXT)
                if text:
                    self.keyboard.text += text
                return True
        except Exception, e:
            pymt_logger.warning('Unable to use scrap module: %s' % str(e)) 

        if key == 27: # escape
            self.hide_keyboard()
            return True
        elif key == 9: # tab
            self.focus_next()
            return True
        if not self.keyboard:
            return
        k = self.interesting_keys.get(key)
        if k:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_down', key)
        else:
            if unicode is not None:
                self.keyboard.text += unicode
            else:
                self.keyboard.text += chr(key)
Esempio n. 4
0
class MTTextInput(MTButton):
    '''
    A text input widget is a simple label widget that will pop up
    a virtual keyboard when touched any input of the virtual keyboard
    will then have effect on the TextInput widget.

    .. versionadded:: 0.5.1
        Support of scrolling textarea, controlled by `scroll` attribute. You can
        now scroll inside the text area without editing text.

    :Parameters:
        `keyboard`: MTVkeyboard object, default to None
            Use another MTVKeyboard than the default one
        `keyboard_to_root`: bool, defaults to False.
            Indicate whether the keyboard should be attached to the root
            window. If True, this will have the effect of the keyboard being
            raised above other widgets.
        `keyboard_type`: str, default to config.
            Configuration section is 'widgets', token 'keyboard_type'.
            Can be one of 'virtual' or 'real'. If real, the virtual keyboard
            will be not showed
        `password`: bool, default to False
            If True, the label will be showed with star
        `group`: str, default to random
            If the group is the same for multiple text input
            You can switch between them with TAB, and use the same keyboard.
        `switch`: bool, default to True
            If True, a switch button will be show to switch from real or virtual
            keyboard
        `scroll`: bool, default to True
            If True, the scrolling of a text area is allowed.
        `scroll_trigger`: int, default to 10
            If scrolling is detected, don't fire event to show/hide keyboard.
    :Events:
        `on_text_change` (text)
            Fired when the content of text input is changed
        `on_text_validate` ()
            Fired when the text is validate (when ENTER is hit on keyboard)
    '''
    _group_id = 0
    _group = {}

    def __init__(self, **kwargs):
        kwargs.setdefault('anchor_x', 'left')
        kwargs.setdefault('anchor_y', 'center')
        kwargs.setdefault('halign', 'left')
        kwargs.setdefault('keyboard', None)
        kwargs.setdefault('keyboard_to_root', False)
        kwargs.setdefault('group', None)
        kwargs.setdefault('switch', True)
        kwargs.setdefault('keyboard_type',
            pymt_config.get('widgets', 'keyboard_type'))

        super(MTTextInput, self).__init__(**kwargs)

        self.register_event_type('on_text_change')
        self.register_event_type('on_text_validate')

        self._scroll_x = 0
        self._can_deactive = True
        self._keyboard = kwargs.get('keyboard')
        self._is_active_input = False

        #: Boolean to activate the password mode on the textinput.
        #: If true, it will show star instead of real characters
        self.password = kwargs.get('password', False)

        #: Boolean to control the scrolling of the textinput content
        self.scroll = kwargs.get('scroll', True)

        #: If the scroll if more than scroll_trigger, no event will be
        #: dispatched, and no show/hide of keyboard.
        self.scroll_trigger = kwargs.get('scroll_trigger', 10)

        #: Keyboard type (virtual or real)
        self.keyboard_type = kwargs.get('keyboard_type')

        #: If True, the keyboard is added to root window, not to parent window.
        self.keyboard_to_root = kwargs.get('keyboard_to_root')

        # initialize group on random if nothing is set
        self._groupname = kwargs.get('group')
        if self._groupname is None:
            MTTextInput._group_id += 1
            self._groupname = 'uniqgroup%d' % MTTextInput._group_id
        # first time ? create the group
        if not self._groupname in self._group:
            self.group['keyboard'] = None
            self.group['widgets'] = []
        self.group['widgets'].append(self)

        # save original color for error
        self._notify_bg_color = self.style['bg-color']
        self._notify_bg_color_active = self.style['bg-color-active']
        self._notify_animation = None

        # switch button between vkeyboard or hardware
        self._switch = None
        if kwargs.get('switch'):
            self._switch = MTButton(
                label=kwargs.get('keyboard_type'), cls='switch-button',
                size=(60, 20), font_size=8,
                pos=(self.x + self.width - 60, self.y + self.height))

        self.interesting_keys = {8: 'backspace', 13: 'enter', 127: 'del',
                                 271: 'enter', 273: 'cursor_up', 274: 'cursor_down',
                                 275: 'cursor_right', 276: 'cursor_left',
                                 278: 'cursor_home', 279: 'cursor_end',
                                 280: 'cursor_pgup', 281: 'cursor_pgdown'}

    def on_resize(self, *largs):
        if hasattr(self, '_switch'):
            self._switch.pos = self.x + self.width - 60, self.y + self.height
        return super(MTTextInput, self).on_resize(*largs)

    def _get_keyboard(self):
        if not self._keyboard:
            self._keyboard = self.group['keyboard']
            if self._keyboard is None:
                self._keyboard = MTVKeyboard()
                self.group['keyboard'] = self._keyboard
        return self._keyboard
    def _set_keyboard(self, value):
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
        self._keyboard = value
    keyboard = property(_get_keyboard, _set_keyboard)

    def on_release(self, touch):
        if not self._can_deactive:
            return
        if self._is_active_input:
            self.hide_keyboard()
        else:
            self.show_keyboard()

    def _kbd_on_text_change(self, value):
        # reset scrolling when text is typed
        self._scroll_x = 0
        self.label = value
        self.dispatch_event('on_text_change', value)

    def _kbd_on_key_up(self, key, repeat=False):
        displayed_str, internal_str, internal_action, scale = key
        if internal_action is None:
            return
        elif internal_action == 'enter':
            self.hide_keyboard()
            self.dispatch_event('on_text_validate')
        elif internal_action == 'escape':
            self.hide_keyboard()

    def on_text_validate(self):
        pass

    def on_text_change(self, text):
        pass

    @property
    def group(self):
        '''Return information (keyboard/widget list) from the current
        group of the widget'''
        if not self._groupname in self._group:
            self._group[self._groupname] = {}
        return self._group[self._groupname]

    def notify_error(self):
        '''Call this function to make animation on background as an error
        '''
        error_color = self.style['bg-color-error']
        if self._notify_animation is not None:
            self._notify_animation.stop()
        self.style['bg-color'] = self._notify_bg_color
        self.style['bg-color-active'] = self._notify_bg_color_active
        self._notify_animation = self.do(Animation(
            style={'bg-color': error_color, 'bg-color-active': error_color},
            f=lambda x: 1 - AnimationAlpha.ease_in_out_quart(x)))

    def deactivate_group(self):
        '''Deactivate all widgets in the group'''
        for w in self.group['widgets']:
            w.hide_keyboard()

    def focus_next(self):
        '''Focus the next textinput in the list'''
        idx = (self.group['widgets'].index(self) + 1)
        idx = idx % len(self.group['widgets'])
        widget = self.group['widgets'][idx]
        widget.show_keyboard()

    def show_keyboard(self):
        '''Show the associed keyboard of this widget.'''
        if self._is_active_input:
            return
        self.deactivate_group()
        self._is_active_input = True

        # activate switch button if necessary
        if self._switch:
            self.add_widget(self._switch)

        # activate the real keyboard
        w = self.get_root_window()
        w.push_handlers(on_key_down=self._window_on_key_down,
                        on_key_up=self._window_on_key_up)

        # activate the virtual keyboard
        if self.keyboard_type == 'virtual':
            to_root = self.keyboard_to_root
            w = self.get_root_window() if to_root else self.get_parent_window()
            w.add_widget(self.keyboard)
        if self.keyboard is not None:
            self._keyboard.push_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
            self._keyboard.text = self.label

    def hide_keyboard(self):
        '''Hide the associed keyboard of this widget.'''
        if not self._is_active_input:
            return
        if self._switch:
            self.remove_widget(self._switch)
        parent = self.keyboard.parent
        if parent is not None:
            # If keyboard type is real, the keyboard is not attached to any
            # parent widget.
            parent.remove_widget(self.keyboard)
        w = self.get_root_window()
        w.remove_handlers(on_key_down=self._window_on_key_down,
                          on_key_up=self._window_on_key_up)
        self._is_active_input = False
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )

    def _window_on_key_down(self, key, scancode=None, unicode=None):
        if key == 27: # escape
            self.hide_keyboard()
            return True
        elif key == 9: # tab
            self.focus_next()
            return True
        if not self.keyboard:
            return
        k = self.interesting_keys.get(key)
        if k:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_down', key)
        else:
            if unicode is not None:
                self.keyboard.text += unicode
            else:
                self.keyboard.text += chr(key)

    def _window_on_key_up(self, key, scancode=None, unicode=None):
        k = self.interesting_keys.get(key)
        if k and self.keyboard:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_up', key)

    def _get_value(self):
        return self.label
    def _set_value(self, value):
        self.label = value
    value = property(_get_value, _set_value,
        doc='Get/set the value of the label')

    def _get_keyboard_type(self):
        return self._keyboard_type
    def _set_keyboard_type(self, t):
        self.hide_keyboard()
        self._keyboard_type = t
        if self._is_active_input:
            self.show_keyboard()
    keyboard_type = property(_get_keyboard_type, _set_keyboard_type,
        doc='Get/set the keyboard type to use')

    @property
    def is_active_input(self):
        '''Return True if the input is active'''
        return self._is_active_input

    #
    # Overload the touch down/move/up for :
    #   1. handle the switch button
    #   2. be able to scroll inside the text input
    #

    def on_touch_down(self, touch):
        # check if it's the switch button that collide.
        if self._switch and self._switch.collide_point(*touch.pos):
            self.hide_keyboard()
            if self.keyboard_type == 'virtual':
                self.keyboard_type = 'real'
            else:
                self.keyboard_type = 'virtual'
            self._switch.label = self.keyboard_type
            self.show_keyboard()
            return True
        # reset scrolling when a touch is comming
        # FIXME: can be not accurate if many touch are comming.
        if self.collide_point(*touch.pos):
            self._scroll_x = 0
            self._can_deactive = True
        # and only now, dispatch as usual.
        return super(MTTextInput, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if not super(MTTextInput, self).on_touch_move(touch):
            return
        # we got a valid touch, 
        self._scroll_x += touch.dxpos - touch.x
        self._can_deactive = True
        if abs(touch.x - touch.oxpos) > self.scroll_trigger and self.scroll:
            self._can_deactive = False
        return True

    def on_touch_up(self, touch):
        if super(MTTextInput, self).on_touch_up(touch):
            if not self.is_active_input:
                self._scroll_x = 0
                self._can_deactive = True
            return True

    def draw(self):
        if self._is_active_input and self.keyboard_type == 'virtual':
            set_color(*self.style.get('bg-color'))
            kx, ky = self.keyboard.to_window(*self.keyboard.center)
            kx, ky = self.to_widget(kx, ky)
            drawLine([self.center[0], self.center[1], kx, ky])

        if self.password:
            pw = '*' * len(self.label)
            old_label = self.label
            self.label = pw
        super(MTTextInput, self).draw()
        if self.password:
            self.label = old_label

    def draw_background(self):
        set_color(*self.style.get('bg-color'))
        state = 'active' is self._is_active_input or None
        drawCSSRectangle(pos=self.pos, size=self.size,
                         style=self.style, state=state)

    def draw_label(self, dx=0, dy=0):
        # before drawing label, adjust the viewport position of the label. it's
        # a new feature that permit to draw only a part of the label.
        width = self._used_label.width - self.width
        scroll_x = self._scroll_x if self.scroll else 0
        if self.is_active_input:
            scroll_x = width + scroll_x
        if scroll_x > width:
            scroll_x = width
        if scroll_x < 0:
            scroll_x = 0
        self.viewport_pos = (scroll_x, 0)
        super(MTTextInput, self).draw_label(dx, dy)

    def on_update(self):
        super(MTTextInput, self).on_update()
        if self._switch:
            self._switch.pos = self.x + self.width - 60, self.y + self.height
Esempio n. 5
0
class MTTextInput(MTButton):
    '''
    A text input widget is a simple label widget that will pop up
    a virtual keyboard when touched any input of the virtual keyboard
    will then have effect on the TextInput widget.

    :Parameters:
        `keyboard`: MTVkeyboard object, default to None
            Use another MTVKeyboard than the default one
        `keyboard_to_root`: bool, defaults to False.
            Indicate whether the keyboard should be attached to the root
            window. If True, this will have the effect of the keyboard being
            raised above other widgets.
        `keyboard_type`: str, default to config.
            Configuration section is 'widgets', token 'keyboard_type'.
            Can be one of 'virtual' or 'real'. If real, the virtual keyboard
            will be not showed
        `password`: bool, default to False
            If True, the label will be showed with star
        `group`: str, default to random
            If the group is the same for multiple text input
            You can switch between them with TAB, and use the same keyboard.
        `switch`: bool, default to True
            If True, a switch button will be show to switch from real or virtual
            keyboard
    :Events:
        `on_text_change` (text)
            Fired when the content of text input is changed
        `on_text_validate` ()
            Fired when the text is validate (when ENTER is hit on keyboard)
    '''
    _group_id = 0
    _group = {}

    def __init__(self, **kwargs):
        kwargs.setdefault('anchor_x', 'left')
        kwargs.setdefault('anchor_y', 'center')
        kwargs.setdefault('halign', 'left')
        kwargs.setdefault('keyboard', None)
        kwargs.setdefault('keyboard_to_root', False)
        kwargs.setdefault('password', False)
        kwargs.setdefault('group', None)
        kwargs.setdefault('switch', True)
        kwargs.setdefault('keyboard_type',
            pymt_config.get('widgets', 'keyboard_type'))
        super(MTTextInput, self).__init__(**kwargs)
        self._keyboard = kwargs.get('keyboard')
        self.is_active_input = False
        self.password = kwargs.get('password')

        # initialize group on random if nothing is set
        self._groupname = kwargs.get('group')
        if self._groupname is None:
            MTTextInput._group_id += 1
            self._groupname = 'uniqgroup%d' % MTTextInput._group_id
        # first time ? create the group
        if not self._groupname in self._group:
            self.group['keyboard'] = None
            self.group['widgets'] = []
        self.group['widgets'].append(self)

        self.register_event_type('on_text_change')
        self.register_event_type('on_text_validate')

        # save original color for error
        self._notify_bg_color = self.style['bg-color']
        self._notify_bg_color_active = self.style['bg-color-active']
        self._notify_animation = None

        # switch button between vkeyboard or hardware
        self._switch = None
        if kwargs.get('switch'):
            self._switch = MTButton(
                label=kwargs.get('keyboard_type'), cls='switch-button',
                size=(60, 20), font_size=8,
                pos=(self.x + self.width - 60, self.y + self.height))

        self.keyboard_type = kwargs.get('keyboard_type')
        self.keyboard_to_root = kwargs.get('keyboard_to_root')

        self.interesting_keys = {8: 'backspace', 13: 'enter', 127: 'del',
                                 271: 'enter', 273: 'cursor_up', 274: 'cursor_down',
                                 275: 'cursor_right', 276: 'cursor_left',
                                 278: 'cursor_home', 279: 'cursor_end',
                                 280: 'cursor_pgup', 281: 'cursor_pgdown'}

    def on_resize(self, *largs):
        if hasattr(self, '_switch'):
            self._switch.pos = self.x + self.width - 60, self.y + self.height
        return super(MTTextInput, self).on_resize(*largs)

    def _get_keyboard(self):
        if not self._keyboard:
            self._keyboard = self.group['keyboard']
            if self._keyboard is None:
                self._keyboard = MTVKeyboard()
                self.group['keyboard'] = self._keyboard
        return self._keyboard
    def _set_keyboard(self, value):
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
        self._keyboard = value
    keyboard = property(_get_keyboard, _set_keyboard)

    def on_press(self, touch):
        if self.is_active_input:
            self.hide_keyboard()
        else:
            self.show_keyboard()

    def _kbd_on_text_change(self, value):
        self.label = value
        self.dispatch_event('on_text_change', value)

    def _kbd_on_key_up(self, key, repeat=False):
        displayed_str, internal_str, internal_action, scale = key
        if internal_action is None:
            return
        elif internal_action == 'enter':
            self.hide_keyboard()
            self.dispatch_event('on_text_validate')
        elif internal_action == 'escape':
            self.hide_keyboard()

    def on_text_validate(self):
        pass

    def on_text_change(self, text):
        pass

    @property
    def group(self):
        '''Return information (keyboard/widget list) from the current
        group of the widget'''
        if not self._groupname in self._group:
            self._group[self._groupname] = {}
        return self._group[self._groupname]

    def notify_error(self):
        '''Call this function to make animation on background as an error
        '''
        error_color = self.style['bg-color-error']
        if self._notify_animation is not None:
            self._notify_animation.stop()
        self.style['bg-color'] = self._notify_bg_color
        self.style['bg-color-active'] = self._notify_bg_color_active
        self._notify_animation = self.do(Animation(
            style={'bg-color': error_color, 'bg-color-active': error_color},
            f=lambda x: 1 - AnimationAlpha.ease_in_out_quart(x)))

    def deactivate_group(self):
        '''Deactivate all widgets in the group'''
        for w in self.group['widgets']:
            w.hide_keyboard()

    def focus_next(self):
        '''Focus the next textinput in the list'''
        idx = (self.group['widgets'].index(self) + 1)
        idx = idx % len(self.group['widgets'])
        widget = self.group['widgets'][idx]
        widget.show_keyboard()

    def show_keyboard(self):
        '''Show the associed keyboard of this widget.'''
        if self.is_active_input:
            return
        self.deactivate_group()
        self.is_active_input = True

        # activate switch button if necessary
        if self._switch:
            self.add_widget(self._switch)

        # activate the real keyboard
        w = self.get_root_window()
        w.push_handlers(on_key_down=self._window_on_key_down,
                        on_key_up=self._window_on_key_up)

        # activate the virtual keyboard
        if self.keyboard_type == 'virtual':
            to_root = self.keyboard_to_root
            w = self.get_root_window() if to_root else self.get_parent_window()
            w.add_widget(self.keyboard)
        if self.keyboard is not None:
            self._keyboard.push_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )
            self._keyboard.text = self.label

    def hide_keyboard(self):
        '''Hide the associed keyboard of this widget.'''
        if not self.is_active_input:
            return
        if self._switch:
            self.remove_widget(self._switch)
        parent = self.keyboard.parent
        if parent is not None:
            # If keyboard type is real, the keyboard is not attached to any
            # parent widget.
            parent.remove_widget(self.keyboard)
        w = self.get_root_window()
        w.remove_handlers(on_key_down=self._window_on_key_down,
                          on_key_up=self._window_on_key_up)
        self.is_active_input = False
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up
            )

    def on_update(self):
        super(MTTextInput, self).on_update()
        if self._switch:
            self._switch.pos = self.x + self.width - 60, self.y + self.height

    def draw(self):
        if self.is_active_input and self.keyboard_type == 'virtual':
            set_color(*self.style.get('bg-color'))
            kx, ky = self.keyboard.to_window(*self.keyboard.center)
            kx, ky = self.to_widget(kx, ky)
            drawLine([self.center[0], self.center[1], kx, ky])

        if self.password:
            pw = '*' * len(self.label)
            old_label = self.label
            self.label = pw
        super(MTTextInput, self).draw()
        if self.password:
            self.label = old_label

    def draw_background(self):
        if self.is_active_input:
            set_color(*self.style.get('bg-color'))
            drawCSSRectangle(pos=self.pos, size=self.size, style=self.style,
                            state='active')
        else:
            set_color(*self.style.get('bg-color'))
            drawCSSRectangle(pos=self.pos, size=self.size, style=self.style)

    def _window_on_key_down(self, key, scancode=None, unicode=None):
        if key == 27: # escape
            self.hide_keyboard()
            return True
        elif key == 9: # tab
            self.focus_next()
            return True
        if not self.keyboard:
            return
        k = self.interesting_keys.get(key)
        if k:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_down', key)
        else:
            if unicode is not None:
                self.keyboard.text += unicode
            else:
                self.keyboard.text += chr(key)

    def _window_on_key_up(self, key, scancode=None, unicode=None):
        k = self.interesting_keys.get(key)
        if k and self.keyboard:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_up', key)

    def _get_value(self):
        return self.label
    def _set_value(self, value):
        self.label = value
    value = property(
        lambda self: self._get_value(),
        lambda self, x: self._set_value(x),
        doc='Get/set the value of the label')

    def _get_keyboard_type(self):
        return self._keyboard_type
    def _set_keyboard_type(self, t):
        self.hide_keyboard()
        self._keyboard_type = t
        if self.is_active_input:
            self.show_keyboard()
    keyboard_type = property(
        lambda self: self._get_keyboard_type(),
        lambda self, x: self._set_keyboard_type(x),
        doc='Get/set the keyboard type to use')

    #
    # Needed if the switch button must be replaced
    #

    def on_touch_down(self, touch):
        if self._switch and self._switch.collide_point(*touch.pos):
            self.hide_keyboard()
            if self.keyboard_type == 'virtual':
                self.keyboard_type = 'real'
            else:
                self.keyboard_type = 'virtual'
            self._switch.label = self.keyboard_type
            self.show_keyboard()
            return True
        return super(MTTextInput, self).on_touch_down(touch)
Esempio n. 6
0
class MTTextInput(MTButton):
    '''
    A text input widget is a simple label widget that will pop up
    a virtual keyboard when touched any input of the virtual keyboard
    will then have effect on the TextInput widget.

    .. versionadded:: 0.5.1
        Support of scrolling textarea, controlled by `scroll` attribute. You can
        now scroll inside the text area without editing text.

    :Parameters:
        `keyboard`: MTVkeyboard object, default to None
            Use another MTVKeyboard than the default one
        `keyboard_to_root`: bool, defaults to False.
            Indicate whether the keyboard should be attached to the root
            window. If True, this will have the effect of the keyboard being
            raised above other widgets.
        `keyboard_type`: str, default to config.
            Configuration section is 'widgets', token 'keyboard_type'.
            Can be one of 'virtual' or 'real'. If real, the virtual keyboard
            will be not showed
        `password`: bool, default to False
            If True, the label will be showed with star
        `group`: str, default to random
            If the group is the same for multiple text input
            You can switch between them with TAB, and use the same keyboard.
        `switch`: bool, default to True
            If True, a switch button will be show to switch from real or virtual
            keyboard
        `scroll`: bool, default to True
            If True, the scrolling of a text area is allowed.
        `scroll_trigger`: int, default to 10
            If scrolling is detected, don't fire event to show/hide keyboard.
    :Events:
        `on_text_change` (text)
            Fired when the content of text input is changed
        `on_text_validate` ()
            Fired when the text is validate (when ENTER is hit on keyboard)
    '''
    _group_id = 0
    _group = {}

    def __init__(self, **kwargs):
        kwargs.setdefault('anchor_x', 'left')
        kwargs.setdefault('anchor_y', 'center')
        kwargs.setdefault('halign', 'left')
        kwargs.setdefault('keyboard', None)
        kwargs.setdefault('keyboard_to_root', False)
        kwargs.setdefault('group', None)
        kwargs.setdefault('switch', True)
        kwargs.setdefault('keyboard_type',
                          pymt_config.get('widgets', 'keyboard_type'))

        super(MTTextInput, self).__init__(**kwargs)

        self.register_event_type('on_text_change')
        self.register_event_type('on_text_validate')

        self._scroll_x = 0
        self._can_deactive = True
        self._keyboard = kwargs.get('keyboard')
        self._is_active_input = False

        #: Boolean to activate the password mode on the textinput.
        #: If true, it will show star instead of real characters
        self.password = kwargs.get('password', False)

        #: Boolean to control the scrolling of the textinput content
        self.scroll = kwargs.get('scroll', True)

        #: If the scroll if more than scroll_trigger, no event will be
        #: dispatched, and no show/hide of keyboard.
        self.scroll_trigger = kwargs.get('scroll_trigger', 10)

        #: Keyboard type (virtual or real)
        self.keyboard_type = kwargs.get('keyboard_type')

        #: If True, the keyboard is added to root window, not to parent window.
        self.keyboard_to_root = kwargs.get('keyboard_to_root')

        # initialize group on random if nothing is set
        self._groupname = kwargs.get('group')
        if self._groupname is None:
            MTTextInput._group_id += 1
            self._groupname = 'uniqgroup%d' % MTTextInput._group_id
        # first time ? create the group
        if not self._groupname in self._group:
            self.group['keyboard'] = None
            self.group['widgets'] = []
        self.group['widgets'].append(self)

        # save original color for error
        self._notify_bg_color = self.style['bg-color']
        self._notify_bg_color_active = self.style['bg-color-active']
        self._notify_animation = None

        # switch button between vkeyboard or hardware
        self._switch = None
        if kwargs.get('switch'):
            self._switch = MTButton(label=kwargs.get('keyboard_type'),
                                    cls='switch-button',
                                    size=(60, 20),
                                    font_size=8,
                                    pos=(self.x + self.width - 60,
                                         self.y + self.height))

        self.interesting_keys = {
            8: 'backspace',
            13: 'enter',
            127: 'del',
            271: 'enter',
            273: 'cursor_up',
            274: 'cursor_down',
            275: 'cursor_right',
            276: 'cursor_left',
            278: 'cursor_home',
            279: 'cursor_end',
            280: 'cursor_pgup',
            281: 'cursor_pgdown',
            303: 'shift_L',
            304: 'shift_R'
        }

    def on_resize(self, *largs):
        if hasattr(self, '_switch'):
            self._switch.pos = self.x + self.width - 60, self.y + self.height
        return super(MTTextInput, self).on_resize(*largs)

    def _get_keyboard(self):
        if not self._keyboard:
            self._keyboard = self.group['keyboard']
            if self._keyboard is None:
                self._keyboard = MTVKeyboard()
                self.group['keyboard'] = self._keyboard
        return self._keyboard

    def _set_keyboard(self, value):
        if self._keyboard is not None:
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up,
                on_key_down=self._kbd_on_key_down)
        self._keyboard = value

    keyboard = property(_get_keyboard, _set_keyboard)

    def on_release(self, touch):
        if not self._can_deactive:
            return
        if self._is_active_input:
            self.hide_keyboard()
        else:
            self.show_keyboard()

    def _kbd_on_text_change(self, value):
        # reset scrolling when text is typed
        self._scroll_x = 0
        self.label = value
        self.dispatch_event('on_text_change', value)

    def _kbd_on_key_down(self, key, repeat=False):
        displayed_str, internal_str, internal_action, scale = key
        if internal_action is None:
            return
        elif internal_action == 'enter':
            self.hide_keyboard()
            self.dispatch_event('on_text_validate')
        elif internal_action == 'escape':
            self.hide_keyboard()

    def _kbd_on_key_up(self, key, repeat=False):
        pass

    def on_text_validate(self):
        pass

    def on_text_change(self, text):
        pass

    @property
    def group(self):
        '''Return information (keyboard/widget list) from the current
        group of the widget'''
        if not self._groupname in self._group:
            self._group[self._groupname] = {}
        return self._group[self._groupname]

    def notify_error(self):
        '''Call this function to make animation on background as an error
        '''
        error_color = self.style['bg-color-error']
        if self._notify_animation is not None:
            self._notify_animation.stop()
        self.style['bg-color'] = self._notify_bg_color
        self.style['bg-color-active'] = self._notify_bg_color_active
        self._notify_animation = self.do(
            Animation(style={
                'bg-color': error_color,
                'bg-color-active': error_color
            },
                      f=lambda x: 1 - AnimationAlpha.ease_in_out_quart(x)))

    def deactivate_group(self):
        '''Deactivate all widgets in the group'''
        for w in self.group['widgets']:
            w.hide_keyboard()

    def focus_next(self):
        '''Focus the next textinput in the list'''
        idx = (self.group['widgets'].index(self) + 1)
        idx = idx % len(self.group['widgets'])
        widget = self.group['widgets'][idx]
        widget.show_keyboard()

    def show_keyboard(self):
        '''Show the associed keyboard of this widget.'''
        if self._is_active_input:
            return
        self.deactivate_group()
        self._is_active_input = True

        # activate switch button if necessary
        if self._switch:
            self.add_widget(self._switch)

        # activate the real keyboard
        w = self.get_root_window()
        w.push_handlers(on_key_down=self._window_on_key_down,
                        on_key_up=self._window_on_key_up)

        # activate the virtual keyboard
        if self.keyboard_type == 'virtual':
            to_root = self.keyboard_to_root
            w = self.get_root_window() if to_root else self.get_parent_window()
            w.add_widget(self.keyboard)
        if self.keyboard is not None:
            self._keyboard.reset_repeat()
            self._keyboard.push_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up,
                on_key_down=self._kbd_on_key_down)
            self._keyboard.text = self.label

    def hide_keyboard(self):
        '''Hide the associed keyboard of this widget.'''
        if not self._is_active_input:
            return
        if self._switch:
            self.remove_widget(self._switch)
        parent = self.keyboard.parent
        if parent is not None:
            # If keyboard type is real, the keyboard is not attached to any
            # parent widget.
            parent.remove_widget(self.keyboard)
        w = self.get_root_window()
        w.remove_handlers(on_key_down=self._window_on_key_down,
                          on_key_up=self._window_on_key_up)
        self._is_active_input = False
        if self._keyboard is not None:
            self._keyboard.reset_repeat()
            self._keyboard.remove_handlers(
                on_text_change=self._kbd_on_text_change,
                on_key_up=self._kbd_on_key_up,
                on_key_down=self._kbd_on_key_down)

    def _window_on_key_down(self, key, scancode=None, unicode=None):
        modifiers = getWindow().modifiers
        if key == ord('v') and 'ctrl' in modifiers:
            text = Clipboard.get('text/plain')
            if text:
                self.keyboard.text += text
            return True

        if key == 27:  # escape
            self.hide_keyboard()
            return True
        elif key == 9:  # tab
            self.focus_next()
            return True
        if not self.keyboard:
            return
        k = self.interesting_keys.get(key)
        if k:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_down', key)
        else:
            if unicode is not None:
                self.keyboard.text += unicode
            else:
                self.keyboard.text += chr(key)

    def _window_on_key_up(self, key, scancode=None, unicode=None):
        k = self.interesting_keys.get(key)
        if k and self.keyboard:
            key = (None, None, k, 1)
            self.keyboard.dispatch_event('on_key_up', key)

    def _get_value(self):
        return self.label

    def _set_value(self, value):
        self.label = value

    value = property(_get_value,
                     _set_value,
                     doc='Get/set the value of the label')

    def _get_keyboard_type(self):
        return self._keyboard_type

    def _set_keyboard_type(self, t):
        self.hide_keyboard()
        self._keyboard_type = t
        if self._is_active_input:
            self.show_keyboard()

    keyboard_type = property(_get_keyboard_type,
                             _set_keyboard_type,
                             doc='Get/set the keyboard type to use')

    @property
    def is_active_input(self):
        '''Return True if the input is active'''
        return self._is_active_input

    #
    # Overload the touch down/move/up for :
    #   1. handle the switch button
    #   2. be able to scroll inside the text input
    #

    def on_touch_down(self, touch):
        # check if it's the switch button that collide.
        if self._switch and self._switch.collide_point(*touch.pos):
            self.hide_keyboard()
            if self.keyboard_type == 'virtual':
                self.keyboard_type = 'real'
            else:
                self.keyboard_type = 'virtual'
            self._switch.label = self.keyboard_type
            self.show_keyboard()
            return True
        # reset scrolling when a touch is comming
        # FIXME: can be not accurate if many touch are comming.
        if self.collide_point(*touch.pos):
            self._scroll_x = 0
            self._can_deactive = True
        # and only now, dispatch as usual.
        return super(MTTextInput, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if not super(MTTextInput, self).on_touch_move(touch):
            return
        # we got a valid touch,
        self._scroll_x += touch.dxpos - touch.x
        self._can_deactive = True
        if abs(touch.x - touch.oxpos) > self.scroll_trigger and self.scroll:
            self._can_deactive = False
        return True

    def on_touch_up(self, touch):
        if super(MTTextInput, self).on_touch_up(touch):
            if not self.is_active_input:
                self._scroll_x = 0
                self._can_deactive = True
            return True

    def draw(self):
        if self._is_active_input and self.keyboard_type == 'virtual':
            set_color(*self.style.get('bg-color'))
            kx, ky = self.keyboard.to_window(*self.keyboard.center)
            kx, ky = self.to_widget(kx, ky)
            drawLine([self.center[0], self.center[1], kx, ky])

        if self.password:
            pw = '*' * len(self.label)
            old_label = self.label
            self.label = pw
        super(MTTextInput, self).draw()
        if self.password:
            self.label = old_label

    def draw_background(self):
        set_color(*self.style.get('bg-color'))
        state = 'active' is self._is_active_input or None
        drawCSSRectangle(pos=self.pos,
                         size=self.size,
                         style=self.style,
                         state=state)

    def draw_label(self, dx=0, dy=0):
        # before drawing label, adjust the viewport position of the label. it's
        # a new feature that permit to draw only a part of the label.
        width = self._used_label.width - self.width
        scroll_x = self._scroll_x if self.scroll else 0
        if self.is_active_input:
            scroll_x = width + scroll_x
        if scroll_x > width:
            scroll_x = width
        if scroll_x < 0:
            scroll_x = 0
        self.viewport_pos = (scroll_x, 0)
        super(MTTextInput, self).draw_label(dx, dy)

    def on_update(self):
        super(MTTextInput, self).on_update()
        if self._switch:
            self._switch.pos = self.x + self.width - 60, self.y + self.height