switch to new deck tree in deck browser

Saves us having to look up collapsed/filtered as we render, and gives
us type completion.
This commit is contained in:
Damien Elmes 2020-05-16 10:52:14 +10:00
parent 75d3eebafe
commit f2086fc2e3
5 changed files with 69 additions and 44 deletions

View file

@ -264,6 +264,8 @@ message DeckTreeNode {
uint32 review_count = 6;
uint32 learn_count = 7;
uint32 new_count = 8;
bool filtered = 16;
}
message RenderExistingCardIn {

View file

@ -15,7 +15,7 @@ from anki import hooks
from anki.cards import Card
from anki.consts import *
from anki.lang import _
from anki.rsbackend import FormatTimeSpanContext, SchedTimingToday
from anki.rsbackend import DeckTreeNode, FormatTimeSpanContext, SchedTimingToday
from anki.utils import ids2str, intTime
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
@ -232,6 +232,10 @@ order by due"""
"List of (base name, did, rev, lrn, new, children)"
return self.col.backend.legacy_deck_tree()
def deck_due_tree(self) -> DeckTreeNode:
"Returns a tree of decks with counts."
return self.col.backend.deck_tree(include_counts=True)
# Getting the next card
##########################################################################

View file

@ -6,12 +6,11 @@ from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass
from typing import Any
import aqt
from anki.errors import DeckRenameError
from anki.lang import _, ngettext
from anki.rsbackend import TR
from anki.rsbackend import TR, DeckTreeNode
from anki.utils import ids2str
from aqt import AnkiQt, gui_hooks
from aqt.qt import *
@ -39,8 +38,13 @@ class DeckBrowserContent:
stats: str
@dataclass
class RenderDeckNodeContext:
current_deck_id: int
class DeckBrowser:
_dueTree: Any
_dueTree: DeckTreeNode
def __init__(self, mw: AnkiQt) -> None:
self.mw = mw
@ -83,7 +87,7 @@ class DeckBrowser:
draggedDeckDid, ontoDeckDid = arg.split(",")
self._dragDeckOnto(draggedDeckDid, ontoDeckDid)
elif cmd == "collapse":
self._collapse(arg)
self._collapse(int(arg))
return False
def _selDeck(self, did):
@ -106,7 +110,7 @@ class DeckBrowser:
def _renderPage(self, reuse=False):
if not reuse:
self._dueTree = self.mw.col.sched.deckDueTree()
self._dueTree = self.mw.col.sched.deck_due_tree()
self.__renderPage(None)
return
self.web.evalWithCallback("window.pageYOffset", self.__renderPage)
@ -143,53 +147,49 @@ where id > ?""",
buf = self.mw.col.backend.studied_today(cards, float(thetime))
return buf
def _renderDeckTree(self, nodes, depth=0):
if not nodes:
return ""
if depth == 0:
buf = """
def _renderDeckTree(self, top: DeckTreeNode) -> str:
buf = """
<tr><th colspan=5 align=left>%s</th><th class=count>%s</th>
<th class=count>%s</th><th class=optscol></th></tr>""" % (
_("Deck"),
tr(TR.STATISTICS_DUE_COUNT),
_("New"),
)
buf += self._topLevelDragRow()
else:
buf = ""
for node in nodes:
buf += self._deckRow(node, depth)
if depth == 0:
buf += self._topLevelDragRow()
_("Deck"),
tr(TR.STATISTICS_DUE_COUNT),
_("New"),
)
buf += self._topLevelDragRow()
ctx = RenderDeckNodeContext(current_deck_id=self.mw.col.conf["curDeck"])
for child in top.children:
buf += self._render_deck_node(child, ctx)
return buf
def _deckRow(self, node, depth):
name, did, due, lrn, new, children = node
deck = self.mw.col.decks.get(did)
collapsed = self.mw.col.decks.get(did)["collapsed"]
if collapsed:
def _render_deck_node(self, node: DeckTreeNode, ctx: RenderDeckNodeContext) -> str:
if node.collapsed:
prefix = "+"
else:
prefix = "-"
due += lrn
due = node.review_count + node.learn_count
def indent():
return "&nbsp;" * 6 * depth
return "&nbsp;" * 6 * (node.level - 1)
if did == self.mw.col.conf["curDeck"]:
if node.deck_id == ctx.current_deck_id:
klass = "deck current"
else:
klass = "deck"
buf = "<tr class='%s' id='%d'>" % (klass, did)
buf = "<tr class='%s' id='%d'>" % (klass, node.deck_id)
# deck link
if children:
if node.children:
collapse = (
"<a class=collapse href=# onclick='return pycmd(\"collapse:%d\")'>%s</a>"
% (did, prefix)
% (node.deck_id, prefix)
)
else:
collapse = "<span class=collapse></span>"
if deck["dyn"]:
if node.filtered:
extraclass = "filtered"
else:
extraclass = ""
@ -200,8 +200,8 @@ where id > ?""",
indent(),
collapse,
extraclass,
did,
name,
node.deck_id,
node.name,
)
# due counts
def nonzeroColour(cnt, klass):
@ -211,16 +211,17 @@ where id > ?""",
buf += "<td align=right>%s</td><td align=right>%s</td>" % (
nonzeroColour(due, "review-count"),
nonzeroColour(new, "new-count"),
nonzeroColour(node.new_count, "new-count"),
)
# options
buf += (
"<td align=center class=opts><a onclick='return pycmd(\"opts:%d\");'>"
"<img src='/_anki/imgs/gears.svg' class=gears></a></td></tr>" % did
"<img src='/_anki/imgs/gears.svg' class=gears></a></td></tr>" % node.deck_id
)
# children
if not collapsed:
buf += self._renderDeckTree(children, depth + 1)
if not node.collapsed:
for child in node.children:
buf += self._render_deck_node(child, ctx)
return buf
def _topLevelDragRow(self):
@ -265,10 +266,21 @@ where id > ?""",
self.mw.col.decks.select(did)
self.mw.onDeckConf()
def _collapse(self, did):
def _collapse(self, did: int) -> None:
self.mw.col.decks.collapse(did)
self._toggle_collapsed_in_node(did, self._dueTree)
self._renderPage(reuse=True)
def _toggle_collapsed_in_node(self, deck_id: int, node: DeckTreeNode) -> bool:
"Toggle collapsed on deck in tree. Returns true if found."
if node.deck_id == deck_id:
node.collapsed = not node.collapsed
return True
for child in node.children:
if self._toggle_collapsed_in_node(deck_id, child):
return True
return False
def _dragDeckOnto(self, draggedDeckDid, ontoDeckDid):
try:
self.mw.col.decks.renameForDragAndDrop(draggedDeckDid, ontoDeckDid)

View file

@ -62,3 +62,5 @@ check_untyped_defs=true
check_untyped_defs=true
[mypy-aqt.preferences]
check_untyped_defs=true
[mypy-aqt.deckbrowser]
check_untyped_defs=true

View file

@ -62,16 +62,21 @@ fn add_child_nodes(
}
}
fn add_collapsed(node: &mut DeckTreeNode, decks: &HashMap<DeckID, Deck>, browser: bool) {
fn add_collapsed_and_filtered(
node: &mut DeckTreeNode,
decks: &HashMap<DeckID, Deck>,
browser: bool,
) {
if let Some(deck) = decks.get(&DeckID(node.deck_id)) {
node.collapsed = if browser {
deck.common.browser_collapsed
} else {
deck.common.study_collapsed
};
node.filtered = deck.is_filtered();
}
for child in &mut node.children {
add_collapsed(child, decks, browser);
add_collapsed_and_filtered(child, decks, browser);
}
}
@ -201,7 +206,7 @@ impl Collection {
.map(|d| (d.id, d))
.collect();
add_collapsed(&mut tree, &decks_map, !counts);
add_collapsed_and_filtered(&mut tree, &decks_map, !counts);
if self.default_deck_is_empty()? {
hide_default_deck(&mut tree);
}