Skip to content

Commit

Permalink
feat(gui): allow directly editing text files
Browse files Browse the repository at this point in the history
  • Loading branch information
craftablescience committed Aug 12, 2024
1 parent 2e86d53 commit cd099e6
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 71 deletions.
6 changes: 5 additions & 1 deletion src/gui/FileViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ NavBar::NavBar(Window* window_, QWidget* parent)
QObject::connect(this->window, &Window::themeUpdated, this, &NavBar::resetButtonIcons, Qt::QueuedConnection);
}

QString NavBar::path() const {
return this->currentPath->text();
}

void NavBar::setPath(const QString& newPath) {
this->currentPath->setText(newPath);
this->processPathChanged(newPath, true, false);
Expand Down Expand Up @@ -222,7 +226,7 @@ FileViewer::FileViewer(Window* window_, QWidget* parent)
auto* mdlPreview = newPreview<MDLPreview>(this, this->window, this);
layout->addWidget(mdlPreview);

auto* textPreview = newPreview<TextPreview>(this);
auto* textPreview = newPreview<TextPreview>(this, this->window, this);
layout->addWidget(textPreview);

auto* texturePreview = newPreview<TexturePreview>(this);
Expand Down
7 changes: 6 additions & 1 deletion src/gui/FileViewer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class NavBar : public QWidget {

explicit NavBar(Window* window_, QWidget* parent = nullptr);

[[nodiscard]] QString path() const;

void setPath(const QString& newPath);

void navigateBack();
Expand Down Expand Up @@ -118,9 +120,12 @@ class FileViewer : public QWidget {
this->previews.at(std::type_index(typeid(T)))->hide();
}

[[nodiscard]] NavBar* getNavBar() {
return this->navbar;
}

private:
Window* window;

NavBar* navbar;

std::unordered_map<std::type_index, QWidget*> previews;
Expand Down
91 changes: 60 additions & 31 deletions src/gui/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,35 @@ void Window::editFile(const QString& oldPath) {
this->markModified(true);
}

void Window::editFileContents(const QString& path, std::vector<std::byte> data) {
auto entry = this->packFile->findEntry(path.toLocal8Bit().constData());
if (!entry) {
return;
}

this->packFile->removeEntry(path.toLocal8Bit().constData());
this->packFile->addEntry(path.toLocal8Bit().constData(), std::move(data), {
.vpk_saveToDirectory = entry->archiveIndex == VPK_DIR_INDEX,
.vpk_preloadBytes = static_cast<uint32_t>(entry->vpk_preloadedData.size()),
});
this->markModified(true);
}

void Window::editFileContents(const QString& path, const QString& data) {
auto entry = this->packFile->findEntry(path.toLocal8Bit().constData());
if (!entry) {
return;
}
auto byteData = data.toLocal8Bit();

this->packFile->removeEntry(path.toLocal8Bit().constData());
this->packFile->addEntry(path.toLocal8Bit().constData(), {reinterpret_cast<std::byte*>(byteData.data()), reinterpret_cast<std::byte*>(byteData.data()) + byteData.size()}, {
.vpk_saveToDirectory = entry->archiveIndex == VPK_DIR_INDEX,
.vpk_preloadBytes = static_cast<uint32_t>(entry->vpk_preloadedData.size()),
});
this->markModified(true);
}

void Window::renameDir(const QString& oldPath, const QString& newPath_) {
// Get new path
QString newPath = newPath_;
Expand Down Expand Up @@ -1134,6 +1163,37 @@ bool Window::clearContents() {
return true;
}

void Window::freezeActions(bool freeze, bool freezeCreationActions, bool freezeFileViewer) const {
this->createEmptyVPKAction->setDisabled(freeze && freezeCreationActions);
this->createVPKFromDirAction->setDisabled(freeze && freezeCreationActions);
this->openAction->setDisabled(freeze && freezeCreationActions);
this->openRelativeToMenu->setDisabled(freeze && freezeCreationActions);
this->openRecentMenu->setDisabled(freeze && freezeCreationActions);
this->saveAction->setDisabled(freeze || !this->modified);
this->saveAsAction->setDisabled(freeze);
this->closeFileAction->setDisabled(freeze);
this->extractAllAction->setDisabled(freeze);
this->addFileAction->setDisabled(freeze);
this->addDirAction->setDisabled(freeze);
this->setPropertiesAction->setDisabled(freeze);
this->toolsGeneralMenu->setDisabled(freeze);
this->toolsVPKMenu->setDisabled(freeze || (!this->packFile || this->packFile->getType() != PackFileType::VPK));

this->searchBar->setDisabled(freeze);
this->entryTree->setDisabled(freeze);
this->fileViewer->setDisabled(freeze && freezeFileViewer);
}

void Window::freezeModifyActions(bool readOnly) const {
if (readOnly) {
this->saveAction->setDisabled(readOnly);
this->saveAsAction->setDisabled(readOnly);
this->addFileAction->setDisabled(readOnly);
this->addDirAction->setDisabled(readOnly);
this->setPropertiesAction->setDisabled(readOnly);
}
}

void Window::mousePressEvent(QMouseEvent* event) {
if (event->button() == Qt::BackButton) {
this->fileViewer->requestNavigateBack();
Expand Down Expand Up @@ -1176,37 +1236,6 @@ void Window::closeEvent(QCloseEvent* event) {
event->accept();
}

void Window::freezeActions(bool freeze, bool freezeCreationActions) const {
this->createEmptyVPKAction->setDisabled(freeze && freezeCreationActions);
this->createVPKFromDirAction->setDisabled(freeze && freezeCreationActions);
this->openAction->setDisabled(freeze && freezeCreationActions);
this->openRelativeToMenu->setDisabled(freeze && freezeCreationActions);
this->openRecentMenu->setDisabled(freeze && freezeCreationActions);
this->saveAction->setDisabled(freeze || !this->modified);
this->saveAsAction->setDisabled(freeze);
this->closeFileAction->setDisabled(freeze);
this->extractAllAction->setDisabled(freeze);
this->addFileAction->setDisabled(freeze);
this->addDirAction->setDisabled(freeze);
this->setPropertiesAction->setDisabled(freeze);
this->toolsGeneralMenu->setDisabled(freeze);
this->toolsVPKMenu->setDisabled(freeze || (!this->packFile || this->packFile->getType() != PackFileType::VPK));

this->searchBar->setDisabled(freeze);
this->entryTree->setDisabled(freeze);
this->fileViewer->setDisabled(freeze);
}

void Window::freezeModifyActions(bool readOnly) const {
if (readOnly) {
this->saveAction->setDisabled(readOnly);
this->saveAsAction->setDisabled(readOnly);
this->addFileAction->setDisabled(readOnly);
this->addDirAction->setDisabled(readOnly);
this->setPropertiesAction->setDisabled(readOnly);
}
}

bool Window::loadPackFile(const QString& path) {
if (!this->clearContents()) {
return false;
Expand Down
12 changes: 8 additions & 4 deletions src/gui/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class Window : public QMainWindow {

void editFile(const QString& oldPath);

void editFileContents(const QString& path, std::vector<std::byte> data);

void editFileContents(const QString& path, const QString& data);

void renameDir(const QString& oldPath, const QString& newPath_ = QString());

void about();
Expand Down Expand Up @@ -99,6 +103,10 @@ class Window : public QMainWindow {

[[nodiscard]] bool clearContents();

void freezeActions(bool freeze, bool freezeCreationActions = true, bool freezeFileViewer = true) const;

void freezeModifyActions(bool readOnly) const;

protected:
void mousePressEvent(QMouseEvent* event) override;

Expand Down Expand Up @@ -145,10 +153,6 @@ class Window : public QMainWindow {

bool dropEnabled;

void freezeActions(bool freeze, bool freezeCreationActions = true) const;

void freezeModifyActions(bool readOnly) const;

bool loadPackFile(const QString& path);

void rebuildOpenInMenu();
Expand Down
104 changes: 87 additions & 17 deletions src/gui/previews/TextPreview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
#include <QPainter>
#include <QStyleOption>
#include <QTextBlock>
#include <QToolBar>
#include <QVBoxLayout>

#include "../FileViewer.h"
#include "../Window.h"

constexpr int LINE_NUMBER_AREA_TEXT_MARGIN = 4;

LineNumberArea::LineNumberArea(TextPreview* textPreview, QWidget* parent)
LineNumberArea::LineNumberArea(TextEditor* textEditor, QWidget* parent)
: QWidget(parent)
, preview(textPreview) {}
, editor(textEditor) {}

QSize LineNumberArea::sizeHint() const {
return {this->preview->getLineNumberAreaWidth(), 0};
return {this->editor->getLineNumberAreaWidth(), 0};
}

void LineNumberArea::paintEvent(QPaintEvent* event) {
this->preview->onLineNumberAreaPaintEvent(event);
this->editor->onLineNumberAreaPaintEvent(event);
}

KeyValuesHighlighter::KeyValuesHighlighter(QTextDocument* document)
Expand Down Expand Up @@ -57,16 +62,14 @@ void KeyValuesHighlighter::highlightBlock(const QString& text) {
}
}

TextPreview::TextPreview(QWidget* parent)
TextEditor::TextEditor(QWidget* parent)
: QPlainTextEdit(parent)
, keyValuesHighlighter(nullptr) {
this->setReadOnly(true);

this->lineNumberArea = new LineNumberArea(this, this);

QObject::connect(this, &TextPreview::blockCountChanged, this, &TextPreview::updateLineNumberAreaWidth);
QObject::connect(this, &TextPreview::updateRequest, this, &TextPreview::updateLineNumberArea);
QObject::connect(this, &TextPreview::cursorPositionChanged, this, &TextPreview::highlightCurrentLine);
QObject::connect(this, &TextEditor::blockCountChanged, this, &TextEditor::updateLineNumberAreaWidth);
QObject::connect(this, &TextEditor::updateRequest, this, &TextEditor::updateLineNumberArea);
QObject::connect(this, &TextEditor::cursorPositionChanged, this, &TextEditor::highlightCurrentLine);

this->updateLineNumberAreaWidth(0);
this->highlightCurrentLine();
Expand All @@ -79,7 +82,7 @@ TextPreview::TextPreview(QWidget* parent)
this->lineNumberArea->setFont(monospace);
}

void TextPreview::setText(const QString& text, const QString& extension) {
void TextEditor::setText(const QString& text, const QString& extension) {
this->document()->setPlainText(text);

// Copied from the header
Expand All @@ -97,7 +100,7 @@ void TextPreview::setText(const QString& text, const QString& extension) {
}
}

int TextPreview::getLineNumberAreaWidth() const {
int TextEditor::getLineNumberAreaWidth() const {
int digits = 1;
int max = qMax(1, this->blockCount());
while (max >= 10) {
Expand All @@ -108,7 +111,7 @@ int TextPreview::getLineNumberAreaWidth() const {
return space;
}

void TextPreview::onLineNumberAreaPaintEvent(QPaintEvent* event) const {
void TextEditor::onLineNumberAreaPaintEvent(QPaintEvent* event) const {
QStyleOption opt;
opt.initFrom(this);

Expand All @@ -133,18 +136,18 @@ void TextPreview::onLineNumberAreaPaintEvent(QPaintEvent* event) const {
}
}

void TextPreview::resizeEvent(QResizeEvent* event) {
void TextEditor::resizeEvent(QResizeEvent* event) {
QPlainTextEdit::resizeEvent(event);

QRect cr = this->contentsRect();
this->lineNumberArea->setGeometry({cr.left(), cr.top(), this->getLineNumberAreaWidth(), cr.height()});
}

void TextPreview::updateLineNumberAreaWidth(int /*newBlockCount*/) {
void TextEditor::updateLineNumberAreaWidth(int /*newBlockCount*/) {
this->setViewportMargins(this->getLineNumberAreaWidth(), 0, 0, 0);
}

void TextPreview::updateLineNumberArea(const QRect& rect, int dy) {
void TextEditor::updateLineNumberArea(const QRect& rect, int dy) {
if (dy) {
this->lineNumberArea->scroll(0, dy);
} else {
Expand All @@ -156,7 +159,7 @@ void TextPreview::updateLineNumberArea(const QRect& rect, int dy) {
}
}

void TextPreview::highlightCurrentLine() {
void TextEditor::highlightCurrentLine() {
QList<QTextEdit::ExtraSelection> extraSelections;
if (!this->isReadOnly()) {
QTextEdit::ExtraSelection selection;
Expand All @@ -172,3 +175,70 @@ void TextPreview::highlightCurrentLine() {
}
this->setExtraSelections(extraSelections);
}

TextPreview::TextPreview(FileViewer* fileViewer_, Window* window_, QWidget* parent)
: QWidget(parent)
, fileViewer(fileViewer_)
, window(window_) {
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0,0,0,0);

this->toolbar = new QToolBar(this);
this->toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
auto* spacer = new QWidget(this->toolbar);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->toolbar->addWidget(spacer);

this->editor = new TextEditor(this);
layout->addWidget(this->editor);

this->editAction = this->toolbar->addAction(this->style()->standardIcon(QStyle::SP_FileDialogDetailedView), tr("Edit"));
QObject::connect(this->editAction, &QAction::triggered, this, [this] {
this->setEditing(true);
});

this->saveAction = this->toolbar->addAction(this->style()->standardIcon(QStyle::SP_DialogSaveButton), tr("Save"));
QObject::connect(this->saveAction, &QAction::triggered, this, [this] {
this->setEditing(false);

auto path = this->fileViewer->getNavBar()->path();
this->window->editFileContents(path, this->editor->toPlainText());

// hack: reselect the entry to reload its contents
this->window->selectEntryInEntryTree(path);
});

this->cancelAction = this->toolbar->addAction(this->style()->standardIcon(QStyle::SP_BrowserReload), tr("Cancel"));
QObject::connect(this->cancelAction, &QAction::triggered, this, [this] {
this->setEditing(false);

// hack: reselect the entry to reload its contents
this->window->selectEntryInEntryTree(this->fileViewer->getNavBar()->path());
});

this->toolbar->raise();

// Do this manually so we're not calling Window::freezeActions
this->editor->setReadOnly(true);
this->editAction->setVisible(true);
this->saveAction->setVisible(false);
this->cancelAction->setVisible(false);
}

void TextPreview::setText(const QString& text, const QString& extension) {
this->editor->setText(text, extension);
}

void TextPreview::setEditing(bool editing) const {
this->editor->setReadOnly(!editing);
this->editAction->setVisible(!editing);
this->saveAction->setVisible(editing);
this->cancelAction->setVisible(editing);
this->fileViewer->getNavBar()->setDisabled(editing);
this->window->freezeActions(editing, true, false);
}

void TextPreview::resizeEvent(QResizeEvent* event) {
QWidget::resizeEvent(event);
this->toolbar->setFixedWidth(this->width());
}
Loading

0 comments on commit cd099e6

Please sign in to comment.