mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Add nice tips buttons.
This commit is contained in:
parent
d55d7f37d7
commit
b6c86fd298
7 changed files with 225 additions and 20 deletions
|
@ -49,6 +49,23 @@ paymentsPricesTopSkip: 12px;
|
|||
paymentsPricesBottomSkip: 13px;
|
||||
paymentsPricePadding: margins(28px, 6px, 28px, 5px);
|
||||
|
||||
paymentsTipSkip: 8px;
|
||||
paymentsTipButton: RoundButton(defaultLightButton) {
|
||||
textFg: paymentsTipActive;
|
||||
textFgOver: paymentsTipActive;
|
||||
textBgOver: transparent;
|
||||
|
||||
width: -16px;
|
||||
height: 28px;
|
||||
textTop: 5px;
|
||||
}
|
||||
paymentsTipChosen: RoundButton(paymentsTipButton) {
|
||||
textFg: windowFgActive;
|
||||
textFgOver: windowFgActive;
|
||||
textBgOver: transparent;
|
||||
}
|
||||
paymentsTipButtonsPadding: margins(26px, 6px, 26px, 6px);
|
||||
|
||||
paymentsSectionsTopSkip: 11px;
|
||||
paymentsSectionButton: SettingsButton(infoProfileButton) {
|
||||
padding: margins(68px, 11px, 14px, 9px);
|
||||
|
|
|
@ -27,6 +27,36 @@ QString formatPhone(QString phone); // #TODO
|
|||
} // namespace App
|
||||
|
||||
namespace Payments::Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kLightOpacity = 0.1;
|
||||
constexpr auto kLightRippleOpacity = 0.11;
|
||||
constexpr auto kChosenOpacity = 0.8;
|
||||
constexpr auto kChosenRippleOpacity = 0.5;
|
||||
|
||||
[[nodiscard]] Fn<QColor()> TransparentColor(
|
||||
const style::color &c,
|
||||
float64 opacity) {
|
||||
return [&c, opacity] {
|
||||
return QColor(
|
||||
c->c.red(),
|
||||
c->c.green(),
|
||||
c->c.blue(),
|
||||
c->c.alpha() * opacity);
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] style::RoundButton TipButtonStyle(
|
||||
const style::RoundButton &original,
|
||||
const style::color &light,
|
||||
const style::color &ripple) {
|
||||
auto result = original;
|
||||
result.textBg = light;
|
||||
result.ripple.color = ripple;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
|
@ -38,7 +68,8 @@ FormSummary::FormSummary(
|
|||
const RequestedInformation ¤t,
|
||||
const PaymentMethodDetails &method,
|
||||
const ShippingOptions &options,
|
||||
not_null<PanelDelegate*> delegate)
|
||||
not_null<PanelDelegate*> delegate,
|
||||
int scrollTop)
|
||||
: _delegate(delegate)
|
||||
, _invoice(invoice)
|
||||
, _method(method)
|
||||
|
@ -56,21 +87,45 @@ FormSummary::FormSummary(
|
|||
rpl::single(formatAmount(computeTotalAmount()))),
|
||||
st::paymentsPanelSubmit))
|
||||
, _cancel(
|
||||
this,
|
||||
(_invoice.receipt.paid
|
||||
? tr::lng_about_done()
|
||||
: tr::lng_cancel()),
|
||||
st::paymentsPanelButton) {
|
||||
this,
|
||||
(_invoice.receipt.paid
|
||||
? tr::lng_about_done()
|
||||
: tr::lng_cancel()),
|
||||
st::paymentsPanelButton)
|
||||
, _tipLightBg(TransparentColor(st::paymentsTipActive, kLightOpacity))
|
||||
, _tipLightRipple(
|
||||
TransparentColor(st::paymentsTipActive, kLightRippleOpacity))
|
||||
, _tipChosenBg(TransparentColor(st::paymentsTipActive, kChosenOpacity))
|
||||
, _tipChosenRipple(
|
||||
TransparentColor(st::paymentsTipActive, kChosenRippleOpacity))
|
||||
, _tipButton(TipButtonStyle(
|
||||
st::paymentsTipButton,
|
||||
_tipLightBg.color(),
|
||||
_tipLightRipple.color()))
|
||||
, _tipChosen(TipButtonStyle(
|
||||
st::paymentsTipChosen,
|
||||
_tipChosenBg.color(),
|
||||
_tipChosenRipple.color()))
|
||||
, _initialScrollTop(scrollTop) {
|
||||
setupControls();
|
||||
}
|
||||
|
||||
rpl::producer<int> FormSummary::scrollTopValue() const {
|
||||
return _scroll->scrollTopValue();
|
||||
}
|
||||
|
||||
void FormSummary::updateThumbnail(const QImage &thumbnail) {
|
||||
_invoice.cover.thumbnail = thumbnail;
|
||||
_thumbnails.fire_copy(thumbnail);
|
||||
}
|
||||
|
||||
QString FormSummary::formatAmount(int64 amount) const {
|
||||
return FillAmountAndCurrency(amount, _invoice.currency);
|
||||
QString FormSummary::formatAmount(
|
||||
int64 amount,
|
||||
bool forceStripDotZero) const {
|
||||
return FillAmountAndCurrency(
|
||||
amount,
|
||||
_invoice.currency,
|
||||
forceStripDotZero);
|
||||
}
|
||||
|
||||
int64 FormSummary::computeTotalAmount() const {
|
||||
|
@ -279,9 +334,7 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
|||
add(tr::lng_payments_tips_label(tr::now), tips);
|
||||
}
|
||||
} else if (_invoice.tipsMax > 0) {
|
||||
const auto text = _invoice.tipsSelected
|
||||
? formatAmount(_invoice.tipsSelected)
|
||||
: tr::lng_payments_tips_add(tr::now);
|
||||
const auto text = formatAmount(_invoice.tipsSelected);
|
||||
const auto label = addRow(
|
||||
tr::lng_payments_tips_label(tr::now),
|
||||
Ui::Text::Link(text, "internal:edit_tips"));
|
||||
|
@ -289,12 +342,116 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
|||
_delegate->panelChooseTips();
|
||||
return false;
|
||||
});
|
||||
setupSuggestedTips(layout);
|
||||
}
|
||||
|
||||
add(tr::lng_payments_total_label(tr::now), total, true);
|
||||
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
|
||||
}
|
||||
|
||||
void FormSummary::setupSuggestedTips(not_null<VerticalLayout*> layout) {
|
||||
if (_invoice.suggestedTips.empty()) {
|
||||
return;
|
||||
}
|
||||
struct Button {
|
||||
RoundButton *widget = nullptr;
|
||||
int minWidth = 0;
|
||||
};
|
||||
struct State {
|
||||
std::vector<Button> buttons;
|
||||
int maxWidth = 0;
|
||||
};
|
||||
const auto outer = layout->add(
|
||||
object_ptr<RpWidget>(layout),
|
||||
st::paymentsTipButtonsPadding);
|
||||
const auto state = outer->lifetime().make_state<State>();
|
||||
for (const auto amount : _invoice.suggestedTips) {
|
||||
const auto text = formatAmount(amount, true);
|
||||
const auto &st = (amount == _invoice.tipsSelected)
|
||||
? _tipChosen
|
||||
: _tipButton;
|
||||
state->buttons.push_back(Button{
|
||||
.widget = CreateChild<RoundButton>(
|
||||
outer,
|
||||
rpl::single(formatAmount(amount, true)),
|
||||
st),
|
||||
});
|
||||
auto &button = state->buttons.back();
|
||||
button.widget->show();
|
||||
button.widget->setClickedCallback([=] {
|
||||
_delegate->panelChangeTips(amount);
|
||||
});
|
||||
button.minWidth = button.widget->width();
|
||||
state->maxWidth = std::max(state->maxWidth, button.minWidth);
|
||||
}
|
||||
outer->widthValue(
|
||||
) | rpl::filter([=](int outerWidth) {
|
||||
return outerWidth >= state->maxWidth;
|
||||
}) | rpl::start_with_next([=](int outerWidth) {
|
||||
const auto skip = st::paymentsTipSkip;
|
||||
const auto &buttons = state->buttons;
|
||||
auto left = outerWidth;
|
||||
auto height = 0;
|
||||
auto rowStart = 0;
|
||||
auto rowEnd = 0;
|
||||
auto buttonWidths = std::vector<float64>();
|
||||
const auto layoutRow = [&] {
|
||||
const auto count = rowEnd - rowStart;
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
buttonWidths.resize(count);
|
||||
ranges::fill(buttonWidths, 0.);
|
||||
auto available = float64(outerWidth - (count - 1) * skip);
|
||||
auto zeros = count;
|
||||
do {
|
||||
const auto started = zeros;
|
||||
const auto average = available / zeros;
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (buttonWidths[i] > 0.) {
|
||||
continue;
|
||||
}
|
||||
const auto min = buttons[rowStart + i].minWidth;
|
||||
if (min > average) {
|
||||
buttonWidths[i] = min;
|
||||
available -= min;
|
||||
--zeros;
|
||||
}
|
||||
}
|
||||
if (started == zeros) {
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (!buttonWidths[i]) {
|
||||
buttonWidths[i] = average;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (zeros > 0);
|
||||
auto x = 0.;
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
const auto button = buttons[rowStart + i].widget;
|
||||
auto right = x + buttonWidths[i];
|
||||
button->setFullWidth(int(std::round(right) - std::round(x)));
|
||||
button->moveToLeft(int(std::round(x)), height, outerWidth);
|
||||
x = right + skip;
|
||||
}
|
||||
height += buttons[0].widget->height() + skip;
|
||||
};
|
||||
for (const auto button : buttons) {
|
||||
if (button.minWidth <= left) {
|
||||
left -= button.minWidth + skip;
|
||||
++rowEnd;
|
||||
continue;
|
||||
}
|
||||
layoutRow();
|
||||
rowStart = rowEnd++;
|
||||
left = outerWidth - button.minWidth - skip;
|
||||
}
|
||||
layoutRow();
|
||||
outer->resize(outerWidth, height - skip);
|
||||
}, outer->lifetime());
|
||||
}
|
||||
|
||||
void FormSummary::setupSections(not_null<VerticalLayout*> layout) {
|
||||
Settings::AddSkip(layout, st::paymentsSectionsTopSkip);
|
||||
|
||||
|
@ -419,6 +576,12 @@ void FormSummary::updateControlsGeometry() {
|
|||
_cancel->moveToRight(right, buttonsTop + padding.top());
|
||||
|
||||
_scroll->updateBars();
|
||||
|
||||
if (buttonsTop > 0 && width() > 0) {
|
||||
if (const auto top = base::take(_initialScrollTop)) {
|
||||
_scroll->scrollToY(top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Payments::Ui
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/rp_widget.h"
|
||||
#include "payments/ui/payments_panel_data.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
|
@ -32,9 +33,11 @@ public:
|
|||
const RequestedInformation ¤t,
|
||||
const PaymentMethodDetails &method,
|
||||
const ShippingOptions &options,
|
||||
not_null<PanelDelegate*> delegate);
|
||||
not_null<PanelDelegate*> delegate,
|
||||
int scrollTop);
|
||||
|
||||
void updateThumbnail(const QImage &thumbnail);
|
||||
[[nodiscard]] rpl::producer<int> scrollTopValue() const;
|
||||
|
||||
private:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
@ -43,10 +46,13 @@ private:
|
|||
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||
void setupCover(not_null<VerticalLayout*> layout);
|
||||
void setupPrices(not_null<VerticalLayout*> layout);
|
||||
void setupSuggestedTips(not_null<VerticalLayout*> layout);
|
||||
void setupSections(not_null<VerticalLayout*> layout);
|
||||
void updateControlsGeometry();
|
||||
|
||||
[[nodiscard]] QString formatAmount(int64 amount) const;
|
||||
[[nodiscard]] QString formatAmount(
|
||||
int64 amount,
|
||||
bool forceStripDotZero = false) const;
|
||||
[[nodiscard]] int64 computeTotalAmount() const;
|
||||
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
|
@ -61,6 +67,14 @@ private:
|
|||
object_ptr<RoundButton> _cancel;
|
||||
rpl::event_stream<QImage> _thumbnails;
|
||||
|
||||
style::complex_color _tipLightBg;
|
||||
style::complex_color _tipLightRipple;
|
||||
style::complex_color _tipChosenBg;
|
||||
style::complex_color _tipChosenRipple;
|
||||
style::RoundButton _tipButton;
|
||||
style::RoundButton _tipChosen;
|
||||
int _initialScrollTop = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
||||
|
|
|
@ -66,10 +66,12 @@ void Panel::showForm(
|
|||
current,
|
||||
method,
|
||||
options,
|
||||
_delegate);
|
||||
_delegate,
|
||||
_formScrollTop.current());
|
||||
_weakFormSummary = form.get();
|
||||
_widget->showInner(std::move(form));
|
||||
_widget->setBackAllowed(false);
|
||||
_formScrollTop = _weakFormSummary->scrollTopValue();
|
||||
}
|
||||
|
||||
void Panel::updateFormThumbnail(const QImage &thumbnail) {
|
||||
|
@ -204,7 +206,7 @@ void Panel::chooseTips(const Invoice &invoice) {
|
|||
case 8: return "KZT";
|
||||
}
|
||||
return currency;
|
||||
})(), // #TODO payments currency,
|
||||
})(), // #TODO payments testing
|
||||
});
|
||||
box->setFocusCallback([=] {
|
||||
row->setFocusFast();
|
||||
|
@ -221,7 +223,7 @@ void Panel::chooseTips(const Invoice &invoice) {
|
|||
st::paymentTipsErrorLabel)),
|
||||
st::paymentTipsErrorPadding);
|
||||
errorWrap->hide(anim::type::instant);
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
const auto submit = [=] {
|
||||
const auto value = row->value().toLongLong();
|
||||
if (value > max) {
|
||||
row->showError();
|
||||
|
@ -230,7 +232,10 @@ void Panel::chooseTips(const Invoice &invoice) {
|
|||
_delegate->panelChangeTips(value);
|
||||
box->closeBox();
|
||||
}
|
||||
});
|
||||
};
|
||||
row->submitted(
|
||||
) | rpl::start_with_next(submit, box->lifetime());
|
||||
box->addButton(tr::lng_settings_save(), submit);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ private:
|
|||
std::unique_ptr<RpWidget> _webviewBottom;
|
||||
QPointer<Checkbox> _saveWebviewInformation;
|
||||
QPointer<FormSummary> _weakFormSummary;
|
||||
rpl::variable<int> _formScrollTop;
|
||||
QPointer<EditInformation> _weakEditInformation;
|
||||
QPointer<EditCard> _weakEditCard;
|
||||
bool _testMode = false;
|
||||
|
|
|
@ -127,7 +127,10 @@ QString FormatPlayedText(qint64 played, qint64 duration) {
|
|||
return tr::lng_duration_played(tr::now, lt_played, FormatDurationText(played), lt_duration, FormatDurationText(duration));
|
||||
}
|
||||
|
||||
QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
||||
QString FillAmountAndCurrency(
|
||||
int64 amount,
|
||||
const QString ¤cy,
|
||||
bool forceStripDotZero) {
|
||||
const auto rule = LookupCurrencyRule(currency);
|
||||
|
||||
const auto prefix = (amount < 0)
|
||||
|
@ -142,7 +145,8 @@ QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
|||
result.append(name);
|
||||
if (rule.space) result.append(' ');
|
||||
}
|
||||
const auto precision = (!rule.stripDotZero || std::floor(value) != value)
|
||||
const auto precision = ((!rule.stripDotZero && !forceStripDotZero)
|
||||
|| std::floor(value) != value)
|
||||
? rule.exponent
|
||||
: 0;
|
||||
result.append(FormatWithSeparators(
|
||||
|
|
|
@ -35,7 +35,8 @@ struct CurrencyRule {
|
|||
|
||||
[[nodiscard]] QString FillAmountAndCurrency(
|
||||
int64 amount,
|
||||
const QString ¤cy);
|
||||
const QString ¤cy,
|
||||
bool forceStripDotZero = false);
|
||||
[[nodiscard]] CurrencyRule LookupCurrencyRule(const QString ¤cy);
|
||||
[[nodiscard]] QString FormatWithSeparators(
|
||||
double amount,
|
||||
|
|
Loading…
Add table
Reference in a new issue