def __init__(self, device_bank_registry=None, banking_info=None, delete_handler=None, track_list_component=None, *a, **k): assert banking_info is not None assert device_bank_registry is not None assert track_list_component is not None self._flattened_chain = FlattenedDeviceChain(collect_devices) self._track_decorator = DecoratorFactory() self._modes = NullModes() self.move_device = None super(DeviceNavigationComponent, self).__init__(item_provider=self._flattened_chain, *a, **k) self._delete_handler = delete_handler self.chain_selection = ChainSelectionComponent(parent=self, is_enabled=False) self.bank_selection = BankSelectionComponent(bank_registry=device_bank_registry, banking_info=banking_info, device_options_provider=self._device_component, is_enabled=False, parent=self) self.move_device = MoveDeviceComponent(parent=self, is_enabled=False) self._last_pressed_button_index = -1 self._selected_on_previous_press = None self._modes = ModesComponent(parent=self) self._modes.add_mode('default', [ partial(self.chain_selection.set_parent, None), partial(self.bank_selection.set_device, None)]) self._modes.add_mode('chain_selection', [self.chain_selection]) self._modes.add_mode('bank_selection', [self.bank_selection]) self._modes.selected_mode = 'default' self.register_disconnectable(self._flattened_chain) self.__on_items_changed.subject = self self.__on_bank_selection_closed.subject = self.bank_selection self._update_selected_track() self._track_list = track_list_component watcher = self.register_disconnectable(DeviceChainStateWatcher(device_navigation=self)) self.__on_device_item_state_changed.subject = watcher self._update_device() self._update_button_colors() return
class DeviceNavigationComponent(DeviceNavigationComponentBase): __events__ = ('drum_pad_selection', 'mute_solo_stop_cancel_action_performed') def __init__(self, device_bank_registry=None, banking_info=None, delete_handler=None, track_list_component=None, *a, **k): self._flattened_chain = FlattenedDeviceChain( partial(collect_devices, self)) self._track_decorator = DecoratorFactory() self._modes = NullModes() self.move_device = None (super(DeviceNavigationComponent, self).__init__)(a, item_provider=self._flattened_chain, **k) self._delete_handler = delete_handler self.chain_selection = ChainSelectionComponent(parent=self, is_enabled=False) self.bank_selection = BankSelectionComponent( bank_registry=device_bank_registry, banking_info=banking_info, device_options_provider=(self._device_component), is_enabled=False, parent=self) self.move_device = MoveDeviceComponent(parent=self, is_enabled=False) self._last_pressed_button_index = -1 self._selected_on_previous_press = None self._modes = ModesComponent(parent=self) self._modes.add_mode('default', [ partial(self.chain_selection.set_parent, None), partial(self.bank_selection.set_device, None) ]) self._modes.add_mode('chain_selection', [self.chain_selection]) self._modes.add_mode('bank_selection', [self.bank_selection]) self._modes.selected_mode = 'default' self.register_disconnectable(self._flattened_chain) self._DeviceNavigationComponent__on_items_changed.subject = self self._DeviceNavigationComponent__on_bank_selection_closed.subject = self.bank_selection self._update_selected_track() self._track_list = track_list_component watcher = self.register_disconnectable( DeviceChainStateWatcher(device_navigation=self)) self._DeviceNavigationComponent__on_device_item_state_changed.subject = watcher self._update_device() self._update_button_colors() @property def modes(self): return self._modes def _in_device_enabling_mode(self): return self._track_list.selected_mode == 'mute' def _on_select_button_pressed(self, button): device_or_pad = self.items[button.index].item if self._in_device_enabling_mode(): self._toggle_device(device_or_pad) self.notify_mute_solo_stop_cancel_action_performed() else: self._last_pressed_button_index = button.index if not (self._delete_handler and self._delete_handler.is_deleting): self._selected_on_previous_press = device_or_pad if self.selected_object != device_or_pad else None self._select_item(device_or_pad) def _on_select_button_released_immediately(self, button): if not self._in_device_enabling_mode(): self._last_pressed_button_index = -1 device_or_pad = self.items[button.index].item if self._delete_handler and self._delete_handler.is_deleting: self._delete_item(device_or_pad) elif self.selected_object == device_or_pad: if device_or_pad != self._selected_on_previous_press: self._on_reselecting_object(device_or_pad) self._selected_on_previous_press = None def _on_select_button_pressed_delayed(self, button): if not self._in_device_enabling_mode(): self._on_pressed_delayed(self.items[button.index].item) def _on_select_button_released(self, button): if button.index == self._last_pressed_button_index: self._modes.selected_mode = 'default' self._last_pressed_button_index = -1 self._end_move_device() @singledispatchmethod def _toggle_device(self, device): if liveobj_valid(device): if device.parameters[0].is_enabled: set_enabled(device, not is_on(device)) @_toggle_device.register(Live.DrumPad.DrumPad) def _(self, drum_pad): if liveobj_valid(drum_pad): drum_pad.mute = not drum_pad.mute @listens('state') def __on_device_item_state_changed(self): self._update_button_colors() @listens('items') def __on_items_changed(self): new_items = [x.item for x in self.items] selected_item = self._flattened_chain.selected_item lost_selection = selected_item not in new_items if lost_selection: if isinstance(selected_item, RackBank2Device): for item in new_items: if isinstance(item, RackBank2Device): if item.rack_device == selected_item.rack_device: self._select_item(item) break lost_selection_on_empty_pad = new_items and is_drum_pad( new_items[(-1)]) and lost_selection if self._should_select_drum_pad() or (lost_selection_on_empty_pad): self._select_item(self._current_drum_pad()) if self.moving: self._show_selected_item() self.notify_drum_pad_selection() def _create_slot(self, index, item, nesting_level): items = self._item_provider.items[self.item_offset:] num_slots = min(self._num_visible_items, len(items)) slot = None if index == 0 and self.can_scroll_left(): slot = IconItemSlot(icon='page_left.svg') slot.is_scrolling_indicator = True elif index == num_slots - 1 and self.can_scroll_right(): slot = IconItemSlot(icon='page_right.svg') slot.is_scrolling_indicator = True else: slot = ItemSlot(item=item, nesting_level=nesting_level) slot.is_scrolling_indicator = False return slot @listenable_property def moving(self): return self.move_device.is_enabled() @property def device_selection_update_allowed(self): return not self._should_select_drum_pad() def _color_for_button(self, button_index, is_selected): item = self.items[button_index] device_or_pad = item.item is_active = liveobj_valid(device_or_pad) and is_active_element( device_or_pad) chain = find_chain_or_track(device_or_pad) if not is_active: return 'DefaultButton.Off' if is_selected: return 'ItemNavigation.ItemSelected' if liveobj_valid(chain): return IndexedColor.from_live_index(chain.color_index, DISPLAY_BUTTON_SHADE_LEVEL) return 'ItemNavigation.ItemNotSelected' def _begin_move_device(self, device): if not self.move_device.is_enabled(): if device.type != Live.Device.DeviceType.instrument: self.move_device.set_device(device) self.move_device.set_enabled(True) self._scroll_overlay.set_enabled(False) self.notify_moving() def _end_move_device(self): if self.move_device: if self.move_device.is_enabled(): self.move_device.set_device(None) self.move_device.set_enabled(False) self._scroll_overlay.set_enabled(True) self.notify_moving() def request_drum_pad_selection(self): self._current_track().drum_pad_selected = True def unfold_current_drum_pad(self): self._current_track().drum_pad_selected = False self._current_drum_pad( ).canonical_parent.view.is_showing_chain_devices = True def sync_selection_to_selected_device(self): self._update_item_provider( self.song.view.selected_track.view.selected_device) @property def is_drum_pad_selected(self): return is_drum_pad(self._flattened_chain.selected_item) @property def is_drum_pad_unfolded(self): selection = self._flattened_chain.selected_item return drum_rack_for_pad(selection).view.is_showing_chain_devices def _current_track(self): return self._track_decorator.decorate( (self.song.view.selected_track), additional_properties={'drum_pad_selected': False}) def _should_select_drum_pad(self): return self._current_track().drum_pad_selected def _current_drum_pad(self): return find_drum_pad(self.items) def _update_selected_track(self): self._selected_track = self.song.view.selected_track selected_track = self._current_track() self.reset_offset() self._flattened_chain.set_device_parent(selected_track) self._device_selection_in_track_changed.subject = selected_track.view self._modes.selected_mode = 'default' self._end_move_device() self._restore_selection(selected_track) def _restore_selection(self, selected_track): to_select = None if self._should_select_drum_pad(): to_select = self._current_drum_pad() if to_select == None: to_select = selected_track.view.selected_device self._select_item(to_select) def back_to_top(self): pass @property def selected_object(self): selected_item = self.item_provider.selected_item return getattr(selected_item, 'proxied_object', selected_item) @singledispatchmethod def _do_select_item(self, device): self._current_track().drum_pad_selected = False appointed_device = device_to_appoint(device) self._appoint_device(appointed_device) self.song.view.select_device(device, False) self.song.appointed_device = appointed_device @_do_select_item.register(RackBank2Device) def _(self, bank_2_device): self._current_track().drum_pad_selected = False self._appoint_device(bank_2_device) @_do_select_item.register(Live.DrumPad.DrumPad) def _(self, pad): self._current_track().drum_pad_selected = True device = self._first_device_on_pad(pad) self._appoint_device(device) def _first_device_on_pad(self, drum_pad): chain = drum_rack_for_pad(drum_pad).view.selected_chain if chain: if chain.devices: return first(chain.devices) def _appoint_device(self, device): if self._device_component.device_changed(device): self._device_component.set_device(device) @singledispatchmethod def _on_reselecting_object(self, device): if liveobj_valid(device) and device.can_have_chains: if not device.can_have_drum_pads: self._toggle(device) else: self.bank_selection.set_device(device) self._modes.selected_mode = 'bank_selection' @_on_reselecting_object.register(RackBank2Device) def _(self, bank_2_device): device = bank_2_device.rack_device if liveobj_valid(device): if device.can_have_chains: if not device.can_have_drum_pads: self._toggle(device) @_on_reselecting_object.register(Live.DrumPad.DrumPad) def _(self, drum_pad): rack = drum_rack_for_pad(drum_pad) self._toggle(rack) if rack.view.is_showing_chain_devices: first_device = self._first_device_on_pad(drum_pad) if first_device: self._select_item(first_device) self.notify_drum_pad_selection() @singledispatchmethod def _on_pressed_delayed(self, device): self._show_chains(device) self._begin_move_device(device) @_on_pressed_delayed.register(RackBank2Device) def _(self, bank_2_device): device = bank_2_device.rack_device self._show_chains(device) self._begin_move_device(device) @_on_pressed_delayed.register(Live.DrumPad.DrumPad) def _(self, _): pass @singledispatchmethod def _delete_item(self, device): delete_device(device) @_delete_item.register(Live.DrumPad.DrumPad) def _(self, pad): pass def _show_chains(self, device): if device.can_have_chains: self.chain_selection.set_parent(device) self._modes.selected_mode = 'chain_selection' @listens('back') def __on_bank_selection_closed(self): self._modes.selected_mode = 'default' def _update_device(self): if not self._should_select_drum_pad(): if not self._is_drum_rack_selected(): self._modes.selected_mode = 'default' self._update_item_provider(self._device_component.device()) def _is_drum_rack_selected(self): selected_item = self._flattened_chain.selected_item instrument = self._find_top_level_instrument() return liveobj_valid(selected_item) and isinstance( selected_item, Live.RackDevice.RackDevice ) and selected_item.can_have_drum_pads and not liveobj_changed( selected_item, instrument) def _find_top_level_instrument(self): return find_if( lambda device: device.type == Live.Device.DeviceType.instrument, self._current_track().devices) @listens('selected_device') def _device_selection_in_track_changed(self): new_selection = self.song.view.selected_track.view.selected_device if self._can_update_device_selection(new_selection): self._modes.selected_mode = 'default' self._update_item_provider(new_selection) def _toggle(self, item): view = item.view if view.is_collapsed: view.is_collapsed = False view.is_showing_chain_devices = True else: view.is_showing_chain_devices = not view.is_showing_chain_devices def _can_update_device_selection(self, new_selection): can_update = liveobj_valid(new_selection) drum_pad_selected_or_requested = self.is_drum_pad_selected or self._should_select_drum_pad( ) if can_update and drum_pad_selected_or_requested: if is_empty_rack(new_selection): can_update = False if not can_update or self.is_drum_pad_selected: can_update = not is_first_device_on_pad( new_selection, self._flattened_chain.selected_item) else: pass if not can_update: if not drum_pad_selected_or_requested: can_update = True return can_update def _update_item_provider(self, selection): self._flattened_chain.selected_item = selection if not is_drum_pad(selection): self._current_track().drum_pad_selected = False self.notify_drum_pad_selection()