From 0fc687953fdc2d45856d2d70c414076535dc92c1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Jun 2022 12:04:38 +0400 Subject: [PATCH] Allow selecting / copying voice transcribe text. --- .../SourceFiles/data/data_media_types.cpp | 26 +++- .../view/media/history_view_document.cpp | 131 +++++++++++++++--- 2 files changed, 132 insertions(+), 25 deletions(-) diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index e7dc84a2b1..d15743ac3f 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/emoji_config.h" #include "api/api_sending.h" +#include "api/api_transcribes.h" #include "storage/storage_shared_media.h" #include "storage/localstorage.h" #include "chat_helpers/stickers_dice_pack.h" // Stickers::DicePacks::IsSlot. @@ -56,6 +57,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "storage/file_upload.h" #include "window/window_session_controller.h" // Window::Show +#include "apiwrap.h" #include "styles/style_chat.h" #include "styles/style_dialogs.h" @@ -858,9 +860,27 @@ TextForMimeData MediaFile::clipboardText() const { } return tr::lng_in_dlg_file(tr::now) + addName; }(); - return WithCaptionClipboardText( - attachType, - parent()->clipboardText()); + auto caption = parent()->clipboardText(); + + if (_document->isVoiceMessage()) { + const auto &entry = _document->session().api().transcribes().entry( + parent()); + if (!entry.requestId + && entry.shown + && !entry.toolong + && !entry.failed + && (entry.pending || !entry.result.isEmpty())) { + const auto text = "{{\n" + + entry.result + + (entry.result.isEmpty() ? "" : " ") + + (entry.pending ? "[...]" : "") + + "\n}}" + + (caption.rich.text.isEmpty() ? "" : "\n"); + caption = TextForMimeData{ text, { text } }.append(std::move(caption)); + } + } + + return WithCaptionClipboardText(attachType, std::move(caption)); } bool MediaFile::allowsEditCaption() const { diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index b23323eadc..1bb9e42917 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -235,6 +235,17 @@ void Document::fillNamedFromData(HistoryDocumentNamed *named) { QSize Document::countOptimalSize() { auto captioned = Get(); + if (_parent->media() != this && !_realParent->groupId()) { + if (captioned) { + RemoveComponents(HistoryDocumentCaptioned::Bit()); + captioned = nullptr; + } + } else if (captioned && captioned->_caption.hasSkipBlock()) { + captioned->_caption.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); + } + auto hasTranscribe = false; const auto voice = Get(); if (voice) { @@ -277,21 +288,17 @@ QSize Document::countOptimalSize() { st::messageTextStyle, text); hasTranscribe = true; + if (const auto skipBlockWidth = captioned + ? 0 + : _parent->skipBlockWidth()) { + voice->transcribeText.updateSkipBlock( + skipBlockWidth, + _parent->skipBlockHeight()); + } } } } - if (_parent->media() != this && !_realParent->groupId()) { - if (captioned) { - RemoveComponents(HistoryDocumentCaptioned::Bit()); - captioned = nullptr; - } - } else if (captioned && captioned->_caption.hasSkipBlock()) { - captioned->_caption.updateSkipBlock( - _parent->skipBlockWidth(), - _parent->skipBlockHeight()); - } - auto thumbed = Get(); const auto &st = thumbed ? st::msgFileThumbLayout : st::msgFileLayout; if (thumbed) { @@ -655,15 +662,17 @@ void Document::draw( } } + auto selection = context.selection; auto captiontop = bottom; if (voice && !voice->transcribeText.isEmpty()) { p.setPen(stm->historyTextFg); - voice->transcribeText.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, context.selection); + voice->transcribeText.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, selection); captiontop += voice->transcribeText.countHeight(captionw) + st::mediaCaptionSkip; + selection = HistoryView::UnshiftItemSelection(selection, voice->transcribeText); } - if (auto captioned = Get()) { + if (const auto captioned = Get()) { p.setPen(stm->historyTextFg); - captioned->_caption.draw(p, st::msgPadding.left(), captiontop, captionw, style::al_left, 0, -1, context.selection); + captioned->_caption.draw(p, st::msgPadding.left(), captiontop, captionw, style::al_left, 0, -1, selection); } } @@ -812,7 +821,7 @@ TextState Document::textState( const auto nametop = st.nameTop - topMinus; const auto nameright = st.padding.left(); const auto linktop = st.linkTop - topMinus; - const auto bottom = st.padding.top() + st.thumbSize + st.padding.bottom() - topMinus; + auto bottom = st.padding.top() + st.thumbSize + st.padding.bottom() - topMinus; const auto rthumb = style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, width); const auto innerSize = st::msgFileLayout.thumbSize; const auto inner = QRect(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize); @@ -844,6 +853,9 @@ TextState Document::textState( const auto voice = Get(); auto namewidth = width - nameleft - nameright; + auto transcribeLength = 0; + auto transcribeHeight = 0; + auto painth = layout.height(); if (voice) { auto waveformbottom = st.padding.top() - topMinus + st::msgWaveformMax + st::msgWaveformMin; if (voice->transcribe) { @@ -867,15 +879,36 @@ TextState Document::textState( return result; } } + transcribeLength = voice->transcribeText.length(); + if (transcribeLength > 0) { + auto captionw = width - st::msgPadding.left() - st::msgPadding.right(); + transcribeHeight = voice->transcribeText.countHeight(captionw); + painth -= transcribeHeight; + if (point.y() >= bottom && point.y() < bottom + transcribeHeight) { + result = TextState(_parent, voice->transcribeText.getState( + point - QPoint(st::msgPadding.left(), bottom), + width - st::msgPadding.left() - st::msgPadding.right(), + request.forText())); + return result; + } + bottom += transcribeHeight; + } } - auto painth = layout.height(); if (const auto captioned = Get()) { + if (point.y() >= bottom) { + result.symbol += transcribeLength; + } + if (transcribeHeight) { + painth -= st::mediaCaptionSkip; + bottom += st::mediaCaptionSkip; + } if (point.y() >= bottom) { result = TextState(_parent, captioned->_caption.getState( point - QPoint(st::msgPadding.left(), bottom), width - st::msgPadding.left() - st::msgPadding.right(), request.forText())); + result.symbol += transcribeLength; return result; } auto captionw = width - st::msgPadding.left() - st::msgPadding.right(); @@ -883,6 +916,8 @@ TextState Document::textState( if (isBubbleBottom()) { painth -= st::msgPadding.bottom(); } + } else if (transcribeHeight && isBubbleBottom()) { + painth -= st::msgPadding.bottom(); } const auto till = voice ? (nameleft + namewidth) : width; if (QRect(0, 0, till, painth).contains(point) @@ -901,7 +936,7 @@ TextState Document::textState( void Document::updatePressed(QPoint point) { // LayoutMode should be passed here. - if (auto voice = Get()) { + if (const auto voice = Get()) { if (voice->seeking()) { const auto thumbed = Get(); const auto &st = thumbed ? st::msgFileThumbLayout : st::msgFileLayout; @@ -920,28 +955,80 @@ void Document::updatePressed(QPoint point) { TextSelection Document::adjustSelection( TextSelection selection, TextSelectType type) const { + auto transcribe = (const Ui::Text::String*)nullptr; + auto caption = (const Ui::Text::String*)nullptr; + if (const auto voice = Get()) { + transcribe = &voice->transcribeText; + } if (const auto captioned = Get()) { - return captioned->_caption.adjustSelection(selection, type); + caption = &captioned->_caption; + } + const auto transcribeLength = transcribe ? transcribe->length() : 0; + if (transcribe && selection.from < transcribeLength) { + const auto adjusted = transcribe->adjustSelection(selection, type); + if (selection.to <= transcribeLength) { + return adjusted; + } + selection = TextSelection(adjusted.from, selection.to); + } + if (caption && selection.to > transcribeLength) { + auto unshifted = transcribe + ? HistoryView::UnshiftItemSelection(selection, *transcribe) + : selection; + const auto adjusted = caption->adjustSelection(unshifted, type); + const auto shifted = transcribe + ? HistoryView::ShiftItemSelection(adjusted, *transcribe) + : adjusted; + if (selection.from >= transcribeLength) { + return shifted; + } + selection = TextSelection(selection.from, shifted.to); } return selection; } uint16 Document::fullSelectionLength() const { + auto result = uint16(); + if (const auto voice = Get()) { + result += voice->transcribeText.length(); + } if (const auto captioned = Get()) { - return captioned->_caption.length(); + result += captioned->_caption.length(); } return 0; } bool Document::hasTextForCopy() const { + if (const auto voice = Get()) { + if (!voice->transcribeText.isEmpty()) { + return true; + } + } return Has(); } TextForMimeData Document::selectedText(TextSelection selection) const { - if (const auto captioned = Get()) { - return captioned->_caption.toTextForMimeData(selection); + auto result = TextForMimeData(); + if (const auto voice = Get()) { + const auto length = voice->transcribeText.length(); + if (selection.from < length) { + result.append( + voice->transcribeText.toTextForMimeData(selection)); + } + if (selection.to <= length) { + return result; + } + selection = HistoryView::UnshiftItemSelection( + selection, + voice->transcribeText); } - return TextForMimeData(); + if (const auto captioned = Get()) { + if (!result.empty()) { + result.append("\n\n"); + } + result.append(captioned->_caption.toTextForMimeData(selection)); + } + return result; } bool Document::uploading() const {