- Bug in Funktionanzeige behoben
- Auszuschliessende Verzeichnisse in Projektfunktionen hinzugefügt - Dateien können als Paramter übergeben werden - Englisch hinzugefügt
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(BareCode
|
||||
VERSION 1.2.0
|
||||
VERSION 1.3.0
|
||||
DESCRIPTION "A modular code editor built with Qt6"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# BareCode
|
||||
|
||||
**Schlanker, modularer Code-Editor für Web-Entwickler**
|
||||
Version 1.2.0 — von **Dany Thinnes** | [Projekt Hirnfrei](https://www.projekt-hirnfrei.de) | [Discord](https://discord.projekt-hirnfrei.de)
|
||||
Version 1.3.0 — von **Dany Thinnes** | [Projekt Hirnfrei](https://www.projekt-hirnfrei.de) | [Discord](https://discord.projekt-hirnfrei.de)
|
||||
|
||||
Entwickelt mit **C++17**, **Qt6** und **CMake**.
|
||||
Kompiliert auf **Linux**, **Windows** und **Haiku**.
|
||||
|
||||
15
main.cpp
15
main.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[])
|
||||
QApplication app(argc, argv);
|
||||
|
||||
app.setApplicationName("BareCode");
|
||||
app.setApplicationVersion("1.2.0");
|
||||
app.setApplicationVersion("1.3.0");
|
||||
app.setOrganizationName("BareCode");
|
||||
|
||||
// Icon
|
||||
@@ -89,5 +89,18 @@ int main(int argc, char *argv[])
|
||||
MainWindow window;
|
||||
window.show();
|
||||
|
||||
// Datei(en) öffnen, die beim Start per Kommandozeile übergeben wurden
|
||||
// (z. B. Doppelklick auf eine Datei im Dateibrowser → "Öffnen mit BareCode").
|
||||
// Das erste Argument ist der Programmname selbst und wird übersprungen.
|
||||
QStringList cliFiles = app.arguments();
|
||||
if (!cliFiles.isEmpty())
|
||||
{
|
||||
cliFiles.removeFirst();
|
||||
}
|
||||
if (!cliFiles.isEmpty())
|
||||
{
|
||||
window.openFilesFromArguments(cliFiles);
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ AboutDialog::AboutDialog(QWidget *parent)
|
||||
info->addLayout(row);
|
||||
};
|
||||
|
||||
makeRow(tr("Version"), "1.2.0");
|
||||
makeRow(tr("Version"), "1.3.0");
|
||||
makeRow(tr("Entwickler"), "Dany Thinnes");
|
||||
makeRow(tr("Projekt"), "Projekt Hirnfrei");
|
||||
makeRow(tr("Framework"), QString("Qt %1").arg(qVersion()));
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
#include <QSettings>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "AboutDialog.h"
|
||||
#include "filetree/FileTreePanel.h"
|
||||
@@ -45,6 +46,21 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
MainWindow::~MainWindow() = default;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Datei(en) von der Kommandozeile öffnen
|
||||
// ---------------------------------------------------------------------------
|
||||
void MainWindow::openFilesFromArguments(const QStringList &filePaths)
|
||||
{
|
||||
for (const QString &path : filePaths)
|
||||
{
|
||||
const QFileInfo info(path);
|
||||
if (info.exists() && info.isFile())
|
||||
{
|
||||
m_editor->openFile(info.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UI aufbauen
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -26,6 +26,11 @@ public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
// Datei(en) öffnen, die beim Programmstart per Kommandozeile übergeben
|
||||
// wurden — z. B. durch Doppelklick auf eine Datei im Dateibrowser
|
||||
// ("Öffnen mit BareCode").
|
||||
void openFilesFromArguments(const QStringList &filePaths);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <QResizeEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QFocusEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
@@ -747,3 +748,15 @@ void CodeEditor::keyPressEvent(QKeyEvent *event)
|
||||
m_varCompleter->handleKeyPress(event);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fokus verloren — z. B. beim Wechsel zu einem anderen Tab.
|
||||
// Ein noch offenes Variablen-Popup muss hier geschlossen werden, sonst
|
||||
// bleibt es als verwaistes Fenster stehen und kann bei mehreren offenen
|
||||
// Dateien den Fokus blockieren.
|
||||
// ---------------------------------------------------------------------------
|
||||
void CodeEditor::focusOutEvent(QFocusEvent *event)
|
||||
{
|
||||
QPlainTextEdit::focusOutEvent(event);
|
||||
m_varCompleter->notifyFocusLost();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,11 +56,13 @@ signals:
|
||||
void fileSaved(const QString &filePath);
|
||||
void navigateToRequested(const QString &filePath, int line);
|
||||
|
||||
protected: void resizeEvent(QResizeEvent *event) override;
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
void focusOutEvent(QFocusEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void updateLineNumberAreaWidth(int newBlockCount);
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <QFileInfo>
|
||||
#include <QFont>
|
||||
#include <QProgressBar>
|
||||
#include <QSettings>
|
||||
#include <QFileDialog>
|
||||
#include <QDir>
|
||||
|
||||
FunctionListPanel::FunctionListPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
@@ -67,6 +70,78 @@ void FunctionListPanel::setupUi()
|
||||
|
||||
root->addLayout(toolRow);
|
||||
|
||||
// ---- Exclude-Verzeichnisse ----
|
||||
QHBoxLayout *excludeRow = new QHBoxLayout();
|
||||
excludeRow->setContentsMargins(6, 0, 6, 4);
|
||||
excludeRow->addWidget(new QLabel(tr("Ausschließen:"), this));
|
||||
|
||||
m_excludeEdit = new QLineEdit(this);
|
||||
m_excludeEdit->setPlaceholderText(tr("vendor lib node_modules (leerzeichen-getrennt)"));
|
||||
m_excludeEdit->setToolTip(tr(
|
||||
"Verzeichnisnamen die beim Scan übersprungen werden.\n"
|
||||
"Leerzeichen-getrennt, z.B.: vendor lib node_modules cache"
|
||||
));
|
||||
m_excludeEdit->setText(tr("vendor lib node_modules"));
|
||||
excludeRow->addWidget(m_excludeEdit, 1);
|
||||
|
||||
// Verzeichnis-Auswahl Button
|
||||
QPushButton *btnBrowse = new QPushButton(tr("+ Verzeichnis"), this);
|
||||
btnBrowse->setToolTip(tr("Verzeichnis aus dem Projekt auswählen und zur Ausschlussliste hinzufügen"));
|
||||
connect(btnBrowse, &QPushButton::clicked, this, [this]()
|
||||
{
|
||||
const QString startPath = m_projectRoot.isEmpty()
|
||||
? QDir::homePath()
|
||||
: m_projectRoot;
|
||||
|
||||
const QString dir = QFileDialog::getExistingDirectory(
|
||||
this,
|
||||
tr("Verzeichnis ausschließen"),
|
||||
startPath,
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
|
||||
);
|
||||
|
||||
if (dir.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Nur den letzten Verzeichnisnamen verwenden, nicht den vollen Pfad
|
||||
// So funktioniert der Exclude auch für gleichnamige Unterverzeichnisse
|
||||
const QString dirName = QFileInfo(dir).fileName();
|
||||
|
||||
// Prüfen ob bereits in der Liste
|
||||
const QStringList current = m_excludeEdit->text()
|
||||
.simplified()
|
||||
.split(' ', Qt::SkipEmptyParts);
|
||||
if (current.contains(dirName, Qt::CaseInsensitive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Anhängen
|
||||
QString newText = m_excludeEdit->text().trimmed();
|
||||
if (!newText.isEmpty())
|
||||
{
|
||||
newText += ' ';
|
||||
}
|
||||
newText += dirName;
|
||||
m_excludeEdit->setText(newText);
|
||||
});
|
||||
excludeRow->addWidget(btnBrowse);
|
||||
|
||||
// Löschen-Button — ganzes Feld leeren
|
||||
QPushButton *btnClear = new QPushButton(tr("✕"), this);
|
||||
btnClear->setFixedWidth(28);
|
||||
btnClear->setFlat(true);
|
||||
btnClear->setToolTip(tr("Ausschlussliste leeren"));
|
||||
connect(btnClear, &QPushButton::clicked, this, [this]()
|
||||
{
|
||||
m_excludeEdit->clear();
|
||||
});
|
||||
excludeRow->addWidget(btnClear);
|
||||
|
||||
root->addLayout(excludeRow);
|
||||
|
||||
// ---- Status ----
|
||||
m_statusLabel = new QLabel(this);
|
||||
m_statusLabel->setContentsMargins(6, 0, 6, 2);
|
||||
@@ -107,9 +182,14 @@ void FunctionListPanel::setupUi()
|
||||
// ---------------------------------------------------------------------------
|
||||
void FunctionListPanel::setProjectRoot(const QString &path)
|
||||
{
|
||||
// Alte Exclude-Liste für das vorherige Projekt speichern
|
||||
saveExcludeDirsForProject();
|
||||
|
||||
m_projectRoot = path;
|
||||
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
loadExcludeDirsForProject();
|
||||
refresh();
|
||||
}
|
||||
else
|
||||
@@ -147,16 +227,21 @@ void FunctionListPanel::refresh()
|
||||
m_watcher->waitForFinished();
|
||||
}
|
||||
|
||||
saveExcludeDirsForProject();
|
||||
|
||||
m_statusLabel->setText(tr("Scanne…"));
|
||||
m_btnRefresh->setEnabled(false);
|
||||
|
||||
const QString root = m_projectRoot;
|
||||
const QString root = m_projectRoot;
|
||||
const QStringList excludeDirs = m_excludeEdit->text()
|
||||
.simplified()
|
||||
.split(' ', Qt::SkipEmptyParts);
|
||||
FunctionScanner *scanner = m_scanner;
|
||||
|
||||
QFuture<QList<FunctionScanner::FunctionInfo>> future =
|
||||
QtConcurrent::run([scanner, root]()
|
||||
QtConcurrent::run([scanner, root, excludeDirs]()
|
||||
{
|
||||
return scanner->scanDirectory(root);
|
||||
return scanner->scanDirectory(root, {"*.php"}, excludeDirs);
|
||||
});
|
||||
|
||||
m_watcher->setFuture(future);
|
||||
@@ -330,3 +415,35 @@ void FunctionListPanel::onGroupingChanged(int /*index*/)
|
||||
populateTree(m_lastResult);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ausschlussliste projektspezifisch speichern/laden
|
||||
// (gleiches Schema wie bei der Suche nach toten Funktionen)
|
||||
// ---------------------------------------------------------------------------
|
||||
void FunctionListPanel::saveExcludeDirsForProject()
|
||||
{
|
||||
if (m_projectRoot.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
|
||||
const QString key = "funclist/exclude/" +
|
||||
QString(m_projectRoot).replace('/', '_').replace('\\', '_');
|
||||
s.setValue(key, m_excludeEdit->text());
|
||||
s.setValue("funclist/excludeDirs", m_excludeEdit->text()); // globaler Fallback
|
||||
}
|
||||
|
||||
void FunctionListPanel::loadExcludeDirsForProject()
|
||||
{
|
||||
QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
|
||||
const QString key = "funclist/exclude/" +
|
||||
QString(m_projectRoot).replace('/', '_').replace('\\', '_');
|
||||
|
||||
// Projektspezifisch vorhanden? Sonst globalen Fallback nehmen
|
||||
const QString saved = s.value(key,
|
||||
s.value("funclist/excludeDirs", "vendor lib node_modules").toString()
|
||||
).toString();
|
||||
|
||||
m_excludeEdit->setText(saved);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <QComboBox>
|
||||
#include <QFutureWatcher>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "FunctionScanner.h"
|
||||
|
||||
@@ -21,6 +22,10 @@
|
||||
// Gruppierung: nach Datei oder nach Klasse
|
||||
// Klick: öffnet Datei und springt zur Definition
|
||||
// Aktualisierung: automatisch nach jedem Speichern
|
||||
//
|
||||
// Verzeichnisse wie vendor/ oder node_modules/ lassen sich über eine
|
||||
// Ausschlussliste vom Scan ausnehmen (gleiches Prinzip wie bei der
|
||||
// Suche nach toten Funktionen).
|
||||
// ---------------------------------------------------------------------------
|
||||
class FunctionListPanel : public QWidget
|
||||
{
|
||||
@@ -48,6 +53,8 @@ private:
|
||||
void setupUi();
|
||||
void populateTree(const QList<FunctionScanner::FunctionInfo> &functions);
|
||||
void applyFilter(const QString &text);
|
||||
void loadExcludeDirsForProject();
|
||||
void saveExcludeDirsForProject();
|
||||
static void formatFunctionItem(QTreeWidgetItem *item,
|
||||
const FunctionScanner::FunctionInfo &f);
|
||||
|
||||
@@ -59,6 +66,9 @@ private:
|
||||
QLabel *m_statusLabel = nullptr;
|
||||
QTreeWidget *m_tree = nullptr;
|
||||
|
||||
// Ausschlussliste — Verzeichnisnamen die beim Scan übersprungen werden
|
||||
QLineEdit *m_excludeEdit = nullptr;
|
||||
|
||||
FunctionScanner *m_scanner = nullptr;
|
||||
QFutureWatcher<QList<FunctionScanner::FunctionInfo>> *m_watcher = nullptr;
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ FunctionScanner::FunctionScanner(QObject *parent)
|
||||
// ---------------------------------------------------------------------------
|
||||
QList<FunctionScanner::FunctionInfo> FunctionScanner::scanDirectory(
|
||||
const QString &rootPath,
|
||||
const QStringList &extensions) const
|
||||
const QStringList &extensions,
|
||||
const QStringList &excludeDirs) const
|
||||
{
|
||||
QList<FunctionInfo> results;
|
||||
|
||||
@@ -28,6 +29,27 @@ QList<FunctionScanner::FunctionInfo> FunctionScanner::scanDirectory(
|
||||
while (it.hasNext())
|
||||
{
|
||||
const QString path = it.next();
|
||||
|
||||
// Exclude-Verzeichnisse prüfen (gleiche Logik wie DeadCodeAnalyzer)
|
||||
bool excluded = false;
|
||||
if (!excludeDirs.isEmpty())
|
||||
{
|
||||
const QStringList parts = path.split('/');
|
||||
for (const QString &excl : excludeDirs)
|
||||
{
|
||||
if (parts.contains(excl.trimmed(), Qt::CaseInsensitive))
|
||||
{
|
||||
excluded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (excluded)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
results.append(scanFile(path));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,11 @@ public:
|
||||
explicit FunctionScanner(QObject *parent = nullptr);
|
||||
|
||||
// Synchroner Scan — für direkten Aufruf aus Threads
|
||||
// excludeDirs: Verzeichnisnamen die komplett übersprungen werden
|
||||
// z.B. {"vendor", "lib", "node_modules"}
|
||||
QList<FunctionInfo> scanDirectory(const QString &rootPath,
|
||||
const QStringList &extensions = {"*.php"}) const;
|
||||
const QStringList &extensions = {"*.php"},
|
||||
const QStringList &excludeDirs = {}) const;
|
||||
|
||||
// Scannt eine einzelne Datei
|
||||
QList<FunctionInfo> scanFile(const QString &filePath) const;
|
||||
|
||||
@@ -14,10 +14,15 @@ VariableCompleter::VariableCompleter(CodeEditor *editor)
|
||||
: QObject(editor)
|
||||
, m_editor(editor)
|
||||
{
|
||||
// Popup als eigenständiges, rahmenloses Werkzeugfenster
|
||||
m_popup = new QListWidget(nullptr);
|
||||
m_popup->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
|
||||
Qt::WindowStaysOnTopHint);
|
||||
// Popup als reines Anzeige-Fenster — dieselbe Fensterart wie
|
||||
// SignatureTooltip, die nachweislich nie den Fokus an sich reißt.
|
||||
// Wichtig: Ein echtes Elternfenster (editor->window()) angeben, sonst
|
||||
// behandelt der Fenstermanager das Popup als eigenständiges Fenster.
|
||||
// Das führte dazu, dass bei mehreren offenen Dateien/Tabs verwaiste
|
||||
// Popups entstehen konnten, die den Fokus übernehmen und die
|
||||
// Texteingabe blockieren.
|
||||
m_popup = new QListWidget(editor->window());
|
||||
m_popup->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
|
||||
m_popup->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
m_popup->setFocusPolicy(Qt::NoFocus);
|
||||
m_popup->setMaximumHeight(200);
|
||||
@@ -155,6 +160,14 @@ void VariableCompleter::hidePopup()
|
||||
m_popup->clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Editor hat den Fokus verloren (z. B. Tab-Wechsel) — Popup schließen
|
||||
// ---------------------------------------------------------------------------
|
||||
void VariableCompleter::notifyFocusLost()
|
||||
{
|
||||
hidePopup();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Variablen im aktuellen Dokument sammeln
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -24,6 +24,11 @@ public:
|
||||
// Muss nach jedem Tastendruck aufgerufen werden
|
||||
void handleKeyPress(QKeyEvent *event);
|
||||
|
||||
// Muss aufgerufen werden, wenn der Editor den Fokus verliert
|
||||
// (z. B. beim Wechsel des Tabs) — schließt ein evtl. offenes Popup,
|
||||
// damit es nicht als "Geisterfenster" stehen bleibt.
|
||||
void notifyFocusLost();
|
||||
|
||||
private slots:
|
||||
void onItemActivated(QListWidgetItem *item);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user