diff --git a/sdk/wlxplugin.h b/sdk/wlxplugin.h index 8f33fb2..eebce8e 100644 --- a/sdk/wlxplugin.h +++ b/sdk/wlxplugin.h @@ -9,6 +9,7 @@ #define lc_newparams 2 #define lc_selectall 3 #define lc_setpercent 4 +#define lc_focus 5 #define lcp_wraptext 1 #define lcp_fittowindow 2 diff --git a/wlx/kate/CMakeLists.txt b/wlx/kate/CMakeLists.txt index 37415dc..ea4083f 100644 --- a/wlx/kate/CMakeLists.txt +++ b/wlx/kate/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(rich_editor_qt VERSION 1.0 LANGUAGES CXX) +project(kate_qt6 VERSION 1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -12,16 +12,16 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) find_package(KF6TextEditor REQUIRED) find_package(KF6Config REQUIRED) -add_library(rich_editor_qt SHARED +add_library(kate_qt6 SHARED src/wlx_plugin.cpp src/editor_widget.cpp ) -target_compile_definitions(rich_editor_qt PRIVATE +target_compile_definitions(kate_qt6 PRIVATE RICH_EDITOR_QT_LOG_DIR="${CMAKE_CURRENT_SOURCE_DIR}/logs" ) -target_link_libraries(rich_editor_qt PRIVATE +target_link_libraries(kate_qt6 PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets @@ -29,7 +29,7 @@ target_link_libraries(rich_editor_qt PRIVATE KF6::ConfigCore ) -set_target_properties(rich_editor_qt PROPERTIES +set_target_properties(kate_qt6 PROPERTIES PREFIX "" SUFFIX ".wlx" ) diff --git a/wlx/kate/README.md b/wlx/kate/README.md index b0317f9..81d3558 100644 --- a/wlx/kate/README.md +++ b/wlx/kate/README.md @@ -1,4 +1,4 @@ -# rich_editor_qt +# kate_qt6 A premium, high-performance Double Commander WLX plugin based on Native Qt6 and KDE's `KTextEditor` framework. @@ -25,4 +25,4 @@ make ``` ## Installation -Copy `rich_editor_qt.wlx` to `~/.config/doublecmd/plugins/wlx/` and add it via Double Commander's Options -> Plugins -> WLX. +Copy `kate_qt6.wlx` to `~/.config/doublecmd/plugins/wlx/` and add it via Double Commander's Options -> Plugins -> WLX. diff --git a/wlx/kate/build/kate_qt6.wlx b/wlx/kate/build/kate_qt6.wlx new file mode 100755 index 0000000..6065b29 Binary files /dev/null and b/wlx/kate/build/kate_qt6.wlx differ diff --git a/wlx/kate/src/editor_widget.cpp b/wlx/kate/src/editor_widget.cpp index a0a6db4..ffb43c5 100644 --- a/wlx/kate/src/editor_widget.cpp +++ b/wlx/kate/src/editor_widget.cpp @@ -87,7 +87,6 @@ EditorWidget::EditorWidget(QWidget *parent) // Disable swap files as early as possible before the view is created. forceDisableAutoReload(); m_view = m_doc->createView(this); - setFocusProxy(m_view); m_zoomLevel = m_view->configValue(QStringLiteral("font")).value().pointSize(); // Create UI Elements @@ -239,8 +238,6 @@ EditorWidget::EditorWidget(QWidget *parent) QWidget *focusTarget = m_view->focusProxy() ? m_view->focusProxy() : m_view; focusTarget->setFocus(Qt::OtherFocusReason); m_isRestoringFocus = false; - } else { - m_isActive = false; } }); @@ -330,12 +327,6 @@ KTextEditor::Cursor EditorWidget::findWordEnd(const KTextEditor::Cursor &cursor) } bool EditorWidget::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::FocusIn) { - if (obj == m_view || obj == m_view->focusProxy()) { - m_isActive = true; - } - } - // Detect clicks outside our editor panel so Double Commander can regain focus. if (event->type() == QEvent::MouseButtonPress && m_isActive) { auto *me = static_cast(event); @@ -370,6 +361,7 @@ bool EditorWidget::eventFilter(QObject *obj, QEvent *event) { (key == Qt::Key_Z && (mods & Qt::ControlModifier)) || (key == Qt::Key_Y && (mods & Qt::ControlModifier)) || (key == Qt::Key_W && (mods & Qt::ControlModifier)) || + (key == Qt::Key_Q && (mods & Qt::ControlModifier)) || (key == Qt::Key_R && (mods & Qt::AltModifier) && (mods & Qt::ShiftModifier)) || (key == Qt::Key_P && (mods & Qt::ControlModifier)) || ((key == Qt::Key_Plus || key == Qt::Key_Equal) && (mods & Qt::ControlModifier)) || @@ -495,6 +487,12 @@ bool EditorWidget::eventFilter(QObject *obj, QEvent *event) { } else if (key == Qt::Key_W && (mods & Qt::ControlModifier)) { toggleReadOnly(); triggered = true; + } else if (key == Qt::Key_Q && (mods & Qt::ControlModifier)) { + if (QWidget *w = window()) { + w->setFocus(Qt::OtherFocusReason); + QCoreApplication::postEvent(w, new QKeyEvent(QEvent::KeyPress, Qt::Key_Q, Qt::ControlModifier)); + } + triggered = true; } else if (key == Qt::Key_R && (mods & Qt::AltModifier) && (mods & Qt::ShiftModifier)) { toggleReadOnly(); triggered = true; @@ -1091,6 +1089,16 @@ bool EditorWidget::isModified() const { return m_doc ? m_doc->isModified() : false; } +void EditorWidget::hostSetFocus(bool focus) { + if (focus) { + setActive(true); + setFocus(Qt::OtherFocusReason); + if (m_view) m_view->setFocus(Qt::OtherFocusReason); + } else { + setActive(false); + } +} + void EditorWidget::setupMenu() { // File QMenu *fileMenu = m_menuBar->addMenu("&File"); diff --git a/wlx/kate/src/editor_widget.h b/wlx/kate/src/editor_widget.h index 6d370f8..d6356a0 100644 --- a/wlx/kate/src/editor_widget.h +++ b/wlx/kate/src/editor_widget.h @@ -28,6 +28,7 @@ class EditorWidget : public QWidget { void copySelection(); void selectAll(); + void hostSetFocus(bool focus); QString currentFilePath() const; bool isModified() const; diff --git a/wlx/kate/src/wlx_plugin.cpp b/wlx/kate/src/wlx_plugin.cpp index 561ecb2..d6f9a3a 100644 --- a/wlx/kate/src/wlx_plugin.cpp +++ b/wlx/kate/src/wlx_plugin.cpp @@ -8,6 +8,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include static void logLoad(const QString &msg) { const QString dir = QStringLiteral(RICH_EDITOR_QT_LOG_DIR); @@ -117,9 +123,68 @@ void DCPCALL ListCloseWindow(HWND ListWin) { } void DCPCALL ListGetDetectString(char* DetectString, int maxlen) { - const char* detectStr = "EXT=\"TXT\" | EXT=\"PAS\" | EXT=\"C\" | EXT=\"CPP\" | EXT=\"H\" | EXT=\"PY\" | EXT=\"JS\" | EXT=\"HTML\" | EXT=\"CSS\" | EXT=\"JSON\" | EXT=\"XML\" | EXT=\"MD\" | EXT=\"SH\""; - strncpy(DetectString, detectStr, maxlen - 1); - DetectString[maxlen - 1] = '\0'; + int argc = 0; + char **argv = nullptr; + bool appCreated = false; + if (!QCoreApplication::instance()) { + new QCoreApplication(argc, argv); + appCreated = true; + } + + QSet allExts; + QMimeDatabase mimeDb; + + QVector parts = KPluginMetaData::findPlugins(QStringLiteral("kf6/parts")); + for (const KPluginMetaData &part : parts) { + if (part.pluginId() == QLatin1String("katepart")) { + QStringList mimeTypes = part.mimeTypes(); + for (const QString &mimeName : mimeTypes) { + QMimeType mimeType = mimeDb.mimeTypeForName(mimeName); + if (mimeType.isValid()) { + QStringList globs = mimeType.globPatterns(); + for (const QString &glob : globs) { + if (glob.startsWith(QLatin1String("*."))) { + QString fExt = glob.mid(2); + int lDot = fExt.lastIndexOf(QLatin1Char('.')); + QString aExt = (lDot == -1) ? fExt : fExt.mid(lDot + 1); + aExt = aExt.toUpper(); + if (!aExt.isEmpty()) { + allExts.insert(aExt); + } + } + } + } + } + } + } + + if (allExts.isEmpty()) { + const char* detectStr = "EXT=\"TXT\" | EXT=\"PAS\" | EXT=\"C\" | EXT=\"CPP\" | EXT=\"H\" | EXT=\"PY\" | EXT=\"JS\" | EXT=\"HTML\" | EXT=\"CSS\" | EXT=\"JSON\" | EXT=\"XML\" | EXT=\"MD\" | EXT=\"SH\""; + strncpy(DetectString, detectStr, maxlen - 1); + DetectString[maxlen - 1] = '\0'; + } else { + QStringList extList = allExts.values(); + extList.sort(); + + QString result; + for (const QString &ext : extList) { + QString addition = QStringLiteral("EXT=\"%1\"").arg(ext); + if (!result.isEmpty()) addition = QLatin1String(" | ") + addition; + + if (result.length() + addition.length() < maxlen - 1) { + result += addition; + } else { + break; + } + } + + QByteArray utf8 = result.toUtf8(); + qstrncpy(DetectString, utf8.constData(), maxlen); + } + + if (appCreated) { + delete QCoreApplication::instance(); + } } int DCPCALL ListSendCommand(HWND ListWin, int Command, int Parameter) { @@ -142,6 +207,11 @@ int DCPCALL ListSendCommand(HWND ListWin, int Command, int Parameter) { case lc_selectall: editor->selectAll(); return LISTPLUGIN_OK; + case lc_focus: + // DC uses lc_focus to indicate focus gained/lost. + // Parameter: 1 = focus gained, 0 = focus lost (common convention). + editor->hostSetFocus(Parameter != 0); + return LISTPLUGIN_OK; } return LISTPLUGIN_ERROR;