def on_item_save(self, item_id: str, event_kwargs: Dict[str, Any]) -> bool: """ update item specified by flow key using the new values/changes given in event_kwargs. """ self.vpo(f"LiszApp.on_item_save('{item_id}', {event_kwargs})") add_item = (item_id == '') item_idx = -1 if add_item else self.find_item_index(item_id) action_or_err = self.edit_validate(item_idx, **event_kwargs) if action_or_err: if action_or_err.startswith('request_delete_confirmation'): self.change_flow(id_of_flow('confirm', 'item_deletion', item_id), popup_kwargs=dict(node_only=action_or_err.endswith('_for_node'))) else: self.show_message(action_or_err, title=get_txt("error on adding new item '{item_id}'" if add_item else "error on editing item '{item_id}'")) return False if add_item: # new list item added (with non-empty new_id) at index 0 of the current node item_idx = 0 self.play_vibrate(LOVE_VIBRATE_PATTERN) event_kwargs['flow_id'] = id_of_flow('focus', 'item', self.current_node_items[item_idx]['id']) self.save_app_states() return True
def on_focus(self, _self, focus: bool): """ change flow on text input change of focus. :param _self: :param focus: True if this text input got focus, False on unfocus. """ if focus: flow_id = self.focus_flow_id or id_of_flow('edit') else: flow_id = self.unfocus_flow_id or id_of_flow('close') App.get_running_app().main_app.change_flow(flow_id)
def on_touch_up(self, touch: MotionEvent) -> bool: """ drop / finish drag """ if touch.grab_current is not self: return super().on_touch_up(touch) if touch.ud[self] != 'drag': return False ma = self.main_app dst_node_path = None child_id = '' moved = False if self.dragging_to_parent_widget_instance or ma.placeholders_above or ma.placeholders_below: src_node = self.dragged_from_node assert src_node.index(self.item_data) == self.item_idx dst_item_id = '' if self.dragging_to_parent_widget_instance: dst_node_path = ma.flow_path[:-1] elif ma.placeholders_above and ma.placeholders_below: # drop into node item_idx = list(ma.placeholders_below.keys())[0] child_id = src_node[item_idx]['id'] dst_node_path = ma.flow_path + [id_of_flow('enter', 'item', child_id), ] else: if ma.placeholders_above: item_idx = list(ma.placeholders_above.keys())[0] else: item_idx = list(ma.placeholders_below.keys())[0] + 1 dst_item_id = src_node[item_idx]['id'] if item_idx < len(src_node) else '' moved = ma.move_item(src_node, self.item_data['id'], dropped_path=dst_node_path, dropped_id=dst_item_id) self._restore_menu_bar() self.app_root.remove_widget(self) ma.cleanup_placeholder() ma.dragging_item_idx = None self.dragged_from_node = None touch.ungrab(self) if moved: ma.play_sound('edited') ma.save_app_states() moved = not child_id and dst_node_path is not None # refresh and maybe set/change focus; on_flow_id() with 'focus' flow action does also a full refresh/redraw if moved: ma.refresh_all() else: flow_id = id_of_flow('focus', 'item', child_id or self.item_data['id']) if flow_id == ma.flow_id: # remove focus from focused item - allowing clipboard item copies of current node (not current item) ma.change_app_state('flow_id', id_of_flow('', '')) # using instead change_flow() recovers last focus else: ma.change_flow(flow_id) return True
def on_item_delete(self, item_id: str, event_kwargs: Dict[str, Any]) -> bool: """ delete item or node of this item """ item_id = event_kwargs.get('item_id', item_id) assert item_id del_node = event_kwargs.get('del_node', False) self.delete_item(item_id, del_node) if not del_node: self.change_flow(id_of_flow('', '')) self.play_sound('deleted') self.refresh_all() return True
def on_fb_ori_main_action(self, flow_id, event_kwargs): """ FBOri main tap """ print("FBOri.on_fb_ori_main_action call", flow_id, event_kwargs) print("FBOri_main create new instance") wid = FlowButton( text="FBOri - Original FlowButton" + str(len(self.framework_root.children)), tap_flow_id=id_of_flow('action', 'fb_ori_main'), tap_kwargs=dict( kwarg1="FBOri tap_kwargs declared in on_fb_ori_main_action")) self.framework_root.add_widget(wid)
def toggle_color_picker(self, wid, color_name='square_fill_ink'): """ show or hide color picker""" print("TOGGLE COLOR PICKER", getattr(wid, color_name), self.color_picker) is_open = self.color_dropdown and self.color_dropdown.attach_to if is_open: self.color_dropdown.dismiss() if self.color_dropdown: self.color_picker.unbind(color=wid.setter(color_name)) self.color_picker = None self.color_dropdown = None if not is_open: self.color_dropdown = Factory.ColorPickerDD() self.change_flow(id_of_flow('suggest')) self.color_dropdown.open(wid) self.color_picker = self.color_dropdown.ids.col_pic self.color_picker.color = getattr(wid, color_name) self.color_picker.bind(color=wid.setter(color_name))
def add_extract_options(node_flow_path: List[str], pre: str = ""): """ add extract options for the node specified by node_flow_path. :param node_flow_path: flow path of node for to add extract options to the child_maps list. :param pre: info button text prefix (for to mark the focused item). """ node = self.flow_path_node(node_flow_path, strict=True) if not node or node_flow_path in added_nodes: return # skip empty and already added nodes added_nodes.append(node_flow_path) parent_node_id = flow_key(self.flow_path[-1]) if self.flow_path else FLOW_PATH_ROOT_ID if child_maps: child_maps.append(dict( # add separator widget cls='Widget', kwargs=dict(size_hint_y=None, height=self.font_size / 3))) copy_icon = id_of_flow('copy', 'node') image_size = (self.font_size * 1.29, self.font_size * 1.29) node_info = self.node_info(node, what=() if self.verbose else ('selected_leaf_count', 'unselected_leaf_count')) if parent_node_id: node_info['name'] = parent_node_id info_text = (f"{self.flow_path_text(node_flow_path, display_root=True)}" f" {node_info[('un' if sel_status is False else '') + 'selected_leaf_count']}" f"/{node_info['selected_leaf_count'] + node_info['unselected_leaf_count']}") child_maps.append(dict( kwargs=dict( text=pre + info_text, tap_flow_id=id_of_flow('open', 'node_info', repr(node_flow_path)), tap_kwargs=dict(popup_kwargs=dict(node_info=node_info)), image_size=image_size, circle_fill_color=self.flow_path_ink, circle_fill_size=image_size, square_fill_color=self.flow_id_ink[:3] + (0.39, )))) attr_tpl = dict(tap_flow_id=id_of_flow('extract', 'node', repr(node_flow_path)), tap_kwargs=dict(popups_to_close=('replace_with_data_map_container', ), extract_type=''), icon_name=id_of_flow('export', 'node'), image_size=image_size) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("copy all node items"), icon_name=copy_icon) attrs['tap_kwargs']['extract_type'] = 'copy' child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("export all node items")) attrs['tap_kwargs']['extract_type'] = 'export' child_maps.append(dict(kwargs=attrs)) if self.debug: attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("cut all node items"), icon_name=id_of_flow('cut', 'node')) attrs['tap_kwargs']['extract_type'] = 'cut' child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("delete all node items"), icon_name=id_of_flow('delete', 'item')) attrs['tap_kwargs']['extract_type'] = 'delete' child_maps.append(dict(kwargs=attrs)) if sel_status is not None: extract_filter = 'sel' if sel_status else 'unsel' ink = self.selected_item_ink if sel_status else self.unselected_item_ink attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("copy {'' if sel_status else 'un'}selected node items"), icon_name=copy_icon, circle_fill_color=ink, circle_fill_size=image_size) attrs['tap_kwargs']['extract_type'] = 'copy_' + extract_filter child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("export {'' if sel_status else 'un'}selected node items"), circle_fill_color=ink, circle_fill_size=image_size) attrs['tap_kwargs']['extract_type'] = 'export_' + extract_filter child_maps.append(dict(kwargs=attrs))
def show_extract_options(self, widget: Widget, flow_path_text: str = '', item: Optional[LiszItem] = None, sel_status: Optional[bool] = None) -> bool: """ open context menu with the available extract actions of the current context/flow/app-status (focus, debug). :param widget: widget to show extract options for. :param flow_path_text: flow path text for to identify the node to extract (def=self.flow_path). :param item: data dict of the item represented by :paramref:`~show_extract_options.widget`. :param sel_status: pass True / False to add option for to export selected / unselected items. :return: True if extract options are available and got displayed else False. """ def add_extract_options(node_flow_path: List[str], pre: str = ""): """ add extract options for the node specified by node_flow_path. :param node_flow_path: flow path of node for to add extract options to the child_maps list. :param pre: info button text prefix (for to mark the focused item). """ node = self.flow_path_node(node_flow_path, strict=True) if not node or node_flow_path in added_nodes: return # skip empty and already added nodes added_nodes.append(node_flow_path) parent_node_id = flow_key(self.flow_path[-1]) if self.flow_path else FLOW_PATH_ROOT_ID if child_maps: child_maps.append(dict( # add separator widget cls='Widget', kwargs=dict(size_hint_y=None, height=self.font_size / 3))) copy_icon = id_of_flow('copy', 'node') image_size = (self.font_size * 1.29, self.font_size * 1.29) node_info = self.node_info(node, what=() if self.verbose else ('selected_leaf_count', 'unselected_leaf_count')) if parent_node_id: node_info['name'] = parent_node_id info_text = (f"{self.flow_path_text(node_flow_path, display_root=True)}" f" {node_info[('un' if sel_status is False else '') + 'selected_leaf_count']}" f"/{node_info['selected_leaf_count'] + node_info['unselected_leaf_count']}") child_maps.append(dict( kwargs=dict( text=pre + info_text, tap_flow_id=id_of_flow('open', 'node_info', repr(node_flow_path)), tap_kwargs=dict(popup_kwargs=dict(node_info=node_info)), image_size=image_size, circle_fill_color=self.flow_path_ink, circle_fill_size=image_size, square_fill_color=self.flow_id_ink[:3] + (0.39, )))) attr_tpl = dict(tap_flow_id=id_of_flow('extract', 'node', repr(node_flow_path)), tap_kwargs=dict(popups_to_close=('replace_with_data_map_container', ), extract_type=''), icon_name=id_of_flow('export', 'node'), image_size=image_size) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("copy all node items"), icon_name=copy_icon) attrs['tap_kwargs']['extract_type'] = 'copy' child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("export all node items")) attrs['tap_kwargs']['extract_type'] = 'export' child_maps.append(dict(kwargs=attrs)) if self.debug: attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("cut all node items"), icon_name=id_of_flow('cut', 'node')) attrs['tap_kwargs']['extract_type'] = 'cut' child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("delete all node items"), icon_name=id_of_flow('delete', 'item')) attrs['tap_kwargs']['extract_type'] = 'delete' child_maps.append(dict(kwargs=attrs)) if sel_status is not None: extract_filter = 'sel' if sel_status else 'unsel' ink = self.selected_item_ink if sel_status else self.unselected_item_ink attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("copy {'' if sel_status else 'un'}selected node items"), icon_name=copy_icon, circle_fill_color=ink, circle_fill_size=image_size) attrs['tap_kwargs']['extract_type'] = 'copy_' + extract_filter child_maps.append(dict(kwargs=attrs)) attrs = deepcopy(attr_tpl) attrs.update(text=get_txt("export {'' if sel_status else 'un'}selected node items"), circle_fill_color=ink, circle_fill_size=image_size) attrs['tap_kwargs']['extract_type'] = 'export_' + extract_filter child_maps.append(dict(kwargs=attrs)) self.vpo(f"LiszApp.show_extract_options({widget}, {flow_path_text}, {item}, {sel_status})") flow_path = self.flow_path_from_text(flow_path_text) if flow_path_text else self.flow_path has_focus = flow_action(self.flow_id) == 'focus' flo_key = flow_key(self.flow_id) child_maps = list() added_nodes = list() node_id = item['id'] if item else '' if node_id and 'node' in item: add_extract_options(flow_path + [id_of_flow('enter', 'item', node_id)], pre=FOCUS_FLOW_PREFIX if has_focus and node_id == flo_key else "") add_extract_options(flow_path) if has_focus and flo_key != node_id: item = self.item_by_id(flo_key) if 'node' in item: add_extract_options(flow_path + [id_of_flow('enter', 'item', flo_key)], pre=FOCUS_FLOW_PREFIX) if not child_maps: self.show_message(get_txt("no extract options available for this item/node")) return False popup_kwargs = dict(parent=widget, child_data_maps=child_maps, auto_width_child_padding=self.font_size * 3) return self.change_flow(id_of_flow('open', 'extract'), popup_kwargs=popup_kwargs)
def add_import_options(info_text: str): """ add menu options for the current node. """ info = self.node_info(node, what=() if self.verbose else ('selected_leaf_count', 'unselected_leaf_count')) if len(node) == 1: info['content'] = node[0]['id'] info_text = str(info['selected_leaf_count'] + info['unselected_leaf_count']) + " items from " + info_text if node_id: info['name'] = node_id info_text = get_txt("node '{node_id}' with ") + info_text info_text += " to" child_maps.append(dict(kwargs=dict( text=info_text, tap_flow_id=id_of_flow('open', 'node_info'), tap_kwargs=dict(popup_kwargs=dict(node_info=info)), image_size=image_size, circle_fill_color=self.flow_path_ink, circle_fill_size=image_size, square_fill_color=self.flow_id_ink[:3] + (0.39,)))) args_tpl: Dict[str, Any] = dict( tap_flow_id=id_of_flow('import', 'node'), tap_kwargs=dict(node_to_import=node, popups_to_close=('replace_with_data_map_container',)), image_size=image_size) if self.flow_path: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("current list begin") if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) item_index = len(self.current_node_items) if item_index: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("current list end") kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("{FLOW_PATH_ROOT_ID} list begin") kwargs['tap_kwargs']['import_items_node'] = self.root_node if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) item_index = len(self.root_node) if item_index: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("{FLOW_PATH_ROOT_ID} list end") kwargs['tap_kwargs']['import_items_node'] = self.root_node kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) if focused_id: item_index = self.find_item_index(focused_id) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("before focused item '{focused_id}'") kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) focused_item = self.item_by_id(focused_id) if 'node' in focused_item: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("into focused sub-list '{focused_id}'") kwargs['tap_kwargs']['import_items_node'] = focused_item['node'] if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("after focused '{focused_id}'") kwargs['tap_kwargs']['import_items_index'] = item_index + 1 if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs))
def show_add_options(self, widget: Widget) -> bool: """ open context menu with all available actions for to add/import new node/item(s). :param widget: FlowButton to open this context menu. :return: True if any add options are available and got displayed else False. """ def add_import_options(info_text: str): """ add menu options for the current node. """ info = self.node_info(node, what=() if self.verbose else ('selected_leaf_count', 'unselected_leaf_count')) if len(node) == 1: info['content'] = node[0]['id'] info_text = str(info['selected_leaf_count'] + info['unselected_leaf_count']) + " items from " + info_text if node_id: info['name'] = node_id info_text = get_txt("node '{node_id}' with ") + info_text info_text += " to" child_maps.append(dict(kwargs=dict( text=info_text, tap_flow_id=id_of_flow('open', 'node_info'), tap_kwargs=dict(popup_kwargs=dict(node_info=info)), image_size=image_size, circle_fill_color=self.flow_path_ink, circle_fill_size=image_size, square_fill_color=self.flow_id_ink[:3] + (0.39,)))) args_tpl: Dict[str, Any] = dict( tap_flow_id=id_of_flow('import', 'node'), tap_kwargs=dict(node_to_import=node, popups_to_close=('replace_with_data_map_container',)), image_size=image_size) if self.flow_path: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("current list begin") if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) item_index = len(self.current_node_items) if item_index: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("current list end") kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("{FLOW_PATH_ROOT_ID} list begin") kwargs['tap_kwargs']['import_items_node'] = self.root_node if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) item_index = len(self.root_node) if item_index: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("{FLOW_PATH_ROOT_ID} list end") kwargs['tap_kwargs']['import_items_node'] = self.root_node kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) if focused_id: item_index = self.find_item_index(focused_id) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("before focused item '{focused_id}'") kwargs['tap_kwargs']['import_items_index'] = item_index if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) focused_item = self.item_by_id(focused_id) if 'node' in focused_item: kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("into focused sub-list '{focused_id}'") kwargs['tap_kwargs']['import_items_node'] = focused_item['node'] if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) kwargs = deepcopy(args_tpl) kwargs['text'] = get_txt("after focused '{focused_id}'") kwargs['tap_kwargs']['import_items_index'] = item_index + 1 if node_id: kwargs['tap_kwargs']['add_node_id'] = node_id child_maps.append(dict(kwargs=kwargs)) self.vpo(f"LiszApp.show_add_options({widget})") image_size = (self.font_size * 1.35, self.font_size * 1.35) focused_id = flow_key(self.flow_id) if flow_action(self.flow_id) == 'focus' else '' child_maps = list() node = node_from_literal(Clipboard.paste()) if node: node_id = '' add_import_options(get_txt("clipboard")) node_files = self.importable_node_files(folder_path=self.documents_root_path) for node_file_info in node_files: if child_maps: # add separator widget child_maps.append(dict(cls='Widget', kwargs=dict(size_hint_y=None, height=self.font_size / 3))) node_id, node, file_path, err_msg = node_file_info if not err_msg: add_import_options(get_txt("export file")) child_maps.append(dict(cls='Widget', kwargs=dict(size_hint_y=None, height=self.font_size / 3))) node_id = '' add_import_options(get_txt("export file")) elif self.verbose: child_maps.append(dict(kwargs=dict( cls='ImageLabel', text=node_id + " error: " + err_msg, square_fill_color=(1, 0, 0, 0.39,)))) if not child_maps: self.show_message(get_txt("neither clipboard nor '{self.documents_root_path}' contains items to import"), title=get_txt("import error(s)", count=1)) return False popup_kwargs = dict(parent=widget, child_data_maps=child_maps, auto_width_child_padding=self.font_size * 3) return self.change_flow(id_of_flow('open', 'alt_add'), popup_kwargs=popup_kwargs)
def edit_item_finished(self, old_id: str, new_id: str, state: str): """ finished list add/edit callback from on_dismiss event of ItemEditor """ self.vpo(f"LiszApp.edit_item_finished('{old_id}', '{new_id}', {state})") self.change_flow(id_of_flow('save', 'item', old_id), new_id=new_id, want_node=state == 'down')