diff --git a/ui/widgets/fields/input_field.cpp b/ui/widgets/fields/input_field.cpp index e674c2d..edc9d51 100644 --- a/ui/widgets/fields/input_field.cpp +++ b/ui/widgets/fields/input_field.cpp @@ -6,8 +6,9 @@ // #include "ui/widgets/fields/input_field.h" -#include "ui/widgets/popup_menu.h" #include "ui/text/text.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/popup_menu.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" #include "ui/painter.h" @@ -134,7 +135,7 @@ bool IsNewline(QChar ch) { } [[nodiscard]] bool IsCustomEmojiLink(QStringView link) { - return link.startsWith(Ui::InputField::kCustomEmojiTagStart); + return link.startsWith(InputField::kCustomEmojiTagStart); } [[nodiscard]] QString MakeUniqueCustomEmojiLink(QStringView link) { @@ -153,7 +154,7 @@ bool IsNewline(QChar ch) { } [[nodiscard]] uint64 CustomEmojiIdFromLink(QStringView link) { - const auto skip = Ui::InputField::kCustomEmojiTagStart.size(); + const auto skip = InputField::kCustomEmojiTagStart.size(); const auto index = link.indexOf('?', skip + 1); return base::StringViewMid( link, @@ -1131,7 +1132,7 @@ QSizeF CustomEmojiObject::intrinsicSize( const auto width = size + st::emojiPadding * 2.; const auto height = std::max(_font->height * 1., size); if (!_skip) { - const auto emoji = Ui::Text::AdjustCustomEmojiSize(st::emojiSize); + const auto emoji = Text::AdjustCustomEmojiSize(st::emojiSize); _skip = (st::emojiSize - emoji) / 2; } return { width, height }; @@ -3190,7 +3191,7 @@ void InputField::inputMethodEventInner(QInputMethodEvent *e) { } _inputMethodCommit = e->commitString(); - const auto weak = Ui::MakeWeak(this); + const auto weak = MakeWeak(this); _inner->QTextEdit::inputMethodEvent(e); if (weak && _inputMethodCommit.has_value()) { @@ -4066,4 +4067,52 @@ int FieldCharacterCount(not_null field) { return field->lastTextSizeWithoutSurrogatePairsCount(); } +void AddLengthLimitLabel(not_null field, int limit) { + struct State { + rpl::variable length; + }; + const auto state = field->lifetime().make_state(); + state->length = rpl::single( + rpl::empty + ) | rpl::then(field->changes()) | rpl::map([=] { + return int(field->getLastText().size()); + }); + const auto allowExceed = std::max(limit / 2, 9); + field->setMaxLength(limit + allowExceed); + const auto threshold = std::min(limit / 2, 9); + auto warningText = state->length.value() | rpl::map([=](int count) { + const auto left = limit - count; + return (left < threshold) ? QString::number(left) : QString(); + }); + const auto warning = CreateChild( + field.get(), + std::move(warningText), + st::defaultInputFieldLimit); + + const auto maxSize = st::defaultInputFieldLimit.style.font->width( + QString::number(-allowExceed)); + const auto add = std::max(maxSize - field->st().textMargins.right(), 0); + if (add) { + field->setAdditionalMargins({ 0, 0, add, 0 }); + } + state->length.value() | rpl::map( + rpl::mappers::_1 > limit + ) | rpl::start_with_next([=](bool exceeded) { + warning->setTextColorOverride(exceeded + ? st::attentionButtonFg->c + : std::optional()); + }, warning->lifetime()); + rpl::combine( + field->sizeValue(), + warning->sizeValue() + ) | rpl::start_with_next([=] { + // Baseline alignment. + const auto top = field->st().textMargins.top() + + field->st().font->ascent + - st::defaultInputFieldLimit.style.font->ascent; + warning->moveToRight(0, top); + }, warning->lifetime()); + warning->setAttribute(Qt::WA_TransparentForMouseEvents); +} + } // namespace Ui diff --git a/ui/widgets/fields/input_field.h b/ui/widgets/fields/input_field.h index 0147b97..734477a 100644 --- a/ui/widgets/fields/input_field.h +++ b/ui/widgets/fields/input_field.h @@ -586,4 +586,6 @@ void PrepareFormattingOptimization(not_null document); [[nodiscard]] int FieldCharacterCount(not_null field); +void AddLengthLimitLabel(not_null field, int limit); + } // namespace Ui diff --git a/ui/widgets/widgets.style b/ui/widgets/widgets.style index 0b5ef52..48b6d04 100644 --- a/ui/widgets/widgets.style +++ b/ui/widgets/widgets.style @@ -910,6 +910,9 @@ defaultInputField: InputField { heightMin: 55px; heightMax: 148px; } +defaultInputFieldLimit: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} defaultIconButton: IconButton { iconPosition: point(-1px, -1px);