Implement adaptive ContactStatus buttons.

This commit is contained in:
John Preston 2019-06-10 14:50:21 +02:00
parent 5e3734d7bf
commit 984f19b1e9
8 changed files with 160 additions and 33 deletions

View File

@ -1196,6 +1196,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_new_contact_block" = "Block user";
"lng_new_contact_add" = "Add contact";
"lng_new_contact_share" = "Share my phone number";
"lng_new_contact_add_name" = "Add {user} to contacts";
"lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment.\n{more_info}";
"lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment.\n{more_info}";
"lng_cant_more_info" = "More info »";

View File

@ -103,6 +103,9 @@ private:
};
class PeerData {
private:
static constexpr auto kSettingsUnknown = MTPDpeerSettings::Flag(1U << 9);
protected:
PeerData(not_null<Data::Session*> owner, PeerId id);
PeerData(const PeerData &other) = delete;
@ -113,7 +116,8 @@ public:
| MTPDpeerSettings::Flag::f_report_spam
| MTPDpeerSettings::Flag::f_add_contact
| MTPDpeerSettings::Flag::f_block_contact
| MTPDpeerSettings::Flag::f_share_contact;
| MTPDpeerSettings::Flag::f_share_contact
| kSettingsUnknown;
using Settings = Data::Flags<
MTPDpeerSettings::Flags,
kEssentialSettings.value()>;
@ -365,9 +369,6 @@ private:
crl::time _lastFullUpdate = 0;
MsgId _pinnedMessageId = 0;
static constexpr auto kSettingsUnknown = MTPDpeerSettings::Flag(1U << 9);
static_assert(!(kEssentialSettings & kSettingsUnknown));
Settings _settings = { kSettingsUnknown };
QString _about;

View File

@ -202,6 +202,19 @@ historyUnblock: FlatButton(historyComposeButton) {
color: attentionButtonFg;
overColor: attentionButtonFgOver;
}
historyContactStatusButton: FlatButton(historyComposeButton) {
height: 49px;
textTop: 16px;
overBgColor: historyComposeButtonBg;
ripple: RippleAnimation(defaultRippleAnimation) {
color: historyComposeButtonBgOver;
}
}
historyContactStatusBlock: FlatButton(historyContactStatusButton) {
color: attentionButtonFg;
overColor: attentionButtonFg;
}
historyContactStatusMinSkip: 16px;
historySendIcon: icon {{ "send_control_send", historySendIconFg }};
historySendIconOver: icon {{ "send_control_send", historySendIconFgOver }};

View File

@ -1686,6 +1686,10 @@ void HistoryWidget::showHistory(
_contactStatus = std::make_unique<HistoryView::ContactStatus>(
this,
_peer);
_contactStatus->heightValue() | rpl::start_with_next([=] {
updateControlsGeometry();
}, _contactStatus->lifetime());
orderWidgets();
} else {
_contactStatus = nullptr;
}
@ -1847,8 +1851,8 @@ void HistoryWidget::updateNotifyControls() {
_silent->setChecked(session().data().notifySilentPosts(_peer));
} else if (hasSilentToggle()) {
refreshSilentToggle();
updateControlsGeometry();
updateControlsVisibility();
updateControlsGeometry();
}
}
}

View File

