mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Suggest sending large texts as files.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="96.5 111.3 766.5 766.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 229.278 428.274 C 220.345 431.087 209.724 433.028 201.037 428.414 C 195.269 425.35 191.59 419.715 189.754 413.573 C 183.55 392.827 189.646 312.841 194.598 289.706 C 197.361 276.798 201.636 264.234 208.088 252.692 C 225.944 220.748 254.345 196.434 289.828 186.42 C 299.548 183.676 309.688 182.14 319.754 181.423 C 366.51 178.095 414.437 180.111 461.345 180.132 C 499.997 180.149 540.285 177.606 578.657 181.934 C 589.925 183.204 601.183 185.766 611.125 191.369 C 638.232 206.647 660.549 233.285 682.155 255.355 L 741.115 315.299 L 790.708 365.561 C 856.391 432.25 853.313 440.991 854.144 532.569 L 854.982 618.399 C 855.281 646.04 857.577 702.741 854.521 727.102 C 852.435 744.448 846.915 761.205 838.282 776.393 C 820.298 808.656 790.121 832.354 754.514 842.176 C 724.089 850.54 664.593 848.635 631.504 848.632 L 502.763 848.626 L 395.022 848.66 C 368.985 848.68 338.582 849.327 312.926 846.33 C 294.671 844.264 277.017 838.552 261.012 829.532 C 228.881 811.132 205.527 780.551 196.236 744.708 C 190.977 725.111 181.752 660.54 192.22 640.91 C 194.552 636.536 200.443 631.193 204.584 628.088 C 212.69 627.794 223.61 625.977 229.827 632.364 C 238.468 641.24 238.784 653.684 239.449 665.298 C 241.05 693.25 237.429 726.072 251.724 751.22 C 263.068 771.178 283.96 788.616 306.011 795.036 C 326.159 800.066 359.183 798.753 380.622 798.749 L 460.193 798.696 L 703.391 798.695 C 736.68 798.43 755.087 795.322 779.818 770.591 C 807.116 743.293 806.834 720.038 807.012 684.161 C 807.237 638.481 802.094 586.261 804.093 541.122 C 797.284 541.268 784.894 540.995 778.473 540.182 L 670.807 540.171 C 620.731 540.215 563.716 547.86 526.38 508.874 C 515.394 497.402 505.295 482.717 501.211 466.939 C 495.154 439.227 496.977 401.501 496.999 372.596 L 497.103 265.614 C 497.25 253.886 497.577 242.161 498.083 230.443 C 460.209 229.51 422.322 229.222 384.438 229.578 C 335.986 229.564 309.732 221.795 269.362 254.551 C 217.116 296.943 257.721 389.29 229.559 427.897 L 229.278 428.274 z M 543.987 230.048 C 569.591 229.725 580.035 227.497 600.857 244.842 C 616.603 257.958 630.079 272.64 644.427 287.248 L 711.181 355.245 L 759.072 403.921 C 769.77 414.798 785.756 430.108 793.914 442.441 C 801.17 453.41 801.179 474.426 802.716 487.848 L 663.551 488.081 C 655.173 488.09 645.943 488.449 637.627 488.107 C 619.637 487.367 594.727 490.338 577.846 484.153 C 545.853 472.432 543.843 436.102 544.247 407.389 C 544.438 393.841 544.247 379.666 544.256 365.887 C 544.495 320.607 544.405 275.326 543.987 230.048 z" fill="#FFFFFF"></path>
|
||||
<path d="M 306.755 391.204 C 311.745 389.305 333.398 408.89 337.28 412.89 C 364.174 440.939 392.794 467.428 419.422 495.698 C 442.291 519.978 433.144 542.942 411.651 563.866 C 381.068 593.638 352.265 625.515 320.324 653.743 C 313.888 659.431 296.743 655.829 288.97 654.425 C 287.039 649.138 280.827 633.452 282.505 628.63 C 289.515 608.489 314.352 590.824 328.353 575.581 C 338.311 564.74 345.544 555.412 356.226 547.597 L 356.28 545.853 C 344.978 549.112 335.336 551.079 323.481 551.267 C 274.259 551.846 224.991 551.501 175.755 551.624 C 155.295 551.676 133.425 553.912 116.627 541.311 C 114.956 537.351 114 533.126 113.802 528.833 C 113.091 514.333 117.1 501.472 133.314 500.562 C 154.052 499.399 174.698 499.879 195.467 499.925 C 239.08 500.336 282.696 500.334 326.309 499.918 C 332.541 499.822 343.551 500.1 347.833 495.685 C 347.436 491.434 313.367 461.839 307.219 455.232 C 291.043 437.85 275.937 429.066 286.23 402.426 C 291.881 397.453 299.873 394.302 306.755 391.204 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -7841,6 +7841,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ai_compose_apply_style" = "Apply Style";
|
||||
"lng_ai_compose_style_tooltip" = "Choose Style";
|
||||
|
||||
"lng_send_as_file_tooltip" = "Send text as a file.";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
||||
@@ -1443,6 +1443,10 @@ historyAiComposeButtonLetters: icon{{ "chat/ai_letters-20x20", historyComposeIco
|
||||
historyAiComposeButtonStar1: icon{{ "chat/ai_star1-20x20", historyComposeIconFg }};
|
||||
historyAiComposeButtonStar2: icon{{ "chat/ai_star2-20x20", historyComposeIconFg }};
|
||||
historyAiComposeButtonPosition: point(-8px, -4px);
|
||||
historySendAsFileButton: IconButton(historyAiComposeButton) {
|
||||
icon: icon {{ "chat/text_to_file-18x18", historyComposeIconFg }};
|
||||
iconOver: icon {{ "chat/text_to_file-18x18", historyComposeIconFgOver }};
|
||||
}
|
||||
historyAiComposeTooltipSkip: 8px;
|
||||
importantTooltipHide: IconButton(defaultIconButton) {
|
||||
width: 34px;
|
||||
|
||||
@@ -285,7 +285,10 @@ void DocumentMedia::checkStickerLarge() {
|
||||
void DocumentMedia::automaticLoad(
|
||||
Data::FileOrigin origin,
|
||||
const HistoryItem *item) {
|
||||
if (_owner->status != FileReady || loaded() || _owner->cancelled()) {
|
||||
if (_owner->status != FileReady
|
||||
|| loaded()
|
||||
|| _owner->uploading()
|
||||
|| _owner->cancelled()) {
|
||||
return;
|
||||
} else if (!item && !_owner->sticker() && !_owner->isAnimation()) {
|
||||
return;
|
||||
|
||||
@@ -273,6 +273,9 @@ HistoryWidget::HistoryWidget(
|
||||
, _aiButton(Ui::CreateChild<HistoryView::Controls::ComposeAiButton>(
|
||||
this,
|
||||
st::historyAiComposeButton))
|
||||
, _sendAsFile(Ui::CreateChild<Ui::IconButton>(
|
||||
this,
|
||||
st::historySendAsFileButton))
|
||||
, _unblock(
|
||||
this,
|
||||
tr::lng_unblock_button(tr::now).toUpper(),
|
||||
@@ -497,6 +500,7 @@ HistoryWidget::HistoryWidget(
|
||||
|
||||
setupFastButtonMode();
|
||||
initAiButton();
|
||||
initSendAsFileButton();
|
||||
|
||||
_fieldCharsCountManager.limitExceeds(
|
||||
) | rpl::on_next([=] {
|
||||
@@ -534,6 +538,20 @@ HistoryWidget::HistoryWidget(
|
||||
_field->setMimeDataHook(WrappedMessageFieldMimeHook([=](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action) {
|
||||
const auto pasteResult = Ui::CheckLargeTextPaste(_field, data);
|
||||
if (pasteResult.exceeds) {
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
return true;
|
||||
}
|
||||
const auto text = _field->getTextWithTags();
|
||||
const auto cursor = _field->textCursor();
|
||||
sendTextAsFile(
|
||||
pasteResult.resultingText,
|
||||
text,
|
||||
cursor.position(),
|
||||
cursor.anchor());
|
||||
return true;
|
||||
}
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
return canSendFiles(data);
|
||||
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||
@@ -1295,6 +1313,7 @@ void HistoryWidget::initVoiceRecordBar() {
|
||||
_field->setDisabled(active);
|
||||
controller()->widget()->setInnerFocus();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
}, lifetime());
|
||||
|
||||
_voiceRecordBar->hideFast();
|
||||
@@ -1314,9 +1333,65 @@ void HistoryWidget::initAiButton() {
|
||||
_aiTooltipManager = std::make_unique<HistoryView::Controls::AiTooltipManager>(
|
||||
this,
|
||||
_aiButton,
|
||||
tr::lng_ai_compose_tooltip(tr::rich),
|
||||
"ai_compose_tooltip_hidden"_cs,
|
||||
[=] { return width(); });
|
||||
}
|
||||
|
||||
void HistoryWidget::initSendAsFileButton() {
|
||||
_sendAsFile->hide();
|
||||
_sendAsFile->setClickedCallback([=] {
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->hideAndRemember();
|
||||
}
|
||||
const auto cursor = _field->textCursor();
|
||||
const auto text = _field->getTextWithTags();
|
||||
sendTextAsFile(text.text, text, cursor.position(), cursor.anchor());
|
||||
});
|
||||
|
||||
_sendAsFileTooltipManager = std::make_unique<HistoryView::Controls::AiTooltipManager>(
|
||||
this,
|
||||
_sendAsFile,
|
||||
tr::lng_send_as_file_tooltip(tr::rich),
|
||||
"send_as_file_tooltip_hidden"_cs,
|
||||
[=] { return width(); });
|
||||
}
|
||||
|
||||
void HistoryWidget::sendTextAsFile(
|
||||
const QString &fileText,
|
||||
TextWithTags restoreText,
|
||||
int restorePosition,
|
||||
int restoreAnchor) {
|
||||
auto result = Ui::PrepareTextAsFile(fileText);
|
||||
|
||||
_field->setTextWithTags({});
|
||||
|
||||
auto box = Box<SendFilesBox>(
|
||||
controller(),
|
||||
std::move(result),
|
||||
TextWithTags{},
|
||||
_peer,
|
||||
Api::SendType::Normal,
|
||||
sendMenuDetails());
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
box->setCancelledCallback(crl::guard(this, [=] {
|
||||
_field->setTextWithTags(restoreText);
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.setPosition(restoreAnchor);
|
||||
if (restorePosition != restoreAnchor) {
|
||||
cursor.setPosition(restorePosition, QTextCursor::KeepAnchor);
|
||||
}
|
||||
_field->setTextCursor(cursor);
|
||||
}));
|
||||
|
||||
Window::ActivateWindow(controller());
|
||||
controller()->show(std::move(box));
|
||||
}
|
||||
|
||||
void HistoryWidget::initTabbedSelector() {
|
||||
refreshTabbedPanel();
|
||||
|
||||
@@ -1830,6 +1905,7 @@ void HistoryWidget::orderWidgets() {
|
||||
_voiceRecordBar->raise();
|
||||
_send->raise();
|
||||
_aiButton->raise();
|
||||
_sendAsFile->raise();
|
||||
_topBars->raise();
|
||||
if (_businessBotStatus) {
|
||||
_businessBotStatus->bar().raise();
|
||||
@@ -1883,6 +1959,9 @@ void HistoryWidget::orderWidgets() {
|
||||
if (_aiTooltipManager) {
|
||||
_aiTooltipManager->raise();
|
||||
}
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->raise();
|
||||
}
|
||||
_attachDragAreas.document->raise();
|
||||
_attachDragAreas.photo->raise();
|
||||
}
|
||||
@@ -1957,6 +2036,7 @@ void HistoryWidget::fieldChanged() {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
|
||||
_saveCloudDraftTimer.cancel();
|
||||
if (!_peer || !(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
|
||||
@@ -3748,6 +3828,7 @@ void HistoryWidget::updateControlsVisibility() {
|
||||
}
|
||||
//checkTabbedSelectorToggleTooltip();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
updateMouseTracking();
|
||||
}
|
||||
|
||||
@@ -6115,6 +6196,7 @@ void HistoryWidget::toggleKeyboard(bool manual) {
|
||||
}
|
||||
updateControlsGeometry();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
updateFieldPlaceholder();
|
||||
if (_botKeyboardHide->isHidden()
|
||||
&& canWriteMessage()
|
||||
@@ -6264,6 +6346,12 @@ bool HistoryWidget::hasEnoughLinesForAi() const {
|
||||
&& Ui::HasEnoughLinesForAi(&session(), _field);
|
||||
}
|
||||
|
||||
bool HistoryWidget::textExceedsMaxSize() const {
|
||||
return _history
|
||||
&& !_voiceRecordBar->isActive()
|
||||
&& _field->getLastText().size() > MaxMessageSize;
|
||||
}
|
||||
|
||||
void HistoryWidget::updateAiButtonVisibility() {
|
||||
const auto hidden = !hasEnoughLinesForAi()
|
||||
|| !_send->isVisible()
|
||||
@@ -6292,6 +6380,34 @@ void HistoryWidget::updateAiButtonGeometry() {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateSendAsFileVisibility() {
|
||||
const auto hidden = !textExceedsMaxSize()
|
||||
|| _send->isHidden()
|
||||
|| _field->isHidden()
|
||||
|| editingMessage();
|
||||
if (_sendAsFile->isHidden() == hidden) {
|
||||
return;
|
||||
}
|
||||
_sendAsFile->setVisible(!hidden);
|
||||
if (!hidden) {
|
||||
updateSendAsFileGeometry();
|
||||
}
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->updateVisibility(!hidden);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateSendAsFileGeometry() {
|
||||
if (_sendAsFile->isHidden()) {
|
||||
return;
|
||||
}
|
||||
const auto x = _send->x() + _send->width() - _sendAsFile->width();
|
||||
_sendAsFile->move(QPoint(x, _field->y()) + st::historyAiComposeButtonPosition);
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::moveFieldControls() {
|
||||
auto keyboardHeight = 0;
|
||||
auto bottom = height();
|
||||
@@ -6362,6 +6478,7 @@ void HistoryWidget::moveFieldControls() {
|
||||
_ttlInfo->move(width() - right - _ttlInfo->width(), buttonsBottom);
|
||||
}
|
||||
updateAiButtonGeometry();
|
||||
updateSendAsFileGeometry();
|
||||
|
||||
_fieldBarCancel->moveToRight(
|
||||
0,
|
||||
@@ -6466,6 +6583,7 @@ void HistoryWidget::inlineBotChanged() {
|
||||
void HistoryWidget::fieldResized() {
|
||||
moveFieldControls();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
updateHistoryGeometry();
|
||||
updateField();
|
||||
}
|
||||
|
||||
@@ -128,7 +128,8 @@ class WebpageProcessor;
|
||||
class CharactersLimitLabel;
|
||||
class PhotoEditSpoilerManager;
|
||||
class ComposeAiButton;
|
||||
class AiTooltipManager;
|
||||
class ComposeTooltipManager;
|
||||
using AiTooltipManager = ComposeTooltipManager;
|
||||
struct VoiceToSend;
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
@@ -529,12 +530,21 @@ private:
|
||||
void updateAiButtonVisibility();
|
||||
void updateAiButtonGeometry();
|
||||
void showAiComposeBox();
|
||||
void initSendAsFileButton();
|
||||
void sendTextAsFile(
|
||||
const QString &fileText,
|
||||
TextWithTags restoreText,
|
||||
int restorePosition,
|
||||
int restoreAnchor);
|
||||
void updateSendAsFileVisibility();
|
||||
void updateSendAsFileGeometry();
|
||||
[[nodiscard]] bool canSendAiComposeDirect() const;
|
||||
|
||||
[[nodiscard]] MsgId resolveReplyToTopicRootId();
|
||||
[[nodiscard]] Data::ForumTopic *resolveReplyToTopic();
|
||||
[[nodiscard]] bool canWriteMessage() const;
|
||||
[[nodiscard]] bool hasEnoughLinesForAi() const;
|
||||
[[nodiscard]] bool textExceedsMaxSize() const;
|
||||
void orderWidgets();
|
||||
|
||||
[[nodiscard]] InlineBotQuery parseInlineBotQuery() const;
|
||||
@@ -835,6 +845,7 @@ private:
|
||||
|
||||
const std::shared_ptr<Ui::SendButton> _send;
|
||||
HistoryView::Controls::ComposeAiButton * const _aiButton = nullptr;
|
||||
Ui::IconButton * const _sendAsFile = nullptr;
|
||||
object_ptr<Ui::FlatButton> _unblock;
|
||||
object_ptr<Ui::FlatButton> _botStart;
|
||||
object_ptr<Ui::FlatButton> _joinChannel;
|
||||
@@ -867,6 +878,7 @@ private:
|
||||
rpl::lifetime _subsectionTabsLifetime;
|
||||
rpl::lifetime _subsectionCheckLifetime;
|
||||
std::unique_ptr<HistoryView::Controls::AiTooltipManager> _aiTooltipManager;
|
||||
std::unique_ptr<HistoryView::Controls::AiTooltipManager> _sendAsFileTooltipManager;
|
||||
std::shared_ptr<Ui::ChatStyle> _fieldChatStyle;
|
||||
bool _cmdStartShown = false;
|
||||
object_ptr<Ui::InputField> _field;
|
||||
|
||||
@@ -9,30 +9,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "history/view/controls/history_view_compose_ai_button.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
namespace {
|
||||
|
||||
constexpr auto kAiComposeTooltipHiddenPref = "ai_compose_tooltip_hidden"_cs;
|
||||
|
||||
} // namespace
|
||||
|
||||
AiTooltipManager::AiTooltipManager(
|
||||
ComposeTooltipManager::ComposeTooltipManager(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<ComposeAiButton*> button,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
rpl::producer<TextWithEntities> text,
|
||||
base::const_string prefKey,
|
||||
Fn<int()> widthProvider)
|
||||
: _button(button)
|
||||
, _prefKey(prefKey)
|
||||
, _widthProvider(std::move(widthProvider)) {
|
||||
_tooltip.reset(Ui::CreateChild<Ui::ImportantTooltip>(
|
||||
parent,
|
||||
Ui::MakeTooltipWithClose(
|
||||
parent,
|
||||
tr::lng_ai_compose_tooltip(tr::rich),
|
||||
std::move(text),
|
||||
st::historyMessagesTTLLabel.minWidth,
|
||||
st::ttlMediaImportantTooltipLabel,
|
||||
st::importantTooltipHide,
|
||||
@@ -48,21 +45,17 @@ AiTooltipManager::AiTooltipManager(
|
||||
}, _tooltip->lifetime());
|
||||
}
|
||||
|
||||
void AiTooltipManager::hideAndRemember() {
|
||||
if (!Core::App().settings().readPref<bool>(
|
||||
kAiComposeTooltipHiddenPref)) {
|
||||
Core::App().settings().writePref<bool>(
|
||||
kAiComposeTooltipHiddenPref,
|
||||
true);
|
||||
void ComposeTooltipManager::hideAndRemember() {
|
||||
if (!Core::App().settings().readPref<bool>(_prefKey)) {
|
||||
Core::App().settings().writePref<bool>(_prefKey, true);
|
||||
}
|
||||
_shown = false;
|
||||
_tooltip->toggleAnimated(false);
|
||||
}
|
||||
|
||||
void AiTooltipManager::updateVisibility(bool buttonShown) {
|
||||
void ComposeTooltipManager::updateVisibility(bool buttonShown) {
|
||||
const auto showTooltip = buttonShown
|
||||
&& !Core::App().settings().readPref<bool>(
|
||||
kAiComposeTooltipHiddenPref);
|
||||
&& !Core::App().settings().readPref<bool>(_prefKey);
|
||||
if (showTooltip) {
|
||||
updateGeometry();
|
||||
}
|
||||
@@ -73,7 +66,7 @@ void AiTooltipManager::updateVisibility(bool buttonShown) {
|
||||
}
|
||||
}
|
||||
|
||||
void AiTooltipManager::updateGeometry() {
|
||||
void ComposeTooltipManager::updateGeometry() {
|
||||
if (_button->isHidden()) {
|
||||
return;
|
||||
}
|
||||
@@ -90,7 +83,7 @@ void AiTooltipManager::updateGeometry() {
|
||||
_tooltip->pointAt(geometry, RectPart::Top, countPosition);
|
||||
}
|
||||
|
||||
void AiTooltipManager::raise() {
|
||||
void ComposeTooltipManager::raise() {
|
||||
_tooltip->raise();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/const_string.h"
|
||||
#include "base/unique_qptr.h"
|
||||
|
||||
namespace Ui {
|
||||
@@ -16,13 +17,13 @@ class RpWidget;
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
|
||||
class ComposeAiButton;
|
||||
|
||||
class AiTooltipManager final {
|
||||
class ComposeTooltipManager final {
|
||||
public:
|
||||
AiTooltipManager(
|
||||
ComposeTooltipManager(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<ComposeAiButton*> button,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
rpl::producer<TextWithEntities> text,
|
||||
base::const_string prefKey,
|
||||
Fn<int()> widthProvider);
|
||||
|
||||
void hideAndRemember();
|
||||
@@ -31,11 +32,14 @@ public:
|
||||
void raise();
|
||||
|
||||
private:
|
||||
const not_null<ComposeAiButton*> _button;
|
||||
const not_null<Ui::RpWidget*> _button;
|
||||
const base::const_string _prefKey;
|
||||
const Fn<int()> _widthProvider;
|
||||
base::unique_qptr<Ui::ImportantTooltip> _tooltip;
|
||||
bool _shown = false;
|
||||
|
||||
};
|
||||
|
||||
using AiTooltipManager = ComposeTooltipManager;
|
||||
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/compose_ai_box.h"
|
||||
#include "boxes/edit_caption_box.h"
|
||||
#include "boxes/send_files_box.h"
|
||||
#include "calls/group/ui/calls_group_stars_coloring.h"
|
||||
#include "calls/group/calls_group_stars_box.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
@@ -71,6 +72,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/controls/history_view_voice_record_bar.h"
|
||||
#include "history/view/controls/history_view_webpage_processor.h"
|
||||
#include "history/view/history_view_reply.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "history/view/history_view_webpage_preview.h"
|
||||
#include "inline_bots/bot_attach_web_view.h"
|
||||
#include "inline_bots/inline_results_widget.h"
|
||||
@@ -1034,6 +1036,11 @@ ComposeControls::ComposeControls(
|
||||
, _aiButton(Ui::CreateChild<Controls::ComposeAiButton>(
|
||||
_wrap.get(),
|
||||
st::historyAiComposeButton))
|
||||
, _sendAsFile(_features.attachments
|
||||
? Ui::CreateChild<Ui::IconButton>(
|
||||
_wrap.get(),
|
||||
st::historySendAsFileButton)
|
||||
: nullptr)
|
||||
, _like(_features.likes
|
||||
? Ui::CreateChild<Ui::IconButton>(_wrap.get(), _st.like)
|
||||
: nullptr)
|
||||
@@ -1833,8 +1840,23 @@ rpl::producer<std::optional<bool>> ComposeControls::attachRequests() const {
|
||||
}
|
||||
|
||||
void ComposeControls::setMimeDataHook(MimeDataHook hook) {
|
||||
_field->setMimeDataHook(
|
||||
WrappedMessageFieldMimeHook(std::move(hook), _field));
|
||||
if (_sendAsFile) {
|
||||
_field->setMimeDataHook(
|
||||
WrappedMessageFieldMimeHook(
|
||||
[=, originalHook = std::move(hook)](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action) {
|
||||
if (checkLargeTextPaste(data, action)) {
|
||||
return true;
|
||||
}
|
||||
return originalHook
|
||||
? originalHook(data, action)
|
||||
: false;
|
||||
}, _field));
|
||||
} else {
|
||||
_field->setMimeDataHook(
|
||||
WrappedMessageFieldMimeHook(std::move(hook), _field));
|
||||
}
|
||||
}
|
||||
|
||||
bool ComposeControls::confirmMediaEdit(Ui::PreparedList &list) {
|
||||
@@ -1906,9 +1928,15 @@ void ComposeControls::showFinished() {
|
||||
}
|
||||
updateWrappingVisibility();
|
||||
_aiButton->raise();
|
||||
if (_sendAsFile) {
|
||||
_sendAsFile->raise();
|
||||
}
|
||||
if (_aiTooltipManager) {
|
||||
_aiTooltipManager->raise();
|
||||
}
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->raise();
|
||||
}
|
||||
_voiceRecordBar->orderControls();
|
||||
}
|
||||
|
||||
@@ -2040,6 +2068,7 @@ void ComposeControls::init() {
|
||||
initTabbedSelector();
|
||||
initSendButton();
|
||||
initAiButton();
|
||||
initSendAsFileButton();
|
||||
initWriteRestriction();
|
||||
initVoiceRecordBar();
|
||||
initKeyHandler();
|
||||
@@ -2349,11 +2378,13 @@ void ComposeControls::initField() {
|
||||
) | rpl::on_next([=] {
|
||||
updateHeight();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
}, _field->lifetime());
|
||||
_field->changes(
|
||||
) | rpl::on_next([=] {
|
||||
fieldChanged();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
}, _field->lifetime());
|
||||
#ifdef Q_OS_MAC
|
||||
// Removed an ability to insert text from the menu bar
|
||||
@@ -3210,6 +3241,7 @@ void ComposeControls::initVoiceRecordBar() {
|
||||
_recording = false;
|
||||
}
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
}, _wrap->lifetime());
|
||||
|
||||
_voiceRecordBar->setStartRecordingFilter([=] {
|
||||
@@ -3308,9 +3340,80 @@ void ComposeControls::initAiButton() {
|
||||
_aiTooltipManager = std::make_unique<Controls::AiTooltipManager>(
|
||||
_wrap.get(),
|
||||
_aiButton,
|
||||
tr::lng_ai_compose_tooltip(tr::rich),
|
||||
"ai_compose_tooltip_hidden"_cs,
|
||||
[=] { return _wrap->width(); });
|
||||
}
|
||||
|
||||
void ComposeControls::initSendAsFileButton() {
|
||||
if (!_sendAsFile) {
|
||||
return;
|
||||
}
|
||||
_sendAsFile->hide();
|
||||
_sendAsFile->setClickedCallback([=] {
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->hideAndRemember();
|
||||
}
|
||||
const auto text = _field->getTextWithTags();
|
||||
auto restore = restoreTextCallback(QString());
|
||||
fireSendTextAsFile(text.text, std::move(restore));
|
||||
});
|
||||
|
||||
_sendAsFileTooltipManager = std::make_unique<Controls::AiTooltipManager>(
|
||||
_wrap.get(),
|
||||
_sendAsFile,
|
||||
tr::lng_send_as_file_tooltip(tr::rich),
|
||||
"send_as_file_tooltip_hidden"_cs,
|
||||
[=] { return _wrap->width(); });
|
||||
}
|
||||
|
||||
void ComposeControls::setSendAsFileConfirmed(
|
||||
Fn<void(std::shared_ptr<Ui::PreparedBundle>, Api::SendOptions)> confirmed) {
|
||||
_sendAsFileConfirmed = std::move(confirmed);
|
||||
}
|
||||
|
||||
void ComposeControls::fireSendTextAsFile(
|
||||
const QString &fileText,
|
||||
Fn<void()> restoreText) {
|
||||
if (!_sendAsFileConfirmed || !_history) {
|
||||
return;
|
||||
}
|
||||
const auto peer = _history->peer;
|
||||
const auto sendType = (_mode == Mode::Scheduled)
|
||||
? (CanScheduleUntilOnline(peer)
|
||||
? Api::SendType::ScheduledToUser
|
||||
: Api::SendType::Scheduled)
|
||||
: Api::SendType::Normal;
|
||||
_show->show(Box<SendFilesBox>(SendFilesBoxDescriptor{
|
||||
.show = _show,
|
||||
.list = Ui::PrepareTextAsFile(fileText),
|
||||
.caption = TextWithTags{},
|
||||
.toPeer = peer,
|
||||
.limits = DefaultLimitsForPeer(peer),
|
||||
.check = DefaultCheckForPeer(_show, peer),
|
||||
.sendType = sendType,
|
||||
.sendMenuDetails = _sendMenuDetails,
|
||||
.stOverride = &_st,
|
||||
.confirmed = _sendAsFileConfirmed,
|
||||
.cancelled = std::move(restoreText),
|
||||
}));
|
||||
}
|
||||
|
||||
bool ComposeControls::checkLargeTextPaste(
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action) {
|
||||
const auto result = Ui::CheckLargeTextPaste(_field, data);
|
||||
if (!result.exceeds) {
|
||||
return false;
|
||||
}
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
return true;
|
||||
}
|
||||
auto restore = restoreTextCallback(QString());
|
||||
fireSendTextAsFile(result.resultingText, std::move(restore));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComposeControls::updateWrappingVisibility() {
|
||||
const auto &restriction = _writeRestriction.current();
|
||||
const auto restricted = !restriction.empty() && _writeRestricted;
|
||||
@@ -3323,6 +3426,7 @@ void ComposeControls::updateWrappingVisibility() {
|
||||
_wrap->setVisible(!hidden && !restricted);
|
||||
updateControlsParents();
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
if (!hidden && !restricted) {
|
||||
updateControlsGeometry(_wrap->size());
|
||||
_wrap->raise();
|
||||
@@ -3514,6 +3618,7 @@ void ComposeControls::updateControlsGeometry(QSize size) {
|
||||
_ttlInfo->move(size.width() - right - _ttlInfo->width(), buttonsTop);
|
||||
}
|
||||
updateAiButtonGeometry();
|
||||
updateSendAsFileGeometry();
|
||||
|
||||
_voiceRecordBar->resizeToWidth(size.width());
|
||||
_voiceRecordBar->moveToLeft(
|
||||
@@ -3553,6 +3658,7 @@ void ComposeControls::updateControlsVisibility() {
|
||||
_starsReaction->show();
|
||||
}
|
||||
updateAiButtonVisibility();
|
||||
updateSendAsFileVisibility();
|
||||
}
|
||||
|
||||
void ComposeControls::updateAiButtonVisibility() {
|
||||
@@ -3584,6 +3690,37 @@ void ComposeControls::updateAiButtonGeometry() {
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeControls::updateSendAsFileVisibility() {
|
||||
if (!_sendAsFile) {
|
||||
return;
|
||||
}
|
||||
const auto hidden = !textExceedsMaxSize()
|
||||
|| _wrap->isHidden()
|
||||
|| _recording.current()
|
||||
|| _field->isHidden();
|
||||
if (_sendAsFile->isHidden() == hidden) {
|
||||
return;
|
||||
}
|
||||
_sendAsFile->setVisible(!hidden);
|
||||
if (!hidden) {
|
||||
updateSendAsFileGeometry();
|
||||
}
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->updateVisibility(!hidden);
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeControls::updateSendAsFileGeometry() {
|
||||
if (!_sendAsFile || _sendAsFile->isHidden()) {
|
||||
return;
|
||||
}
|
||||
const auto x = _send->x() + _send->width() - _sendAsFile->width();
|
||||
_sendAsFile->move(QPoint(x, _field->y()) + st::historyAiComposeButtonPosition);
|
||||
if (_sendAsFileTooltipManager) {
|
||||
_sendAsFileTooltipManager->updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
bool ComposeControls::updateLikeShown() {
|
||||
auto shown = _like && !HasSendText(_field);
|
||||
if (_likeShown != shown) {
|
||||
@@ -3646,6 +3783,12 @@ bool ComposeControls::hasEnoughLinesForAi() const {
|
||||
&& Ui::HasEnoughLinesForAi(&session(), _field);
|
||||
}
|
||||
|
||||
bool ComposeControls::textExceedsMaxSize() const {
|
||||
return _history
|
||||
&& !_recording.current()
|
||||
&& _field->getLastText().size() > MaxMessageSize;
|
||||
}
|
||||
|
||||
bool ComposeControls::updateBotCommandShown() {
|
||||
auto shown = false;
|
||||
const auto peer = _history ? _history->peer.get() : nullptr;
|
||||
|
||||
@@ -68,6 +68,7 @@ class EmojiButton;
|
||||
class SendAsButton;
|
||||
class SilentToggle;
|
||||
class DropdownMenu;
|
||||
struct PreparedBundle;
|
||||
struct PreparedList;
|
||||
struct SendStarButtonState;
|
||||
class ReactionFlyAnimation;
|
||||
@@ -102,7 +103,8 @@ class TTLButton;
|
||||
class WebpageProcessor;
|
||||
class CharactersLimitLabel;
|
||||
class ComposeAiButton;
|
||||
class AiTooltipManager;
|
||||
class ComposeTooltipManager;
|
||||
using AiTooltipManager = ComposeTooltipManager;
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
namespace HistoryView {
|
||||
@@ -196,6 +198,10 @@ public:
|
||||
[[nodiscard]] rpl::producer<QString> sendCommandRequests() const;
|
||||
[[nodiscard]] rpl::producer<MessageToEdit> editRequests() const;
|
||||
[[nodiscard]] rpl::producer<std::optional<bool>> attachRequests() const;
|
||||
void setSendAsFileConfirmed(
|
||||
Fn<void(
|
||||
std::shared_ptr<Ui::PreparedBundle>,
|
||||
Api::SendOptions)> confirmed);
|
||||
[[nodiscard]] rpl::producer<FileChosen> fileChosen() const;
|
||||
[[nodiscard]] rpl::producer<PhotoChosen> photoChosen() const;
|
||||
[[nodiscard]] rpl::producer<FullReplyTo> jumpToItemRequests() const;
|
||||
@@ -328,6 +334,15 @@ private:
|
||||
void updateControlsGeometry(QSize size);
|
||||
void updateAiButtonVisibility();
|
||||
void updateAiButtonGeometry();
|
||||
void initSendAsFileButton();
|
||||
void fireSendTextAsFile(
|
||||
const QString &fileText,
|
||||
Fn<void()> restoreText);
|
||||
[[nodiscard]] bool checkLargeTextPaste(
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action);
|
||||
void updateSendAsFileVisibility();
|
||||
void updateSendAsFileGeometry();
|
||||
void setupSendMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<void(Api::SendOptions)> send);
|
||||
@@ -363,6 +378,7 @@ private:
|
||||
bool updateBotCommandShown();
|
||||
bool updateLikeShown();
|
||||
[[nodiscard]] bool hasEnoughLinesForAi() const;
|
||||
[[nodiscard]] bool textExceedsMaxSize() const;
|
||||
|
||||
void cancelInlineBot();
|
||||
void clearInlineBot();
|
||||
@@ -441,6 +457,7 @@ private:
|
||||
|
||||
const std::shared_ptr<Ui::SendButton> _send;
|
||||
Controls::ComposeAiButton * const _aiButton = nullptr;
|
||||
Ui::IconButton * const _sendAsFile = nullptr;
|
||||
Ui::IconButton *_editStars = nullptr;
|
||||
Ui::IconButton *_like = nullptr;
|
||||
rpl::variable<int> _minStarsCount;
|
||||
@@ -476,6 +493,7 @@ private:
|
||||
const std::unique_ptr<FieldHeader> _header;
|
||||
const std::unique_ptr<Controls::VoiceRecordBar> _voiceRecordBar;
|
||||
std::unique_ptr<Controls::AiTooltipManager> _aiTooltipManager;
|
||||
std::unique_ptr<Controls::AiTooltipManager> _sendAsFileTooltipManager;
|
||||
std::shared_ptr<Ui::ChatStyle> _chatStyle;
|
||||
|
||||
const Fn<SendMenu::Details()> _sendMenuDetails;
|
||||
@@ -491,6 +509,7 @@ private:
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents;
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _editLastMessageRequests;
|
||||
rpl::event_stream<std::optional<bool>> _attachRequests;
|
||||
Fn<void(std::shared_ptr<Ui::PreparedBundle>, Api::SendOptions)> _sendAsFileConfirmed;
|
||||
rpl::event_stream<> _likeToggled;
|
||||
rpl::event_stream<ReplyNextRequest> _replyNextRequests;
|
||||
rpl::event_stream<> _focusRequests;
|
||||
|
||||
@@ -916,6 +916,12 @@ void ChatWidget::setupComposeControls() {
|
||||
[=] { chooseAttach(overrideCompress); });
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->setSendAsFileConfirmed(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
|
||||
_composeControls->fileChosen(
|
||||
) | rpl::on_next([=](ChatHelpers::FileChosen data) {
|
||||
controller()->hideLayer(anim::type::normal);
|
||||
|
||||
@@ -403,6 +403,12 @@ void ScheduledWidget::setupComposeControls() {
|
||||
[=] { _choosingAttach = false; chooseAttach(); });
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->setSendAsFileConfirmed(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
|
||||
_composeControls->fileChosen(
|
||||
) | rpl::on_next([=](ChatHelpers::FileChosen data) {
|
||||
controller()->hideLayer(anim::type::normal);
|
||||
|
||||
@@ -766,6 +766,12 @@ void ReplyArea::initActions() {
|
||||
[=] { chooseAttach(overrideCompress); });
|
||||
}, _lifetime);
|
||||
|
||||
_controls->setSendAsFileConfirmed(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
|
||||
_controls->fileChosen(
|
||||
) | rpl::on_next([=](ChatHelpers::FileChosen data) {
|
||||
_controller->uiShow()->hideLayer();
|
||||
|
||||
@@ -691,6 +691,12 @@ void ShortcutMessages::setupComposeControls() {
|
||||
});
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->setSendAsFileConfirmed(crl::guard(this, [=](
|
||||
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||
Api::SendOptions options) {
|
||||
sendingFilesConfirmed(std::move(bundle), options);
|
||||
}));
|
||||
|
||||
_composeControls->fileChosen(
|
||||
) | rpl::on_next([=](ChatHelpers::FileChosen data) {
|
||||
_controller->hideLayer(anim::type::normal);
|
||||
|
||||
@@ -64,8 +64,13 @@ void SingleFilePreview::preparePreview(const PreparedFile &file) {
|
||||
? fallbackName
|
||||
: file.displayName;
|
||||
data.name = displayName;
|
||||
data.statusText = FormatImageSizeText(file.originalDimensions);
|
||||
data.fileIsImage = true;
|
||||
if (file.originalDimensions.isValid()) {
|
||||
data.statusText = FormatImageSizeText(file.originalDimensions);
|
||||
data.fileIsImage = true;
|
||||
} else {
|
||||
data.statusText = FormatSizeText(file.size);
|
||||
data.fileIsImage = false;
|
||||
}
|
||||
} else {
|
||||
auto fileinfo = QFileInfo(filepath);
|
||||
auto filename = file.displayName.isEmpty()
|
||||
|
||||
@@ -9,10 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/options.h"
|
||||
#include "boxes/compose_ai_box.h"
|
||||
#include "config.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "history/view/controls/history_view_compose_ai_button.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/text/text.h"
|
||||
#include "ui/text/text_entity.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
|
||||
@@ -43,7 +47,63 @@ bool HasEnoughLinesForAi(
|
||||
const auto contentHeight = field->height()
|
||||
- margins.top()
|
||||
- margins.bottom();
|
||||
return contentHeight >= (3 * lineHeight);
|
||||
if (contentHeight < (3 * lineHeight)) {
|
||||
return false;
|
||||
}
|
||||
const auto &text = field->getLastText();
|
||||
if (text.size() > MaxMessageSize) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &ch : text) {
|
||||
if (!Text::IsTrimmed(ch) && !Text::IsReplacedBySpace(ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PreparedList PrepareTextAsFile(const QString &text) {
|
||||
auto content = text.toUtf8();
|
||||
auto result = PreparedList();
|
||||
auto file = PreparedFile(QString());
|
||||
file.content = content;
|
||||
file.displayName = u"message.txt"_q;
|
||||
file.size = content.size();
|
||||
file.information = std::make_unique<PreparedFileInformation>();
|
||||
file.information->filemime = u"text/plain"_q;
|
||||
result.files.push_back(std::move(file));
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr auto kSendAsFilePasteMultiplier = 8;
|
||||
|
||||
int SendAsFilePasteThreshold() {
|
||||
return kSendAsFilePasteMultiplier * MaxMessageSize;
|
||||
}
|
||||
|
||||
LargeTextPasteResult CheckLargeTextPaste(
|
||||
not_null<Ui::InputField*> field,
|
||||
not_null<const QMimeData*> data) {
|
||||
const auto pasteText = Core::ReadMimeText(data);
|
||||
if (pasteText.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
const auto cursor = field->textCursor();
|
||||
const auto currentText = field->getLastText();
|
||||
const auto selStart = cursor.selectionStart();
|
||||
const auto selEnd = cursor.selectionEnd();
|
||||
const auto resultingSize = currentText.size()
|
||||
- (selEnd - selStart)
|
||||
+ pasteText.size();
|
||||
if (resultingSize < SendAsFilePasteThreshold()) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
.exceeds = true,
|
||||
.resultingText = currentText.mid(0, selStart)
|
||||
+ pasteText
|
||||
+ currentText.mid(selEnd),
|
||||
};
|
||||
}
|
||||
|
||||
void UpdateCaptionAiButtonGeometry(
|
||||
|
||||
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class QMimeData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -23,6 +25,8 @@ class ComposeAiButton;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
struct PreparedList;
|
||||
|
||||
extern const char kOptionHideAiButton[];
|
||||
|
||||
[[nodiscard]] bool HasEnoughLinesForAi(
|
||||
@@ -44,4 +48,16 @@ void UpdateCaptionAiButtonGeometry(
|
||||
not_null<HistoryView::Controls::ComposeAiButton*> button,
|
||||
not_null<Ui::InputField*> field);
|
||||
|
||||
[[nodiscard]] PreparedList PrepareTextAsFile(const QString &text);
|
||||
[[nodiscard]] int SendAsFilePasteThreshold();
|
||||
|
||||
struct LargeTextPasteResult {
|
||||
bool exceeds = false;
|
||||
QString resultingText;
|
||||
};
|
||||
|
||||
[[nodiscard]] LargeTextPasteResult CheckLargeTextPaste(
|
||||
not_null<Ui::InputField*> field,
|
||||
not_null<const QMimeData*> data);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
Reference in New Issue
Block a user