wip redesign info top bar

This commit is contained in:
John Preston 2017-11-26 21:05:52 +04:00
parent 837dac50fa
commit 6afe18503d
9 changed files with 327 additions and 121 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 B

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 390 B

View File

@ -21,12 +21,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/info_top_bar.h"
#include <rpl/never.h>
#include <rpl/merge.h>
#include "styles/style_info.h"
#include "lang/lang_keys.h"
#include "info/info_wrap_widget.h"
#include "info/info_controller.h"
#include "info/profile/info_profile_values.h"
#include "storage/storage_shared_media.h"
#include "boxes/confirm_box.h"
#include "boxes/peer_list_controllers.h"
#include "mainwidget.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/input_fields.h"
@ -37,30 +41,48 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Info {
TopBar::TopBar(QWidget *parent, const style::InfoTopBar &st)
TopBar::TopBar(
QWidget *parent,
const style::InfoTopBar &st,
SelectedItems &&selectedItems)
: RpWidget(parent)
, _st(st) {
, _st(st)
, _selectedItems(Section::MediaType::kCount) {
setAttribute(Qt::WA_OpaquePaintEvent);
setSelectedItems(std::move(selectedItems));
finishSelectionAnimations();
}
void TopBar::setTitle(rpl::producer<QString> &&title) {
_title.create(this, std::move(title), _st.title);
if (_title) {
delete _title;
}
_title = Ui::CreateChild<Ui::FadeWrapScaled<Ui::FlatLabel>>(
this,
object_ptr<Ui::FlatLabel>(this, std::move(title), _st.title));
_title->toggle(!selectionMode(), anim::type::instant);
_defaultControls.push_back(_title.data());
if (_back) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
}
updateControlsGeometry(width());
}
void TopBar::enableBackButton(bool enable) {
if (enable) {
_back.create(this, _st.back);
_back->clicks()
| rpl::start_to_stream(_backClicks, _back->lifetime());
} else {
_back.destroy();
void TopBar::enableBackButton() {
if (_back) {
return;
}
_back = Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.back));
_back->toggle(!selectionMode(), anim::type::instant);
_back->entity()->clicks()
| rpl::start_to_stream(_backClicks, _back->lifetime());
_defaultControls.push_back(_back.data());
if (_title) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents, enable);
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
}
updateControlsGeometry(width());
}
@ -104,26 +126,30 @@ void TopBar::createSearchView(
field->setParent(wrap);
auto search = addButton(
base::make_unique_q<Ui::FadeWrapScaled<Ui::IconButton>>(
base::make_unique_q<Ui::FadeWrapScaled<Ui::FadeWrapScaled<Ui::IconButton>>>(
this,
object_ptr<Ui::IconButton>(this, _st.search)));
auto cancel = Ui::CreateChild<Ui::CrossButton>(
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>>(
wrap,
_st.searchRow.fieldCancel);
object_ptr<Ui::CrossButton>(wrap, _st.searchRow.fieldCancel));
_defaultControls.push_back(cancel);
auto toggleSearchMode = [=](bool enabled, anim::type animated) {
if (!enabled) {
setFocus();
}
if (_title) {
_title->setVisible(!enabled);
_title->entity()->setVisible(!enabled);
}
field->setVisible(enabled);
cancel->toggleAnimated(enabled);
cancel->entity()->toggleAnimated(enabled);
if (animated == anim::type::instant) {
cancel->finishAnimations();
cancel->entity()->finishAnimations();
}
search->toggle(!enabled, animated);
search->wrapped()->toggle(!enabled, animated);
if (enabled) {
field->setFocus();
}
@ -137,7 +163,7 @@ void TopBar::createSearchView(
}
};
cancel->addClickHandler(cancelSearch);
cancel->entity()->addClickHandler(cancelSearch);
field->connect(field, &Ui::InputField::cancelled, cancelSearch);
wrap->widthValue()
@ -187,7 +213,7 @@ void TopBar::createSearchView(
}
toggleSearchMode(false, anim::type::instant);
wrap->setVisible(visible);
search->toggle(visible, anim::type::instant);
search->wrapped()->toggle(visible, anim::type::instant);
}, wrap->lifetime());
}
@ -202,7 +228,23 @@ 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);
}
void TopBar::updateDefaultControlsGeometry(int newWidth) {
auto right = 0;
for (auto &button : _buttons) {
if (!button) continue;
@ -225,6 +267,32 @@ void TopBar::updateControlsGeometry(int newWidth) {
}
}
void TopBar::updateSelectionControlsGeometry(int newWidth) {
if (!_selectionText) {
return;
}
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;
_cancelSelection->moveToLeft(left, 0);
left += _cancelSelection->width();
const auto top = 0;
const auto availableWidth = newWidth - left - right;
_selectionText->resizeToWidth(availableWidth);
_selectionText->moveToLeft(
left,
top,
newWidth);
}
void TopBar::paintEvent(QPaintEvent *e) {
Painter p(this);
@ -251,6 +319,182 @@ void TopBar::startHighlightAnimation() {
_st.highlightDuration);
}
void TopBar::setSelectedItems(SelectedItems &&items) {
_selectedItems = std::move(items);
if (selectionMode()) {
if (_selectionText) {
updateSelectionState();
} else {
createSelectionControls();
}
}
toggleSelectionControls();
}
SelectedItems TopBar::takeSelectedItems() {
_canDelete = false;
return std::move(_selectedItems);
}
rpl::producer<> TopBar::cancelSelectionRequests() const {
return _cancelSelectionClicks.events();
}
void TopBar::updateSelectionState() {
Expects(_selectionText && _delete);
_canDelete = computeCanDelete();
_selectionText->entity()->setValue(generateSelectedText());
_delete->entity()->setVisible(_canDelete);
updateSelectionControlsGeometry(width());
}
void TopBar::createSelectionControls() {
auto wrap = [&](auto created) {
_selectionControls.push_back(created);
created->toggle(false, anim::type::instant);
return created;
};
_canDelete = computeCanDelete();
_cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaCancel)));
_cancelSelection->entity()->clicks()
| rpl::start_to_stream(
_cancelSelectionClicks,
_cancelSelection->lifetime());
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::LabelWithNumbers>>(
this,
object_ptr<Ui::LabelWithNumbers>(
this,
_st.title,
_st.titlePosition.y(),
generateSelectedText())));
_selectionText->entity()->resize(0, _st.height);
_forward = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaForward)));
_forward->entity()->addClickHandler([this] { performForward(); });
_delete = wrap(Ui::CreateChild<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.mediaDelete)));
_delete->entity()->addClickHandler([this] { performDelete(); });
_delete->entity()->setVisible(_canDelete);
updateControlsGeometry(width());
}
bool TopBar::computeCanDelete() const {
return ranges::find_if(
_selectedItems.list,
[](const SelectedItem &item) { return !item.canDelete; }
) == _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;
auto phrase = [&] {
switch (_selectedItems.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, _selectedItems.list.size());
}
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;
}
SelectedItemSet TopBar::collectItems() const {
auto result = SelectedItemSet();
for (auto value : _selectedItems.list) {
if (auto item = App::histItemById(value.msgId)) {
result.insert(result.size(), item);
}
}
return result;
}
void TopBar::performForward() {
auto items = collectItems();
if (items.empty()) {
_cancelSelectionClicks.fire({});
return;
}
auto callback = [items = std::move(items), that = weak(this)](
not_null<PeerData*> peer) {
App::main()->setForwardDraft(peer->id, items);
if (that) {
that->_cancelSelectionClicks.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 TopBar::performDelete() {
auto items = collectItems();
if (items.empty()) {
_cancelSelectionClicks.fire({});
} else {
Ui::show(Box<DeleteMessagesBox>(items));
}
}
rpl::producer<QString> TitleValue(
const Section &section,
not_null<PeerData*> peer) {

View File

@ -21,6 +21,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/rp_widget.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/effects/numbers_animation.h"
#include "info/info_wrap_widget.h"
namespace style {
struct InfoTopBar;
@ -31,6 +34,7 @@ class IconButton;
class FlatLabel;
class InputField;
class SearchFieldController;
class LabelWithNumbers;
} // namespace Ui
namespace Info {
@ -43,14 +47,17 @@ rpl::producer<QString> TitleValue(
class TopBar : public Ui::RpWidget {
public:
TopBar(QWidget *parent, const style::InfoTopBar &st);
TopBar(
QWidget *parent,
const style::InfoTopBar &st,
SelectedItems &&items);
auto backRequest() const {
return _backClicks.events();
}
void setTitle(rpl::producer<QString> &&title);
void enableBackButton(bool enable);
void enableBackButton();
void highlight();
template <typename ButtonWidget>
@ -64,16 +71,37 @@ public:
not_null<Ui::SearchFieldController*> controller,
rpl::producer<bool> &&shown);
void setSelectedItems(SelectedItems &&items);
SelectedItems takeSelectedItems();
rpl::producer<> cancelSelectionRequests() const;
void finishSelectionAnimations();
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void updateControlsGeometry(int newWidth);
void updateDefaultControlsGeometry(int newWidth);
void updateSelectionControlsGeometry(int newWidth);
void pushButton(base::unique_qptr<Ui::RpWidget> button);
void removeButton(not_null<Ui::RpWidget*> button);
void startHighlightAnimation();
bool selectionMode() const;
Ui::StringWithNumbers generateSelectedText() const;
[[nodiscard]] bool computeCanDelete() const;
[[nodiscard]] SelectedItemSet collectSelectedItems() const;
void updateSelectionState();
void createSelectionControls();
void toggleSelectionControls();
void clearSelectionControls();
SelectedItemSet collectItems() const;
void performForward();
void performDelete();
void setSearchField(
base::unique_qptr<Ui::InputField> field,
rpl::producer<bool> &&shown);
@ -84,14 +112,26 @@ private:
const style::InfoTopBar &_st;
Animation _a_highlight;
bool _highlight = false;
object_ptr<Ui::IconButton> _back = { nullptr };
QPointer<Ui::FadeWrap<Ui::IconButton>> _back;
std::vector<base::unique_qptr<Ui::RpWidget>> _buttons;
object_ptr<Ui::FlatLabel> _title = { nullptr };
QPointer<Ui::FadeWrap<Ui::FlatLabel>> _title;
base::unique_qptr<Ui::RpWidget> _searchView;
rpl::event_stream<> _backClicks;
SelectedItems _selectedItems;
bool _canDelete = false;
QPointer<Ui::FadeWrap<Ui::IconButton>> _cancelSelection;
QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText;
QPointer<Ui::FadeWrap<Ui::IconButton>> _forward;
QPointer<Ui::FadeWrap<Ui::IconButton>> _delete;
rpl::event_stream<> _cancelSelectionClicks;
using FadingControl = QPointer<Ui::FadeWrap<RpWidget>>;
std::vector<FadingControl> _defaultControls;
std::vector<FadingControl> _selectionControls;
};
} // namespace Info

View File

@ -30,7 +30,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/info_top_bar.h"
#include "info/info_top_bar_override.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
@ -84,7 +83,7 @@ WrapWidget::WrapWidget(
selectedListValue()
| rpl::start_with_next([this](SelectedItems &&items) {
InvokeQueued(this, [this, items = std::move(items)]() mutable {
refreshTopBarOverride(std::move(items));
if (_topBar) _topBar->setSelectedItems(std::move(items));
});
}, lifetime());
restoreHistoryStack(memento->takeStack());
@ -283,18 +282,24 @@ void WrapWidget::setupTop() {
// createTopBar();
//}
createTopBar();
refreshTopBarOverride();
}
void WrapWidget::createTopBar() {
auto wrapValue = wrap();
_topBar.create(this, TopBarStyle(wrapValue));
auto selectedItems = _topBar
? _topBar->takeSelectedItems()
: SelectedItems(Section::MediaType::kCount);
_topBar.create(this, TopBarStyle(wrapValue), std::move(selectedItems));
_topBar->cancelSelectionRequests()
| rpl::start_with_next([this](auto) {
_content->cancelSelection();
}, _topBar->lifetime());
_topBar->setTitle(TitleValue(
_controller->section(),
_controller->peer()));
if (wrapValue == Wrap::Narrow || hasStackHistory()) {
_topBar->enableBackButton(true);
_topBar->enableBackButton();
_topBar->backRequest()
| rpl::start_with_next([this] {
showBackFromStack();
@ -418,81 +423,6 @@ void WrapWidget::showProfileMenu() {
_topBarMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
}
void WrapWidget::refreshTopBarOverride(SelectedItems &&items) {
auto empty = items.list.empty();
if (!empty) {
if (_topBarOverride) {
_topBarOverride->setItems(std::move(items));
} else {
createTopBarOverride(std::move(items));
}
}
toggleTopBarOverride(!empty);
}
void WrapWidget::refreshTopBarOverride() {
if (_topBarOverride) {
auto items = _topBarOverride->takeItems();
createTopBarOverride(std::move(items));
topBarOverrideStep();
}
}
void WrapWidget::toggleTopBarOverride(bool shown) {
if (_topBarOverrideShown == shown) {
return;
}
_topBarOverrideShown = shown;
_topBar->show();
_topBarOverrideAnimation.start(
[this] { topBarOverrideStep(); },
_topBarOverrideShown ? 0. : 1.,
_topBarOverrideShown ? 1. : 0.,
st::slideWrapDuration,
anim::easeOutCirc);
}
void WrapWidget::topBarOverrideStep() {
auto shown = _topBarOverrideAnimation.current(
_topBarOverrideShown ? 1. : 0.);
auto topBarTop = anim::interpolate(0, _topBar->height(), shown);
auto overrideTop = anim::interpolate(-_topBar->height(), 0, shown);
_topBar->moveToLeft(0, topBarTop);
if (_topBarOverride) {
_topBarOverride->moveToLeft(0, overrideTop);
}
if (!_topBarOverrideAnimation.animating()) {
if (_topBarOverrideShown) {
_topBar->hide();
} else {
_topBarOverride = nullptr;
}
}
}
void WrapWidget::createTopBarOverride(SelectedItems &&items) {
_topBarOverride.create(
this,
TopBarStyle(wrap()),
std::move(items));
// This was done for tabs support.
//
//if (_topTabs) {
// _topTabs->hide();
//}
if (_topBar) {
_topBar->hide();
}
_topBarOverride->cancelRequests()
| rpl::start_with_next([this](auto) {
_content->cancelSelection();
}, _topBarOverride->lifetime());
_topBarOverride->resizeToWidth(width());
_topBarOverride->show();
}
bool WrapWidget::requireTopBarSearch() const {
if (!_controller->searchFieldController()) {
return false;
@ -832,10 +762,6 @@ void WrapWidget::showNewContent(
showNewContent(memento);
}
if (animationParams) {
refreshTopBarOverride(SelectedItems(Section::MediaType::kCount));
_topBarOverrideAnimation.finish();
topBarOverrideStep();
showAnimated(
saveToStack
? SlideDirection::FromRight
@ -876,9 +802,6 @@ void WrapWidget::resizeEvent(QResizeEvent *e) {
if (_topBar) {
_topBar->resizeToWidth(width());
}
if (_topBarOverride) {
_topBarOverride->resizeToWidth(width());
}
updateContentGeometry();
}

View File

@ -52,7 +52,6 @@ class MoveMemento;
class ContentMemento;
class ContentWidget;
class TopBar;
class TopBarOverride;
enum class Wrap {
Layer,
@ -190,11 +189,6 @@ private:
//void convertProfileFromStackToTab();
rpl::producer<SelectedItems> selectedListValue() const;
void refreshTopBarOverride();
void refreshTopBarOverride(SelectedItems &&items);
void createTopBarOverride(SelectedItems &&items);
void toggleTopBarOverride(bool shown);
void topBarOverrideStep();
bool requireTopBarSearch() const;
void addProfileMenuButton();
@ -209,7 +203,6 @@ private:
//object_ptr<Ui::SettingsSlider> _topTabs = { nullptr };
object_ptr<TopBar> _topBar = { nullptr };
object_ptr<Ui::RpWidget> _topBarSurrogate = { nullptr };
object_ptr<TopBarOverride> _topBarOverride = { nullptr };
Animation _topBarOverrideAnimation;
bool _topBarOverrideShown = false;
object_ptr<Ui::FadeShadow> _topShadow;

View File

@ -794,6 +794,10 @@ void ListWidget::refreshViewer() {
_idsLimit)
| rpl::start_with_next([=](
SparseIdsMergedSlice &&slice) {
if (!slice.fullCount()) {
// Don't display anything while full count is unknown.
return;
}
_slice = std::move(slice);
if (auto nearest = _slice.nearest(idForViewer)) {
_universalAroundId = GetUniversalId(*nearest);
@ -906,8 +910,10 @@ void ListWidget::refreshRows() {
_sections.push_back(std::move(section));
}
if (_layouts.size() > kMediaCountForSearch) {
_controller->setSearchEnabledByContent(true);
if (auto count = _slice.fullCount()) {
if (*count > kMediaCountForSearch) {
_controller->setSearchEnabledByContent(true);
}
}
clearStaleLayouts();