Move Info::TopBarOverride to Info::TopBar.

This allows to improve animations in shared media items selection.
This commit is contained in:
John Preston 2017-11-27 15:43:57 +04:00
parent 6afe18503d
commit d014b47958
27 changed files with 341 additions and 502 deletions

View file

@ -26,18 +26,18 @@ template <typename T>
class unique_qptr {
public:
unique_qptr() = default;
unique_qptr(std::nullptr_t) {
unique_qptr(std::nullptr_t) noexcept {
}
explicit unique_qptr(T *pointer)
explicit unique_qptr(T *pointer) noexcept
: _object(pointer) {
}
unique_qptr(const unique_qptr &other) = delete;
unique_qptr &operator=(const unique_qptr &other) = delete;
unique_qptr(unique_qptr &&other)
unique_qptr(unique_qptr &&other) noexcept
: _object(base::take(other._object)) {
}
unique_qptr &operator=(unique_qptr &&other) {
unique_qptr &operator=(unique_qptr &&other) noexcept {
if (_object != other._object) {
destroy();
_object = base::take(other._object);
@ -48,14 +48,14 @@ public:
template <
typename U,
typename = std::enable_if_t<std::is_base_of_v<T, U>>>
unique_qptr(unique_qptr<U> &&other)
unique_qptr(unique_qptr<U> &&other) noexcept
: _object(base::take(other._object)) {
}
template <
typename U,
typename = std::enable_if_t<std::is_base_of_v<T, U>>>
unique_qptr &operator=(unique_qptr<U> &&other) {
unique_qptr &operator=(unique_qptr<U> &&other) noexcept {
if (_object != other._object) {
destroy();
_object = base::take(other._object);
@ -63,49 +63,60 @@ public:
return *this;
}
unique_qptr &operator=(std::nullptr_t) {
unique_qptr &operator=(std::nullptr_t) noexcept {
destroy();
return *this;
}
void reset(T *value = nullptr) {
template <typename ...Args>
explicit unique_qptr(std::in_place_t, Args &&...args)
: _object(new T(std::forward<Args>(args)...)) {
}
template <typename ...Args>
T *emplace(Args &&...args) {
reset(new T(std::forward<Args>(args)...));
return get();
}
void reset(T *value = nullptr) noexcept {
if (_object != value) {
destroy();
_object = value;
}
}
T *get() const {
T *get() const noexcept {
return static_cast<T*>(_object.data());
}
operator T*() const {
operator T*() const noexcept {
return get();
}
T *release() {
T *release() noexcept {
return static_cast<T*>(base::take(_object).data());
}
explicit operator bool() const {
explicit operator bool() const noexcept {
return _object != nullptr;
}
T *operator->() const {
T *operator->() const noexcept {
return get();
}
T &operator*() const {
T &operator*() const noexcept {
return *get();
}
void destroy() {
delete base::take(_object).data();
}
~unique_qptr() {
~unique_qptr() noexcept {
destroy();
}
private:
void destroy() noexcept {
delete base::take(_object).data();
}
template <typename U>
friend class unique_qptr;
@ -115,7 +126,7 @@ private:
template <typename T, typename ...Args>
inline unique_qptr<T> make_unique_q(Args &&...args) {
return unique_qptr<T>(new T(std::forward<Args>(args)...));
return unique_qptr<T>(std::in_place, std::forward<Args>(args)...);
}
} // namespace base

View file

@ -1350,7 +1350,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
if (!_megagroupSelectedRemove) {
_megagroupSelectedRemove.create(this, st::groupStickersRemove);
_megagroupSelectedRemove->showFast();
_megagroupSelectedRemove->show(anim::type::instant);
_megagroupSelectedRemove->setClickedCallback([this] {
setMegagroupSelectedSet(MTP_inputStickerSetEmpty());
});

View file

@ -85,7 +85,9 @@ GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent) : InnerFooter(p
}
});
connect(_field, &Ui::InputField::changed, this, [this] {
_cancel->toggleAnimated(!_field->getLastText().isEmpty());
_cancel->toggle(
!_field->getLastText().isEmpty(),
anim::type::normal);
_pan->searchForGifs(_field->getLastText());
});
_cancel->setClickedCallback([this] {

View file

@ -267,7 +267,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window:
_mainMenuToggle->hide();
if (_forwardCancel) _forwardCancel->hide();
_filter->hide();
_cancelSearch->hide();
_cancelSearch->hide(anim::type::instant);
_jumpToDate->hide(anim::type::instant);
_chooseFromUser->hide(anim::type::instant);
_lockUnlock->hide();
@ -827,10 +827,8 @@ void DialogsWidget::onFilterUpdate(bool force) {
_inner->onFilterUpdate(filterText, force);
if (filterText.isEmpty()) {
clearSearchCache();
_cancelSearch->hideAnimated();
} else {
_cancelSearch->showAnimated();
}
_cancelSearch->toggle(!filterText.isEmpty(), anim::type::normal);
updateJumpToDateVisibility();
if (filterText.size() < MinUsernameLength) {

View file

@ -128,7 +128,7 @@ FixedBar::FixedBar(
connect(_field, &Ui::FlatInput::submitted, this, [this] { applySearch(); });
_searchTimer.setCallback([this] { applySearch(); });
_cancel->hideFast();
_cancel->hide(anim::type::instant);
}
void FixedBar::applyFilter(const FilterValue &value) {
@ -148,7 +148,7 @@ void FixedBar::showSearch() {
void FixedBar::toggleSearch() {
_searchShown = !_searchShown;
_cancel->toggleAnimated(_searchShown);
_cancel->toggle(_searchShown, anim::type::normal);
_searchShownAnimation.start([this] { searchAnimationCallback(); }, _searchShown ? 0. : 1., _searchShown ? 1. : 0., st::historyAdminLogSearchSlideDuration);
_search->setDisabled(_searchShown);
if (_searchShown) {
@ -227,7 +227,7 @@ void FixedBar::setAnimatingMode(bool enabled) {
setAttribute(Qt::WA_OpaquePaintEvent);
showChildren();
_field->hide();
_cancel->hide();
_cancel->setVisible(false);
}
show();
}

View file

@ -190,6 +190,8 @@ infoTopBar: InfoTopBar {
highlightBg: windowBgOver;
highlightDuration: 240;
}
infoTopBarScale: 0.7;
infoTopBarDuration: 150;
infoLayerTopMinimal: 20px;
infoLayerTopMaximal: 40px;

View file

@ -50,18 +50,50 @@ TopBar::TopBar(
, _selectedItems(Section::MediaType::kCount) {
setAttribute(Qt::WA_OpaquePaintEvent);
setSelectedItems(std::move(selectedItems));
finishSelectionAnimations();
updateControlsVisibility(anim::type::instant);
}
template <typename Callback>
void TopBar::registerUpdateControlCallback(
QObject *guard,
Callback &&callback) {
_updateControlCallbacks[guard] =[
object = weak(guard),
callback = std::forward<Callback>(callback)
](anim::type animated) {
if (!object) {
return false;
}
callback(animated);
return true;
};
}
template <typename Widget, typename IsVisible>
void TopBar::registerToggleControlCallback(
Widget *widget,
IsVisible &&callback) {
registerUpdateControlCallback(widget, [
widget,
isVisible = std::forward<IsVisible>(callback)
](anim::type animated) {
widget->toggle(isVisible(), animated);
});
}
void TopBar::setTitle(rpl::producer<QString> &&title) {
if (_title) {
delete _title;
}
_title = Ui::CreateChild<Ui::FadeWrapScaled<Ui::FlatLabel>>(
_title = Ui::CreateChild<Ui::FadeWrap<Ui::FlatLabel>>(
this,
object_ptr<Ui::FlatLabel>(this, std::move(title), _st.title));
object_ptr<Ui::FlatLabel>(this, std::move(title), _st.title),
st::infoTopBarScale);
_title->setDuration(st::infoTopBarDuration);
_title->toggle(!selectionMode(), anim::type::instant);
_defaultControls.push_back(_title.data());
registerToggleControlCallback(_title.data(), [=] {
return !selectionMode() && !searchMode();
});
if (_back) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
@ -73,13 +105,17 @@ void TopBar::enableBackButton() {
if (_back) {
return;
}
_back = Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
_back = Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.back));
object_ptr<Ui::IconButton>(this, _st.back),
st::infoTopBarScale);
_back->setDuration(st::infoTopBarDuration);
_back->toggle(!selectionMode(), anim::type::instant);
_back->entity()->clicks()
| rpl::start_to_stream(_backClicks, _back->lifetime());
_defaultControls.push_back(_back.data());
registerToggleControlCallback(_back.data(), [=] {
return !selectionMode();
});
if (_title) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
@ -95,14 +131,24 @@ void TopBar::createSearchView(
std::move(shown));
}
void TopBar::pushButton(base::unique_qptr<Ui::RpWidget> button) {
auto weak = button.get();
_buttons.push_back(std::move(button));
weak->setParent(this);
Ui::FadeWrap<Ui::RpWidget> *TopBar::pushButton(
base::unique_qptr<Ui::RpWidget> button) {
auto wrapped = base::make_unique_q<Ui::FadeWrap<Ui::RpWidget>>(
this,
object_ptr<Ui::RpWidget>::fromRaw(button.release()),
st::infoTopBarScale);
auto weak = wrapped.get();
_buttons.push_back(std::move(wrapped));
weak->setDuration(st::infoTopBarDuration);
registerToggleControlCallback(weak, [=] {
return !selectionMode()
&& !_searchModeEnabled;
});
weak->widthValue()
| rpl::start_with_next([this] {
updateControlsGeometry(width());
}, lifetime());
return weak;
}
void TopBar::setSearchField(
@ -122,59 +168,75 @@ void TopBar::createSearchView(
this,
_st.searchRow.height);
auto wrap = _searchView.get();
registerUpdateControlCallback(wrap, [=](anim::type) {
wrap->setVisible(!selectionMode() && _searchModeAvailable);
});
field->setParent(wrap);
auto search = addButton(
base::make_unique_q<Ui::FadeWrapScaled<Ui::FadeWrapScaled<Ui::IconButton>>>(
this,
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.search))));
_defaultControls.push_back(search);
auto cancel = Ui::CreateChild<Ui::FadeWrapScaled<Ui::CrossButton>>(
auto fieldWrap = Ui::CreateChild<Ui::FadeWrap<Ui::InputField>>(
wrap,
object_ptr<Ui::CrossButton>(wrap, _st.searchRow.fieldCancel));
_defaultControls.push_back(cancel);
object_ptr<Ui::InputField>::fromRaw(field),
st::infoTopBarScale);
fieldWrap->setDuration(st::infoTopBarDuration);
auto toggleSearchMode = [=](bool enabled, anim::type animated) {
if (!enabled) {
auto focusLifetime = field->lifetime().make_state<rpl::lifetime>();
registerUpdateControlCallback(fieldWrap, [=](anim::type animated) {
auto fieldShown = !selectionMode() && searchMode();
if (!fieldShown && field->hasFocus()) {
setFocus();
}
if (_title) {
_title->entity()->setVisible(!enabled);
fieldWrap->toggle(fieldShown, animated);
if (fieldShown) {
*focusLifetime = field->shownValue()
| rpl::filter([](bool shown) { return shown; })
| rpl::take(1)
| rpl::start_with_next([=] {
field->setFocus();
});
} else {
focusLifetime->destroy();
}
field->setVisible(enabled);
cancel->entity()->toggleAnimated(enabled);
if (animated == anim::type::instant) {
cancel->entity()->finishAnimations();
}
search->wrapped()->toggle(!enabled, animated);
if (enabled) {
field->setFocus();
}
};
});
auto button = base::make_unique_q<Ui::IconButton>(this, _st.search);
auto search = button.get();
search->addClickHandler([=] {
_searchModeEnabled = true;
updateControlsVisibility(anim::type::normal);
});
auto searchWrap = pushButton(std::move(button));
registerToggleControlCallback(searchWrap, [=] {
return !selectionMode()
&& _searchModeAvailable
&& !_searchModeEnabled;
});
auto cancel = Ui::CreateChild<Ui::CrossButton>(
wrap,
_st.searchRow.fieldCancel);
registerToggleControlCallback(cancel, [=] {
return !selectionMode() && searchMode();
});
auto cancelSearch = [=] {
if (!field->getLastText().isEmpty()) {
field->setText(QString());
} else {
toggleSearchMode(false, anim::type::normal);
_searchModeEnabled = false;
updateControlsVisibility(anim::type::normal);
}
};
cancel->entity()->addClickHandler(cancelSearch);
cancel->addClickHandler(cancelSearch);
field->connect(field, &Ui::InputField::cancelled, cancelSearch);
wrap->widthValue()
| rpl::start_with_next([=](int newWidth) {
auto availableWidth = newWidth
- _st.searchRow.fieldCancelSkip;
field->setGeometryToLeft(
fieldWrap->resizeToWidth(availableWidth);
fieldWrap->moveToLeft(
_st.searchRow.padding.left(),
_st.searchRow.padding.top(),
availableWidth,
field->height());
_st.searchRow.padding.top());
cancel->moveToRight(0, 0);
}, wrap->lifetime());
@ -191,10 +253,6 @@ void TopBar::createSearchView(
newWidth);
}, wrap->lifetime());
search->entity()->addClickHandler([=] {
toggleSearchMode(true, anim::type::normal);
});
field->alive()
| rpl::start_with_done([=] {
field->setParent(nullptr);
@ -202,18 +260,14 @@ void TopBar::createSearchView(
setSearchField(nullptr, rpl::never<bool>());
}, _searchView->lifetime());
toggleSearchMode(
!field->getLastText().isEmpty(),
anim::type::instant);
_searchModeEnabled = !field->getLastText().isEmpty();
updateControlsVisibility(anim::type::instant);
std::move(shown)
| rpl::start_with_next([=](bool visible) {
if (!field->getLastText().isEmpty()) {
return;
}
toggleSearchMode(false, anim::type::instant);
wrap->setVisible(visible);
search->wrapped()->toggle(visible, anim::type::instant);
auto alreadyInSearch = !field->getLastText().isEmpty();
_searchModeAvailable = visible || alreadyInSearch;
updateControlsVisibility(anim::type::instant);
}, wrap->lifetime());
}
@ -228,17 +282,6 @@ int TopBar::resizeGetHeight(int newWidth) {
return _st.height;
}
void TopBar::finishSelectionAnimations() {
ranges::for_each(ranges::view::concat(
_defaultControls,
_selectionControls
), [](auto &&control) {
if (auto pointer = control.data()) {
pointer->finishAnimating();
}
});
}
void TopBar::updateControlsGeometry(int newWidth) {
updateDefaultControlsGeometry(newWidth);
updateSelectionControlsGeometry(newWidth);
@ -286,7 +329,7 @@ void TopBar::updateSelectionControlsGeometry(int newWidth) {
const auto top = 0;
const auto availableWidth = newWidth - left - right;
_selectionText->resizeToWidth(availableWidth);
_selectionText->resizeToNaturalWidth(availableWidth);
_selectionText->moveToLeft(
left,
top,
@ -319,16 +362,31 @@ void TopBar::startHighlightAnimation() {
_st.highlightDuration);
}
void TopBar::updateControlsVisibility(anim::type animated) {
for (auto i = _updateControlCallbacks.begin(); i != _updateControlCallbacks.end();) {
auto &&[widget, callback] = *i;
if (!callback(animated)) {
i = _updateControlCallbacks.erase(i);
} else {
++i;
}
}
}
void TopBar::setSelectedItems(SelectedItems &&items) {
auto wasSelectionMode = selectionMode();
_selectedItems = std::move(items);
if (selectionMode()) {
if (_selectionText) {
updateSelectionState();
if (!wasSelectionMode) {
_selectionText->entity()->finishAnimating();
}
} else {
createSelectionControls();
}
}
toggleSelectionControls();
updateControlsVisibility(anim::type::normal);
}
SelectedItems TopBar::takeSelectedItems() {
@ -352,33 +410,43 @@ void TopBar::updateSelectionState() {
void TopBar::createSelectionControls() {
auto wrap = [&](auto created) {
_selectionControls.push_back(created);
registerToggleControlCallback(
created,
[this] { return selectionMode(); });
created->toggle(false, anim::type::instant);
return created;
};
_canDelete = computeCanDelete();
_cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
_cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaCancel)));
object_ptr<Ui::IconButton>(this, _st.mediaCancel),
st::infoTopBarScale));
_cancelSelection->setDuration(st::infoTopBarDuration);
_cancelSelection->entity()->clicks()
| rpl::start_to_stream(
_cancelSelectionClicks,
_cancelSelection->lifetime());
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::LabelWithNumbers>>(
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::LabelWithNumbers>>(
this,
object_ptr<Ui::LabelWithNumbers>(
this,
_st.title,
_st.titlePosition.y(),
generateSelectedText())));
generateSelectedText()),
st::infoTopBarScale));
_selectionText->setDuration(st::infoTopBarDuration);
_selectionText->entity()->resize(0, _st.height);
_forward = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
_forward = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaForward)));
object_ptr<Ui::IconButton>(this, _st.mediaForward),
st::infoTopBarScale));
_forward->setDuration(st::infoTopBarDuration);
_forward->entity()->addClickHandler([this] { performForward(); });
_delete = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
_delete = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaDelete)));
object_ptr<Ui::IconButton>(this, _st.mediaDelete),
st::infoTopBarScale));
_delete->setDuration(st::infoTopBarDuration);
_delete->entity()->addClickHandler([this] { performDelete(); });
_delete->entity()->setVisible(_canDelete);
@ -392,23 +460,6 @@ bool TopBar::computeCanDelete() const {
) == _selectedItems.list.end();
}
void TopBar::toggleSelectionControls() {
auto toggle = [](bool shown) {
return [=](auto &&control) {
if (auto pointer = control.data()) {
pointer->toggle(shown, anim::type::normal);
}
};
};
auto shown = selectionMode();
ranges::for_each(_defaultControls, toggle(!shown));
ranges::for_each(_selectionControls, toggle(shown));
if (!shown) {
clearSelectionControls();
}
}
Ui::StringWithNumbers TopBar::generateSelectedText() const {
using Data = Ui::StringWithNumbers;
using Type = Storage::SharedMediaType;
@ -422,7 +473,7 @@ Ui::StringWithNumbers TopBar::generateSelectedText() const {
case Type::VoiceFile: return lng_media_selected_audio__generic<Data>;
// case Type::RoundFile: return lng_media_selected_round__generic<Data>;
}
Unexpected("Type in TopBarOverride::generateText()");
Unexpected("Type in TopBar::generateSelectedText()");
}();
return phrase(lt_count, _selectedItems.list.size());
}
@ -431,27 +482,8 @@ bool TopBar::selectionMode() const {
return !_selectedItems.list.empty();
}
void TopBar::clearSelectionControls() {
for (auto &&control : _selectionControls) {
if (auto pointer = control.data()) {
pointer->shownValue()
| rpl::filter([](bool shown) { return !shown; })
| rpl::start_with_next([control] {
if (auto pointer = control.data()) {
InvokeQueued(pointer, [pointer] { delete pointer; });
}
}, pointer->lifetime());
}
}
auto isStale = [](auto &&control) { return !control; };
_defaultControls |= ranges::action::remove_if(isStale);
_selectionControls |= ranges::action::remove_if(isStale);
_cancelSelection = nullptr;
_selectionText = nullptr;
_forward = nullptr;
_delete = nullptr;
bool TopBar::searchMode() const {
return _searchModeAvailable && _searchModeEnabled;
}
SelectedItemSet TopBar::collectItems() const {

View file

@ -75,7 +75,6 @@ public:
SelectedItems takeSelectedItems();
rpl::producer<> cancelSelectionRequests() const;
void finishSelectionAnimations();
protected:
int resizeGetHeight(int newWidth) override;
@ -85,17 +84,18 @@ private:
void updateControlsGeometry(int newWidth);
void updateDefaultControlsGeometry(int newWidth);
void updateSelectionControlsGeometry(int newWidth);
void pushButton(base::unique_qptr<Ui::RpWidget> button);
Ui::FadeWrap<Ui::RpWidget> *pushButton(base::unique_qptr<Ui::RpWidget> button);
void removeButton(not_null<Ui::RpWidget*> button);
void startHighlightAnimation();
void updateControlsVisibility(anim::type animated);
bool selectionMode() const;
bool searchMode() const;
Ui::StringWithNumbers generateSelectedText() const;
[[nodiscard]] bool computeCanDelete() const;
[[nodiscard]] SelectedItemSet collectSelectedItems() const;
void updateSelectionState();
void createSelectionControls();
void toggleSelectionControls();
void clearSelectionControls();
SelectedItemSet collectItems() const;
@ -109,6 +109,12 @@ private:
not_null<Ui::InputField*> field,
rpl::producer<bool> &&shown);
template <typename Callback>
void registerUpdateControlCallback(QObject *guard, Callback &&callback);
template <typename Widget, typename IsVisible>
void registerToggleControlCallback(Widget *widget, IsVisible &&callback);
const style::InfoTopBar &_st;
Animation _a_highlight;
bool _highlight = false;
@ -116,6 +122,8 @@ private:
std::vector<base::unique_qptr<Ui::RpWidget>> _buttons;
QPointer<Ui::FadeWrap<Ui::FlatLabel>> _title;
bool _searchModeEnabled = false;
bool _searchModeAvailable = false;
base::unique_qptr<Ui::RpWidget> _searchView;
rpl::event_stream<> _backClicks;
@ -128,9 +136,8 @@ private:
QPointer<Ui::FadeWrap<Ui::IconButton>> _delete;
rpl::event_stream<> _cancelSelectionClicks;
using FadingControl = QPointer<Ui::FadeWrap<RpWidget>>;
std::vector<FadingControl> _defaultControls;
std::vector<FadingControl> _selectionControls;
using UpdateCallback = base::lambda<bool(anim::type)>;
std::map<QObject*, UpdateCallback> _updateControlCallbacks;
};

View file

@ -1,174 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_top_bar_override.h"
#include <rpl/merge.h>
#include "styles/style_info.h"
#include "lang/lang_keys.h"
#include "info/info_wrap_widget.h"
#include "storage/storage_shared_media.h"
#include "ui/effects/numbers_animation.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "mainwidget.h"
#include "boxes/confirm_box.h"
#include "boxes/peer_list_controllers.h"
namespace Info {
TopBarOverride::TopBarOverride(
QWidget *parent,
const style::InfoTopBar &st,
SelectedItems &&items)
: RpWidget(parent)
, _st(st)
, _items(std::move(items))
, _canDelete(computeCanDelete())
, _cancel(this, _st.mediaCancel)
, _text(this, _st.title, _st.titlePosition.y(), generateText())
, _forward(this, _st.mediaForward)
, _delete(this, _st.mediaDelete) {
setAttribute(Qt::WA_OpaquePaintEvent);
updateControlsVisibility();
_forward->addClickHandler([this] { performForward(); });
_delete->addClickHandler([this] { performDelete(); });
}
Ui::StringWithNumbers TopBarOverride::generateText() const {
using Data = Ui::StringWithNumbers;
using Type = Storage::SharedMediaType;
auto phrase = [&] {
switch (_items.type) {
case Type::Photo: return lng_media_selected_photo__generic<Data>;
case Type::Video: return lng_media_selected_video__generic<Data>;
case Type::File: return lng_media_selected_file__generic<Data>;
case Type::MusicFile: return lng_media_selected_song__generic<Data>;
case Type::Link: return lng_media_selected_link__generic<Data>;
case Type::VoiceFile: return lng_media_selected_audio__generic<Data>;
// case Type::RoundFile: return lng_media_selected_round__generic<Data>;
}
Unexpected("Type in TopBarOverride::generateText()");
}();
return phrase(lt_count, _items.list.size());
}
bool TopBarOverride::computeCanDelete() const {
return ranges::find_if(_items.list, [](const SelectedItem &item) {
return !item.canDelete;
}) == _items.list.end();
}
void TopBarOverride::setItems(SelectedItems &&items) {
_items = std::move(items);
_canDelete = computeCanDelete();
_text->setValue(generateText());
updateControlsVisibility();
updateControlsGeometry(width());
}
SelectedItems TopBarOverride::takeItems() {
_canDelete = false;
return std::move(_items);
}
rpl::producer<> TopBarOverride::cancelRequests() const {
return rpl::merge(
_cancel->clicks(),
_correctionCancelRequests.events());
}
int TopBarOverride::resizeGetHeight(int newWidth) {
updateControlsGeometry(newWidth);
return _st.height;
}
void TopBarOverride::updateControlsGeometry(int newWidth) {
auto right = _st.mediaActionsSkip;
if (_canDelete) {
_delete->moveToRight(right, 0, newWidth);
right += _delete->width();
}
_forward->moveToRight(right, 0, newWidth);
right += _forward->width();
auto left = 0;
_cancel->moveToLeft(left, 0);
left += _cancel->width();
const auto availableWidth = newWidth - left - right;
_text->setGeometryToLeft(left, 0, availableWidth, _st.height, newWidth);
}
void TopBarOverride::updateControlsVisibility() {
_delete->setVisible(_canDelete);
}
void TopBarOverride::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), _st.bg);
}
SelectedItemSet TopBarOverride::collectItems() const {
auto result = SelectedItemSet();
for (auto value : _items.list) {
if (auto item = App::histItemById(value.msgId)) {
result.insert(result.size(), item);
}
}
return result;
}
void TopBarOverride::performForward() {
auto items = collectItems();
if (items.empty()) {
_correctionCancelRequests.fire({});
return;
}
auto callback = [items = std::move(items), that = weak(this)](
not_null<PeerData*> peer) {
App::main()->setForwardDraft(peer->id, items);
if (that) {
that->_correctionCancelRequests.fire({});
}
};
Ui::show(Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
[](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] {
box->closeBox();
});
}));
}
void TopBarOverride::performDelete() {
auto items = collectItems();
if (items.empty()) {
_correctionCancelRequests.fire({});
} else {
Ui::show(Box<DeleteMessagesBox>(items));
}
}
} // namespace Info

