mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-06 03:42:56 +00:00
Port the Edit-Style field into Create with AI
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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<Ui::GenericBox*> box, CreateAiBoxArgs &&args) {
|
||||
});
|
||||
box->setStyle(st::aiComposeBox);
|
||||
|
||||
const auto prompt = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::InputField>(
|
||||
const auto promptWrap = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::PaddingWrap<Ui::InputField>>(
|
||||
box,
|
||||
st::createAiPromptField,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_ai_compose_create_placeholder()));
|
||||
object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::aiTonePromptField,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
rpl::producer<QString>()),
|
||||
st::aiToneFieldsMargin));
|
||||
const auto prompt = promptWrap->entity();
|
||||
prompt->setSubmitSettings(Ui::InputField::SubmitSettings::None);
|
||||
prompt->setMaxLength(state->session->appConfig().get<int>(
|
||||
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<Ui::GenericBox*> chooser) {
|
||||
Ui::ChooseLanguageBox(
|
||||
|
||||
@@ -163,6 +163,79 @@ void ChooseToneIconBox(
|
||||
|
||||
} // namespace
|
||||
|
||||
not_null<Ui::FlatLabel*> AddAiComposeFieldDecor(
|
||||
not_null<Ui::InputField*> field,
|
||||
rpl::producer<QString> placeholder) {
|
||||
struct FieldDecor {
|
||||
not_null<Ui::RpWidget*> bg;
|
||||
not_null<Ui::FlatLabel*> placeholder;
|
||||
Ui::Animations::Simple anim;
|
||||
bool hidden = false;
|
||||
};
|
||||
const auto parent = field->parentWidget();
|
||||
const auto decor = field->lifetime().make_state<FieldDecor>(FieldDecor{
|
||||
.bg = Ui::CreateChild<Ui::RpWidget>(parent),
|
||||
.placeholder = Ui::CreateChild<Ui::FlatLabel>(
|
||||
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<Ui::AbstractButton*> AddAiToneIconPreview(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
@@ -391,80 +464,8 @@ void SetupToneBox(
|
||||
u"aicompose_tone_prompt_length_max"_q,
|
||||
1024));
|
||||
|
||||
struct FieldDecor {
|
||||
not_null<Ui::RpWidget*> bg;
|
||||
not_null<Ui::FlatLabel*> placeholder;
|
||||
Ui::Animations::Simple anim;
|
||||
bool hidden = false;
|
||||
};
|
||||
const auto makeDecor = [=](
|
||||
not_null<Ui::InputField*> field,
|
||||
rpl::producer<QString> placeholderText) {
|
||||
const auto parent = field->parentWidget();
|
||||
const auto decor = field->lifetime().make_state<FieldDecor>(FieldDecor{
|
||||
.bg = Ui::CreateChild<Ui::RpWidget>(parent),
|
||||
.placeholder = Ui::CreateChild<Ui::FlatLabel>(
|
||||
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;
|
||||
|
||||
@@ -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<Ui::FlatLabel*> AddAiComposeFieldDecor(
|
||||
not_null<Ui::InputField*> field,
|
||||
rpl::producer<QString> placeholder);
|
||||
|
||||
not_null<Ui::AbstractButton*> AddAiToneIconPreview(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
|
||||
Reference in New Issue
Block a user