Sanguine_Studio/app/gsceditor.cpp
2025-12-14 21:37:14 -05:00

203 lines
5.1 KiB
C++

#include "gsceditor.h"
#include <QAbstractItemView>
#include <QKeyEvent>
#include <QScrollbar>
#include <QFile>
#include <QFileDialog>
#define TAB_STOP 4
GSCEditor::GSCEditor(QWidget *aParent)
: QTextEdit(aParent)
, mSaved(true)
, mCompleter()
{
QFont font;
font.setFamily("Courier");
font.setFixedPitch(true);
font.setPointSize(10);
font.setStyleHint(QFont::Monospace);
setFont(font);
setTabStopDistance(TAB_STOP * fontMetrics().horizontalAdvance(' '));
connect(this, &QTextEdit::textChanged, this, &GSCEditor::checkPlainTextChanged);
}
GSCEditor::~GSCEditor()
{
delete mCompleter;
}
void GSCEditor::checkPlainTextChanged()
{
const QString plainText = toPlainText();
if (mLastText != plainText)
{
mSaved = false;
}
else
{
mSaved = true;
}
emit saveStatusChanged(mSaved);
}
void GSCEditor::setCompleter(QCompleter *aCompleter)
{
if (aCompleter)
{
aCompleter->disconnect(this);
}
mCompleter = aCompleter;
if (!mCompleter) { return; }
mCompleter->setWidget(this);
mCompleter->setCompletionMode(QCompleter::PopupCompletion);
mCompleter->setCaseSensitivity(Qt::CaseInsensitive);
// TODO: Wtf is a QOverload
connect(mCompleter, QOverload<const QString&>::of(&QCompleter::activated), this, &GSCEditor::insertCompletion);
}
QCompleter *GSCEditor::completer() const
{
return mCompleter;
}
void GSCEditor::setSaveableText(const QString &aText)
{
setPlainText(aText);
mLastText = toPlainText();
}
bool GSCEditor::saved() const
{
return mSaved;
}
void GSCEditor::save()
{
const QString filePath = property("Path").toString();
QFile saveFile(filePath);
if (!saveFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
{
qDebug() << "ERROR: Failed to open file " << filePath;
return;
}
saveFile.write(toPlainText().toUtf8());
mLastText = toPlainText();
mSaved = true;
emit saveStatusChanged(mSaved);
}
void GSCEditor::saveAs()
{
QString filePath = property("Path").toString();
QFileDialog saveAsDialog(this);
saveAsDialog.setFileMode(QFileDialog::AnyFile);
saveAsDialog.setWindowTitle("Open Folder");
saveAsDialog.setDirectory(QDir(filePath).path());
if (saveAsDialog.exec() == QDialog::Rejected) { return; }
filePath = saveAsDialog.selectedFiles().first();
QFile saveFile(filePath);
if (!saveFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
{
qDebug() << "ERROR: Failed to open file " << filePath;
return;
}
saveFile.write(toPlainText().toUtf8());
mLastText = toPlainText();
mSaved = true;
emit saveStatusChanged(mSaved);
}
void GSCEditor::keyPressEvent(QKeyEvent *aEvent)
{
if (mCompleter && mCompleter->popup()->isVisible())
{
switch (aEvent->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
aEvent->ignore();
return;
default:
break;
}
}
// Accept CTRL+SPACE to auto-complete
const bool isShortcut = (aEvent->modifiers().testFlag(Qt::ControlModifier)
&& aEvent->key() == Qt::Key_Space);
if (!mCompleter || !isShortcut)
{
QTextEdit::keyPressEvent(aEvent);
}
const bool ctrlOrShift = aEvent->modifiers().testFlag(Qt::ControlModifier)
|| aEvent->modifiers().testFlag(Qt::ShiftModifier);
if (!mCompleter || (ctrlOrShift && aEvent->text().isEmpty())) { return; }
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=");
const bool hasModifier = (aEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = mTextUnderCursor();
if (!isShortcut && (hasModifier || aEvent->text().isEmpty() || completionPrefix.length() < 3 || eow.contains(aEvent->text().right(1))))
{
mCompleter->popup()->hide();
return;
}
if (completionPrefix != mCompleter->completionPrefix())
{
mCompleter->setCompletionPrefix(completionPrefix);
mCompleter->popup()->setCurrentIndex(mCompleter->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(mCompleter->popup()->sizeHintForColumn(0) + mCompleter->popup()->verticalScrollBar()->sizeHint().width());
mCompleter->complete(cr);
}
void GSCEditor::focusInEvent(QFocusEvent *aEvent)
{
if (mCompleter)
{
mCompleter->setWidget(this);
}
QTextEdit::focusInEvent(aEvent);
}
void GSCEditor::insertCompletion(const QString &aCompletion)
{
if (mCompleter->widget() != this) { return; }
QTextCursor cursor = textCursor();
int extra = aCompletion.length() - mCompleter->completionPrefix().length();
cursor.movePosition(QTextCursor::Left);
cursor.movePosition(QTextCursor::EndOfWord);
cursor.insertText(aCompletion.right(extra));
setTextCursor(cursor);
}
QString GSCEditor::mTextUnderCursor() const
{
QTextCursor cursor = textCursor();
cursor.select(QTextCursor::WordUnderCursor);
return cursor.selectedText();
}