update editor, key handling in webview
keyPressEvent/etc no longer work with qwebengineview, so we need to install an event filter to catch things like ctrl+c. drag & drop doesn't seem to be supported until 5.7
This commit is contained in:
parent
9abc9fde9f
commit
d7339d9a27
247
aqt/editor.py
247
aqt/editor.py
@ -81,7 +81,7 @@ function sendState() {
|
|||||||
'sub': document.queryCommandState("subscript"),
|
'sub': document.queryCommandState("subscript"),
|
||||||
'col': document.queryCommandValue("forecolor")
|
'col': document.queryCommandValue("forecolor")
|
||||||
};
|
};
|
||||||
py.run("state:" + JSON.stringify(r));
|
pycmd("state:" + JSON.stringify(r));
|
||||||
};
|
};
|
||||||
|
|
||||||
function setFormat(cmd, arg, nosave) {
|
function setFormat(cmd, arg, nosave) {
|
||||||
@ -100,7 +100,7 @@ function clearChangeTimer() {
|
|||||||
|
|
||||||
function onFocus(elem) {
|
function onFocus(elem) {
|
||||||
currentField = elem;
|
currentField = elem;
|
||||||
py.run("focus:" + currentField.id.substring(1));
|
pycmd("focus:" + currentField.id.substring(1));
|
||||||
// don't adjust cursor on mouse clicks
|
// don't adjust cursor on mouse clicks
|
||||||
if (mouseDown) { return; }
|
if (mouseDown) { return; }
|
||||||
// do this twice so that there's no flicker on newer versions
|
// do this twice so that there's no flicker on newer versions
|
||||||
@ -156,7 +156,7 @@ function saveField(type) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// type is either 'blur' or 'key'
|
// type is either 'blur' or 'key'
|
||||||
py.run(type + ":" + currentField.innerHTML);
|
pycmd(type + ":" + currentField.innerHTML);
|
||||||
clearChangeTimer();
|
clearChangeTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ document.onclick = function (evt) {
|
|||||||
|
|
||||||
</script></head><body>
|
</script></head><body>
|
||||||
<div id="fields"></div>
|
<div id="fields"></div>
|
||||||
<div id="dupes"><a href="#" onclick="py.run('dupes');return false;">%s</a></div>
|
<div id="dupes" style="display:none;"><a href="#" onclick="pycmd('dupes');return false;">%s</a></div>
|
||||||
</body></html>
|
</body></html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -279,14 +279,13 @@ class Editor(object):
|
|||||||
self.setupButtons()
|
self.setupButtons()
|
||||||
self.setupWeb()
|
self.setupWeb()
|
||||||
self.setupTags()
|
self.setupTags()
|
||||||
self.setupKeyboard()
|
|
||||||
|
|
||||||
# Initial setup
|
# Initial setup
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
def setupOuter(self):
|
def setupOuter(self):
|
||||||
l = QVBoxLayout()
|
l = QVBoxLayout()
|
||||||
l.setMargin(0)
|
l.setContentsMargins(0,0,0,0)
|
||||||
l.setSpacing(0)
|
l.setSpacing(0)
|
||||||
self.widget.setLayout(l)
|
self.widget.setLayout(l)
|
||||||
self.outerLayout = l
|
self.outerLayout = l
|
||||||
@ -294,13 +293,15 @@ class Editor(object):
|
|||||||
def setupWeb(self):
|
def setupWeb(self):
|
||||||
self.web = EditorWebView(self.widget, self)
|
self.web = EditorWebView(self.widget, self)
|
||||||
self.web.allowDrops = True
|
self.web.allowDrops = True
|
||||||
self.web.setBridge(self.bridge)
|
self.web.onBridgeCmd = self.onBridgeCmd
|
||||||
self.outerLayout.addWidget(self.web, 1)
|
self.outerLayout.addWidget(self.web, 1)
|
||||||
# pick up the window colour
|
# pick up the window colour - missing on qt5.5
|
||||||
p = self.web.palette()
|
if hasattr(self.web.page(), "setBackgroundColor"):
|
||||||
p.setBrush(QPalette.Base, Qt.transparent)
|
self.web.page().setBackgroundColor(Qt.transparent)
|
||||||
self.web.page().setPalette(p)
|
self.web.onLoadFinished = self._loadFinished
|
||||||
self.web.setAttribute(Qt.WA_OpaquePaintEvent, False)
|
self.web.stdHtml(_html % (
|
||||||
|
getBase(self.mw.col), anki.js.jquery,
|
||||||
|
_("Show Duplicates")))
|
||||||
|
|
||||||
# Top buttons
|
# Top buttons
|
||||||
######################################################################
|
######################################################################
|
||||||
@ -309,9 +310,9 @@ class Editor(object):
|
|||||||
check=False, native=False, canDisable=True):
|
check=False, native=False, canDisable=True):
|
||||||
b = QPushButton(text)
|
b = QPushButton(text)
|
||||||
if check:
|
if check:
|
||||||
b.connect(b, SIGNAL("clicked(bool)"), func)
|
b.clicked["bool"].connect(func)
|
||||||
else:
|
else:
|
||||||
b.connect(b, SIGNAL("clicked()"), func)
|
b.clicked.connect(func)
|
||||||
if size:
|
if size:
|
||||||
b.setFixedHeight(20)
|
b.setFixedHeight(20)
|
||||||
b.setFixedWidth(20)
|
b.setFixedWidth(20)
|
||||||
@ -348,10 +349,10 @@ class Editor(object):
|
|||||||
# icons
|
# icons
|
||||||
self.iconsBox = QHBoxLayout()
|
self.iconsBox = QHBoxLayout()
|
||||||
if not isMac:
|
if not isMac:
|
||||||
self.iconsBox.setMargin(6)
|
self.iconsBox.setContentsMargins(6,6,6,6)
|
||||||
self.iconsBox.setSpacing(0)
|
self.iconsBox.setSpacing(0)
|
||||||
else:
|
else:
|
||||||
self.iconsBox.setMargin(0)
|
self.iconsBox.setContentsMargins(0,0,0,0)
|
||||||
self.iconsBox.setSpacing(14)
|
self.iconsBox.setSpacing(14)
|
||||||
self.outerLayout.addLayout(self.iconsBox)
|
self.outerLayout.addLayout(self.iconsBox)
|
||||||
b = self._addButton
|
b = self._addButton
|
||||||
@ -386,24 +387,19 @@ class Editor(object):
|
|||||||
_("Cloze deletion (Ctrl+Shift+C)"), text="[...]")
|
_("Cloze deletion (Ctrl+Shift+C)"), text="[...]")
|
||||||
but.setFixedWidth(24)
|
but.setFixedWidth(24)
|
||||||
s = self.clozeShortcut2 = QShortcut(
|
s = self.clozeShortcut2 = QShortcut(
|
||||||
QKeySequence(_("Ctrl+Alt+Shift+C")), self.parentWindow)
|
QKeySequence(_("Ctrl+Alt+Shift+C")), self.parentWindow,
|
||||||
s.connect(s, SIGNAL("activated()"), self.onCloze)
|
activated=self.onCloze)
|
||||||
# fixme: better image names
|
# fixme: better image names
|
||||||
b("mail-attachment", self.onAddMedia, _("F3"),
|
b("mail-attachment", self.onAddMedia, _("F3"),
|
||||||
_("Attach pictures/audio/video (F3)"))
|
_("Attach pictures/audio/video (F3)"))
|
||||||
b("media-record", self.onRecSound, _("F5"), _("Record audio (F5)"))
|
b("media-record", self.onRecSound, _("F5"), _("Record audio (F5)"))
|
||||||
b("adv", self.onAdvanced, text=downArrow())
|
b("adv", self.onAdvanced, text=downArrow())
|
||||||
s = QShortcut(QKeySequence("Ctrl+T, T"), self.widget)
|
s = QShortcut(QKeySequence("Ctrl+T, T"), self.widget, activated=self.insertLatex)
|
||||||
s.connect(s, SIGNAL("activated()"), self.insertLatex)
|
s = QShortcut(QKeySequence("Ctrl+T, E"), self.widget, activated=self.insertLatexEqn)
|
||||||
s = QShortcut(QKeySequence("Ctrl+T, E"), self.widget)
|
s = QShortcut(QKeySequence("Ctrl+T, M"), self.widget, activated=self.insertLatexMathEnv)
|
||||||
s.connect(s, SIGNAL("activated()"), self.insertLatexEqn)
|
s = QShortcut(QKeySequence("Ctrl+Shift+X"), self.widget, activated=self.onHtmlEdit)
|
||||||
s = QShortcut(QKeySequence("Ctrl+T, M"), self.widget)
|
|
||||||
s.connect(s, SIGNAL("activated()"), self.insertLatexMathEnv)
|
|
||||||
s = QShortcut(QKeySequence("Ctrl+Shift+X"), self.widget)
|
|
||||||
s.connect(s, SIGNAL("activated()"), self.onHtmlEdit)
|
|
||||||
# tags
|
# tags
|
||||||
s = QShortcut(QKeySequence("Ctrl+Shift+T"), self.widget)
|
s = QShortcut(QKeySequence("Ctrl+Shift+T"), self.widget, activated=lambda: self.tags.setFocus())
|
||||||
s.connect(s, SIGNAL("activated()"), lambda: self.tags.setFocus())
|
|
||||||
runHook("setupEditorButtons", self)
|
runHook("setupEditorButtons", self)
|
||||||
|
|
||||||
def enableButtons(self, val=True):
|
def enableButtons(self, val=True):
|
||||||
@ -432,20 +428,20 @@ class Editor(object):
|
|||||||
parent=self.parentWindow
|
parent=self.parentWindow
|
||||||
CardLayout(self.mw, self.note, ord=ord, parent=parent,
|
CardLayout(self.mw, self.note, ord=ord, parent=parent,
|
||||||
addMode=self.addMode)
|
addMode=self.addMode)
|
||||||
self.loadNote()
|
|
||||||
if isWin:
|
if isWin:
|
||||||
self.parentWindow.activateWindow()
|
self.parentWindow.activateWindow()
|
||||||
|
|
||||||
# JS->Python bridge
|
# JS->Python bridge
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def bridge(self, str):
|
def onBridgeCmd(self, cmd):
|
||||||
if not self.note or not runHook:
|
if not self.note or not runHook:
|
||||||
# shutdown
|
# shutdown
|
||||||
return
|
return
|
||||||
# focus lost or key/button pressed?
|
# focus lost or key/button pressed?
|
||||||
if str.startswith("blur") or str.startswith("key"):
|
if cmd.startswith("blur") or cmd.startswith("key"):
|
||||||
(type, txt) = str.split(":", 1)
|
(type, txt) = cmd.split(":", 1)
|
||||||
|
txt = urllib.parse.unquote(txt)
|
||||||
txt = self.mungeHTML(txt)
|
txt = self.mungeHTML(txt)
|
||||||
# misbehaving apps may include a null byte in the text
|
# misbehaving apps may include a null byte in the text
|
||||||
txt = txt.replace("\x00", "")
|
txt = txt.replace("\x00", "")
|
||||||
@ -474,24 +470,24 @@ class Editor(object):
|
|||||||
runHook("editTimer", self.note)
|
runHook("editTimer", self.note)
|
||||||
self.checkValid()
|
self.checkValid()
|
||||||
# focused into field?
|
# focused into field?
|
||||||
elif str.startswith("focus"):
|
elif cmd.startswith("focus"):
|
||||||
(type, num) = str.split(":", 1)
|
(type, num) = cmd.split(":", 1)
|
||||||
self.enableButtons()
|
self.enableButtons()
|
||||||
self.currentField = int(num)
|
self.currentField = int(num)
|
||||||
runHook("editFocusGained", self.note, self.currentField)
|
runHook("editFocusGained", self.note, self.currentField)
|
||||||
# state buttons changed?
|
# state buttons changed?
|
||||||
elif str.startswith("state"):
|
elif cmd.startswith("state"):
|
||||||
(cmd, txt) = str.split(":", 1)
|
(cmd, txt) = cmd.split(":", 1)
|
||||||
r = json.loads(txt)
|
r = json.loads(txt)
|
||||||
self._buttons['text_bold'].setChecked(r['bold'])
|
self._buttons['text_bold'].setChecked(r['bold'])
|
||||||
self._buttons['text_italic'].setChecked(r['italic'])
|
self._buttons['text_italic'].setChecked(r['italic'])
|
||||||
self._buttons['text_under'].setChecked(r['under'])
|
self._buttons['text_under'].setChecked(r['under'])
|
||||||
self._buttons['text_super'].setChecked(r['super'])
|
self._buttons['text_super'].setChecked(r['super'])
|
||||||
self._buttons['text_sub'].setChecked(r['sub'])
|
self._buttons['text_sub'].setChecked(r['sub'])
|
||||||
elif str.startswith("dupes"):
|
elif cmd.startswith("dupes"):
|
||||||
self.showDupes()
|
self.showDupes()
|
||||||
else:
|
else:
|
||||||
print(str)
|
print(cmd)
|
||||||
|
|
||||||
def mungeHTML(self, txt):
|
def mungeHTML(self, txt):
|
||||||
if txt == "<br>":
|
if txt == "<br>":
|
||||||
@ -501,7 +497,7 @@ class Editor(object):
|
|||||||
# Setting/unsetting the current note
|
# Setting/unsetting the current note
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def _loadFinished(self, w):
|
def _loadFinished(self):
|
||||||
self._loaded = True
|
self._loaded = True
|
||||||
if self.note:
|
if self.note:
|
||||||
self.loadNote()
|
self.loadNote()
|
||||||
@ -513,13 +509,8 @@ class Editor(object):
|
|||||||
self.disableButtons()
|
self.disableButtons()
|
||||||
if focus:
|
if focus:
|
||||||
self.stealFocus = True
|
self.stealFocus = True
|
||||||
# change timer
|
|
||||||
if self.note:
|
if self.note:
|
||||||
self.web.setHtml(_html % (
|
self.loadNote()
|
||||||
getBase(self.mw.col), anki.js.jquery,
|
|
||||||
_("Show Duplicates")), loadCB=self._loadFinished)
|
|
||||||
self.updateTags()
|
|
||||||
self.updateKeyboard()
|
|
||||||
else:
|
else:
|
||||||
self.hideCompleters()
|
self.hideCompleters()
|
||||||
if hide:
|
if hide:
|
||||||
@ -543,11 +534,13 @@ class Editor(object):
|
|||||||
self.web.eval("setFonts(%s);" % (
|
self.web.eval("setFonts(%s);" % (
|
||||||
json.dumps(self.fonts())))
|
json.dumps(self.fonts())))
|
||||||
self.checkValid()
|
self.checkValid()
|
||||||
|
self.updateTags()
|
||||||
self.widget.show()
|
self.widget.show()
|
||||||
if self.stealFocus:
|
if self.stealFocus:
|
||||||
self.web.setFocus()
|
self.web.setFocus()
|
||||||
self.stealFocus = False
|
self.stealFocus = False
|
||||||
|
|
||||||
|
|
||||||
def focus(self):
|
def focus(self):
|
||||||
self.web.setFocus()
|
self.web.setFocus()
|
||||||
|
|
||||||
@ -560,9 +553,6 @@ class Editor(object):
|
|||||||
if not self.note:
|
if not self.note:
|
||||||
return
|
return
|
||||||
self.saveTags()
|
self.saveTags()
|
||||||
if self.mw.app.focusWidget() != self.web:
|
|
||||||
# if no fields are focused, there's nothing to save
|
|
||||||
return
|
|
||||||
# move focus out of fields and save tags
|
# move focus out of fields and save tags
|
||||||
self.parentWindow.setFocus()
|
self.parentWindow.setFocus()
|
||||||
# and process events so any focus-lost hooks fire
|
# and process events so any focus-lost hooks fire
|
||||||
@ -606,8 +596,7 @@ class Editor(object):
|
|||||||
d = QDialog(self.widget)
|
d = QDialog(self.widget)
|
||||||
form = aqt.forms.edithtml.Ui_Dialog()
|
form = aqt.forms.edithtml.Ui_Dialog()
|
||||||
form.setupUi(d)
|
form.setupUi(d)
|
||||||
d.connect(form.buttonBox, SIGNAL("helpRequested()"),
|
form.buttonBox.helpRequested.connect(lambda: openHelp("editor"))
|
||||||
lambda: openHelp("editor"))
|
|
||||||
form.textEdit.setPlainText(self.note.fields[self.currentField])
|
form.textEdit.setPlainText(self.note.fields[self.currentField])
|
||||||
form.textEdit.moveCursor(QTextCursor.End)
|
form.textEdit.moveCursor(QTextCursor.End)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -630,13 +619,12 @@ class Editor(object):
|
|||||||
g.setFlat(True)
|
g.setFlat(True)
|
||||||
tb = QGridLayout()
|
tb = QGridLayout()
|
||||||
tb.setSpacing(12)
|
tb.setSpacing(12)
|
||||||
tb.setMargin(6)
|
tb.setContentsMargins(6,6,6,6)
|
||||||
# tags
|
# tags
|
||||||
l = QLabel(_("Tags"))
|
l = QLabel(_("Tags"))
|
||||||
tb.addWidget(l, 1, 0)
|
tb.addWidget(l, 1, 0)
|
||||||
self.tags = aqt.tagedit.TagEdit(self.widget)
|
self.tags = aqt.tagedit.TagEdit(self.widget)
|
||||||
self.tags.connect(self.tags, SIGNAL("lostFocus"),
|
self.tags.lostFocus.connect(self.saveTags)
|
||||||
self.saveTags)
|
|
||||||
self.tags.setToolTip(shortcut(_("Jump to tags with Ctrl+Shift+T")))
|
self.tags.setToolTip(shortcut(_("Jump to tags with Ctrl+Shift+T")))
|
||||||
tb.addWidget(self.tags, 1, 1)
|
tb.addWidget(self.tags, 1, 1)
|
||||||
g.setLayout(tb)
|
g.setLayout(tb)
|
||||||
@ -724,7 +712,7 @@ to a cloze type first, via Edit>Change Note Type."""))
|
|||||||
self.onColourChanged()
|
self.onColourChanged()
|
||||||
hbox = QHBoxLayout()
|
hbox = QHBoxLayout()
|
||||||
hbox.addWidget(self.foregroundFrame)
|
hbox.addWidget(self.foregroundFrame)
|
||||||
hbox.setMargin(5)
|
hbox.setContentsMargins(5,5,5,5)
|
||||||
but.setLayout(hbox)
|
but.setLayout(hbox)
|
||||||
|
|
||||||
# use last colour
|
# use last colour
|
||||||
@ -912,7 +900,7 @@ to a cloze type first, via Edit>Change Note Type."""))
|
|||||||
# from missing media
|
# from missing media
|
||||||
pass
|
pass
|
||||||
# strip all other attributes, including implicit max-width
|
# strip all other attributes, including implicit max-width
|
||||||
for attr, val in tag.attrs:
|
for attr, val in tag.attrs.items():
|
||||||
if attr != "src":
|
if attr != "src":
|
||||||
del tag[attr]
|
del tag[attr]
|
||||||
# strip superfluous elements
|
# strip superfluous elements
|
||||||
@ -928,17 +916,13 @@ to a cloze type first, via Edit>Change Note Type."""))
|
|||||||
def onAdvanced(self):
|
def onAdvanced(self):
|
||||||
m = QMenu(self.mw)
|
m = QMenu(self.mw)
|
||||||
a = m.addAction(_("LaTeX"))
|
a = m.addAction(_("LaTeX"))
|
||||||
a.setShortcut(QKeySequence("Ctrl+T, T"))
|
a.triggered.connect(self.insertLatex)
|
||||||
a.connect(a, SIGNAL("triggered()"), self.insertLatex)
|
|
||||||
a = m.addAction(_("LaTeX equation"))
|
a = m.addAction(_("LaTeX equation"))
|
||||||
a.setShortcut(QKeySequence("Ctrl+T, E"))
|
a.triggered.connect(self.insertLatexEqn)
|
||||||
a.connect(a, SIGNAL("triggered()"), self.insertLatexEqn)
|
|
||||||
a = m.addAction(_("LaTeX math env."))
|
a = m.addAction(_("LaTeX math env."))
|
||||||
a.setShortcut(QKeySequence("Ctrl+T, M"))
|
a.triggered.connect(self.insertLatexMathEnv)
|
||||||
a.connect(a, SIGNAL("triggered()"), self.insertLatexMathEnv)
|
|
||||||
a = m.addAction(_("Edit HTML"))
|
a = m.addAction(_("Edit HTML"))
|
||||||
a.setShortcut(QKeySequence("Ctrl+Shift+X"))
|
a.triggered.connect(self.onHtmlEdit)
|
||||||
a.connect(a, SIGNAL("triggered()"), self.onHtmlEdit)
|
|
||||||
m.exec_(QCursor.pos())
|
m.exec_(QCursor.pos())
|
||||||
|
|
||||||
# LaTeX
|
# LaTeX
|
||||||
@ -953,118 +937,65 @@ to a cloze type first, via Edit>Change Note Type."""))
|
|||||||
def insertLatexMathEnv(self):
|
def insertLatexMathEnv(self):
|
||||||
self.web.eval("wrap('[$$]', '[/$$]');")
|
self.web.eval("wrap('[$$]', '[/$$]');")
|
||||||
|
|
||||||
# Keyboard layout
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def setupKeyboard(self):
|
|
||||||
if isWin and self.mw.pm.profile['preserveKeyboard']:
|
|
||||||
a = ctypes.windll.user32.ActivateKeyboardLayout
|
|
||||||
a.restype = ctypes.c_void_p
|
|
||||||
a.argtypes = [ctypes.c_void_p, ctypes.c_uint]
|
|
||||||
g = ctypes.windll.user32.GetKeyboardLayout
|
|
||||||
g.restype = ctypes.c_void_p
|
|
||||||
g.argtypes = [ctypes.c_uint]
|
|
||||||
else:
|
|
||||||
a = g = None
|
|
||||||
self.activateKeyboard = a
|
|
||||||
self.getKeyboard = g
|
|
||||||
|
|
||||||
def updateKeyboard(self):
|
|
||||||
self.keyboardLayouts = {}
|
|
||||||
|
|
||||||
def saveKeyboard(self):
|
|
||||||
if not self.getKeyboard:
|
|
||||||
return
|
|
||||||
self.keyboardLayouts[self.currentField] = self.getKeyboard(0)
|
|
||||||
|
|
||||||
def restoreKeyboard(self):
|
|
||||||
if not self.getKeyboard:
|
|
||||||
return
|
|
||||||
if self.currentField in self.keyboardLayouts:
|
|
||||||
self.activateKeyboard(self.keyboardLayouts[self.currentField], 0)
|
|
||||||
|
|
||||||
# Pasting, drag & drop, and keyboard layouts
|
# Pasting, drag & drop, and keyboard layouts
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
# fixme: drag & drop
|
||||||
|
# fixme: middle click to paste
|
||||||
|
|
||||||
class EditorWebView(AnkiWebView):
|
class EditorWebView(AnkiWebView):
|
||||||
|
|
||||||
def __init__(self, parent, editor):
|
def __init__(self, parent, editor):
|
||||||
AnkiWebView.__init__(self)
|
AnkiWebView.__init__(self)
|
||||||
self.editor = editor
|
self.editor = editor
|
||||||
self.strip = self.editor.mw.pm.profile['stripHTML']
|
self.strip = self.editor.mw.pm.profile['stripHTML']
|
||||||
|
self.setAcceptDrops(True)
|
||||||
def keyPressEvent(self, evt):
|
|
||||||
if evt.matches(QKeySequence.Paste):
|
|
||||||
self.onPaste()
|
|
||||||
return evt.accept()
|
|
||||||
elif evt.matches(QKeySequence.Copy):
|
|
||||||
self.onCopy()
|
|
||||||
return evt.accept()
|
|
||||||
elif evt.matches(QKeySequence.Cut):
|
|
||||||
self.onCut()
|
|
||||||
return evt.accept()
|
|
||||||
QWebView.keyPressEvent(self, evt)
|
|
||||||
|
|
||||||
def onCut(self):
|
def onCut(self):
|
||||||
self.triggerPageAction(QWebPage.Cut)
|
self.triggerPageAction(QWebEnginePage.Cut)
|
||||||
self._flagAnkiText()
|
self._flagAnkiText()
|
||||||
|
|
||||||
def onCopy(self):
|
def onCopy(self):
|
||||||
self.triggerPageAction(QWebPage.Copy)
|
self.triggerPageAction(QWebEnginePage.Copy)
|
||||||
self._flagAnkiText()
|
self._flagAnkiText()
|
||||||
|
|
||||||
def onPaste(self):
|
def onPaste(self):
|
||||||
mime = self.mungeClip()
|
mime = self.mungeClip()
|
||||||
self.triggerPageAction(QWebPage.Paste)
|
self.triggerPageAction(QWebEnginePage.Paste)
|
||||||
self.restoreClip()
|
self.restoreClip()
|
||||||
|
|
||||||
def mouseReleaseEvent(self, evt):
|
# def mouseReleaseEvent(self, evt):
|
||||||
if not isMac and not isWin and evt.button() == Qt.MidButton:
|
# if not isMac and not isWin and evt.button() == Qt.MidButton:
|
||||||
# middle click on x11; munge the clipboard before standard
|
# # middle click on x11; munge the clipboard before standard
|
||||||
# handling
|
# # handling
|
||||||
mime = self.mungeClip(mode=QClipboard.Selection)
|
# mime = self.mungeClip(mode=QClipboard.Selection)
|
||||||
AnkiWebView.mouseReleaseEvent(self, evt)
|
# AnkiWebView.mouseReleaseEvent(self, evt)
|
||||||
self.restoreClip(mode=QClipboard.Selection)
|
# self.restoreClip(mode=QClipboard.Selection)
|
||||||
else:
|
# else:
|
||||||
AnkiWebView.mouseReleaseEvent(self, evt)
|
# AnkiWebView.mouseReleaseEvent(self, evt)
|
||||||
|
#
|
||||||
def focusInEvent(self, evt):
|
# def dropEvent(self, evt):
|
||||||
window = False
|
# oldmime = evt.mimeData()
|
||||||
if evt.reason() in (Qt.ActiveWindowFocusReason, Qt.PopupFocusReason):
|
# # coming from this program?
|
||||||
# editor area got focus again; need to tell js not to adjust cursor
|
# if evt.source():
|
||||||
self.eval("mouseDown++;")
|
# if oldmime.hasHtml():
|
||||||
window = True
|
# mime = QMimeData()
|
||||||
AnkiWebView.focusInEvent(self, evt)
|
# mime.setHtml(self.editor._filterHTML(oldmime.html()))
|
||||||
if evt.reason() == Qt.TabFocusReason:
|
# else:
|
||||||
self.eval("focusField(0);")
|
# # old qt on linux won't give us html when dragging an image;
|
||||||
elif evt.reason() == Qt.BacktabFocusReason:
|
# # in that case just do the default action (which is to ignore
|
||||||
n = len(self.editor.note.fields) - 1
|
# # the drag)
|
||||||
self.eval("focusField(%d);" % n)
|
# return AnkiWebView.dropEvent(self, evt)
|
||||||
elif window:
|
# else:
|
||||||
self.eval("mouseDown--;")
|
# mime = self._processMime(oldmime)
|
||||||
|
# # create a new event with the new mime data and run it
|
||||||
def dropEvent(self, evt):
|
# new = QDropEvent(evt.pos(), evt.possibleActions(), mime,
|
||||||
oldmime = evt.mimeData()
|
# evt.mouseButtons(), evt.keyboardModifiers())
|
||||||
# coming from this program?
|
# evt.accept()
|
||||||
if evt.source():
|
# AnkiWebView.dropEvent(self, new)
|
||||||
if oldmime.hasHtml():
|
# # tell the drop target to take focus so the drop contents are saved
|
||||||
mime = QMimeData()
|
# self.eval("dropTarget.focus();")
|
||||||
mime.setHtml(self.editor._filterHTML(oldmime.html()))
|
# self.setFocus()
|
||||||
else:
|
|
||||||
# old qt on linux won't give us html when dragging an image;
|
|
||||||
# in that case just do the default action (which is to ignore
|
|
||||||
# the drag)
|
|
||||||
return AnkiWebView.dropEvent(self, evt)
|
|
||||||
else:
|
|
||||||
mime = self._processMime(oldmime)
|
|
||||||
# create a new event with the new mime data and run it
|
|
||||||
new = QDropEvent(evt.pos(), evt.possibleActions(), mime,
|
|
||||||
evt.mouseButtons(), evt.keyboardModifiers())
|
|
||||||
evt.accept()
|
|
||||||
QWebView.dropEvent(self, new)
|
|
||||||
# tell the drop target to take focus so the drop contents are saved
|
|
||||||
self.eval("dropTarget.focus();")
|
|
||||||
self.setFocus()
|
|
||||||
|
|
||||||
def mungeClip(self, mode=QClipboard.Clipboard):
|
def mungeClip(self, mode=QClipboard.Clipboard):
|
||||||
clip = self.editor.mw.app.clipboard()
|
clip = self.editor.mw.app.clipboard()
|
||||||
@ -1207,10 +1138,10 @@ class EditorWebView(AnkiWebView):
|
|||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
a = m.addAction(_("Cut"))
|
a = m.addAction(_("Cut"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.onCut)
|
a.triggered.connect(self.onCut)
|
||||||
a = m.addAction(_("Copy"))
|
a = m.addAction(_("Copy"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.onCopy)
|
a.triggered.connect(self.onCopy)
|
||||||
a = m.addAction(_("Paste"))
|
a = m.addAction(_("Paste"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.onPaste)
|
a.triggered.connect(self.onPaste)
|
||||||
runHook("EditorWebView.contextMenuEvent", self, m)
|
runHook("EditorWebView.contextMenuEvent", self, m)
|
||||||
m.popup(QCursor.pos())
|
m.popup(QCursor.pos())
|
||||||
|
@ -44,7 +44,6 @@ class Reviewer(object):
|
|||||||
self.web.resetHandlers()
|
self.web.resetHandlers()
|
||||||
self.mw.keyHandler = self._keyHandler
|
self.mw.keyHandler = self._keyHandler
|
||||||
self.web.onBridgeCmd = self._linkHandler
|
self.web.onBridgeCmd = self._linkHandler
|
||||||
self.web.setKeyHandler(self._catchEsc)
|
|
||||||
if isMac:
|
if isMac:
|
||||||
self.bottom.web.setFixedHeight(46)
|
self.bottom.web.setFixedHeight(46)
|
||||||
else:
|
else:
|
||||||
@ -273,11 +272,6 @@ The front of this card is empty. Please run Tools>Empty Cards.""")
|
|||||||
# Handlers
|
# Handlers
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
def _catchEsc(self, evt):
|
|
||||||
if evt.key() == Qt.Key_Escape:
|
|
||||||
self.web.eval("$('#typeans').blur();")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _keyHandler(self, evt):
|
def _keyHandler(self, evt):
|
||||||
key = str(evt.text())
|
key = str(evt.text())
|
||||||
if key == "e":
|
if key == "e":
|
||||||
|
@ -76,26 +76,43 @@ class AnkiWebView(QWebEngineView):
|
|||||||
self.setPage(self._page)
|
self.setPage(self._page)
|
||||||
self.resetHandlers()
|
self.resetHandlers()
|
||||||
self.allowDrops = False
|
self.allowDrops = False
|
||||||
# reset each time new html is set; used to detect if still in same state
|
|
||||||
self.key = None
|
|
||||||
self.setCanFocus(canFocus)
|
self.setCanFocus(canFocus)
|
||||||
|
self.installEventFilter(self)
|
||||||
|
|
||||||
def keyPressEvent(self, evt):
|
def eventFilter(self, obj, evt):
|
||||||
|
if not isinstance(evt, QKeyEvent) or obj != self:
|
||||||
|
return False
|
||||||
if evt.matches(QKeySequence.Copy):
|
if evt.matches(QKeySequence.Copy):
|
||||||
self.triggerPageAction(QWebEnginePage.Copy)
|
self.onCopy()
|
||||||
evt.accept()
|
return True
|
||||||
# work around a bug with windows qt where shift triggers buttons
|
if evt.matches(QKeySequence.Cut):
|
||||||
if isWin and evt.modifiers() & Qt.ShiftModifier and not evt.text():
|
self.onCut()
|
||||||
evt.accept()
|
return True
|
||||||
return
|
if evt.matches(QKeySequence.Paste):
|
||||||
QWebEngineView.keyPressEvent(self, evt)
|
self.onPaste()
|
||||||
|
return True
|
||||||
|
if evt.matches(QKeySequence.Cancel):
|
||||||
|
# cheap hack to work around webengine swallowing escape key that
|
||||||
|
# usually closes dialogs
|
||||||
|
w = self.parent()
|
||||||
|
while w:
|
||||||
|
if isinstance(w, QDialog) or isinstance(w, QMainWindow):
|
||||||
|
from aqt import mw
|
||||||
|
if w != mw:
|
||||||
|
w.close()
|
||||||
|
break
|
||||||
|
w = w.parent()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def keyReleaseEvent(self, evt):
|
def onCopy(self):
|
||||||
if self._keyHandler:
|
self.triggerPageAction(QWebEnginePage.Copy)
|
||||||
if self._keyHandler(evt):
|
|
||||||
evt.accept()
|
def onCut(self):
|
||||||
return
|
self.triggerPageAction(QWebEnginePage.Cut)
|
||||||
QWebEngineView.keyReleaseEvent(self, evt)
|
|
||||||
|
def onPaste(self):
|
||||||
|
self.triggerPageAction(QWebEnginePage.Paste)
|
||||||
|
|
||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
if not self._canFocus:
|
if not self._canFocus:
|
||||||
@ -109,12 +126,7 @@ class AnkiWebView(QWebEngineView):
|
|||||||
def dropEvent(self, evt):
|
def dropEvent(self, evt):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setKeyHandler(self, handler=None):
|
|
||||||
# handler should return true if event should be swallowed
|
|
||||||
self._keyHandler = handler
|
|
||||||
|
|
||||||
def setHtml(self, html):
|
def setHtml(self, html):
|
||||||
self.key = None
|
|
||||||
app = QApplication.instance()
|
app = QApplication.instance()
|
||||||
oldFocus = app.focusWidget()
|
oldFocus = app.focusWidget()
|
||||||
self._page.setHtml(html)
|
self._page.setHtml(html)
|
||||||
@ -176,6 +188,5 @@ button {
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def resetHandlers(self):
|
def resetHandlers(self):
|
||||||
self.setKeyHandler(None)
|
|
||||||
self.onBridgeCmd = self.defaultOnBridgeCmd
|
self.onBridgeCmd = self.defaultOnBridgeCmd
|
||||||
self.onLoadFinished = self.defaultOnLoadFinished
|
self.onLoadFinished = self.defaultOnLoadFinished
|
||||||
|
Loading…
Reference in New Issue
Block a user