Improve settings main cover.

This commit is contained in:
John Preston 2022-02-08 20:25:24 +03:00
parent 7b0513a1ea
commit 340db7662b
17 changed files with 207 additions and 151 deletions

View file

@ -136,30 +136,12 @@ infoTopBarMenu: IconButton(infoTopBarBack) {
iconPosition: point(18px, -1px);
rippleAreaPosition: point(1px, 6px);
}
infoTopBarNotifications: IconButton(infoTopBarMenu) {
infoTopBarCall: IconButton(infoTopBarMenu) {
width: 42px;
icon: icon {{ "info_notifications", boxTitleCloseFg }};
iconOver: icon {{ "info_notifications", boxTitleCloseFgOver }};
iconPosition: point(5px, 10px);
rippleAreaPosition: point(0px, 6px);
}
infoNotificationsActive: icon {{
"info_notifications",
windowBgActive
}};
infoTopBarCall: IconButton(infoTopBarNotifications) {
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
iconPosition: point(5px, -1px);
}
infoTopBarSave: IconButton(infoTopBarNotifications) {
width: 48px;
icon: icon {{ "passport_ready", windowActiveTextFg }};
iconOver: icon {{ "passport_ready", windowActiveTextFg }};
iconPosition: point(11px, 17px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: lightButtonBgOver;
}
rippleAreaPosition: point(0px, 6px);
}
infoTopBarForward: IconButton(infoTopBarBack) {
width: 46px;
@ -186,24 +168,25 @@ infoTopBar: InfoTopBar {
searchRow: infoTopBarSearchRow;
highlightBg: windowBgOver;
highlightDuration: 240;
radius: 0px;
}
infoTopBarScale: 0.7;
infoTopBarDuration: 150;
infoLayerTopMinimal: 20px;
infoLayerTopMaximal: 40px;
infoLayerTopBarHeight: boxTitleHeight;
infoLayerTopBarHeight: 56px;
infoLayerTopBarBackIcon: icon {{ "info_back", boxTitleCloseFg }};
infoLayerTopBarBackIconOver: icon {{ "info_back", boxTitleCloseFgOver }};
infoLayerTopBarBack: IconButton(infoTopBarBack) {
width: infoLayerTopBarHeight;
width: 48px;
height: infoLayerTopBarHeight;
iconPosition: point(10px, -1px);
icon: infoLayerTopBarBackIcon;
iconOver: infoLayerTopBarBackIconOver;
rippleAreaSize: 40px;
rippleAreaPosition: point(6px, 4px);
rippleAreaPosition: point(6px, 8px);
}
infoLayerTopBarMediaCancel: IconButton(infoLayerTopBarBack) {
icon: icon {{ "info_close", boxTitleCloseFg }};
@ -211,7 +194,7 @@ infoLayerTopBarMediaCancel: IconButton(infoLayerTopBarBack) {
}
infoLayerTopBarClose: IconButton(infoLayerTopBarMediaCancel) {
iconPosition: point(4px, -1px);
rippleAreaPosition: point(0px, 4px);
rippleAreaPosition: point(0px, 8px);
}
infoLayerTopBarMenu: IconButton(infoLayerTopBarClose) {
width: 40px;
@ -219,24 +202,11 @@ infoLayerTopBarMenu: IconButton(infoLayerTopBarClose) {
iconOver: icon {{ "title_menu_dots", boxTitleCloseFgOver }};
iconPosition: point(16px, -1px);
}
infoLayerTopBarNotifications: IconButton(infoLayerTopBarMenu) {
icon: icon {{ "info_notifications", boxTitleCloseFg }};
iconOver: icon {{ "info_notifications", boxTitleCloseFgOver }};
iconPosition: point(3px, 9px);
}
infoLayerTopBarCall: IconButton(infoLayerTopBarMenu) {
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
iconPosition: point(3px, -1px);
}
infoLayerTopBarSave: IconButton(infoLayerTopBarNotifications) {
icon: icon {{ "passport_ready", windowActiveTextFg }};
iconOver: icon {{ "passport_ready", windowActiveTextFg }};
iconPosition: point(11px, 16px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: lightButtonBgOver;
}
}
infoLayerTopBarForward: IconButton(infoLayerTopBarBack) {
width: 45px;
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
@ -252,7 +222,7 @@ infoLayerTopBar: InfoTopBar(infoTopBar) {
height: infoLayerTopBarHeight;
back: infoLayerTopBarBack;
title: boxTitle;
titlePosition: point(24px, 13px);
titlePosition: point(24px, 18px);
bg: boxBg;
mediaCancel: infoLayerTopBarMediaCancel;
mediaActionsSkip: 6px;
@ -260,6 +230,7 @@ infoLayerTopBar: InfoTopBar(infoTopBar) {
mediaDelete: infoLayerTopBarDelete;
search: infoTopBarSearch;
searchRow: infoTopBarSearchRow;
radius: boxRadius;
}
infoTopBarMenuPosition: point(-2px, 35px);

View file

@ -226,10 +226,6 @@ rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
}
rpl::producer<bool> ContentWidget::canSaveChanges() const {
return rpl::single(false);
}
void ContentWidget::saveChanges(FnMut<void()> done) {
done();
}

View file

@ -69,7 +69,6 @@ public:
virtual void cancelSelection() {
}
virtual rpl::producer<bool> canSaveChanges() const;
virtual void saveChanges(FnMut<void()> done);
protected:

View file

@ -316,18 +316,6 @@ rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
limitAfter);
}
void Controller::setCanSaveChanges(rpl::producer<bool> can) {
_canSaveChanges = std::move(can);
}
rpl::producer<bool> Controller::canSaveChanges() const {
return _canSaveChanges.value();
}
bool Controller::canSaveChangesNow() const {
return _canSaveChanges.current();
}
Controller::~Controller() = default;
} // namespace Info

View file

@ -188,10 +188,6 @@ public:
return base::take(_searchStartsFocused);
}
void setCanSaveChanges(rpl::producer<bool> can);
rpl::producer<bool> canSaveChanges() const;
bool canSaveChangesNow() const;
void saveSearchState(not_null<ContentMemento*> memento);
void showSection(
@ -222,7 +218,6 @@ private:
std::unique_ptr<Ui::SearchFieldController> _searchFieldController;
std::unique_ptr<Api::DelayedSearchController> _searchController;
rpl::variable<bool> _seachEnabledByContent = false;
rpl::variable<bool> _canSaveChanges = false;
bool _searchStartsFocused = false;
rpl::lifetime _lifetime;

View file

@ -221,7 +221,7 @@ int LayerWidget::resizeGetHeight(int newWidth) {
// First resize content to new width and get the new desired height.
auto contentLeft = 0;
auto contentTop = st::boxRadius;
auto contentTop = 0;
auto contentBottom = st::boxRadius;
auto contentWidth = newWidth;
auto contentHeight = desiredHeight - contentTop - contentBottom;

View file

@ -43,7 +43,10 @@ TopBar::TopBar(
, _navigation(navigation)
, _st(st)
, _selectedItems(Section::MediaType::kCount) {
setAttribute(Qt::WA_OpaquePaintEvent);
if (_st.radius) {
_roundRect.emplace(_st.radius, _st.bg);
}
setAttribute(Qt::WA_OpaquePaintEvent, !_roundRect);
setSelectedItems(std::move(selectedItems));
updateControlsVisibility(anim::type::instant);
}
@ -367,8 +370,22 @@ void TopBar::paintEvent(QPaintEvent *e) {
_highlight = false;
startHighlightAnimation();
}
auto brush = anim::brush(_st.bg, _st.highlightBg, highlight);
p.fillRect(e->rect(), brush);
if (!_roundRect) {
const auto brush = anim::brush(_st.bg, _st.highlightBg, highlight);
p.fillRect(e->rect(), brush);
} else if (highlight > 0.) {
p.setPen(Qt::NoPen);
p.setBrush(anim::brush(_st.bg, _st.highlightBg, highlight));
p.drawRoundedRect(
rect() + style::margins(0, 0, 0, _st.radius * 2),
_st.radius,
_st.radius);
} else {
_roundRect->paintSomeRounded(
p,
rect(),
RectPart::TopLeft | RectPart::TopRight);
}
}
void TopBar::highlight() {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/rp_widget.h"
#include "ui/round_rect.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/effects/animations.h"
#include "ui/effects/numbers_animation.h"
@ -139,6 +140,7 @@ private:
const not_null<Window::SessionNavigation*> _navigation;
const style::InfoTopBar &_st;
std::optional<Ui::RoundRect> _roundRect;
Ui::Animations::Simple _a_highlight;
bool _highlight = false;
QPointer<Ui::FadeWrap<Ui::IconButton>> _back;

View file

@ -390,14 +390,10 @@ void WrapWidget::createTopBar() {
&& (wrapValue != Wrap::Side || hasStackHistory())) {
addTopBarMenuButton();
addProfileCallsButton();
// addProfileNotificationsButton();
} else if (section.type() == Section::Type::Settings
&& (section.settingsType() == Section::SettingsType::Main
|| section.settingsType() == Section::SettingsType::Chat)) {
addTopBarMenuButton();
} else if (section.type() == Section::Type::Settings
&& section.settingsType() == Section::SettingsType::Information) {
addContentSaveButton();
}
_topBar->lower();
@ -407,18 +403,8 @@ void WrapWidget::createTopBar() {
}
void WrapWidget::checkBeforeClose(Fn<void()> close) {
const auto confirmed = [=] {
Ui::hideLayer();
close();
};
if (_controller->canSaveChangesNow()) {
Ui::show(Box<Ui::ConfirmBox>(
tr::lng_settings_close_sure(tr::now),
tr::lng_close(tr::now),
confirmed));
} else {
confirmed();
}
Ui::hideLayer();
close();
}
void WrapWidget::addTopBarMenuButton() {
@ -435,25 +421,8 @@ void WrapWidget::addTopBarMenuButton() {
});
}
void WrapWidget::addContentSaveButton() {
Expects(_topBar != nullptr);
_topBar->addButtonWithVisibility(
base::make_unique_q<Ui::IconButton>(
_topBar,
(wrap() == Wrap::Layer
? st::infoLayerTopBarSave
: st::infoTopBarSave)),
_controller->canSaveChanges()
)->addClickHandler([=] {
_content->saveChanges(crl::guard(_content.data(), [=] {
_controller->showBackFromStack();
}));
});
}
bool WrapWidget::closeByOutsideClick() const {
return !_controller->canSaveChangesNow();
return true;
}
void WrapWidget::addProfileCallsButton() {
@ -491,39 +460,6 @@ void WrapWidget::addProfileCallsButton() {
}
}
void WrapWidget::addProfileNotificationsButton() {
Expects(_topBar != nullptr);
const auto peer = key().peer();
if (!peer) {
return;
}
auto notifications = _topBar->addButton(
base::make_unique_q<Ui::IconButton>(
_topBar,
(wrap() == Wrap::Layer
? st::infoLayerTopBarNotifications
: st::infoTopBarNotifications)));
notifications->addClickHandler([=] {
const auto muteForSeconds = peer->owner().notifyIsMuted(peer)
? 0
: Data::NotifySettings::kDefaultMutePeriod;
peer->owner().updateNotifySettings(peer, muteForSeconds);
});
Profile::NotificationsEnabledValue(
peer
) | rpl::start_with_next([notifications](bool enabled) {
const auto iconOverride = enabled
? &st::infoNotificationsActive
: nullptr;
const auto rippleOverride = enabled
? &st::lightButtonBgOver
: nullptr;
notifications->setIconOverride(iconOverride, iconOverride);
notifications->setRippleColorOverride(rippleOverride);
}, notifications->lifetime());
}
void WrapWidget::showTopBarMenu() {
if (_topBarMenu) {
_topBarMenu->hideAnimated(

View file

@ -190,9 +190,7 @@ private:
bool requireTopBarSearch() const;
void addTopBarMenuButton();
void addContentSaveButton();
void addProfileCallsButton();
void addProfileNotificationsButton();
void showTopBarMenu();
rpl::variable<Wrap> _wrap;

View file

@ -59,8 +59,6 @@ Widget::Widget(QWidget *parent, not_null<Controller*> controller)
) | rpl::start_with_next([=](const Ui::ScrollToRequest &request) {
scrollTo(request);
}, _inner->lifetime());
controller->setCanSaveChanges(rpl::single(false));
}
not_null<PollData*> Widget::poll() const {

View file

@ -52,8 +52,6 @@ Widget::Widget(
) | rpl::start_with_next([=](Type type) {
controller->showSettings(type);
}, _inner->lifetime());
controller->setCanSaveChanges(_inner->sectionCanSaveChanges());
}
Widget::~Widget() = default;
@ -81,10 +79,6 @@ void Widget::setInternalState(
restoreState(memento);
}
rpl::producer<bool> Widget::canSaveChanges() const {
return _inner->sectionCanSaveChanges();
}
void Widget::saveChanges(FnMut<void()> done) {
_inner->sectionSaveChanges(std::move(done));
}

View file

@ -63,7 +63,6 @@ public:
const QRect &geometry,
not_null<Memento*> memento);
rpl::producer<bool> canSaveChanges() const override;
void saveChanges(FnMut<void()> done) override;
rpl::producer<bool> desiredShadowVisibility() const override;

View file

@ -339,3 +339,13 @@ sessionList: PeerList(defaultPeerList) {
sessionListThreeDotsIcon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }};
sessionListThreeDotsIconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }};
sessionListThreeDotsSkip: 12px;
settingsPhotoLeft: 22px;
settingsPhotoTop: 8px;
settingsPhotoBottom: 16px;
settingsNameLeft: 112px;
settingsNameTop: 12px;
settingsPhoneLeft: settingsNameLeft;
settingsPhoneTop: 37px;
settingsUsernameLeft: settingsNameLeft;
settingsUsernameTop: 58px;

