mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
clayout refactor
- tabs became difficult to read when users had a lot of templates, so we use a combobox instead - move the More button to the top and integrate adding/removing templates - start with no focus to avoid accidental modifications - display confirmation before adding template - remove 'edit to customize' text that some users had trouble editing
This commit is contained in:
parent
23f8e15816
commit
d55318d83f
2 changed files with 157 additions and 50 deletions
122
aqt/clayout.py
122
aqt/clayout.py
|
@ -38,43 +38,65 @@ class CardLayout(QDialog):
|
||||||
self.emptyFields.append(name)
|
self.emptyFields.append(name)
|
||||||
note[name] = "(%s)" % name
|
note[name] = "(%s)" % name
|
||||||
note.flush()
|
note.flush()
|
||||||
self.setupTabs()
|
self.setupTopArea()
|
||||||
self.setupMainArea()
|
self.setupMainArea()
|
||||||
self.setupButtons()
|
self.setupButtons()
|
||||||
self.setWindowTitle(_("Card Types for %s") % self.model['name'])
|
self.setWindowTitle(_("Card Types for %s") % self.model['name'])
|
||||||
v1 = QVBoxLayout()
|
v1 = QVBoxLayout()
|
||||||
v1.addWidget(self.tabs)
|
v1.addWidget(self.topArea)
|
||||||
v1.addWidget(self.mainArea)
|
v1.addWidget(self.mainArea)
|
||||||
v1.addLayout(self.buttons)
|
v1.addLayout(self.buttons)
|
||||||
|
v1.setContentsMargins(12,12,12,12)
|
||||||
self.setLayout(v1)
|
self.setLayout(v1)
|
||||||
self.redraw()
|
self.redraw()
|
||||||
restoreGeom(self, "CardLayout")
|
restoreGeom(self, "CardLayout")
|
||||||
self.setWindowModality(Qt.ApplicationModal)
|
self.setWindowModality(Qt.ApplicationModal)
|
||||||
self.show()
|
self.show()
|
||||||
|
# take the focus away from the first input area when starting up,
|
||||||
|
# as users tend to accidentally type into the template
|
||||||
|
self.setFocus()
|
||||||
|
|
||||||
def redraw(self):
|
def redraw(self):
|
||||||
self.cards = self.col.previewCards(self.note, 2)
|
self.cards = self.col.previewCards(self.note, 2)
|
||||||
self.redrawing = True
|
|
||||||
self.updateMainArea()
|
|
||||||
self.redrawing = False
|
|
||||||
idx = self.ord
|
idx = self.ord
|
||||||
if idx >= len(self.cards):
|
if idx >= len(self.cards):
|
||||||
idx = len(self.cards) - 1
|
self.ord = len(self.cards) - 1
|
||||||
self.selectCard(idx)
|
|
||||||
|
|
||||||
def setupTabs(self):
|
self.redrawing = True
|
||||||
cloze = self.model['type'] == MODEL_CLOZE
|
self.updateTopArea()
|
||||||
self.tabs = QTabWidget()
|
self.updateMainArea()
|
||||||
self.tabs.setTabsClosable(not cloze)
|
self.redrawing = False
|
||||||
self.tabs.setUsesScrollButtons(True)
|
self.onCardSelected(self.ord)
|
||||||
if not cloze:
|
|
||||||
add = QPushButton("+")
|
def setupTopArea(self):
|
||||||
add.setFixedWidth(30)
|
self.topArea = QWidget()
|
||||||
add.setToolTip(_("Add new card"))
|
self.topAreaForm = aqt.forms.clayout_top.Ui_Form()
|
||||||
add.clicked.connect(self.onAddCard)
|
self.topAreaForm.setupUi(self.topArea)
|
||||||
self.tabs.setCornerWidget(add)
|
self.topAreaForm.templateOptions.setText(_("Options") + " "+downArrow())
|
||||||
self.tabs.currentChanged.connect(self.onCardSelected)
|
self.topAreaForm.templateOptions.clicked.connect(self.onMore)
|
||||||
self.tabs.tabCloseRequested.connect(self.onRemoveTab)
|
self.topAreaForm.templatesBox.currentIndexChanged.connect(self.onCardSelected)
|
||||||
|
|
||||||
|
def updateTopArea(self):
|
||||||
|
cnt = self.mw.col.models.useCount(self.model)
|
||||||
|
self.topAreaForm.changesLabel.setText(ngettext(
|
||||||
|
"Changes below will affect the %(cnt)d note that uses this card type.",
|
||||||
|
"Changes below will affect the %(cnt)d notes that use this card type.",
|
||||||
|
cnt) % dict(cnt=cnt))
|
||||||
|
combo = self.topAreaForm.templatesBox
|
||||||
|
combo.clear()
|
||||||
|
combo.addItems(self._templateNameIncludingOrdinal(t) for t in self.model['tmpls'])
|
||||||
|
combo.setCurrentIndex(self.ord)
|
||||||
|
combo.setVisible(not self._isCloze())
|
||||||
|
|
||||||
|
def _templateNameIncludingOrdinal(self, tmpl):
|
||||||
|
return _("Card Type %(n)d of %(total)d: %(name)s") % dict(
|
||||||
|
n=tmpl['ord']+1,
|
||||||
|
total=len(self.model['tmpls']),
|
||||||
|
name=tmpl['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
def _isCloze(self):
|
||||||
|
return self.model['type'] == MODEL_CLOZE
|
||||||
|
|
||||||
def setupMainArea(self):
|
def setupMainArea(self):
|
||||||
w = self.mainArea = QWidget()
|
w = self.mainArea = QWidget()
|
||||||
|
@ -132,20 +154,16 @@ class CardLayout(QDialog):
|
||||||
head=base, js=jsinc)
|
head=base, js=jsinc)
|
||||||
|
|
||||||
def updateMainArea(self):
|
def updateMainArea(self):
|
||||||
self.tabs.clear()
|
if self._isCloze():
|
||||||
for t in self.model['tmpls']:
|
|
||||||
# dummy widget for now
|
|
||||||
self.tabs.addTab(QWidget(), t['name'])
|
|
||||||
|
|
||||||
if self.model['type'] == MODEL_CLOZE:
|
|
||||||
cnt = len(self.mm.availOrds(
|
cnt = len(self.mm.availOrds(
|
||||||
self.model, joinFields(self.note.fields)))
|
self.model, joinFields(self.note.fields)))
|
||||||
for g in self.pform.groupBox, self.pform.groupBox_2:
|
for g in self.pform.groupBox, self.pform.groupBox_2:
|
||||||
g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
|
g.setTitle(g.title() + _(" (1 of %d)") % max(cnt, 1))
|
||||||
|
|
||||||
def onRemoveTab(self, idx):
|
def onRemove(self):
|
||||||
if len(self.model['tmpls']) < 2:
|
if len(self.model['tmpls']) < 2:
|
||||||
return showInfo(_("At least one card type is required."))
|
return showInfo(_("At least one card type is required."))
|
||||||
|
idx = self.ord
|
||||||
cards = self.mm.tmplUseCount(self.model, idx)
|
cards = self.mm.tmplUseCount(self.model, idx)
|
||||||
cards = ngettext("%d card", "%d cards", cards) % cards
|
cards = ngettext("%d card", "%d cards", cards) % cards
|
||||||
msg = (_("Delete the '%(a)s' card type, and its %(b)s?") %
|
msg = (_("Delete the '%(a)s' card type, and its %(b)s?") %
|
||||||
|
@ -172,15 +190,11 @@ Please create a new card type first."""))
|
||||||
addField.setAutoDefault(False)
|
addField.setAutoDefault(False)
|
||||||
l.addWidget(addField)
|
l.addWidget(addField)
|
||||||
addField.clicked.connect(self.onAddField)
|
addField.clicked.connect(self.onAddField)
|
||||||
if self.model['type'] != MODEL_CLOZE:
|
if not self._isCloze():
|
||||||
flip = QPushButton(_("Flip"))
|
flip = QPushButton(_("Flip"))
|
||||||
flip.setAutoDefault(False)
|
flip.setAutoDefault(False)
|
||||||
l.addWidget(flip)
|
l.addWidget(flip)
|
||||||
flip.clicked.connect(self.onFlip)
|
flip.clicked.connect(self.onFlip)
|
||||||
more = QPushButton(_("More") + " "+downArrow())
|
|
||||||
more.setAutoDefault(False)
|
|
||||||
l.addWidget(more)
|
|
||||||
more.clicked.connect(lambda: self.onMore(more))
|
|
||||||
l.addStretch()
|
l.addStretch()
|
||||||
close = QPushButton(_("Close"))
|
close = QPushButton(_("Close"))
|
||||||
close.setAutoDefault(False)
|
close.setAutoDefault(False)
|
||||||
|
@ -190,19 +204,11 @@ Please create a new card type first."""))
|
||||||
# Cards
|
# Cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def selectCard(self, idx):
|
|
||||||
if self.tabs.currentIndex() == idx:
|
|
||||||
# trigger a re-read
|
|
||||||
self.onCardSelected(idx)
|
|
||||||
else:
|
|
||||||
self.tabs.setCurrentIndex(idx)
|
|
||||||
|
|
||||||
def onCardSelected(self, idx):
|
def onCardSelected(self, idx):
|
||||||
if self.redrawing:
|
if self.redrawing:
|
||||||
return
|
return
|
||||||
self.card = self.cards[idx]
|
self.card = self.cards[idx]
|
||||||
self.ord = idx
|
self.ord = idx
|
||||||
self.tabs.setCurrentIndex(idx)
|
|
||||||
self.playedAudio = {}
|
self.playedAudio = {}
|
||||||
self.readCard()
|
self.readCard()
|
||||||
self.renderPreview()
|
self.renderPreview()
|
||||||
|
@ -295,7 +301,7 @@ Please create a new card type first."""))
|
||||||
if c.template()['ord'] != self.ord]:
|
if c.template()['ord'] != self.ord]:
|
||||||
return showWarning(_("That name is already used."))
|
return showWarning(_("That name is already used."))
|
||||||
self.card.template()['name'] = name
|
self.card.template()['name'] = name
|
||||||
self.tabs.setTabText(self.tabs.currentIndex(), name)
|
self.redraw()
|
||||||
|
|
||||||
def onReorder(self):
|
def onReorder(self):
|
||||||
n = len(self.cards)
|
n = len(self.cards)
|
||||||
|
@ -328,10 +334,15 @@ Please create a new card type first."""))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def onAddCard(self):
|
def onAddCard(self):
|
||||||
|
cnt = self.mw.col.models.useCount(self.model)
|
||||||
|
txt = ngettext("This will create %d card. Proceed?",
|
||||||
|
"This will create %d cards. Proceed?", cnt) % cnt
|
||||||
|
if not askUser(txt):
|
||||||
|
return
|
||||||
name = self._newCardName()
|
name = self._newCardName()
|
||||||
t = self.mm.newTemplate(name)
|
t = self.mm.newTemplate(name)
|
||||||
old = self.card.template()
|
old = self.card.template()
|
||||||
t['qfmt'] = "%s<br>\n%s" % (_("Edit to customize"), old['qfmt'])
|
t['qfmt'] = old['qfmt']
|
||||||
t['afmt'] = old['afmt']
|
t['afmt'] = old['afmt']
|
||||||
self.mm.addTemplate(self.model, t)
|
self.mm.addTemplate(self.model, t)
|
||||||
self.ord = len(self.cards)
|
self.ord = len(self.cards)
|
||||||
|
@ -353,23 +364,36 @@ adjust the template manually to switch the question and answer."""))
|
||||||
dst['qfmt'] = m.group(2).strip()
|
dst['qfmt'] = m.group(2).strip()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def onMore(self, button):
|
def onMore(self):
|
||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
a = m.addAction(_("Rename"))
|
|
||||||
|
if not self._isCloze():
|
||||||
|
a = m.addAction(_("Add Card Type..."))
|
||||||
|
a.triggered.connect(self.onAddCard)
|
||||||
|
|
||||||
|
a = m.addAction(_("Remove Card Type..."))
|
||||||
|
a.triggered.connect(self.onRemove)
|
||||||
|
|
||||||
|
a = m.addAction(_("Rename Card Type..."))
|
||||||
a.triggered.connect(self.onRename)
|
a.triggered.connect(self.onRename)
|
||||||
if self.model['type'] != MODEL_CLOZE:
|
|
||||||
a = m.addAction(_("Reposition"))
|
a = m.addAction(_("Reposition Card Type..."))
|
||||||
a.triggered.connect(self.onReorder)
|
a.triggered.connect(self.onReorder)
|
||||||
|
|
||||||
|
m.addSeparator()
|
||||||
|
|
||||||
t = self.card.template()
|
t = self.card.template()
|
||||||
if t['did']:
|
if t['did']:
|
||||||
s = _(" (on)")
|
s = _(" (on)")
|
||||||
else:
|
else:
|
||||||
s = _(" (off)")
|
s = _(" (off)")
|
||||||
a = m.addAction(_("Deck Override") + s)
|
a = m.addAction(_("Deck Override...") + s)
|
||||||
a.triggered.connect(self.onTargetDeck)
|
a.triggered.connect(self.onTargetDeck)
|
||||||
a = m.addAction(_("Browser Appearance"))
|
|
||||||
|
a = m.addAction(_("Browser Appearance..."))
|
||||||
a.triggered.connect(self.onBrowserDisplay)
|
a.triggered.connect(self.onBrowserDisplay)
|
||||||
m.exec_(button.mapToGlobal(QPoint(0,0)))
|
|
||||||
|
m.exec_(self.topAreaForm.templateOptions.mapToGlobal(QPoint(0,0)))
|
||||||
|
|
||||||
def onBrowserDisplay(self):
|
def onBrowserDisplay(self):
|
||||||
d = QDialog()
|
d = QDialog()
|
||||||
|
|
83
designer/clayout_top.ui
Normal file
83
designer/clayout_top.ui
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="templatesBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>10</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>1</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="templateOptions">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="autoDefault">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="changesLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
Reference in a new issue