def __init__(self,
              device_bank_registry=None,
              info_layer=None,
              delete_handler=None,
              *a,
              **k):
     super(DeviceNavigationComponent, self).__init__(*a, **k)
     self._make_navigation_node = partial(
         make_navigation_node, device_bank_registry=device_bank_registry)
     self._state_buttons = None
     self._delete_handler = delete_handler
     self._device_list = self.register_component(
         ScrollableListWithTogglesComponent())
     self._on_selection_clicked_in_controller.subject = self._device_list
     self._on_selection_changed_in_controller.subject = self._device_list
     self._on_state_changed_in_controller.subject = self._device_list
     self._current_node = None
     self._message_box = self.register_component(
         MessageBoxComponent(layer=info_layer, is_enabled=False))
     self._message_box.text = consts.MessageBoxText.EMPTY_DEVICE_CHAIN
     self._selected_track = None
     self._on_selected_track_changed.subject = self.song().view
     with inject(selection=const(NamedTuple(
             selected_device=None))).everywhere():
         self._on_selected_track_changed()
Exemple #2
0
class DeviceNavigationComponent(CompoundComponent):
    """
    Component that displays an overview of the devices in the current
    track and navigates in its hierarchy.
    """
    def __init__(self, device_bank_registry=None, *a, **k):
        super(DeviceNavigationComponent, self).__init__(*a, **k)
        self._make_navigation_node = partial(
            make_navigation_node, device_bank_registry=device_bank_registry)
        self._state_buttons = None
        self._delete_button = None
        self._device_list = self.register_component(
            ScrollableListWithTogglesComponent())
        self._on_selection_clicked_in_controller.subject = self._device_list
        self._on_selection_changed_in_controller.subject = self._device_list
        self._on_state_changed_in_controller.subject = self._device_list
        self._current_node = None
        self._message_box = self.register_component(MessageBoxComponent())
        self._message_box.text = consts.MessageBoxText.EMPTY_DEVICE_CHAIN
        self._message_box.set_enabled(False)
        self._selected_track = None
        self._on_selected_track_changed.subject = self.song().view
        with inject(selection=lambda: NamedTuple(selected_device=None)
                    ).everywhere():
            self._on_selected_track_changed()

    info_layer = forward_property('_message_box')('layer')

    @property
    def current_node(self):
        return self._current_node

    def set_select_buttons(self, select_buttons):
        self._device_list.set_select_buttons(select_buttons)

    def set_state_buttons(self, state_buttons):
        self._state_buttons = state_buttons
        self._device_list.set_state_buttons(state_buttons)

    def set_exit_button(self, exit_button):
        raise not exit_button or exit_button.is_momentary() or AssertionError
        self._on_exit_value.subject = exit_button
        self._update_exit_button()

    def set_enter_button(self, enter_button):
        raise not enter_button or enter_button.is_momentary() or AssertionError
        self._on_enter_value.subject = enter_button
        self._update_enter_button()

    def set_delete_button(self, button):
        raise not button or button.is_momentary() or AssertionError
        self._delete_button = button

    def set_display_line(self, line):
        self._device_list.set_display_line(line)

    def set_blank_display_line(self, line):
        if line:
            line.reset()

    @property
    def selected_object(self):
        selected = None
        if self._current_node:
            children = self._current_node.children
            option = self._device_list.selected_option
            if children and in_range(option, 0, len(children)):
                _, selected = children[option]
        return selected

    def back_to_top(self):
        if consts.PROTO_SONG_IS_ROOT:
            self._set_current_node(self._make_navigation_node(self.song()))
        else:
            self._set_current_node(
                self._make_navigation_node(self._selected_track))

    @subject_slot('selected_track')
    def _on_selected_track_changed(self):
        self._selected_track = self.song().view.selected_track
        self._on_selected_device_changed.subject = self._selected_track.view
        self.back_to_top()

    @subject_slot('selected_device')
    def _on_selected_device_changed(self):
        selected_device = self._selected_track.view.selected_device
        if selected_device == None:
            self._set_current_node(self._make_exit_node())
            return
        is_just_default_child_selection = False
        if self._current_node and self._current_node.children:
            selected = self.selected_object
            if isinstance(selected, Live.DrumPad.DrumPad) and find_if(
                    lambda pad: pad.chains and pad.chains[0].devices and pad.
                    chains[0].devices[0] == selected_device,
                    selected.canonical_parent.drum_pads):
                is_just_default_child_selection = True
            if isinstance(
                    selected, Live.Chain.Chain
            ) and selected_device and selected_device.canonical_parent == selected and selected.devices[
                    0] == selected_device:
                is_just_default_child_selection = True
        if not is_just_default_child_selection:
            if selected_device:
                target = selected_device.canonical_parent
                node = (not self._current_node or self._current_node.object !=
                        target) and self._make_navigation_node(
                            target, is_entering=False)
                self._set_current_node(node)

    def _set_current_node(self, node):
        if node is None:
            return
        self.disconnect_disconnectable(self._current_node)
        self._current_node = node
        self.register_slot_manager(node)
        self._on_children_changed_in_node.subject = node
        self._on_selected_child_changed_in_node.subject = node
        self._on_state_changed_in_node.subject = node
        self._on_children_changed_in_node()
        for index, value in enumerate(node.state):
            self._on_state_changed_in_node(index, value)

        node.preselect()

    @depends(selection=lambda: NamedTuple(selected_device=None))
    def _update_info(self, selection=None):
        if self._selected_track != None and len(
                self._selected_track.devices
        ) == 0 and selection.selected_device == None:
            self._message_box.set_enabled(True)
        else:
            self._message_box.set_enabled(False)

    def update(self):
        if self.is_enabled():
            self._update_enter_button()
            self._update_exit_button()
            self._update_info()

    @subject_slot('state')
    def _on_state_changed_in_node(self, index, value):
        self._device_list.set_option_state(index, value)

    @subject_slot('children')
    def _on_children_changed_in_node(self):
        names = map(lambda x: x[0], self._current_node.children)
        self._device_list.option_names = names
        self._device_list.selected_option = self._current_node.selected_child
        self._update_enter_button()
        self._update_exit_button()

    @subject_slot('selected_child')
    def _on_selected_child_changed_in_node(self, index):
        self._device_list.selected_option = index
        self._update_enter_button()
        self._update_exit_button()
        self._update_info()

    @subject_slot('toggle_option')
    def _on_state_changed_in_controller(self, index, value):
        if self._current_node:
            self._current_node.set_state(index, value)
            if self._current_node.state[index] != value:
                self._device_list.set_option_state(
                    index, self._current_node.state[index])

    @subject_slot('change_option')
    def _on_selection_changed_in_controller(self, value):
        self._current_node.selected_child = value
        self._update_enter_button()
        self._update_exit_button()

    @subject_slot('press_option', in_front=True)
    def _on_selection_clicked_in_controller(self, index):
        if self._delete_button and self._delete_button.is_pressed():
            if self._current_node:
                self._current_node.delete_child(index)
            return True
        elif consts.PROTO_FAST_DEVICE_NAVIGATION:
            if self._device_list.selected_option == index:
                self._set_current_node(self._make_enter_node())
                return True
            elif not in_range(index, 0, len(self._device_list.option_names)):
                self._set_current_node(self._make_exit_node())
                return True
        return index == None

    @subject_slot('value')
    def _on_enter_value(self, value):
        if self.is_enabled():
            self._update_enter_button()
            if value:
                self._set_current_node(self._make_enter_node())

    @subject_slot('value')
    def _on_exit_value(self, value):
        if self.is_enabled():
            self._update_exit_button()
            if value:
                self._set_current_node(self._make_exit_node())

    def _make_enter_node(self):
        if self._device_list.selected_option >= 0:
            if self._device_list.selected_option < len(
                    self._current_node.children):
                child = self._current_node.children[
                    self._device_list.selected_option][1]
                return self._make_navigation_node(child, is_entering=True)

    def _make_exit_node(self):
        return self._make_navigation_node(self._current_node
                                          and self._current_node.parent,
                                          is_entering=False)

    def _update_enter_button(self):
        button = self._on_enter_value.subject
        if self.is_enabled() and button:
            with disconnectable(self._make_enter_node()) as node:
                if node:
                    button.set_light(button.is_pressed())
                else:
                    button.set_light('DefaultButton.Disabled')

    def _update_exit_button(self):
        button = self._on_exit_value.subject
        if self.is_enabled() and button:
            with disconnectable(self._make_exit_node()) as node:
                if node:
                    button.set_light(button.is_pressed())
                else:
                    button.set_light('DefaultButton.Disabled')