def _section_root( self, *, root: SidebarItem, name: str, icon: Union[str, ColoredIcon], collapse_key: Config.Bool.V, type: SidebarItemType | None = None, ) -> SidebarItem: def update(expanded: bool) -> None: CollectionOp( self.browser, lambda col: col.set_config_bool(collapse_key, not expanded), ).run_in_background(initiator=self) top = SidebarItem( name, icon, on_expanded=update, expanded=not self.col.get_config_bool(collapse_key), item_type=type, ) root.add_child(top) return top
def render(root: SidebarItem, nodes: Iterable[DeckTreeNode], head: str = "") -> None: def toggle_expand(node: DeckTreeNode) -> Callable[[bool], None]: return lambda expanded: set_deck_collapsed( parent=self, deck_id=DeckId(node.deck_id), collapsed=not expanded, scope=DeckCollapseScope.BROWSER, ).run_in_background(initiator=self, ) for node in nodes: item = SidebarItem( name=node.name, icon=icon_filtered if node.filtered else icon, search_node=SearchNode(deck=head + node.name), on_expanded=toggle_expand(node), expanded=not node.collapsed, item_type=SidebarItemType.DECK, id=node.deck_id, name_prefix=head, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead)
def rename_tag(self, item: SidebarItem, new_name: str) -> None: if not new_name or new_name == item.name: return old_name = item.name old_full_name = item.full_name new_full_name = item.name_prefix + new_name item.name = new_name item.full_name = new_full_name def success(out: OpChangesWithCount) -> None: if out.count: tooltip(tr.browsing_notes_updated(count=out.count), parent=self) else: # revert renaming of sidebar item item.full_name = old_full_name item.name = old_name showInfo(tr.browsing_tag_rename_warning_empty(), parent=self) rename_tag( parent=self.browser, current_name=old_full_name, new_name=new_full_name, ).success(success).run_in_background()
def _deck_tree(self, root: SidebarItem) -> None: icon = "icons:book-outline.svg" icon_current = "icons:book-clock-outline.svg" icon_filtered = "icons:book-cog-outline.svg" def render( root: SidebarItem, nodes: Iterable[DeckTreeNode], head: str = "" ) -> None: def toggle_expand(node: DeckTreeNode) -> Callable[[bool], None]: return lambda expanded: set_deck_collapsed( parent=self, deck_id=DeckId(node.deck_id), collapsed=not expanded, scope=DeckCollapseScope.BROWSER, ).run_in_background( initiator=self, ) for node in nodes: item = SidebarItem( name=node.name, icon=icon_filtered if node.filtered else icon, search_node=SearchNode(deck=head + node.name), on_expanded=toggle_expand(node), expanded=not node.collapsed, item_type=SidebarItemType.DECK, id=node.deck_id, name_prefix=head, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead) tree = self.col.decks.deck_tree() root = self._section_root( root=root, name=tr.browsing_sidebar_decks(), icon=icon, collapse_key=Config.Bool.COLLAPSE_DECKS, type=SidebarItemType.DECK_ROOT, ) root.search_node = SearchNode(deck="_*") current = root.add_simple( name=tr.browsing_current_deck(), icon=icon_current, type=SidebarItemType.DECK_CURRENT, search_node=SearchNode(deck="current"), ) current.id = self.mw.col.decks.selected() render(root, tree.children)
def remove_tags(self, item: SidebarItem) -> None: tags = self.mw.col.tags.join(self._selected_tags()) item.name = "..." remove_tags_from_all_notes( parent=self.browser, space_separated_tags=tags).run_in_background()
def rename_deck(self, item: SidebarItem, new_name: str) -> None: if not new_name: return # update UI immediately, to avoid redraw item.name = new_name full_name = item.name_prefix + new_name deck_id = DeckId(item.id) def after_fetch(deck: Deck) -> None: if full_name == deck.name: return rename_deck( parent=self, deck_id=deck_id, new_name=full_name, ).run_in_background() QueryOp( parent=self.browser, op=lambda col: col.get_deck(deck_id), success=after_fetch, ).run_in_background()
def rename_flag(self, item: SidebarItem, new_name: str) -> None: labels = self.col.get_config("flagLabels", {}) labels[str(item.id)] = new_name self.col.set_config("flagLabels", labels) item.name = new_name self.browser._update_flag_labels() self.refresh()
def rename_tag(self, item: SidebarItem, new_name: str) -> None: if not new_name or new_name == item.name: return new_name_base = new_name old_name = item.full_name new_name = item.name_prefix + new_name item.name = new_name_base item.full_name = new_name rename_tag( parent=self.browser, current_name=old_name, new_name=new_name, ).run_in_background()
def _notetype_tree(self, root: SidebarItem) -> None: icon = ":/icons/notetype.svg" root = self._section_root( root=root, name=tr.browsing_sidebar_notetypes(), icon=icon, collapse_key=Config.Bool.COLLAPSE_NOTETYPES, type=SidebarItemType.NOTETYPE_ROOT, ) for nt in sorted(self.col.models.all(), key=lambda nt: nt["name"].lower()): item = SidebarItem( nt["name"], icon, search_node=SearchNode(note=nt["name"]), item_type=SidebarItemType.NOTETYPE, id=nt["id"], ) for c, tmpl in enumerate(nt["tmpls"]): child = SidebarItem( tmpl["name"], icon, search_node=self.col.group_searches( SearchNode(note=nt["name"]), SearchNode(template=c) ), item_type=SidebarItemType.NOTETYPE_TEMPLATE, name_prefix=f"{nt['name']}::", id=tmpl["ord"], ) item.add_child(child) root.add_child(item)
def _card_state_tree(self, root: SidebarItem) -> None: icon = ":/icons/circle.svg" icon_outline = ":/icons/circle-outline.svg" root = self._section_root( root=root, name=tr.browsing_sidebar_card_state(), icon=icon_outline, collapse_key=Config.Bool.COLLAPSE_CARD_STATE, type=SidebarItemType.CARD_STATE_ROOT, ) type = SidebarItemType.CARD_STATE colored_icon = ColoredIcon(path=icon, color=colors.DISABLED) root.add_simple( tr.actions_new(), icon=colored_icon.with_color(colors.NEW_COUNT), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_NEW), ) root.add_simple( name=tr.scheduling_learning(), icon=colored_icon.with_color(colors.LEARN_COUNT), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_LEARN), ) root.add_simple( name=tr.scheduling_review(), icon=colored_icon.with_color(colors.REVIEW_COUNT), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_REVIEW), ) root.add_simple( name=tr.browsing_suspended(), icon=colored_icon.with_color(colors.SUSPENDED_FG), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_SUSPENDED), ) root.add_simple( name=tr.browsing_buried(), icon=colored_icon.with_color(colors.BURIED_FG), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_BURIED), )
def _tag_tree(self, root: SidebarItem) -> None: icon = ":/icons/tag-outline.svg" icon_off = ":/icons/tag-off-outline.svg" def render(root: SidebarItem, nodes: Iterable[TagTreeNode], head: str = "") -> None: def toggle_expand(node: TagTreeNode) -> Callable[[bool], None]: full_name = head + node.name return lambda expanded: set_tag_collapsed( parent=self, tag=full_name, collapsed=not expanded ).run_in_background(initiator=self) for node in nodes: item = SidebarItem( name=node.name, icon=icon, search_node=SearchNode(tag=head + node.name), on_expanded=toggle_expand(node), expanded=not node.collapsed, item_type=SidebarItemType.TAG, name_prefix=head, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead) tree = self.col.tags.tree() root = self._section_root( root=root, name=tr.browsing_sidebar_tags(), icon=icon, collapse_key=Config.Bool.COLLAPSE_TAGS, type=SidebarItemType.TAG_ROOT, ) root.search_node = SearchNode(tag="_*") root.add_simple( name=tr.browsing_sidebar_untagged(), icon=icon_off, type=SidebarItemType.TAG_NONE, search_node=SearchNode(negated=SearchNode(tag="_*")), ) render(root, tree.children)
def _saved_searches_tree(self, root: SidebarItem) -> None: icon = ":/icons/heart-outline.svg" saved = self._get_saved_searches() root = self._section_root( root=root, name=tr.browsing_sidebar_saved_searches(), icon=icon, collapse_key=Config.Bool.COLLAPSE_SAVED_SEARCHES, type=SidebarItemType.SAVED_SEARCH_ROOT, ) for name, filt in sorted(saved.items()): item = SidebarItem( name, icon, search_node=SearchNode(parsable_text=filt), item_type=SidebarItemType.SAVED_SEARCH, ) root.add_child(item)
def rename_deck(self, item: SidebarItem, new_name: str) -> None: if not new_name or new_name == item.name: return # update UI immediately, to avoid redraw item.name = new_name rename_deck( parent=self, deck_id=DeckId(item.id), new_name=item.name_prefix + new_name, ).run_in_background()
def _root_tree(self) -> SidebarItem: root: SidebarItem | None = None for stage in SidebarStage: if stage == SidebarStage.ROOT: root = SidebarItem("", "", item_type=SidebarItemType.ROOT) handled = gui_hooks.browser_will_build_tree( False, root, stage, self.browser) if not handled: self._build_stage(root, stage) return root
def _save_search(self, name: str, search: str, update: bool = False) -> None: conf = self._get_saved_searches() if not update and name in conf: if conf[name] == search: # nothing to do return if not askUser(tr.browsing_confirm_saved_search_overwrite(name=name)): # don't overwrite existing saved search return conf[name] = search self._set_saved_searches(conf) self.refresh(SidebarItem(name, "", item_type=SidebarItemType.SAVED_SEARCH))
def _flags_tree(self, root: SidebarItem) -> None: icon = ColoredIcon(path=":/icons/flag.svg", color=colors.DISABLED) root = self._section_root( root=root, name=tr.browsing_sidebar_flags(), icon=icon, collapse_key=Config.Bool.COLLAPSE_FLAGS, type=SidebarItemType.FLAG_ROOT, ) root.search_node = SearchNode(flag=SearchNode.FLAG_ANY) for flag in load_flags(self.col): root.add_child( SidebarItem( name=flag.label, icon=flag.icon, search_node=flag.search_node, item_type=SidebarItemType.FLAG, id=flag.index, )) root.add_simple( tr.browsing_no_flag(), icon=icon, type=SidebarItemType.FLAG, search_node=SearchNode(flag=SearchNode.FLAG_NONE), )
def _flags_tree(self, root: SidebarItem) -> None: icon = ":/icons/flag.svg" icon_outline = ":/icons/flag-outline.svg" root = self._section_root( root=root, name=tr.browsing_sidebar_flags(), icon=icon_outline, collapse_key=Config.Bool.COLLAPSE_FLAGS, type=SidebarItemType.FLAG_ROOT, ) root.search_node = SearchNode(flag=SearchNode.FLAG_ANY) for flag in self.mw.flags.all(): root.add_child( SidebarItem( name=flag.label, icon=flag.icon, search_node=flag.search_node, item_type=SidebarItemType.FLAG, id=flag.index, )) root.add_simple( tr.browsing_no_flag(), icon=icon_outline, type=SidebarItemType.FLAG, search_node=SearchNode(flag=SearchNode.FLAG_NONE), )
def render(root: SidebarItem, nodes: Iterable[TagTreeNode], head: str = "") -> None: def toggle_expand(node: TagTreeNode) -> Callable[[bool], None]: full_name = head + node.name return lambda expanded: set_tag_collapsed( parent=self, tag=full_name, collapsed=not expanded ).run_in_background(initiator=self) for node in nodes: item = SidebarItem( name=node.name, icon=icon, search_node=SearchNode(tag=head + node.name), on_expanded=toggle_expand(node), expanded=not node.collapsed, item_type=SidebarItemType.TAG, name_prefix=head, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead)
def _section_root( self, *, root: SidebarItem, name: str, icon: Union[str, ColoredIcon], collapse_key: Config.Bool.Key.V, type: Optional[SidebarItemType] = None, ) -> SidebarItem: def update(expanded: bool) -> None: self.col.set_config_bool(collapse_key, not expanded) top = SidebarItem( name, icon, on_expanded=update, expanded=not self.col.get_config_bool(collapse_key), item_type=type, ) root.add_child(top) return top
def rename_saved_search(self, item: SidebarItem, new_name: str) -> None: old_name = item.name conf = self._get_saved_searches() try: filt = conf[old_name] except KeyError: return if new_name in conf and not askUser( tr.browsing_confirm_saved_search_overwrite(name=new_name)): return conf[new_name] = filt del conf[old_name] self._set_saved_searches(conf) item.name = new_name self.refresh()
def _notetype_tree(self, root: SidebarItem) -> None: notetype_icon = ":/icons/newspaper.svg" template_icon = ":/icons/iframe-braces-outline.svg" field_icon = ":/icons/form-textbox.svg" root = self._section_root( root=root, name=tr.browsing_sidebar_notetypes(), icon=notetype_icon, collapse_key=Config.Bool.COLLAPSE_NOTETYPES, type=SidebarItemType.NOTETYPE_ROOT, ) root.search_node = SearchNode(note="_*") for nt in sorted(self.col.models.all(), key=lambda nt: nt["name"].lower()): item = SidebarItem( nt["name"], notetype_icon, search_node=SearchNode(note=nt["name"]), item_type=SidebarItemType.NOTETYPE, id=nt["id"], ) for c, tmpl in enumerate(nt["tmpls"]): child = SidebarItem( tmpl["name"], template_icon, search_node=self.col.group_searches( SearchNode(note=nt["name"]), SearchNode(template=c)), item_type=SidebarItemType.NOTETYPE_TEMPLATE, id=tmpl["ord"], ) item.add_child(child) for c, fld in enumerate(nt["flds"]): child = SidebarItem( fld["name"], field_icon, search_node=self.col.group_searches( SearchNode(note=nt["name"]), SearchNode(field_name=fld["name"])), item_type=SidebarItemType.NOTETYPE_FIELD, id=fld["ord"], ) item.add_child(child) root.add_child(item)
def _flags_tree(self, root: SidebarItem) -> None: icon = ColoredIcon(path=":/icons/flag.svg", color=colors.DISABLED) root = self._section_root( root=root, name=tr.browsing_sidebar_flags(), icon=icon, collapse_key=Config.Bool.COLLAPSE_FLAGS, type=SidebarItemType.FLAG_ROOT, ) root.search_node = SearchNode(flag=SearchNode.FLAG_ANY) type = SidebarItemType.FLAG root.add_simple( tr.actions_red_flag(), icon=icon.with_color(colors.FLAG1_FG), type=type, search_node=SearchNode(flag=SearchNode.FLAG_RED), ) root.add_simple( tr.actions_orange_flag(), icon=icon.with_color(colors.FLAG2_FG), type=type, search_node=SearchNode(flag=SearchNode.FLAG_ORANGE), ) root.add_simple( tr.actions_green_flag(), icon=icon.with_color(colors.FLAG3_FG), type=type, search_node=SearchNode(flag=SearchNode.FLAG_GREEN), ) root.add_simple( tr.actions_blue_flag(), icon=icon.with_color(colors.FLAG4_FG), type=type, search_node=SearchNode(flag=SearchNode.FLAG_BLUE), ) root.add_simple( tr.browsing_no_flag(), icon=icon.with_color(colors.DISABLED), type=type, search_node=SearchNode(flag=SearchNode.FLAG_NONE), )
def _today_tree(self, root: SidebarItem) -> None: icon = ":/icons/clock-outline.svg" root = self._section_root( root=root, name=tr.browsing_today(), icon=icon, collapse_key=Config.Bool.COLLAPSE_TODAY, type=SidebarItemType.TODAY_ROOT, ) type = SidebarItemType.TODAY root.add_simple( name=tr.browsing_sidebar_due_today(), icon=icon, type=type, search_node=SearchNode(due_on_day=0), ) root.add_simple( name=tr.browsing_added_today(), icon=icon, type=type, search_node=SearchNode(added_in_days=1), ) root.add_simple( name=tr.browsing_edited_today(), icon=icon, type=type, search_node=SearchNode(edited_in_days=1), ) root.add_simple( name=tr.browsing_studied_today(), icon=icon, type=type, search_node=SearchNode(rated=SearchNode.Rated(days=1)), ) root.add_simple( name=tr.browsing_sidebar_first_review(), icon=icon, type=type, search_node=SearchNode(introduced_in_days=1), ) root.add_simple( name=tr.browsing_sidebar_rescheduled(), icon=icon, type=type, search_node=SearchNode(rated=SearchNode.Rated( days=1, rating=SearchNode.RATING_BY_RESCHEDULE)), ) root.add_simple( name=tr.browsing_again_today(), icon=icon, type=type, search_node=SearchNode(rated=SearchNode.Rated( days=1, rating=SearchNode.RATING_AGAIN)), ) root.add_simple( name=tr.browsing_sidebar_overdue(), icon=icon, type=type, search_node=self.col.group_searches( SearchNode(card_state=SearchNode.CARD_STATE_DUE), SearchNode(negated=SearchNode(due_on_day=0)), ), )
def rename_flag(self, item: SidebarItem, new_name: str) -> None: item.name = new_name self.mw.flags.rename_flag(item.id, new_name)