Update the look of the chatbar to be floating with the rich text controls on top and send buttons inline

This commit is contained in:
James Graham
2026-01-17 15:46:00 +00:00
parent 79de8a792c
commit 6b318ec754
25 changed files with 945 additions and 806 deletions

View File

@@ -6,6 +6,7 @@
#include "chattextitemhelper.h"
#include "clipboard.h"
#include "neochatroom.h"
#include <qnamespace.h>
ChatKeyHelper::ChatKeyHelper(QObject *parent)
: QObject(parent)
@@ -29,7 +30,10 @@ bool ChatKeyHelper::handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers)
return backspace();
case Qt::Key_Enter:
case Qt::Key_Return:
return insertReturn();
return insertReturn(modifiers);
case Qt::Key_Escape:
case Qt::Key_Cancel:
return cancel();
default:
return false;
}
@@ -155,16 +159,28 @@ bool ChatKeyHelper::backspace()
return false;
}
bool ChatKeyHelper::insertReturn()
bool ChatKeyHelper::insertReturn(Qt::KeyboardModifiers modifiers)
{
if (!textItem) {
return false;
}
if (textItem->isCompleting) {
bool shiftPressed = modifiers.testFlag(Qt::ShiftModifier);
if (shiftPressed && !sendMessageWithEnter) {
Q_EMIT unhandledReturn(false);
return true;
}
if (!shiftPressed && textItem->isCompleting) {
Q_EMIT unhandledReturn(true);
return true;
}
if (!shiftPressed && sendMessageWithEnter) {
Q_EMIT unhandledReturn(false);
return true;
}
QTextCursor cursor = textItem->textCursor();
if (cursor.isNull()) {
return false;
@@ -173,6 +189,18 @@ bool ChatKeyHelper::insertReturn()
return true;
}
bool ChatKeyHelper::cancel()
{
if (!textItem) {
return false;
}
if (textItem->isCompleting) {
Q_EMIT closeCompletion();
return true;
}
return false;
}
bool ChatKeyHelper::pasteImage()
{
if (!textItem) {

View File

@@ -45,6 +45,15 @@ public:
*/
Q_INVOKABLE bool handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers);
/**
* @brief Whether the enter/return should send message.
*
* If false, return/enter adds a new line.
*
* shift + return/enter does the opposite to return/enter.
*/
bool sendMessageWithEnter = true;
Q_SIGNALS:
/**
* @brief There is an unhandled up key press.
@@ -98,6 +107,14 @@ Q_SIGNALS:
*/
void unhandledReturn(bool isCompleting);
/**
* @brief The completion dialog should be closed if open.
*
* Current trigger conditions:
* - Return clicked when a completion has been started.
*/
void closeCompletion();
/**
* @brief An image has been pasted.
*/
@@ -116,7 +133,9 @@ private:
bool backspace();
bool insertReturn();
bool insertReturn(Qt::KeyboardModifiers modifiers);
bool cancel();
bool pasteImage();
};

View File

@@ -342,6 +342,14 @@ std::optional<int> ChatTextItemHelper::cursorPosition() const
return m_textItem->property("cursorPosition").toInt();
}
QRect ChatTextItemHelper::cursorRectangle() const
{
if (!m_textItem) {
return {};
}
return m_textItem->property("cursorRectangle").toRect();
}
int ChatTextItemHelper::selectionStart() const
{
if (!m_textItem) {

View File

@@ -172,6 +172,11 @@ public:
*/
std::optional<int> cursorPosition() const;
/**
* @brief Return the rectangle where the cursor of the underlying text item is rendered.
*/
QRect cursorRectangle() const;
/**
* @brief Set the cursor position of the underlying text item to the given value.
*/

View File

@@ -7,27 +7,29 @@
#include <QTextCharFormat>
#include <QTextCursor>
#include <KLocalizedString>
QString RichFormat::styleString(Format format)
{
switch (format) {
case Paragraph:
return u"Paragraph"_s;
return i18nc("As in the default paragraph text style in the chat bar", "Paragraph Style");
case Heading1:
return u"Heading 1"_s;
return i18nc("As in heading level 1 text style in the chat bar", "Heading 1");
case Heading2:
return u"Heading 2"_s;
return i18nc("As in heading level 2 text style in the chat bar", "Heading 2");
case Heading3:
return u"Heading 3"_s;
return i18nc("As in heading level 3 text style in the chat bar", "Heading 3");
case Heading4:
return u"Heading 4"_s;
return i18nc("As in heading level 4 text style in the chat bar", "Heading 4");
case Heading5:
return u"Heading 5"_s;
return i18nc("As in heading level 5 text style in the chat bar", "Heading 5");
case Heading6:
return u"Heading 6"_s;
return i18nc("As in heading level 6 text style in the chat bar", "Heading 6");
case Code:
return u"Code"_s;
return i18nc("As in code text style in the chat bar", "Code");
case Quote:
return u"\"Quote\""_s;
return i18nc("As in quote text style in the chat bar", "\"Quote\"");
default:
return {};
}

View File

@@ -77,6 +77,21 @@ void CompletionModel::setTextItem(ChatTextItemHelper *textItem)
Q_EMIT textItemChanged();
}
bool CompletionModel::isCompleting() const
{
if (!m_textItem) {
return false;
}
return m_textItem->isCompleting;
}
void CompletionModel::ignoreCurrentCompletion()
{
m_ignoreCurrentCompletion = true;
m_textItem->isCompleting = false;
Q_EMIT isCompletingChanged();
}
void CompletionModel::updateTextStart()
{
auto cursor = m_textItem->textCursor();
@@ -193,6 +208,15 @@ void CompletionModel::updateCompletion()
if (cursor.isNull()) {
return;
}
if (m_ignoreCurrentCompletion) {
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
if (cursor.selectedText() == u' ') {
m_ignoreCurrentCompletion = false;
}
return;
}
cursor.setPosition(m_textStart);
while (!cursor.selectedText().endsWith(u' ') && !cursor.atBlockEnd()) {
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
@@ -242,6 +266,7 @@ void CompletionModel::updateCompletion()
endResetModel();
m_textItem->isCompleting = rowCount() > 0;
Q_EMIT isCompletingChanged();
}
CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const

View File

@@ -62,6 +62,11 @@ class CompletionModel : public QAbstractListModel
*/
Q_PROPERTY(UserListModel *userListModel READ userListModel WRITE setUserListModel NOTIFY userListModelChanged)
/**
* @brief The UserListModel to be used for room completions.
*/
Q_PROPERTY(bool isCompleting READ isCompleting NOTIFY isCompletingChanged)
public:
/**
* @brief Defines the different types of completion available.
@@ -98,6 +103,10 @@ public:
ChatTextItemHelper *textItem() const;
void setTextItem(ChatTextItemHelper *textItem);
bool isCompleting() const;
Q_INVOKABLE void ignoreCurrentCompletion();
/**
* @brief Get the given role value at the given index.
*
@@ -137,12 +146,14 @@ Q_SIGNALS:
void autoCompletionTypeChanged();
void roomListModelChanged();
void userListModelChanged();
void isCompletingChanged();
private:
QPointer<NeoChatRoom> m_room;
ChatBarType::Type m_type = ChatBarType::None;
QPointer<ChatTextItemHelper> m_textItem;
bool m_ignoreCurrentCompletion = false;
int m_textStart = 0;
void updateTextStart();