Add chat history keyboard scrolling and remove unused PTT setting

This commit is contained in:
evgeny committed 2026-10-21 13:05:31 +02:00
1 parent 5870efdf04
commit 4f6808156f
10 files changed
+81 -31

No files matched your search

+1 -1
View File
@@ -492,7 +492,7 @@ if(TARGET X11::X11)
target_link_libraries(test_message_notification_x11 PRIVATE chatgui_notifications ${QT_TEST_LIB} X11::X11 pthread ${CMAKE_DL_LIBS})
endif()
add_executable(test_file_drop tests/test_file_drop.cpp src/inputbar.cpp src/audiorecorder.cpp src/audio_device.cpp src/sound_manager.cpp
add_executable(test_file_drop tests/test_file_drop.cpp src/inputbar.cpp src/chatview.cpp src/audiorecorder.cpp src/audio_device.cpp src/sound_manager.cpp
src/voicemessageencoder.cpp src/emojipanel.cpp src/emojitabbar.cpp src/emoji.cpp src/animtimer.cpp src/lottieicon.cpp
src/voice_audio_io.cpp transport/miniaudio_impl.c transport/audio_diagnostics.cpp)
target_include_directories(test_file_drop PRIVATE ${UTUN_INCLUDE_DIRS})
@@ -78,11 +78,6 @@ ChannelSettingsDialog::ChannelSettingsDialog(const QString& channelId, DbManager
m_soundOnMessage->setToolTip(QCoreApplication::translate("ChatGui", "Play a notification sound when a message arrives in this channel."));
layout->addWidget(m_soundOnMessage);
m_pttMode = new QCheckBox(QCoreApplication::translate("ChatGui", "Push-To-Talk (PTT) mode"), this);
m_pttMode->setToolTip(QCoreApplication::translate("ChatGui", "Record voice messages with push-to-talk. (Coming soon)"));
m_pttMode->setEnabled(false);
layout->addWidget(m_pttMode);
layout->addStretch();
auto* btnLayout = new QHBoxLayout();
@@ -116,8 +111,6 @@ ChannelSettingsDialog::ChannelSettingsDialog(const QString& channelId, DbManager
m_autoPlayVoice->setChecked(m_db->getUiStateInt(autoPlayKey, 0) != 0);
QString soundKey = QStringLiteral("ch_%1_sound_on_message").arg(channelId);
m_soundOnMessage->setChecked(m_db->getUiStateInt(soundKey, 1) != 0);
QString pttKey = QStringLiteral("ch_%1_ptt_mode").arg(channelId);
m_pttMode->setChecked(m_db->getUiStateInt(pttKey, 0) != 0);
}
}
@@ -138,7 +131,6 @@ void ChannelSettingsDialog::onSave() {
}
};
saveKey("auto_play_voice", m_autoPlayVoice->isChecked() ? 1 : 0);
saveKey("ptt_mode", m_pttMode->isChecked() ? 1 : 0);
/* sound_on_message: сохранить + разослать пирам (троттлинг db_sync на сервере) */
{
+1 -2
View File
@@ -1,4 +1,4 @@
/* Локальные настройки выбранного канала: autoplay голосовых, звук сообщений и режим PTT. */
/* Локальные настройки выбранного канала: autoplay голосовых и звук сообщений. */
#pragma once
#include <QDialog>
@@ -20,5 +20,4 @@ private:
DbManager* m_db;
QCheckBox* m_autoPlayVoice;
QCheckBox* m_soundOnMessage;
QCheckBox* m_pttMode;
};
+25
View File
@@ -208,7 +208,32 @@ void ChatView::mouseReleaseEvent(QMouseEvent *event) {
QListView::mouseReleaseEvent(event);
}
bool ChatView::scrollWithKey(QKeyEvent *event) {
if (event->modifiers() != Qt::NoModifier) return false;
QAbstractSlider::SliderAction action;
switch (event->key()) {
case Qt::Key_Up: action = QAbstractSlider::SliderSingleStepSub; break;
case Qt::Key_Down: action = QAbstractSlider::SliderSingleStepAdd; break;
case Qt::Key_PageUp: action = QAbstractSlider::SliderPageStepSub; break;
case Qt::Key_PageDown: action = QAbstractSlider::SliderPageStepAdd; break;
default: return false;
}
auto* bar = verticalScrollBar();
const int before = bar->value();
bar->triggerAction(action);
GUI_DEBUG("history keyboard scroll: key=%d action=%d position=%d->%d range=%d..%d",
event->key(), int(action), before, bar->value(), bar->minimum(), bar->maximum());
event->accept();
return true;
}
bool ChatView::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::KeyPress && isVisible() && isEnabled() && scrollWithKey(static_cast<QKeyEvent*>(event))) return true;
return QListView::eventFilter(object, event);
}
void ChatView::keyPressEvent(QKeyEvent *event) {
if (scrollWithKey(event)) return;
if (event == QKeySequence::Copy) {
copySelection();
return;
+4
View File
@@ -42,6 +42,8 @@ signals:
void latestRequested();
protected:
/* Up/Down/PgUp/PgDown от поля ввода и окна, установивших этот вид как eventFilter. */
bool eventFilter(QObject *object, QEvent *event) override;
bool viewportEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
@@ -52,6 +54,8 @@ protected:
void keyPressEvent(QKeyEvent *event) override;
private:
/* Прокрутить историю по клавише без модификаторов; true, если событие принято. Фокус сохраняется. */
bool scrollWithKey(QKeyEvent *event);
void copySelection();
void updateScrollDownBtn();
+1
View File
@@ -148,6 +148,7 @@ InputBar::InputBar(EmojiPanel *emojiPanel, AudioRecorder *recorder, QWidget *par
m_input->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_input->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_input->setTabChangesFocus(true);
setFocusProxy(m_input);
m_input->setStyleSheet(
"QTextEdit { border: 1px solid palette(mid); border-radius: 6px;"
" padding: 4px 8px; background: palette(base); }"
+2
View File
@@ -680,6 +680,8 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
m_inputBar = new InputBar(m_emojiPanel, recorder, this);
layout->addWidget(m_inputBar);
m_inputBar->installFileDropTarget(m_view->viewport());
m_inputBar->focusProxy()->installEventFilter(m_view);
window()->installEventFilter(m_view);
connect(m_inputBar, &InputBar::sendMessage, this, [this](const QString &text, const QByteArray& reply, quint64 request) {
if (!gui_bridge_get_inst() || !gui_bridge_is_uasync_ready()) {
+47
View File
@@ -1,5 +1,6 @@
/* Реальные Qt-события на viewport: drop только готовит вложение, отправку инициирует пользователь. */
#include "../src/inputbar.h"
#include "../src/chatview.h"
#include "../../../lib/debug_config.h"
extern "C" {
#include "media_async/attachment_send.h"
@@ -23,6 +24,10 @@ extern "C" {
#include <QAbstractButton>
#include <QPushButton>
#include <QCheckBox>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QStringListModel>
#include <QScrollBar>
static QString makeFile(const QTemporaryDir& dir, const QString& name, const QByteArray& contents = "test") {
const QString path = dir.filePath(name);
@@ -48,6 +53,48 @@ static bool dropUrls(QWidget* target, const QList<QUrl>& urls, bool process = tr
class FileDropTest : public QObject {
Q_OBJECT
private slots:
void historyKeyboardNavigation_data() {
QTest::addColumn<int>("key");
QTest::addColumn<int>("focus");
for (int key : {Qt::Key_Up, Qt::Key_Down, Qt::Key_PageUp, Qt::Key_PageDown})
for (int focus = 0; focus < 3; focus++)
QTest::newRow(qPrintable(QStringLiteral("key-%1-focus-%2").arg(key).arg(focus))) << key << focus;
}
void historyKeyboardNavigation() {
QFETCH(int, key); QFETCH(int, focus);
QMainWindow window;
auto* body = new QWidget(&window); auto* layout = new QVBoxLayout(body);
auto* view = new ChatView(body); auto* input = new InputBar(nullptr, nullptr, body);
layout->addWidget(view, 1); layout->addWidget(input); window.setCentralWidget(body);
QStringList rows; for (int i = 0; i < 100; i++) rows.append(QString::number(i));
QStringListModel model(rows); view->setModel(&model);
view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
auto* editor = input->findChild<QTextEdit*>(); QVERIFY(editor); QCOMPARE(input->focusProxy(), editor);
editor->installEventFilter(view); window.installEventFilter(view);
const QString draft = QStringLiteral("first line\nsecond line\nthird line"); editor->setPlainText(draft);
auto cursor = editor->textCursor(); cursor.setPosition(5); editor->setTextCursor(cursor);
window.resize(600, 400); window.show(); window.activateWindow();
QWidget* target = focus == 0 ? static_cast<QWidget*>(editor) : focus == 1 ? static_cast<QWidget*>(&window) : view;
if (focus == 0) input->setFocus(); else target->setFocus();
QTRY_COMPARE(QApplication::focusWidget(), target);
auto* bar = view->verticalScrollBar(); QTRY_VERIFY(bar->maximum() > bar->pageStep() * 2);
bar->setValue(bar->maximum() / 2);
const int before = bar->value();
const bool up = key == Qt::Key_Up || key == Qt::Key_PageUp;
const int step = key == Qt::Key_Up || key == Qt::Key_Down ? bar->singleStep() : bar->pageStep();
QTest::keyClick(target, static_cast<Qt::Key>(key));
QCOMPARE(bar->value(), before + (up ? -step : step));
QCOMPARE(QApplication::focusWidget(), target); QCOMPARE(editor->textCursor().position(), 5); QCOMPARE(editor->toPlainText(), draft);
bar->setValue(up ? bar->minimum() : bar->maximum());
const int boundary = bar->value(); QTest::keyClick(target, static_cast<Qt::Key>(key));
QCOMPARE(bar->value(), boundary); QCOMPARE(editor->textCursor().position(), 5);
if (focus == 0) {
bar->setValue(before);
QTest::keyClick(editor, Qt::Key_Down, Qt::ShiftModifier);
QVERIFY(editor->textCursor().hasSelection()); QCOMPARE(bar->value(), before);
QCOMPARE(editor->toPlainText(), draft); QCOMPARE(QApplication::focusWidget(), editor);
}
}
void encryptedAttachmentCapturesTargetAndResetsChoice_data() {
QTest::addColumn<bool>("picker");
QTest::newRow("drop") << false; QTest::newRow("file-picker") << true;
-10
View File
@@ -1728,11 +1728,6 @@ with per-link BBR statistics</translation>
<source>Public</source>
<translation>Public</translation>
</message>
<message>
<location filename="../src/channelsettingsdialog.cpp" line="+4"/>
<source>Push-To-Talk (PTT) mode</source>
<translation>Push-To-Talk (PTT) mode</translation>
</message>
<message>
<location filename="../src/nodespage.cpp" line="-14"/>
<source>Query error</source>
@@ -1805,11 +1800,6 @@ with per-link BBR statistics</translation>
<source>Recipient: %1</source>
<translation>Recipient: %1</translation>
</message>
<message>
<location filename="../src/channelsettingsdialog.cpp" line="+1"/>
<source>Record voice messages with push-to-talk. (Coming soon)</source>
<translation>Record voice messages with push-to-talk. (Coming soon)</translation>
</message>
<message>
<location filename="../src/audiodevicesettingspage.cpp" line="-81"/>
<source>Recording device:</source>
-10
View File
@@ -1731,11 +1731,6 @@ with per-link BBR statistics</source>
<source>Public</source>
<translation>Публичный</translation>
</message>
<message>
<location filename="../src/channelsettingsdialog.cpp" line="+4"/>
<source>Push-To-Talk (PTT) mode</source>
<translation>Режим «нажми и говори» (PTT)</translation>
</message>
<message>
<location filename="../src/nodespage.cpp" line="-14"/>
<source>Query error</source>
@@ -1808,11 +1803,6 @@ with per-link BBR statistics</source>
<source>Recipient: %1</source>
<translation>Получатель: %1</translation>
</message>
<message>
<location filename="../src/channelsettingsdialog.cpp" line="+1"/>
<source>Record voice messages with push-to-talk. (Coming soon)</source>
<translation>Запись голосовых сообщений в режиме «нажми и говори». (Скоро)</translation>
</message>
<message>
<location filename="../src/audiodevicesettingspage.cpp" line="-81"/>
<source>Recording device:</source>