View file

@ -1,77 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/rp_widget.h"
#include "info/info_wrap_widget.h"
#include "boxes/peer_list_controllers.h"
namespace style {
struct InfoTopBar;
} // namespace style
namespace Ui {
class IconButton;
class LabelWithNumbers;
struct StringWithNumbers;
} // namespace Ui
namespace Info {
class TopBarOverride : public Ui::RpWidget {
public:
TopBarOverride(
QWidget *parent,
const style::InfoTopBar &st,
SelectedItems &&items);
void setItems(SelectedItems &&items);
SelectedItems takeItems();
rpl::producer<> cancelRequests() const;
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void updateControlsVisibility();
void updateControlsGeometry(int newWidth);
Ui::StringWithNumbers generateText() const;
[[nodiscard]] bool computeCanDelete() const;
[[nodiscard]] SelectedItemSet collectItems() const;
void performForward();
void performDelete();
const style::InfoTopBar &_st;
SelectedItems _items;
bool _canDelete = false;
object_ptr<Ui::IconButton> _cancel;
object_ptr<Ui::LabelWithNumbers> _text;
object_ptr<Ui::IconButton> _forward;
object_ptr<Ui::IconButton> _delete;
rpl::event_stream<> _correctionCancelRequests;
};
} // namespace Info

