diff --git a/tools/chatgui/src/message_text.cpp b/tools/chatgui/src/message_text.cpp index c5ba9ebc5..60cbc19a1 100644 --- a/tools/chatgui/src/message_text.cpp +++ b/tools/chatgui/src/message_text.cpp @@ -237,6 +237,8 @@ QRectF messageCodeCopyRect(const QTextLayout& layout, const MessageCodeBlock& bl if (!line.isValid()) return {}; const QFontMetricsF metrics(layout.font()); const qreal height = metrics.height() + 8; - const qreal buttonWidth = qMin(qreal(width), metrics.horizontalAdvance(QCoreApplication::translate("ChatGui", "Copy")) + 16); + const qreal labelWidth = qMax(metrics.horizontalAdvance(QCoreApplication::translate("ChatGui", "Copy")), + metrics.horizontalAdvance(QCoreApplication::translate("ChatGui", "Copied"))); + const qreal buttonWidth = qMin(qreal(width), labelWidth + 16); return QRectF(width - buttonWidth, line.y() - height + 2, buttonWidth, height - 4); } diff --git a/tools/chatgui/src/messagedelegate.cpp b/tools/chatgui/src/messagedelegate.cpp index 9e9b48929..aace6d3df 100644 --- a/tools/chatgui/src/messagedelegate.cpp +++ b/tools/chatgui/src/messagedelegate.cpp @@ -190,6 +190,17 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, if (copyCode) { const auto& block = L.formattedText->code[codeBlock]; QApplication::clipboard()->setText(L.formattedText->text.mid(block.start, block.length), QClipboard::Clipboard); + m_copiedCode = index; m_copiedBlock = codeBlock; m_copiedSource = index.data(MsgTextRole).toString(); + const quint64 feedback = ++m_copyFeedbackSequence; + const auto* copyView = qobject_cast(option.widget); + const QPointer repaint = copyView ? copyView->viewport() : const_cast(option.widget); + if (repaint) repaint->update(); + QTimer::singleShot(1500, this, [this, feedback, repaint] { + if (feedback != m_copyFeedbackSequence) return; + m_copiedCode = QPersistentModelIndex(); m_copiedBlock = -1; m_copiedSource.clear(); + if (repaint) repaint->update(); + GUI_DEBUG("message code: copy feedback cleared"); + }); GUI_INFO("message code copied: row=%d block=%d chars=%d", index.row(), codeBlock, block.length); return true; } @@ -1616,14 +1627,17 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o layout.draw(painter, L.textRect.topLeft(), selections); if (L.formattedText) { painter->save(); - for (const auto& block : L.formattedText->code) { + for (int number = 0; number < L.formattedText->code.size(); ++number) { + const auto& block = L.formattedText->code[number]; + const bool copied = m_copiedCode == index && m_copiedBlock == number && m_copiedSource == msgText; const auto button = messageCodeCopyRect(layout, block, L.textRect.width()).translated(L.textRect.topLeft()); painter->fillRect(QRectF(L.textRect.x(), button.top() - 2, L.textRect.width(), button.height() + 4), option.palette.alternateBase()); - painter->setPen(option.palette.mid().color()); painter->setBrush(option.palette.button()); + painter->setPen(option.palette.mid().color()); + painter->setBrush(copied ? option.palette.highlight() : option.palette.button()); painter->drawRoundedRect(button.adjusted(0.5, 0.5, -0.5, -0.5), 4, 4); - painter->setPen(option.palette.buttonText().color()); - painter->drawText(button, Qt::AlignCenter, QCoreApplication::translate("ChatGui", "Copy")); + painter->setPen(copied ? option.palette.highlightedText().color() : option.palette.buttonText().color()); + painter->drawText(button, Qt::AlignCenter, QCoreApplication::translate("ChatGui", copied ? "Copied" : "Copy")); const QRectF label(L.textRect.x() + 8, button.top(), qMax(0.0, button.left() - L.textRect.x() - 16), button.height()); painter->setPen(option.palette.text().color()); const QString language = block.languageName.isEmpty() ? QStringLiteral("text") : block.languageName; diff --git a/tools/chatgui/src/messagedelegate.h b/tools/chatgui/src/messagedelegate.h index 60e2086d1..0f2076ba6 100644 --- a/tools/chatgui/src/messagedelegate.h +++ b/tools/chatgui/src/messagedelegate.h @@ -163,6 +163,10 @@ private: QPoint m_codePressPosition; int m_codePressBlock = -1; QString m_codePressSource; + QPersistentModelIndex m_copiedCode; // Последняя успешно нажатая Copy; состояние только GUI, не данные сообщения. + int m_copiedBlock = -1; + QString m_copiedSource; // Изменённый текст сразу скрывает подтверждение старого блока. + quint64 m_copyFeedbackSequence = 0; // Повторное нажатие продлевает подтверждение, старый timeout его не снимает. QPoint m_mediaPressPosition; // Порог клика <5px, как для цитат/реакций. std::function m_downloadCb;