Add slide animations in CreatePollBox.

This commit is contained in:
John Preston 2018-12-25 11:41:22 +04:00
parent 6f176803d4
commit f291e365e5
2 changed files with 80 additions and 23 deletions

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h" #include "data/data_poll.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/wrap/vertical_layout.h" #include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
@ -56,6 +57,9 @@ private:
void toggleRemoveAlways(bool toggled); void toggleRemoveAlways(bool toggled);
void show(anim::type animated);
void destroy(FnMut<void()> done);
//[[nodisacrd]] bool hasShadow() const; //[[nodisacrd]] bool hasShadow() const;
//void destroyShadow(); //void destroyShadow();
@ -75,6 +79,21 @@ private:
[[nodiscard]] rpl::producer<Qt::MouseButton> removeClicks() const; [[nodiscard]] rpl::producer<Qt::MouseButton> removeClicks() const;
inline bool operator<(const Option &other) const {
return field() < other.field();
}
friend inline bool operator<(
const Option &option,
Ui::InputField *field) {
return option.field() < field;
}
friend inline bool operator<(
Ui::InputField *field,
const Option &option) {
return field < option.field();
}
private: private:
Option() = default; Option() = default;
@ -82,7 +101,7 @@ private:
void createRemove(); void createRemove();
void createWarning(); void createWarning();
base::unique_qptr<Ui::InputField> _field; base::unique_qptr<Ui::SlideWrap<Ui::InputField>> _field;
base::unique_qptr<Ui::PlainShadow> _shadow; base::unique_qptr<Ui::PlainShadow> _shadow;
base::unique_qptr<Ui::CrossButton> _remove; base::unique_qptr<Ui::CrossButton> _remove;
rpl::variable<bool> *_removeAlways = nullptr; rpl::variable<bool> *_removeAlways = nullptr;
@ -97,12 +116,15 @@ private:
void checkLastOption(); void checkLastOption();
void validateState(); void validateState();
void fixAfterErase(); void fixAfterErase();
void destroy(Option &&option);
void removeDestroyed(not_null<Ui::InputField*> field);
int findField(not_null<Ui::InputField*> field) const; int findField(not_null<Ui::InputField*> field) const;
not_null<QWidget*> _outer; not_null<QWidget*> _outer;
not_null<Ui::VerticalLayout*> _container; not_null<Ui::VerticalLayout*> _container;
int _position = 0; int _position = 0;
std::vector<Option> _list; std::vector<Option> _list;
std::set<Option, std::less<>> _destroyed;
rpl::variable<bool> _valid = false; rpl::variable<bool> _valid = false;
rpl::variable<int> _usedCount = 0; rpl::variable<int> _usedCount = 0;
rpl::event_stream<not_null<QWidget*>> _scrollToWidget; rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
@ -157,12 +179,14 @@ Options::Option Options::Option::Create(
auto result = Option(); auto result = Option();
const auto field = container->insert( const auto field = container->insert(
position, position,
object_ptr<Ui::InputField>( object_ptr<Ui::SlideWrap<Ui::InputField>>(
container, container,
st::createPollOptionField, object_ptr<Ui::InputField>(
langFactory(lng_polls_create_option_add))); container,
InitField(outer, field); st::createPollOptionField,
field->setMaxLength(kOptionLimit + kErrorLimit); langFactory(lng_polls_create_option_add))));
InitField(outer, field->entity());
field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
result._field.reset(field); result._field.reset(field);
result.createShadow(); result.createShadow();
@ -181,9 +205,9 @@ void Options::Option::createShadow() {
if (_shadow) { if (_shadow) {
return; return;
} }
const auto value = Ui::CreateChild<Ui::PlainShadow>(_field.get()); const auto value = Ui::CreateChild<Ui::PlainShadow>(field().get());
value->show(); value->show();
_field->sizeValue( field()->sizeValue(
) | rpl::start_with_next([=](QSize size) { ) | rpl::start_with_next([=](QSize size) {
const auto left = st::createPollFieldPadding.left(); const auto left = st::createPollFieldPadding.left();
value->setGeometry( value->setGeometry(
@ -202,11 +226,11 @@ void Options::Option::createShadow() {
void Options::Option::createRemove() { void Options::Option::createRemove() {
using namespace rpl::mappers; using namespace rpl::mappers;
const auto field = _field.get(); const auto field = this->field();
auto &lifetime = field->lifetime(); auto &lifetime = field->lifetime();
const auto remove = Ui::CreateChild<Ui::CrossButton>( const auto remove = Ui::CreateChild<Ui::CrossButton>(
field, field.get(),
st::createPollOptionRemove); st::createPollOptionRemove);
remove->hide(anim::type::instant); remove->hide(anim::type::instant);
@ -239,7 +263,7 @@ void Options::Option::createRemove() {
void Options::Option::createWarning() { void Options::Option::createWarning() {
using namespace rpl::mappers; using namespace rpl::mappers;
const auto field = _field.get(); const auto field = this->field();
const auto warning = CreateWarningLabel( const auto warning = CreateWarningLabel(
field, field,
field, field,
@ -261,31 +285,31 @@ void Options::Option::createWarning() {
} }
bool Options::Option::isEmpty() const { bool Options::Option::isEmpty() const {
return _field->getLastText().trimmed().isEmpty(); return field()->getLastText().trimmed().isEmpty();
} }
bool Options::Option::isGood() const { bool Options::Option::isGood() const {
return !_field->getLastText().trimmed().isEmpty() && !isTooLong(); return !field()->getLastText().trimmed().isEmpty() && !isTooLong();
} }
bool Options::Option::isTooLong() const { bool Options::Option::isTooLong() const {
return (_field->getLastText().size() > kOptionLimit); return (field()->getLastText().size() > kOptionLimit);
} }
bool Options::Option::hasFocus() const { bool Options::Option::hasFocus() const {
return _field->hasFocus(); return field()->hasFocus();
} }
void Options::Option::setFocus() const { void Options::Option::setFocus() const {
FocusAtEnd(_field); FocusAtEnd(field());
} }
void Options::Option::clearValue() { void Options::Option::clearValue() {
_field->setText(QString()); field()->setText(QString());
} }
void Options::Option::setPlaceholder() const { void Options::Option::setPlaceholder() const {
_field->setPlaceholder(langFactory(lng_polls_create_option_add)); field()->setPlaceholder(langFactory(lng_polls_create_option_add));
} }
void Options::Option::toggleRemoveAlways(bool toggled) { void Options::Option::toggleRemoveAlways(bool toggled) {
@ -293,16 +317,16 @@ void Options::Option::toggleRemoveAlways(bool toggled) {
} }
not_null<Ui::InputField*> Options::Option::field() const { not_null<Ui::InputField*> Options::Option::field() const {
return _field.get(); return _field->entity();
} }
void Options::Option::removePlaceholder() const { void Options::Option::removePlaceholder() const {
_field->setPlaceholder(nullptr); field()->setPlaceholder(nullptr);
} }
PollAnswer Options::Option::toPollAnswer(char id) const { PollAnswer Options::Option::toPollAnswer(char id) const {
return PollAnswer{ return PollAnswer{
_field->getLastText().trimmed(), field()->getLastText().trimmed(),
QByteArray(1, id) QByteArray(1, id)
}; };
} }
@ -344,6 +368,23 @@ rpl::producer<> Options::backspaceInFront() const {
return _backspaceInFront.events(); return _backspaceInFront.events();
} }
void Options::Option::show(anim::type animated) {
_field->hide(anim::type::instant);
_field->show(animated);
}
void Options::Option::destroy(FnMut<void()> done) {
if (anim::Disabled() || _field->isHidden()) {
Ui::PostponeCall(std::move(done));
return;
}
_field->hide(anim::type::normal);
App::CallDelayed(
st::slideWrapDuration * 2,
_field.get(),
std::move(done));
}
std::vector<PollAnswer> Options::toPollAnswers() const { std::vector<PollAnswer> Options::toPollAnswers() const {
auto result = std::vector<PollAnswer>(); auto result = std::vector<PollAnswer>();
result.reserve(_list.size()); result.reserve(_list.size());
@ -405,10 +446,19 @@ void Options::removeEmptyTail() {
if (focusLast) { if (focusLast) {
emptyItem->setFocus(); emptyItem->setFocus();
} }
for (auto i = emptyItem + 1; i != end; ++i) {
destroy(std::move(*i));
}
_list.erase(emptyItem + 1, end); _list.erase(emptyItem + 1, end);
fixAfterErase(); fixAfterErase();
} }
void Options::destroy(Option &&option) {
const auto field = option.field();
option.destroy([=] { removeDestroyed(field); });
_destroyed.emplace(std::move(option));
}
void Options::fixAfterErase() { void Options::fixAfterErase() {
Expects(!_list.empty()); Expects(!_list.empty());
@ -434,7 +484,7 @@ void Options::addEmptyOption() {
_list.push_back(Option::Create( _list.push_back(Option::Create(
_outer, _outer,
_container, _container,
_position + _list.size())); _position + _list.size() + _destroyed.size()));
const auto field = _list.back().field(); const auto field = _list.back().field();
QObject::connect(field, &Ui::InputField::submitted, [=] { QObject::connect(field, &Ui::InputField::submitted, [=] {
const auto index = findField(field); const auto index = findField(field);
@ -482,15 +532,23 @@ void Options::addEmptyOption() {
if (item->hasFocus()) { if (item->hasFocus()) {
(item + 1)->setFocus(); (item + 1)->setFocus();
} }
destroy(std::move(*item));
_list.erase(item); _list.erase(item);
fixAfterErase(); fixAfterErase();
validateState(); validateState();
})); }));
}, field->lifetime()); }, field->lifetime());
_list.back().show((_list.size() == 1)
? anim::type::instant
: anim::type::normal);
//fixShadows(); //fixShadows();
} }
void Options::removeDestroyed(not_null<Ui::InputField*> field) {
_destroyed.erase(_destroyed.find(field));
}
void Options::validateState() { void Options::validateState() {
checkLastOption(); checkLastOption();
_valid = (ranges::count_if(_list, &Option::isGood) > 1) _valid = (ranges::count_if(_list, &Option::isGood) > 1)

View File

@ -111,7 +111,6 @@ private:
void resetAnswersAnimation() const; void resetAnswersAnimation() const;
void step_radial(TimeMs ms, bool timer); void step_radial(TimeMs ms, bool timer);
void checkPollResultsReload(TimeMs ms) const;
void toggleRipple(Answer &answer, bool pressed); void toggleRipple(Answer &answer, bool pressed);
not_null<PollData*> _poll; not_null<PollData*> _poll;