View file

@ -36,7 +36,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_info.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h"
#include "info/info_top_bar_override.h"
#include "core/file_utilities.h"
namespace Layout = Overview::Layout;

View file

@ -38,7 +38,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h"
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/info_top_bar_override.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_button.h"

View file

@ -25,7 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/flatten_latest.h>
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "info/info_top_bar_override.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_widget.h"
#include "info/profile/info_profile_text.h"

View file

@ -187,7 +187,7 @@ void Members::setupButtons() {
});
//_searchField->hide();
//_cancelSearch->hideFast();
//_cancelSearch->setVisible(false);
auto addMemberShown = CanAddMemberValue(_peer)
| rpl::start_spawning(lifetime());
@ -344,15 +344,14 @@ void Members::addMember() {
//
//void Members::toggleSearch(anim::type animated) {
// _searchShown = !_searchShown;
// _cancelSearch->toggle(_searchShown, animated);
// if (animated == anim::type::normal) {
// _cancelSearch->toggleAnimated(_searchShown);
// _searchShownAnimation.start(
// [this] { searchAnimationCallback(); },
// _searchShown ? 0. : 1.,
// _searchShown ? 1. : 0.,
// st::slideWrapDuration);
// } else {
// _cancelSearch->toggleFast(_searchShown);
// _searchShownAnimation.finish();
// searchAnimationCallback();
// }

View file

@ -27,9 +27,9 @@ constexpr int kWideScale = 5;
} // namespace
FadeAnimation::FadeAnimation(TWidget *widget, bool scaled)
FadeAnimation::FadeAnimation(TWidget *widget, float64 scale)
: _widget(widget)
, _scaled(scaled) {
, _scale(scale) {
}
bool FadeAnimation::paint(Painter &p) {
@ -37,13 +37,28 @@ bool FadeAnimation::paint(Painter &p) {
auto opacity = _animation.current(getms(), _visible ? 1. : 0.);
p.setOpacity(opacity);
if (_scaled) {
if (_scale < 1.) {
PainterHighQualityEnabler hq(p);
auto targetRect = QRect((1 - kWideScale) / 2 * _size.width(), (1 - kWideScale) / 2 * _size.height(), kWideScale * _size.width(), kWideScale * _size.height());
auto scale = opacity;
auto shownWidth = anim::interpolate((1 - kWideScale) / 2 * _size.width(), 0, scale);
auto shownHeight = anim::interpolate((1 - kWideScale) / 2 * _size.height(), 0, scale);
p.drawPixmap(targetRect.marginsAdded(QMargins(shownWidth, shownHeight, shownWidth, shownHeight)), _cache);
auto targetRect = QRect(
(1 - kWideScale) / 2 * _size.width(),
(1 - kWideScale) / 2 * _size.height(),
kWideScale * _size.width(),
kWideScale * _size.height());
auto scale = opacity + (1. - opacity) * _scale;
auto shownWidth = anim::interpolate(
(1 - kWideScale) / 2 * _size.width(),
0,
scale);
auto shownHeight = anim::interpolate(
(1 - kWideScale) / 2 * _size.height(),
0,
scale);
auto margins = QMargins(
shownWidth,
shownHeight,
shownWidth,
shownHeight);
p.drawPixmap(targetRect.marginsAdded(margins), _cache);
} else {
p.drawPixmap(0, 0, _cache);
}
@ -70,18 +85,17 @@ QPixmap FadeAnimation::grabContent() {
return App::pixmapFromImageInPlace(std::move(image));
}
auto widgetContent = myGrab(_widget);
if (!_scaled) {
return widgetContent;
if (_scale < 1.) {
auto result = QImage(kWideScale * _size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
p.drawPixmap((kWideScale - 1) / 2 * _size.width(), (kWideScale - 1) / 2 * _size.height(), widgetContent);
}
return App::pixmapFromImageInPlace(std::move(result));
}
auto result = QImage(kWideScale * _size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
p.drawPixmap((kWideScale - 1) / 2 * _size.width(), (kWideScale - 1) / 2 * _size.height(), widgetContent);
}
return App::pixmapFromImageInPlace(std::move(result));
return widgetContent;
}
void FadeAnimation::setFinishedCallback(FinishedCallback &&callback) {

View file

@ -27,7 +27,7 @@ namespace Ui {
class FadeAnimation {
public:
FadeAnimation(TWidget *widget, bool scaled = false);
FadeAnimation(TWidget *widget, float64 scale = 1.);
bool paint(Painter &p);
void refreshCache();
@ -63,7 +63,7 @@ private:
QPixmap grabContent();
TWidget *_widget = nullptr;
bool _scaled = false;
float64 _scale = 1.;
Animation _animation;
QSize _size;

View file

@ -105,6 +105,10 @@ int NumbersAnimation::countWidth() const {
anim::easeOutCirc(1., _a_ready.current(1.)));
}
int NumbersAnimation::maxWidth() const {
return std::max(_fromWidth, _toWidth);
}
void NumbersAnimation::stepAnimation(TimeMs ms) {
_a_ready.step(ms);
}
@ -212,6 +216,12 @@ void LabelWithNumbers::setValue(const StringWithNumbers &value) {
_afterWidth = _st.style.font->width(_after);
}
void LabelWithNumbers::finishAnimating() {
_beforeWidthAnimation.finish();
_numbers.finishAnimating();
update();
}
void LabelWithNumbers::paintEvent(QPaintEvent *e) {
Painter p(this);

View file

@ -43,6 +43,7 @@ public:
void paint(Painter &p, int x, int y, int outerWidth);
int countWidth() const;
int maxWidth() const;
private:
struct Digit {
@ -90,6 +91,11 @@ public:
const StringWithNumbers &value);
void setValue(const StringWithNumbers &value);
void finishAnimating();
int naturalWidth() const override {
return _beforeWidth + _numbers.maxWidth() + _afterWidth;
}
protected:
void paintEvent(QPaintEvent *e) override;

View file

@ -58,9 +58,9 @@ base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView(
return !value.isEmpty();
})
| rpl::start_with_next([cancel](bool shown) {
cancel->toggleAnimated(shown);
cancel->toggle(shown, anim::type::normal);
}, cancel->lifetime());
cancel->finishAnimations();
cancel->finishAnimating();
auto shadow = CreateChild<Ui::PlainShadow>(wrap);
shadow->show();

View file

@ -472,57 +472,70 @@ private:
template <typename Object>
class object_ptr {
public:
object_ptr(std::nullptr_t) {
object_ptr(std::nullptr_t) noexcept {
}
// No default constructor, but constructors with at least
// one argument are simply make functions.
template <typename Parent, typename... Args>
explicit object_ptr(Parent &&parent, Args&&... args) : _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
explicit object_ptr(Parent &&parent, Args&&... args)
: _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
}
static object_ptr<Object> fromRaw(Object *value) noexcept {
object_ptr<Object> result = { nullptr };
result._object = value;
return result;
}
object_ptr(const object_ptr &other) = delete;
object_ptr &operator=(const object_ptr &other) = delete;
object_ptr(object_ptr &&other) : _object(base::take(other._object)) {
object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) {
}
object_ptr &operator=(object_ptr &&other) {
object_ptr &operator=(object_ptr &&other) noexcept {
auto temp = std::move(other);
destroy();
std::swap(_object, temp._object);
return *this;
}
template <typename OtherObject, typename = std::enable_if_t<std::is_base_of<Object, OtherObject>::value>>
object_ptr(object_ptr<OtherObject> &&other) : _object(base::take(other._object)) {
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr(object_ptr<OtherObject> &&other) noexcept
: _object(base::take(other._object)) {
}
template <typename OtherObject, typename = std::enable_if_t<std::is_base_of<Object, OtherObject>::value>>
object_ptr &operator=(object_ptr<OtherObject> &&other) {
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr &operator=(object_ptr<OtherObject> &&other) noexcept {
_object = base::take(other._object);
return *this;
}
object_ptr &operator=(std::nullptr_t) {
object_ptr &operator=(std::nullptr_t) noexcept {
_object = nullptr;
return *this;
}
// So we can pass this pointer to methods like connect().
Object *data() const {
Object *data() const noexcept {
return static_cast<Object*>(_object.data());
}
operator Object*() const {
operator Object*() const noexcept {
return data();
}
explicit operator bool() const {
explicit operator bool() const noexcept {
return _object != nullptr;
}
Object *operator->() const {
Object *operator->() const noexcept {
return data();
}
Object &operator*() const {
Object &operator*() const noexcept {
return *data();
}
@ -530,10 +543,12 @@ public:
template <typename Parent, typename... Args>
Object *create(Parent &&parent, Args&&... args) {
destroy();
_object = new Object(std::forward<Parent>(parent), std::forward<Args>(args)...);
_object = new Object(
std::forward<Parent>(parent),
std::forward<Args>(args)...);
return data();
}
void destroy() {
void destroy() noexcept {
delete base::take(_object);
}
void destroyDelayed() {
@ -545,7 +560,7 @@ public:
}
}
~object_ptr() {
~object_ptr() noexcept {
if (auto pointer = _object) {
if (!pointer->parent()) {
destroy();
@ -554,7 +569,8 @@ public:
}
template <typename ResultType, typename SourceType>
friend object_ptr<ResultType> static_object_cast(object_ptr<SourceType> source);
friend object_ptr<ResultType> static_object_cast(
object_ptr<SourceType> source);
private:
template <typename OtherObject>
@ -565,14 +581,23 @@ private:
};
template <typename ResultType, typename SourceType>
inline object_ptr<ResultType> static_object_cast(object_ptr<SourceType> source) {
inline object_ptr<ResultType> static_object_cast(
object_ptr<SourceType> source) {
auto result = object_ptr<ResultType>(nullptr);
result._object = static_cast<ResultType*>(base::take(source._object).data());
result._object = static_cast<ResultType*>(
base::take(source._object).data());
return std::move(result);
}
void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button, const QPoint &globalPoint);
void sendSynteticMouseEvent(
QWidget *widget,
QEvent::Type type,
Qt::MouseButton button,
const QPoint &globalPoint);
inline void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button) {
inline void sendSynteticMouseEvent(
QWidget *widget,
QEvent::Type type,
Qt::MouseButton button) {
return sendSynteticMouseEvent(widget, type, button, QCursor::pos());
}

View file

@ -478,7 +478,7 @@ CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : Ripple
, _a_loading(animation(this, &CrossButton::step_loading)) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer);
hide();
setVisible(false);
}
void CrossButton::step_loading(TimeMs ms, bool timer) {
@ -490,21 +490,29 @@ void CrossButton::step_loading(TimeMs ms, bool timer) {
}
}
void CrossButton::toggleAnimated(bool visible) {
if (_shown == visible) {
return;
void CrossButton::toggle(bool visible, anim::type animated) {
if (_shown != visible) {
_shown = visible;
if (animated == anim::type::normal) {
if (isHidden()) {
setVisible(true);
}
_a_show.start(
[this] { animationCallback(); },
_shown ? 0. : 1.,
_shown ? 1. : 0.,
_st.duration);
}
}
_shown = visible;
if (isHidden()) {
show();
if (animated == anim::type::instant) {
finishAnimating();
}
_a_show.start([this] { animationCallback(); }, _shown ? 0. : 1., _shown ? 1. : 0., _st.duration);
}
void CrossButton::animationCallback() {
update();
if (!_shown && !_a_show.animating()) {
hide();
if (!_a_show.animating()) {
setVisible(_shown);
}
}

View file

@ -212,28 +212,19 @@ class CrossButton : public RippleButton {
public:
CrossButton(QWidget *parent, const style::CrossButton &st);
void showAnimated() {
toggleAnimated(true);
void toggle(bool shown, anim::type animated);
void show(anim::type animated) {
return toggle(true, animated);
}
void hideAnimated() {
toggleAnimated(false);
void hide(anim::type animated) {
return toggle(false, animated);
}
void toggleAnimated(bool visible);
void showFast() {
toggleFast(true);
}
void hideFast() {
toggleFast(false);
}
void toggleFast(bool visible) {
toggleAnimated(visible);
finishAnimations();
}
void finishAnimations() {
void finishAnimating() {
_a_show.finish();
animationCallback();
}
bool isShown() const {
bool toggled() const {
return _shown;
}
void setLoadingAnimation(bool enabled);

View file

@ -383,11 +383,7 @@ MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, base::l
void MultiSelect::Inner::onQueryChanged() {
auto query = getQuery();
if (query.isEmpty()) {
_cancel->hideAnimated();
} else {
_cancel->showAnimated();
}
_cancel->toggle(!query.isEmpty(), anim::type::normal);
updateFieldGeometry();
if (_queryChangedCallback) {
_queryChangedCallback(query);
@ -422,7 +418,7 @@ void MultiSelect::Inner::setSubmittedCallback(base::lambda<void(bool ctrlShiftEn
void MultiSelect::Inner::updateFieldGeometry() {
auto fieldFinalWidth = _fieldWidth;
if (_cancel->isShown()) {
if (_cancel->toggled()) {
fieldFinalWidth -= _st.fieldCancelSkip;
}
_field->resizeToWidth(fieldFinalWidth);

View file

@ -27,9 +27,9 @@ namespace Ui {
FadeWrap<RpWidget>::FadeWrap(
QWidget *parent,
object_ptr<RpWidget> &&child,
bool scaled)
float64 scale)
: Parent(parent, std::move(child))
, _animation(this, scaled)
, _animation(this, scale)
, _duration(st::fadeWrapDuration) {
if (auto weak = wrapped()) {
weak->show();

View file

@ -36,13 +36,10 @@ class FadeWrap<RpWidget> : public Wrap<RpWidget> {
using Parent = Wrap<RpWidget>;
public:
FadeWrap(QWidget *parent, object_ptr<RpWidget> &&child)
: FadeWrap(parent, std::move(child), false) {
}
FadeWrap(
QWidget *parent,
object_ptr<RpWidget> &&child,
bool scaled);
float64 scale = 1.);
FadeWrap *setDuration(int duration);
FadeWrap *toggle(bool shown, anim::type animated);
@ -81,14 +78,11 @@ class FadeWrap : public Wrap<Widget, FadeWrap<RpWidget>> {
using Parent = Wrap<Widget, FadeWrap<RpWidget>>;
public:
FadeWrap(QWidget *parent, object_ptr<Widget> &&child)
: Parent(parent, std::move(child)) {
}
FadeWrap(
QWidget *parent,
object_ptr<Widget> &&child,
bool scaled)
: Parent(parent, std::move(child), scaled) {
float64 scale = 1.)
: Parent(parent, std::move(child), scale) {
}
FadeWrap *setDuration(int duration) {
@ -123,7 +117,7 @@ class FadeWrapScaled : public FadeWrap<Widget> {
public:
FadeWrapScaled(QWidget *parent, object_ptr<Widget> &&child)
: Parent(parent, std::move(child), true) {
: Parent(parent, std::move(child), 0.) {
}
};

View file

@ -727,7 +727,7 @@ Editor::Editor(QWidget*, const QString &path)
_scroll->scrollToY(top, bottom);
});
_close->setClickedCallback([this] { closeEditor(); });
_close->showFast();
_close->show(anim::type::instant);
_select->resizeToWidth(st::windowMinWidth);
_select->setQueryChangedCallback([this](const QString &query) { _inner->filterRows(query); _scroll->scrollToY(0); });

View file

@ -234,8 +234,6 @@
<(src_loc)/info/info_section_widget.h
<(src_loc)/info/info_top_bar.cpp
<(src_loc)/info/info_top_bar.h
<(src_loc)/info/info_top_bar_override.cpp
<(src_loc)/info/info_top_bar_override.h
<(src_loc)/info/info_wrap_widget.cpp
<(src_loc)/info/info_wrap_widget.h
<(src_loc)/info/common_groups/info_common_groups_inner_widget.cpp