@ -16,11 +16,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "auth_session.h"
#include "styles/style_history.h"
#include "styles/style_info.h"
namespace HistoryView {
namespace {
QString PeerFirstName(not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
return user->firstName;
}
return QString();
}
bool BarCurrentlyHidden(not_null<PeerData*> peer) {
const auto settings = peer->settings();
if (!settings) {
@ -109,33 +115,112 @@ bool BarCurrentlyHidden(not_null<PeerData*> peer) {
// controller()->showBackFromStack();
//}
ContactStatus::Bar::Bar(QWidget *parent)
ContactStatus::Bar::Bar(QWidget *parent, const QString &name)
: RpWidget(parent)
, _block(this, lang(lng_new_contact_block), st::historyUnblock)
, _add(this, lang(lng_new_contact_add), st::historyComposeButton)
, _share(this, lang(lng_new_contact_share), st::historyComposeButton)
, _report(this, lang(lng_report_spam), st::historyUnblock)
, _close(this, st::infoTopBarClose) {
, _name(name)
, _block(
this,
lang(lng_new_contact_block).toUpper(),
st::historyContactStatusBlock)
, _add(
this,
lang(lng_new_contact_add).toUpper(),
st::historyContactStatusButton)
, _share(
this,
lang(lng_new_contact_share).toUpper(),
st::historyContactStatusButton)
, _report(
this,
lang(lng_report_spam).toUpper(),
st::historyContactStatusBlock)
, _close(this, st::historyReplyCancel) {
resize(_close->size());
}
void ContactStatus::Bar::showState(State state) {
_add->setVisible(state == State::BlockOrAdd);
_block->setVisible(state == State::BlockOrAdd);
_add->setVisible(state == State::AddOrBlock || state == State::Add);
_block->setVisible(state == State::AddOrBlock);
_share->setVisible(state == State::SharePhoneNumber);
_report->setVisible(state == State::ReportSpam);
_add->setText((state == State::Add)
? lng_new_contact_add_name(lt_user, _name).toUpper()
: lang(lng_new_contact_add).toUpper());
updateButtonsGeometry();
}
void ContactStatus::Bar::resizeEvent(QResizeEvent *e) {
_close->moveToRight(0, 0);
_add->setGeometry(0, 0, width() / 2, height());
_block->setGeometry(width() / 2, 0, width() - (width() / 2), height());
_share->setGeometry(rect());
_report->setGeometry(rect());
updateButtonsGeometry();
}
ContactStatus::ContactStatus(not_null<Ui::RpWidget*> parent, not_null<PeerData*> peer)
: _bar(parent, object_ptr<Bar>(parent))
void ContactStatus::Bar::updateButtonsGeometry() {
const auto full = width();
const auto closeWidth = _close->width();
const auto available = full - closeWidth;
const auto skip = st::historyContactStatusMinSkip;
const auto buttonWidth = [&](const object_ptr<Ui::FlatButton> &button) {
return button->textWidth() + 2 * skip;
};
auto accumulatedLeft = 0;
const auto placeButton = [&](
const object_ptr<Ui::FlatButton> &button,
int buttonWidth,
int rightTextMargin = 0) {
button->setGeometry(accumulatedLeft, 0, buttonWidth, height());
button->setTextMargins({ 0, 0, rightTextMargin, 0 });
accumulatedLeft += buttonWidth;
};
const auto placeOne = [&](const object_ptr<Ui::FlatButton> &button) {
if (button->isHidden()) {
return;
}
const auto thatWidth = buttonWidth(button);
const auto margin = std::clamp(
thatWidth + closeWidth - available,
0,
closeWidth);
placeButton(button, full, margin);
};
if (!_add->isHidden() && !_block->isHidden()) {
const auto addWidth = buttonWidth(_add);
const auto blockWidth = buttonWidth(_block);
const auto half = full / 2;
if (addWidth <= half
&& blockWidth + 2 * closeWidth <= full - half) {
placeButton(_add, half);
placeButton(_block, full - half);
} else if (addWidth + blockWidth <= available) {
const auto margin = std::clamp(
addWidth + blockWidth + closeWidth - available,
0,
closeWidth);
const auto realBlockWidth = blockWidth + 2 * closeWidth - margin;
if (addWidth > realBlockWidth) {
placeButton(_add, addWidth);
placeButton(_block, full - addWidth, margin);
} else {
placeButton(_add, full - realBlockWidth);
placeButton(_block, realBlockWidth, margin);
}
} else {
const auto forAdd = (available * addWidth)
/ (addWidth + blockWidth);
placeButton(_add, forAdd);
placeButton(_block, full - forAdd, closeWidth);
}
} else {
placeOne(_add);
placeOne(_share);
placeOne(_report);
}
}
ContactStatus::ContactStatus(
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer)
: _bar(parent, object_ptr<Bar>(parent, PeerFirstName(peer)))
, _shadow(parent) {
setupWidgets(parent);
setupState(peer);
@ -190,7 +275,7 @@ auto ContactStatus::PeerState(not_null<PeerData*> peer)
return State::None;
}
}
return State::BlockOrAdd;
return State::AddOrBlock;
});
}
@ -209,6 +294,7 @@ void ContactStatus::setupState(not_null<PeerData*> peer) {
PeerState(
peer
) | rpl::start_with_next([=](State state) {
_state = state;
if (state == State::None) {
_bar.hide(anim::type::normal);
} else {

View File

@ -29,17 +29,22 @@ public:
int height() const;
rpl::producer<int> heightValue() const;
rpl::lifetime &lifetime() {
return _lifetime;
}
private:
enum class State {
None,
ReportSpam,
BlockOrAdd,
Add,
AddOrBlock,
SharePhoneNumber,
};
class Bar : public Ui::RpWidget {
public:
explicit Bar(QWidget *parent);
Bar(QWidget *parent, const QString &name);
void showState(State state);
@ -47,6 +52,9 @@ private:
void resizeEvent(QResizeEvent *e) override;
private:
void updateButtonsGeometry();
QString _name;
object_ptr<Ui::FlatButton> _block;
object_ptr<Ui::FlatButton> _add;
object_ptr<Ui::FlatButton> _share;

View File

@ -164,7 +164,11 @@ QPoint RippleButton::prepareRippleStartPosition() const {
RippleButton::~RippleButton() = default;
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple)
FlatButton::FlatButton(
QWidget *parent,
const QString &text,
const style::FlatButton &st)
: RippleButton(parent, st.ripple)
, _text(text)
, _st(st) {
if (_st.width < 0) {
@ -182,7 +186,7 @@ void FlatButton::setText(const QString &text) {
update();
}
void FlatButton::setWidth(int32 w) {
void FlatButton::setWidth(int w) {
_width = w;
if (_width < 0) {
_width = textWidth() - _st.width;
@ -204,8 +208,8 @@ void FlatButton::onStateChanged(State was, StateChangeSource source) {
void FlatButton::paintEvent(QPaintEvent *e) {
QPainter p(this);
QRect r(0, height() - _st.height, width(), _st.height);
p.fillRect(r, isOver() ? _st.overBgColor : _st.bgColor);
const auto inner = QRect(0, height() - _st.height, width(), _st.height);
p.fillRect(inner, isOver() ? _st.overBgColor : _st.bgColor);
paintRipple(p, 0, 0);
@ -213,8 +217,17 @@ void FlatButton::paintEvent(QPaintEvent *e) {
p.setRenderHint(QPainter::TextAntialiasing);
p.setPen(isOver() ? _st.overColor : _st.color);
r.setTop(_st.textTop);
p.drawText(r, _text, style::al_top);
const auto textRect = inner.marginsRemoved(
_textMargins
).marginsRemoved(
{ 0, _st.textTop, 0, 0 }
);
p.drawText(textRect, _text, style::al_top);
}
void FlatButton::setTextMargins(QMargins margins) {
_textMargins = margins;
update();
}
RoundButton::RoundButton(QWidget *parent, Fn<QString()> textFactory, const style::RoundButton &st) : RippleButton(parent, st.ripple)

View File

@ -69,7 +69,6 @@ protected:
private:
void ensureRipple();
void handleRipples(bool wasDown, bool wasPress);
const style::RippleAnimation &_st;
std::unique_ptr<RippleAnimation> _ripple;
@ -83,7 +82,8 @@ public:
FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st);
void setText(const QString &text);
void setWidth(int32 w);
void setWidth(int w);
void setTextMargins(QMargins margins);
int32 textWidth() const;
@ -93,8 +93,9 @@ protected:
void onStateChanged(State was, StateChangeSource source) override;
private:
QString _text, _textForAutoSize;
int _width;
QString _text;
QMargins _textMargins;
int _width = 0;
const style::FlatButton &_st;