mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
290 lines
7.2 KiB
C++
290 lines
7.2 KiB
C++
#include <QtWidgets>
|
|
#include <QString>
|
|
#include <QDebug>
|
|
#include <QShortcut>
|
|
#include "vcaptain.h"
|
|
#include "veditarea.h"
|
|
#include "vedittab.h"
|
|
#include "vfilelist.h"
|
|
#include "vnavigationmode.h"
|
|
#include "vconfigmanager.h"
|
|
|
|
extern VConfigManager *g_config;
|
|
|
|
VCaptain::VCaptain(QWidget *p_parent)
|
|
: QWidget(p_parent),
|
|
m_mode(CaptainMode::Normal),
|
|
m_widgetBeforeCaptain(NULL),
|
|
m_nextMajorKey('a'),
|
|
m_ignoreFocusChange(false)
|
|
{
|
|
connect(qApp, &QApplication::focusChanged,
|
|
this, &VCaptain::handleFocusChanged);
|
|
|
|
setWindowFlags(Qt::FramelessWindowHint);
|
|
|
|
// Make it as small as possible. This widget will stay at the top-left corner
|
|
// of VMainWindow.
|
|
resize(1, 1);
|
|
|
|
// Register Captain mode leader key.
|
|
// This can fix the Input Method blocking issue.
|
|
m_captainModeShortcut = new QShortcut(QKeySequence(g_config->getShortcutKeySequence("CaptainMode")),
|
|
this);
|
|
m_captainModeShortcut->setContext(Qt::ApplicationShortcut);
|
|
connect(m_captainModeShortcut, &QShortcut::activated,
|
|
this, &VCaptain::trigger);
|
|
|
|
// Register Navigation mode as Captain mode target.
|
|
registerCaptainTarget(tr("NavigationMode"),
|
|
g_config->getCaptainShortcutKeySequence("NavigationMode"),
|
|
this,
|
|
navigationModeByCaptain);
|
|
}
|
|
|
|
QChar VCaptain::getNextMajorKey()
|
|
{
|
|
QChar ret = m_nextMajorKey;
|
|
if (m_nextMajorKey == 'z') {
|
|
m_nextMajorKey = QChar();
|
|
} else if (!m_nextMajorKey.isNull()) {
|
|
m_nextMajorKey = QChar(m_nextMajorKey.toLatin1() + 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void VCaptain::registerNavigationTarget(VNavigationMode *p_target)
|
|
{
|
|
QChar key = getNextMajorKey();
|
|
if (!key.isNull()) {
|
|
p_target->registerNavigation(key);
|
|
m_naviTargets.push_back(NaviModeTarget(p_target, true));
|
|
}
|
|
}
|
|
|
|
void VCaptain::handleFocusChanged(QWidget *p_old, QWidget * p_now)
|
|
{
|
|
Q_UNUSED(p_now);
|
|
|
|
if (p_old == this
|
|
&& !m_ignoreFocusChange
|
|
&& !checkMode(CaptainMode::Normal)) {
|
|
exitCaptainMode();
|
|
}
|
|
}
|
|
|
|
void VCaptain::trigger()
|
|
{
|
|
if (!checkMode(CaptainMode::Normal)) {
|
|
exitCaptainMode();
|
|
return;
|
|
}
|
|
|
|
triggerCaptainMode();
|
|
}
|
|
|
|
void VCaptain::keyPressEvent(QKeyEvent *p_event)
|
|
{
|
|
int key = p_event->key();
|
|
Qt::KeyboardModifiers modifiers = p_event->modifiers();
|
|
|
|
Q_ASSERT(!checkMode(CaptainMode::Normal));
|
|
|
|
if (VUtils::isMetaKey(key)) {
|
|
QWidget::keyPressEvent(p_event);
|
|
return;
|
|
}
|
|
|
|
if (handleKeyPress(key, modifiers)) {
|
|
p_event->accept();
|
|
} else {
|
|
QWidget::keyPressEvent(p_event);
|
|
}
|
|
}
|
|
|
|
bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers)
|
|
{
|
|
bool ret = true;
|
|
|
|
m_ignoreFocusChange = true;
|
|
|
|
if (checkMode(CaptainMode::Navigation)) {
|
|
ret = handleKeyPressNavigationMode(p_key, p_modifiers);
|
|
m_ignoreFocusChange = false;
|
|
return ret;
|
|
}
|
|
|
|
ret = handleKeyPressCaptainMode(p_key, p_modifiers);
|
|
m_ignoreFocusChange = false;
|
|
return ret;
|
|
}
|
|
|
|
bool VCaptain::handleKeyPressNavigationMode(int p_key,
|
|
Qt::KeyboardModifiers /* p_modifiers */)
|
|
{
|
|
Q_ASSERT(m_mode == CaptainMode::Navigation);
|
|
bool hasConsumed = false;
|
|
bool pending = false;
|
|
for (auto &target : m_naviTargets) {
|
|
if (hasConsumed) {
|
|
target.m_available = false;
|
|
target.m_target->hideNavigation();
|
|
continue;
|
|
}
|
|
if (target.m_available) {
|
|
bool succeed = false;
|
|
// May change focus, so we need to ignore focus change here.
|
|
bool consumed = target.m_target->handleKeyNavigation(p_key, succeed);
|
|
if (consumed) {
|
|
hasConsumed = true;
|
|
if (succeed) {
|
|
// Exit.
|
|
m_widgetBeforeCaptain = NULL;
|
|
} else {
|
|
// Consumed but not succeed. Need more keys.
|
|
pending = true;
|
|
}
|
|
} else {
|
|
// Do not ask this target any more.
|
|
target.m_available = false;
|
|
target.m_target->hideNavigation();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pending) {
|
|
return true;
|
|
}
|
|
|
|
exitCaptainMode();
|
|
return true;
|
|
}
|
|
|
|
void VCaptain::triggerNavigationMode()
|
|
{
|
|
setMode(CaptainMode::Navigation);
|
|
|
|
for (auto &target : m_naviTargets) {
|
|
target.m_available = true;
|
|
target.m_target->showNavigation();
|
|
}
|
|
}
|
|
|
|
void VCaptain::exitNavigationMode()
|
|
{
|
|
setMode(CaptainMode::Normal);
|
|
|
|
for (auto &target : m_naviTargets) {
|
|
target.m_available = true;
|
|
target.m_target->hideNavigation();
|
|
}
|
|
}
|
|
|
|
void VCaptain::restoreFocus()
|
|
{
|
|
if (m_widgetBeforeCaptain) {
|
|
m_widgetBeforeCaptain->setFocus();
|
|
m_widgetBeforeCaptain = NULL;
|
|
}
|
|
}
|
|
|
|
void VCaptain::exitCaptainMode()
|
|
{
|
|
if (checkMode(CaptainMode::Normal)) {
|
|
return;
|
|
} else if (checkMode(CaptainMode::Navigation)) {
|
|
exitNavigationMode();
|
|
}
|
|
|
|
setMode(CaptainMode::Normal);
|
|
m_ignoreFocusChange = false;
|
|
|
|
restoreFocus();
|
|
|
|
emit captainModeChanged(false);
|
|
}
|
|
|
|
bool VCaptain::registerCaptainTarget(const QString &p_name,
|
|
const QString &p_key,
|
|
void *p_target,
|
|
CaptainFunc p_func)
|
|
{
|
|
if (p_key.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
QString normKey = QKeySequence(p_key).toString();
|
|
|
|
if (m_captainTargets.contains(normKey)) {
|
|
return false;
|
|
}
|
|
|
|
CaptainModeTarget target(p_name,
|
|
normKey,
|
|
p_target,
|
|
p_func);
|
|
m_captainTargets.insert(normKey, target);
|
|
|
|
qDebug() << "registered:" << target.toString() << normKey;
|
|
|
|
return true;
|
|
}
|
|
|
|
void VCaptain::triggerCaptainTarget(const QString &p_key)
|
|
{
|
|
auto it = m_captainTargets.find(p_key);
|
|
if (it == m_captainTargets.end()) {
|
|
return;
|
|
}
|
|
|
|
const CaptainModeTarget &target = it.value();
|
|
|
|
qDebug() << "triggered:" << target.toString();
|
|
|
|
CaptainData data(m_widgetBeforeCaptain);
|
|
if (!target.m_function(target.m_target, (void *)&data)) {
|
|
m_widgetBeforeCaptain = NULL;
|
|
}
|
|
}
|
|
|
|
bool VCaptain::navigationModeByCaptain(void *p_target, void *p_data)
|
|
{
|
|
Q_UNUSED(p_data);
|
|
VCaptain *obj = static_cast<VCaptain *>(p_target);
|
|
obj->triggerNavigationMode();
|
|
return true;
|
|
}
|
|
|
|
void VCaptain::triggerCaptainMode()
|
|
{
|
|
m_widgetBeforeCaptain = QApplication::focusWidget();
|
|
|
|
m_ignoreFocusChange = false;
|
|
|
|
setMode(CaptainMode::Pending);
|
|
|
|
emit captainModeChanged(true);
|
|
|
|
// Focus to listen pending key press.
|
|
setFocus();
|
|
}
|
|
|
|
bool VCaptain::handleKeyPressCaptainMode(int p_key,
|
|
Qt::KeyboardModifiers p_modifiers)
|
|
{
|
|
Q_ASSERT(checkMode(CaptainMode::Pending));
|
|
QString normKey = QKeySequence(p_key | p_modifiers).toString();
|
|
triggerCaptainTarget(normKey);
|
|
|
|
if (!checkMode(CaptainMode::Navigation)) {
|
|
exitCaptainMode();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VCaptain::setCaptainModeEnabled(bool p_enabled)
|
|
{
|
|
m_captainModeShortcut->setEnabled(p_enabled);
|
|
}
|