Ejemplo n.º 1
0
class Keyboard(Container):
    """ Keyboard class. """

    def __init__(self, util, bb, callback):
        """ Initializer

        :param util: utility object
        :param bb: bounding box
        :param callback: function to call on Enter
        """
        Container.__init__(self, util, bb, (0, 0, 0))
        self.bb = bb
        self.util = util
        self.config = util.config
        self.callback = callback
        self.move_listeners = []
        self.text_listeners = []
        self.buttons = {}
        self.factory = Factory(util)
        self.caps = False
        self.text = ""

        self.controls = ["Caps", "Del", "abc", "ABC", "123", "#+=", "Enter"]
        self.keyboards = {}
        self.current_keyboard_type = None
        self.create_keyboard(KEYBOARD_abc, LAYOUT_1, TRANSITION_MAP_1)

    def get_layout(self, span):
        """ Create layout

        :param span:
        :return: layout
        """
        h = int(self.bb.h / 4)
        u = int((self.bb.w) / 20)
        g = self.bb.h - (h * 4)

        layout = []
        for k, n in enumerate(span):
            z = 0
            for i, m in enumerate(n):
                x = (z * u) + 1
                y = self.bb.y + (k * h) + 1
                w = (u * m) - 1
                d = h - 1
                if k == 3 and (g - 1) > 0:
                    d += (g - 1)
                if (k == 0 and i == 9) or (k == 1 and i == 10) or (k == 2 and i == 8) or (k == 3 and i == 2):
                    w -= 1
                layout.append(pygame.Rect(x, y, w, d))
                z += m

        return layout

    def create_keyboard(self, keyboard_type, span, transition_map):
        """ Create keyboard

        :param keyboard_type: type
        :param span: span
        :param transition_map: transition map
        :return:
        """
        layout = self.get_layout(span)
        buttons = []
        keys = None
        self.current_keyboard_type = keyboard_type

        try:
            buttons = self.keyboards[keyboard_type]
            self.components = buttons
            return
        except:
            pass

        if keyboard_type == KEYBOARD_abc:
            keys = KEYBOARD_1
        elif keyboard_type == KEYBOARD_ABC:
            keys = KEYBOARD_2
        elif keyboard_type == KEYBOARD_123:
            keys = KEYBOARD_3
        elif keyboard_type == KEYBOARD_symbol:
            keys = KEYBOARD_4

        for i, k in enumerate(keys):
            if not k:
                c = Component(self.util, layout[i], bgr=self.config[COLORS][COLOR_DARK])
                c.name = "gap" + str(i)
                buttons.append(c)
                continue
            s = State()
            s.index = i
            s.name = k
            s.l_name = k
            s.comparator_item = s.index
            s.bgr = self.config[COLORS][COLOR_DARK]
            s.show_bgr = True
            s.bounding_box = layout[i]
            s.key_map = transition_map[i]
            button = self.factory.create_keyboard_menu_button(s, layout[i], self.press_key)
            buttons.append(button)
        buttons[0].set_selected(True)
        self.keyboards[keyboard_type] = buttons
        self.components = buttons

        if keyboard_type != KEYBOARD_abc:
            self.set_observers()

    def press_key(self, state):
        """ Key press handler

        :param state: button state
        """
        if state.name not in self.controls and len(self.text) == 32:
            return

        self.unselect()
        state.event_origin.set_selected(True)

        if state.name == "Caps" or state.name == "ABC":
            if state.name == "Caps":
                self.caps = not self.caps
            if self.caps:
                self.create_keyboard(KEYBOARD_ABC, LAYOUT_1, TRANSITION_MAP_1)
            else:
                self.create_keyboard(KEYBOARD_abc, LAYOUT_1, TRANSITION_MAP_1)
        elif state.name == "123":
            self.create_keyboard(KEYBOARD_123, LAYOUT_2, TRANSITION_MAP_2)
        elif state.name == "#+=":
            self.create_keyboard(KEYBOARD_symbol, LAYOUT_3, TRANSITION_MAP_3)
        elif state.name == "Space":
            self.text += " "
            self.notify_text_listeners(self.text)
        elif state.name == "Del":
            if len(self.text) > 0:
                self.text = self.text[0: -1]
                self.notify_text_listeners(self.text)
        elif state.name == "Enter":
            if len(self.text) == 0:
                return
            s = State()
            s.callback_var = self.text
            self.callback(s)
        else:
            self.text += state.name
            self.notify_text_listeners(self.text)

        self.clean_draw_update()

    def delete(self, state):
        """ Delete character

        :param state: button state
        """
        self.text = ""
        self.notify_text_listeners(self.text)

    def add_move_listener(self, listener):
        """ Add arrow button event listener

        :param listener: event listener
        """
        if listener not in self.move_listeners:
            self.move_listeners.append(listener)

    def notify_move_listeners(self):
        """ Notify arrow button event listeners """

        for listener in self.move_listeners:
            listener(None)

    def add_text_listener(self, listener):
        """ Add text event listener

        :param listener: event listener
        """
        if listener not in self.text_listeners:
            self.text_listeners.append(listener)

    def notify_text_listeners(self, text):
        """ Notify all text listeners

        :param state: button state
        """
        for listener in self.text_listeners:
            listener(text)

    def unselect(self):
        """ Unselect currently selected button """

        for c in self.components:
            if isinstance(c, Button) and c.selected:
                c.set_selected(False)
                return

    def get_current_key(self):
        """ Get currently selected key

        :return: selected component
        """
        for c in self.components:
            if isinstance(c, Button) and c.selected:
                return c
        return self.components[0]

    def select_key_by_index(self, index):
        """ Slecte key by index

        :param index:
        :return:
        """
        for c in self.components:
            if isinstance(c, Button) and c.state.index == index:
                c.set_selected(True)
                return

    def handle_event(self, event):
        """ Menu event handler

        :param event: menu event
        """
        if not self.visible: return

        if event.type == USER_EVENT_TYPE and event.sub_type == SUB_TYPE_KEYBOARD and event.action == pygame.KEYUP:
            key_events = [kbd_keys[KEY_LEFT], kbd_keys[KEY_RIGHT], kbd_keys[KEY_UP], kbd_keys[KEY_DOWN], kbd_keys[KEY_SELECT]]

            if event.keyboard_key not in key_events:
                Container.handle_event(self, event)
                return

            current_key = self.get_current_key()

            if event.keyboard_key == kbd_keys[KEY_SELECT]:
                self.press_key(current_key.state)
                self.notify_move_listeners()
                return

            transition_map = current_key.state.key_map
            next_key_index = 0
            self.unselect()

            if event.keyboard_key == kbd_keys[KEY_LEFT]:
                next_key_index = transition_map[0]
            elif event.keyboard_key == kbd_keys[KEY_RIGHT]:
                next_key_index = transition_map[1]
            elif event.keyboard_key == kbd_keys[KEY_UP]:
                next_key_index = transition_map[2]
            elif event.keyboard_key == kbd_keys[KEY_DOWN]:
                next_key_index = transition_map[3]

            self.select_key_by_index(next_key_index)
            self.clean_draw_update()
            self.notify_move_listeners()
        else:
            Container.handle_event(self, event)

    def set_observers(self):
        """ Set observers """

        for b in self.components:
            if not isinstance(b, Button):
                continue
            if self.update_observer and self.press:
                b.add_press_listener(self.update_observer)
            if self.update_observer and self.release:
                b.add_release_listener(self.update_observer)
            if self.redraw_observer:
                b.add_release_listener(self.redraw_observer)
        if self.redraw_observer:
            self.add_move_listener(self.redraw_observer)

    def add_menu_observers(self, update_observer, redraw_observer=None, press=True, release=True):
        """ Add menu observer

        :param update_observer: observer for updating menu
        :param redraw_observer: observer to redraw the whole screen
        :param press: True - add observer as press listener (default)
        :param release: True - add observer as release listener (default)
        """
        self.update_observer = update_observer
        self.redraw_observer = redraw_observer
        self.press = press
        self.release = release

        self.set_observers()