View file

@ -53,9 +53,6 @@ public:
virtual rpl::producer<Type> sectionShowOther() {
return nullptr;
}
virtual rpl::producer<bool> sectionCanSaveChanges() {
return rpl::single(false);
}
virtual void sectionSaveChanges(FnMut<void()> done) {
done();
}

View file

@ -11,13 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_codes.h"
#include "settings/settings_chat.h"
#include "boxes/language_box.h"
#include "boxes/username_box.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/about_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/buttons.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "info/profile/info_profile_cover.h"
#include "data/data_user.h"
#include "data/data_session.h"
@ -31,19 +36,171 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "api/api_cloud_password.h"
#include "api/api_global_privacy.h"
#include "api/api_sensitive_content.h"
#include "info/profile/info_profile_values.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "core/click_handler_types.h"
#include "core/application.h"
#include "base/call_delayed.h"
#include "base/platform/base_platform_info.h"
#include "facades.h"
#include "styles/style_settings.h"
#include "base/platform/base_platform_info.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace Settings {
namespace {
class Cover final : public Ui::FixedHeightWidget {
public:
Cover(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<UserData*> user);
~Cover();
private:
void setupChildGeometry();
void initViewers();
void refreshStatusText();
void refreshNameGeometry(int newWidth);
void refreshPhoneGeometry(int newWidth);
void refreshUsernameGeometry(int newWidth);
const not_null<Window::SessionController*> _controller;
const not_null<UserData*> _user;
object_ptr<Ui::UserpicButton> _userpic;
object_ptr<Ui::FlatLabel> _name = { nullptr };
object_ptr<Ui::FlatLabel> _phone = { nullptr };
object_ptr<Ui::FlatLabel> _username = { nullptr };
};
Cover::Cover(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<UserData*> user)
: FixedHeightWidget(
parent,
st::settingsPhotoTop
+ st::infoProfilePhoto.size.height()
+ st::settingsPhotoBottom)
, _controller(controller)
, _user(user)
, _userpic(
this,
controller,
_user,
Ui::UserpicButton::Role::OpenPhoto,
st::infoProfilePhoto)
, _name(this, st::infoProfileNameLabel)
, _phone(this, st::defaultFlatLabel)
, _username(this, st::infoProfileMegagroupStatusLabel) {
_user->updateFull();
_name->setSelectable(true);
_name->setContextCopyText(tr::lng_profile_copy_fullname(tr::now));
initViewers();
setupChildGeometry();
_userpic->switchChangePhotoOverlay(_user->isSelf());
_userpic->uploadPhotoRequests(
) | rpl::start_with_next([=] {
_user->session().api().peerPhoto().upload(
_user,
_userpic->takeResultImage());
}, _userpic->lifetime());
}
Cover::~Cover() = default;
void Cover::setupChildGeometry() {
using namespace rpl::mappers;
widthValue(
) | rpl::start_with_next([=](int newWidth) {
_userpic->moveToLeft(
st::settingsPhotoLeft,
st::settingsPhotoTop,
newWidth);
refreshNameGeometry(newWidth);
refreshPhoneGeometry(newWidth);
refreshUsernameGeometry(newWidth);
}, lifetime());
}
void Cover::initViewers() {
Info::Profile::NameValue(
_user
) | rpl::start_with_next([=](const TextWithEntities &value) {
_name->setText(value.text);
refreshNameGeometry(width());
}, lifetime());
Info::Profile::PhoneValue(
_user
) | rpl::start_with_next([=](const TextWithEntities &value) {
_phone->setText(value.text);
refreshPhoneGeometry(width());
}, lifetime());
Info::Profile::UsernameValue(
_user
) | rpl::start_with_next([=](const TextWithEntities &value) {
_username->setMarkedText(Ui::Text::Link(value.text.isEmpty()
? tr::lng_settings_username_add(tr::now)
: value.text));
refreshUsernameGeometry(width());
}, lifetime());
_username->setClickHandlerFilter([=](auto&&...) {
const auto username = _user->userName();
if (username.isEmpty()) {
_controller->show(Box<UsernameBox>(&_user->session()));
} else {
QGuiApplication::clipboard()->setText(
_user->session().createInternalLinkFull(username));
Ui::Toast::Show(tr::lng_username_copied(tr::now));
}
return false;
});
}
void Cover::refreshNameGeometry(int newWidth) {
const auto nameLeft = st::settingsNameLeft;
const auto nameTop = st::settingsNameTop;
const auto nameWidth = newWidth - nameLeft - st::infoProfileNameRight;
_name->resizeToNaturalWidth(nameWidth);
_name->moveToLeft(nameLeft, nameTop, newWidth);
}
void Cover::refreshPhoneGeometry(int newWidth) {
const auto phoneLeft = st::settingsPhoneLeft;
const auto phoneTop = st::settingsPhoneTop;
const auto phoneWidth = newWidth - phoneLeft - st::infoProfileNameRight;
_phone->resizeToWidth(phoneWidth);
_phone->moveToLeft(phoneLeft, phoneTop, newWidth);
}
void Cover::refreshUsernameGeometry(int newWidth) {
const auto usernameLeft = st::settingsUsernameLeft;
const auto usernameTop = st::settingsUsernameTop;
const auto usernameRight = st::infoProfileNameRight;
const auto usernameWidth = newWidth - usernameLeft - usernameRight;
_username->resizeToWidth(usernameWidth);
_username->moveToLeft(usernameLeft, usernameTop, newWidth);
}
} // namespace
void SetupLanguageButton(
not_null<Ui::VerticalLayout*> container,
@ -364,11 +521,10 @@ void Main::keyPressEvent(QKeyEvent *e) {
void Main::setupContent(not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto cover = content->add(object_ptr<Info::Profile::Cover>(
const auto cover = content->add(object_ptr<Cover>(
content,
controller->session().user(),
controller));
cover->setOnlineCount(rpl::single(0));
controller,
controller->session().user()));
SetupSections(controller, content, [=](Type type) {
_showOther.fire_copy(type);

@ -1 +1 @@
Subproject commit 681da392eb0523e87f9c1e9b3c732757e0c18850
Subproject commit 6685a1565eb19f6c434d72f5bf1147c14e98ac2c