diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index ede99257c4..0a213446c1 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -1590,14 +1590,6 @@ aiComposeBoxInfoButton: IconButton(boxTitleClose) { } aiComposeShadowOpacity: 0.3; -createAiPromptField: InputField(aiTonePromptField) { - textBg: aiComposeCardBg; - textBgActive: aiComposeCardBg; - borderRadius: 22px; - textMargins: margins(16px, 16px, 16px, 16px); - heightMin: 54px; - heightMax: 240px; -} createAiSelectorArrowsIcon: icon{{ "arrows_select", windowSubTextFg }}; createAiReloadIcon: icon{{ "chat/refresh-18x18", windowFgActive }}; createAiReloadButton: RoundButton(defaultActiveButton) { diff --git a/Telegram/SourceFiles/boxes/create_ai_box.cpp b/Telegram/SourceFiles/boxes/create_ai_box.cpp index 88fe1b2d1f..8acf6d0f71 100644 --- a/Telegram/SourceFiles/boxes/create_ai_box.cpp +++ b/Telegram/SourceFiles/boxes/create_ai_box.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/object_ptr.h" #include "base/weak_ptr.h" +#include "boxes/create_ai_tone_box.h" #include "chat_helpers/compose/compose_show.h" #include "data/data_file_origin.h" #include "data/data_msg_id.h" @@ -33,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/labels.h" +#include "ui/wrap/padding_wrap.h" #include "window/themes/window_theme.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" @@ -350,18 +352,31 @@ void CreateAiBox(not_null box, CreateAiBoxArgs &&args) { }); box->setStyle(st::aiComposeBox); - const auto prompt = box->setPinnedToTopContent( - object_ptr( + const auto promptWrap = box->setPinnedToTopContent( + object_ptr>( box, - st::createAiPromptField, - Ui::InputField::Mode::MultiLine, - tr::lng_ai_compose_create_placeholder())); + object_ptr( + box, + st::aiTonePromptField, + Ui::InputField::Mode::MultiLine, + rpl::producer()), + st::aiToneFieldsMargin)); + const auto prompt = promptWrap->entity(); prompt->setSubmitSettings(Ui::InputField::SubmitSettings::None); prompt->setMaxLength(state->session->appConfig().get( u"aicompose_tone_prompt_length_max"_q, 1024)); state->prompt = prompt; + const auto promptPlaceholder = AddAiComposeFieldDecor( + prompt, + tr::lng_ai_compose_create_placeholder()); + promptPlaceholder->heightValue( + ) | rpl::on_next([=](int phHeight) { + const auto pad = st::aiToneFieldPadding; + prompt->setMinHeight(phHeight + pad.top() + pad.bottom()); + }, prompt->lifetime()); + const auto chooseLanguage = [=] { box->uiShow()->showBox(Box([=](not_null chooser) { Ui::ChooseLanguageBox( diff --git a/Telegram/SourceFiles/boxes/create_ai_tone_box.cpp b/Telegram/SourceFiles/boxes/create_ai_tone_box.cpp index c7ae9415e3..54bdcfbf6e 100644 --- a/Telegram/SourceFiles/boxes/create_ai_tone_box.cpp +++ b/Telegram/SourceFiles/boxes/create_ai_tone_box.cpp @@ -163,6 +163,79 @@ void ChooseToneIconBox( } // namespace +not_null AddAiComposeFieldDecor( + not_null field, + rpl::producer placeholder) { + struct FieldDecor { + not_null bg; + not_null placeholder; + Ui::Animations::Simple anim; + bool hidden = false; + }; + const auto parent = field->parentWidget(); + const auto decor = field->lifetime().make_state(FieldDecor{ + .bg = Ui::CreateChild(parent), + .placeholder = Ui::CreateChild( + parent, + std::move(placeholder), + st::aiTonePlaceholderLabel), + }); + decor->bg->setAttribute(Qt::WA_TransparentForMouseEvents); + decor->placeholder->setAttribute(Qt::WA_TransparentForMouseEvents); + decor->bg->paintRequest( + ) | rpl::on_next([bg = decor->bg] { + auto p = QPainter(bg); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::aiToneFieldBg); + const auto r = st::aiToneFieldRadius; + p.drawRoundedRect(bg->rect(), r, r); + }, decor->bg->lifetime()); + decor->bg->lower(); + decor->placeholder->raise(); + + const auto applyPosition = [=] { + const auto pad = st::aiToneFieldPadding; + const auto progress = decor->anim.value(decor->hidden ? 1. : 0.); + const auto shift = int(base::SafeRound( + progress * (-st::defaultInputField.placeholderShift))); + decor->placeholder->moveToLeft( + field->x() + pad.left() + shift, + field->y() + pad.top()); + decor->placeholder->setOpacity(1. - progress); + }; + field->geometryValue( + ) | rpl::on_next([=](QRect g) { + if (g.isEmpty()) { + return; + } + const auto pad = st::aiToneFieldPadding; + decor->bg->setGeometry(g); + decor->placeholder->resizeToWidth( + g.width() - pad.left() - pad.right()); + applyPosition(); + }, field->lifetime()); + + const auto animate = [=](bool hidden) { + if (decor->hidden == hidden) { + return; + } + decor->hidden = hidden; + decor->anim.start( + applyPosition, + hidden ? 0. : 1., + hidden ? 1. : 0., + st::defaultInputField.duration); + }; + field->changes( + ) | rpl::on_next([=] { + animate(!field->getLastText().isEmpty()); + }, field->lifetime()); + decor->hidden = !field->getLastText().isEmpty(); + applyPosition(); + return decor->placeholder; +} + not_null AddAiToneIconPreview( not_null container, not_null session, @@ -391,80 +464,8 @@ void SetupToneBox( u"aicompose_tone_prompt_length_max"_q, 1024)); - struct FieldDecor { - not_null bg; - not_null placeholder; - Ui::Animations::Simple anim; - bool hidden = false; - }; - const auto makeDecor = [=]( - not_null field, - rpl::producer placeholderText) { - const auto parent = field->parentWidget(); - const auto decor = field->lifetime().make_state(FieldDecor{ - .bg = Ui::CreateChild(parent), - .placeholder = Ui::CreateChild( - parent, - std::move(placeholderText), - st::aiTonePlaceholderLabel), - }); - decor->bg->setAttribute(Qt::WA_TransparentForMouseEvents); - decor->placeholder->setAttribute(Qt::WA_TransparentForMouseEvents); - decor->bg->paintRequest( - ) | rpl::on_next([bg = decor->bg] { - auto p = QPainter(bg); - auto hq = PainterHighQualityEnabler(p); - p.setPen(Qt::NoPen); - p.setBrush(st::aiToneFieldBg); - const auto r = st::aiToneFieldRadius; - p.drawRoundedRect(bg->rect(), r, r); - }, decor->bg->lifetime()); - decor->bg->lower(); - decor->placeholder->raise(); - - const auto applyPosition = [=] { - const auto pad = st::aiToneFieldPadding; - const auto progress = decor->anim.value(decor->hidden ? 1. : 0.); - const auto shift = int(base::SafeRound( - progress * (-st::defaultInputField.placeholderShift))); - decor->placeholder->moveToLeft( - field->x() + pad.left() + shift, - field->y() + pad.top()); - decor->placeholder->setOpacity(1. - progress); - }; - field->geometryValue( - ) | rpl::on_next([=](QRect g) { - if (g.isEmpty()) { - return; - } - const auto pad = st::aiToneFieldPadding; - decor->bg->setGeometry(g); - decor->placeholder->resizeToWidth( - g.width() - pad.left() - pad.right()); - applyPosition(); - }, field->lifetime()); - - const auto animate = [=](bool hidden) { - if (decor->hidden == hidden) { - return; - } - decor->hidden = hidden; - decor->anim.start( - applyPosition, - hidden ? 0. : 1., - hidden ? 1. : 0., - st::defaultInputField.duration); - }; - field->changes( - ) | rpl::on_next([=] { - animate(!field->getLastText().isEmpty()); - }, field->lifetime()); - decor->hidden = !field->getLastText().isEmpty(); - applyPosition(); - return decor; - }; - makeDecor(name, tr::lng_ai_compose_tone_name_placeholder()); - const auto promptDecor = makeDecor( + AddAiComposeFieldDecor(name, tr::lng_ai_compose_tone_name_placeholder()); + const auto promptPlaceholder = AddAiComposeFieldDecor( prompt, tr::lng_ai_compose_tone_prompt_placeholder()); @@ -500,7 +501,7 @@ void SetupToneBox( rpl::combine( prompt->topValue(), - promptDecor->placeholder->heightValue(), + promptPlaceholder->heightValue(), box->getDelegate()->contentHeightMaxValue() ) | rpl::on_next([=](int top, int phHeight, int contentHeight) { const auto pad = st::aiToneFieldPadding; diff --git a/Telegram/SourceFiles/boxes/create_ai_tone_box.h b/Telegram/SourceFiles/boxes/create_ai_tone_box.h index 4760eeaacb..0e75be55ac 100644 --- a/Telegram/SourceFiles/boxes/create_ai_tone_box.h +++ b/Telegram/SourceFiles/boxes/create_ai_tone_box.h @@ -17,11 +17,21 @@ class Session; namespace Ui { class AbstractButton; +class FlatLabel; class GenericBox; +class InputField; class Show; class VerticalLayout; } // namespace Ui +// Turns an Ui::InputField into a tone-style island: a rounded opaque +// background painted behind the field plus a gray placeholder label that +// fades and shifts on typing. Returns the placeholder label so callers can +// observe its height (e.g. for auto-grow min-height computations). +not_null AddAiComposeFieldDecor( + not_null field, + rpl::producer placeholder); + not_null AddAiToneIconPreview( not_null container, not_null session,