-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a basic implementation of the external Neovim commandline. Utilizes basic Qt Widgets (QLineEdit, QTextEdit) to display a floating command prompt.
- Loading branch information
Showing
14 changed files
with
704 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
project(qcommandline) | ||
|
||
set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||
|
||
find_package(Qt5Widgets REQUIRED) | ||
|
||
if (WIN32 AND USE_STATIC_QT) | ||
add_definitions(-DUSE_STATIC_QT) | ||
endif () | ||
|
||
set(SOURCES | ||
blockdisplay.cpp | ||
linemodel.cpp | ||
extcmdlinewidget.cpp | ||
) | ||
|
||
add_library(extcmdline STATIC ${SOURCES}) | ||
target_link_libraries(extcmdline Qt5::Widgets) | ||
target_include_directories(extcmdline PUBLIC ../shellwidget) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include "blockdisplay.h" | ||
|
||
#include <QList> | ||
#include <QDebug> | ||
#include <QStringList> | ||
|
||
namespace NeovimQt { namespace Cmdline { | ||
|
||
BlockDisplay::BlockDisplay() | ||
{ | ||
setVisible(false); | ||
setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); | ||
setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); | ||
setReadOnly(true); | ||
setFocusPolicy(Qt::NoFocus); | ||
} | ||
|
||
QSize BlockDisplay::sizeHint() const | ||
{ | ||
QSize parentSizeHint = Super::sizeHint(); | ||
int padding = contentsMargins().top() + contentsMargins().bottom(); | ||
parentSizeHint.setHeight(document()->size().height() + padding); | ||
return parentSizeHint; | ||
} | ||
|
||
int BlockDisplay::GetMaxLineLength() const | ||
{ | ||
QStringList allLines = toPlainText().split('\n'); | ||
|
||
int maxChars = 0; | ||
for (const auto& line : allLines) { | ||
maxChars = qMax(line.size(), maxChars); | ||
} | ||
|
||
return maxChars; | ||
} | ||
|
||
} } // namespace NeovimQt::Cmdline |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <QTextEdit> | ||
|
||
namespace NeovimQt { namespace Cmdline { | ||
|
||
class BlockDisplay : public QTextEdit { | ||
using Super = QTextEdit; | ||
|
||
public: | ||
BlockDisplay(); | ||
|
||
virtual QSize sizeHint() const override; | ||
virtual int GetMaxLineLength() const; | ||
}; | ||
|
||
} } // namespace NeovimQt::Cmdline |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
#include "extcmdlinewidget.h" | ||
|
||
#include <QDebug> | ||
#include <QCoreApplication> | ||
|
||
namespace NeovimQt { namespace Cmdline { | ||
|
||
ExtCmdlineWidget::ExtCmdlineWidget(ShellWidget& parent) | ||
{ | ||
setParent(&parent); | ||
setVisible(false); | ||
|
||
m_cmdTextBoxFrame = new QFrame(); | ||
m_cmdTextBoxFrame->setFrameShape(QFrame::StyledPanel); | ||
m_cmdTextBoxFrame->setFrameShadow(QFrame::Raised); | ||
QVBoxLayout* frameLayout = new QVBoxLayout(m_cmdTextBoxFrame); | ||
|
||
m_cmdTextBox = new ShellWidget(); | ||
frameLayout->addWidget(m_cmdTextBox); | ||
|
||
m_cmdBlockText = new BlockDisplay(); | ||
m_cmdTextBox->setIgnoreFocus(true); | ||
|
||
m_vLayout = new QVBoxLayout(); | ||
m_vLayout->addWidget(m_cmdBlockText); | ||
m_vLayout->addWidget(m_cmdTextBoxFrame); | ||
setLayout(m_vLayout); | ||
|
||
// The size of this widget must change when text content changes. | ||
connect(m_cmdTextBox, SIGNAL(textChanged(QString)), this, | ||
SLOT(resizeLineEditToContent())); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineShow( | ||
const QVariantList& content, | ||
int pos, | ||
const QString& firstc, | ||
const QString& prompt, | ||
int indent, | ||
int level) | ||
{ | ||
ShellWidget* parentShellWidget{ static_cast<ShellWidget*>(parentWidget()) }; | ||
|
||
m_cmdTextBox->CopyCursorStyle(parentShellWidget->getCursor()); | ||
|
||
QString cmdlineContent; | ||
|
||
LineModel lineModel{ content, pos, firstc, prompt, indent, level }; | ||
if (level > m_model.size()) { | ||
m_model.append(std::move(lineModel)); | ||
} | ||
else { | ||
m_model[level - 1] = std::move(lineModel); | ||
} | ||
|
||
m_cmdTextBox->clearShell(); | ||
|
||
const QString& text{ m_model.last().getPromptText() }; | ||
|
||
auto getShellSize = [&]() noexcept | ||
{ | ||
const int cellSize{ m_cmdTextBox->cellSize().width() }; | ||
const int columns{ m_cmdTextBox->size().width() / cellSize }; | ||
const int rows{ text.size() / columns + 1 }; | ||
|
||
return QSize{ std::max(columns, 1), rows}; | ||
}; | ||
|
||
const QSize cmdSize{ getShellSize() }; | ||
m_cmdTextBox->resizeShell(cmdSize); | ||
|
||
for (int i=0;i<cmdSize.height();i++) { | ||
int startPos = i * cmdSize.width(); | ||
QString row{ text.mid(startPos, cmdSize.width()) }; | ||
m_cmdTextBox->put(row, i, 0); | ||
} | ||
|
||
const int cols{ cmdSize.width() }; | ||
|
||
m_cmdTextBox->setNeovimCursor(pos / cols, pos % cols + 1); | ||
m_cmdTextBox->update(); | ||
|
||
updateGeometry(); | ||
show(); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlinePos( | ||
int pos, | ||
int level) | ||
{ | ||
if (level > m_model.size()) { | ||
qDebug() << "Invalid value for 'level'" << level; | ||
return; | ||
} | ||
|
||
m_model[level - 1].m_position = pos; | ||
Cursor cursor; | ||
//setCursorPosition(pos); | ||
qDebug() << "Handle Cursor Position"; | ||
m_cmdTextBox->setNeovimCursor(0, pos); | ||
m_cmdTextBox->resizeShell(1, m_model[level -1].getPromptText().size()); | ||
//m_cmdTextBox->setCursorColors(Qt::blue, Qt::red); | ||
//m_cmdTextBox->setCursorStuff(); | ||
update(); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineSpecialChar( | ||
const QString& c, | ||
bool shift, | ||
int level) | ||
{ | ||
if (level > m_model.size()) { | ||
qDebug() << "Invalid value for 'level'" << level; | ||
} | ||
|
||
LineModel& line = m_model[level - 1]; | ||
|
||
// Special characters are inserted in two steps | ||
if (shift) { | ||
// Step One: insert a placeholder character. | ||
line.m_content.insert(line.m_position, c); | ||
} | ||
else { | ||
// Step Two: replace the placeholder with the desired character. | ||
line.m_content.replace(line.m_position, 1, c); | ||
} | ||
|
||
m_cmdTextBox->put(line.getPromptText(),0,0); // FIXME | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineHide() | ||
{ | ||
if (!m_model.empty()) { | ||
m_model.pop_back(); | ||
} | ||
|
||
hide(); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineBlockShow(const QVariantList& lines) | ||
{ | ||
QString blockText; | ||
for (const auto& varLine: lines) { | ||
QVariantList line = varLine.toList(); | ||
qDebug() << line; | ||
for (const auto& varSegment : line) { | ||
QVariantList segment = varSegment.toList(); | ||
const QString segmentText = segment.at(1).toString(); | ||
|
||
blockText += segmentText; | ||
} | ||
} | ||
m_cmdBlockText->setText(blockText); | ||
m_cmdBlockText->updateGeometry(); | ||
m_cmdBlockText->show(); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineBlockAppend(const QVariantList& line) | ||
{ | ||
for (const auto& varSegment : line) { | ||
QVariantList segment = varSegment.toList(); | ||
const QString segmentText = segment.at(1).toString(); | ||
|
||
m_cmdBlockText->append(segmentText); | ||
} | ||
|
||
m_cmdBlockText->updateGeometry(); | ||
} | ||
|
||
void ExtCmdlineWidget::handleCmdlineBlockHide() | ||
{ | ||
m_cmdBlockText->hide(); | ||
} | ||
|
||
void ExtCmdlineWidget::resizeLineEditToContent() | ||
{ | ||
updateGeometry(); | ||
} | ||
|
||
void ExtCmdlineWidget::updateGeometry() | ||
{ | ||
if (!parentWidget()) { | ||
qDebug() << "No parentWidget, cannot update size/position"; | ||
return; | ||
} | ||
|
||
ShellWidget* parentShellWidget{ static_cast<ShellWidget*>(parentWidget()) }; | ||
|
||
const int maxWidth = parentShellWidget->width() * 0.75; | ||
const int maxHeight = parentShellWidget->height() * 0.75; | ||
|
||
const QSize sizeHintThis = sizeHint(); | ||
const int width = qMin(sizeHintThis.width(), maxWidth); | ||
const int height = qMin(sizeHintThis.height(), maxHeight); | ||
|
||
const int anchorX = (parentShellWidget->width() - width) / 2; | ||
const int anchorY = (parentShellWidget->height() - height) / 2; | ||
|
||
setGeometry(anchorX, anchorY, width, height); | ||
setMaximumWidth(maxWidth); | ||
setMaximumHeight(maxHeight); | ||
QWidget::updateGeometry(); | ||
} | ||
|
||
void ExtCmdlineWidget::setCursorPosition(int pos) | ||
{ | ||
if (!parentWidget()) { | ||
qDebug() << "No parentWidget, cannot update cursor position"; | ||
return; | ||
} | ||
|
||
ShellWidget* parentShellWidget{ static_cast<ShellWidget*>(parentWidget()) }; | ||
|
||
// Neovim cursor position is set by inverting the foreground/background colors, Qt cursor hidden. | ||
const int posCursorReal = pos + m_model.back().m_prompt.size() + m_model.back().m_indent + 1; | ||
QTextCharFormat fmtInverse; | ||
fmtInverse.setBackground(parentShellWidget->foreground()); | ||
fmtInverse.setForeground(parentShellWidget->background()); | ||
//m_cmdTextBox->setTextFormat({ { posCursorReal, 1, fmtInverse} }); | ||
|
||
// Ensures ':' is shown when users scrolls full left | ||
if (pos == 0) { | ||
m_cmdTextBox->setNeovimCursor(0,0); | ||
return; | ||
} | ||
|
||
// Ensure the entire nvim cursor is visble by setting the Qt cursor position. | ||
// Moving left: left edge of cursor. Moving right: right edge of cursor. | ||
static int last_pos = 0; | ||
|
||
int cursorPositionQt = posCursorReal; | ||
if (last_pos <= pos) { | ||
++cursorPositionQt; | ||
} | ||
m_cmdTextBox->setNeovimCursor(0,cursorPositionQt); | ||
|
||
last_pos = pos; | ||
} | ||
|
||
int ExtCmdlineWidget::getMaxPromptLength() const | ||
{ | ||
int maxLineLength = 0; | ||
for (const auto& line : m_model) { | ||
maxLineLength = qMax(line.getPromptText().size(), maxLineLength); | ||
} | ||
|
||
return qMax(m_cmdBlockText->GetMaxLineLength(), maxLineLength); | ||
} | ||
|
||
QSize ExtCmdlineWidget::sizeHint() const | ||
{ | ||
QSize sizeHint = QFrame::sizeHint(); | ||
|
||
QFontMetrics fm = fontMetrics(); | ||
|
||
const int maxLineLength = getMaxPromptLength(); | ||
|
||
const int widthHint = fm.averageCharWidth() * (maxLineLength + 2) + | ||
layout()->contentsMargins().left() + layout()->contentsMargins().right(); | ||
|
||
constexpr int minWidth = 300; | ||
|
||
sizeHint.setWidth(qMax(widthHint, minWidth)); | ||
|
||
return sizeHint; | ||
} | ||
|
||
void ExtCmdlineWidget::updatePalette() | ||
{ | ||
if (!parentWidget()) { | ||
qDebug() << "No parentWidget, cannot set palette"; | ||
return; | ||
} | ||
|
||
ShellWidget* parentShellWidget{ static_cast<ShellWidget*>(parentWidget()) }; | ||
|
||
m_palette.setColor(QPalette::Base, parentShellWidget->background()); | ||
m_palette.setColor(QPalette::Text, parentShellWidget->foreground()); | ||
m_palette.setColor(QPalette::Background, parentShellWidget->foreground()); | ||
|
||
m_cmdBlockText->setPalette(m_palette); | ||
|
||
m_cmdTextBox->setBackground(parentShellWidget->background()); | ||
m_cmdTextBox->setForeground(parentShellWidget->foreground()); | ||
m_cmdTextBox->setStyleSheet(".ShellWidget{background-color: red; border: 1px solid black; border-radius: 10px;}"); | ||
} | ||
|
||
void ExtCmdlineWidget::setFont(const QFont &font) | ||
{ | ||
m_cmdBlockText->setFont(font); | ||
//m_cmdTextBox->setFont(font); | ||
} | ||
|
||
} } // namespace NeovimQt::Cmdline |
Oops, something went wrong.