mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add info rows to PeerShortInfoBox.
This commit is contained in:
parent
2ca5f26546
commit
dcc14a4726
7 changed files with 185 additions and 53 deletions
|
@ -8,7 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peers/peer_short_info_box.h"
|
#include "boxes/peers/peer_short_info_box.h"
|
||||||
|
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/wrap/wrap.h"
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "info/profile/info_profile_text.h"
|
||||||
#include "media/streaming/media_streaming_instance.h"
|
#include "media/streaming/media_streaming_instance.h"
|
||||||
#include "media/streaming/media_streaming_player.h"
|
#include "media/streaming/media_streaming_player.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -17,6 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kShadowMaxAlpha = 80;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PeerShortInfoBox::PeerShortInfoBox(
|
PeerShortInfoBox::PeerShortInfoBox(
|
||||||
|
@ -30,6 +38,14 @@ PeerShortInfoBox::PeerShortInfoBox(
|
||||||
, _fields(std::move(fields))
|
, _fields(std::move(fields))
|
||||||
, _name(this, nameValue(), st::shortInfoName)
|
, _name(this, nameValue(), st::shortInfoName)
|
||||||
, _status(this, std::move(status), st::shortInfoStatus)
|
, _status(this, std::move(status), st::shortInfoStatus)
|
||||||
|
, _scroll(this, st::defaultSolidScroll)
|
||||||
|
, _rows(
|
||||||
|
static_cast<Ui::VerticalLayout*>(
|
||||||
|
_scroll->setOwnedWidget(
|
||||||
|
object_ptr<Ui::OverrideMargins>(
|
||||||
|
_scroll.data(),
|
||||||
|
object_ptr<Ui::VerticalLayout>(
|
||||||
|
_scroll.data())))->entity()))
|
||||||
, _videoPaused(std::move(videoPaused)) {
|
, _videoPaused(std::move(videoPaused)) {
|
||||||
std::move(
|
std::move(
|
||||||
userpic
|
userpic
|
||||||
|
@ -54,8 +70,80 @@ void PeerShortInfoBox::prepare() {
|
||||||
? tr::lng_view_button_group()
|
? tr::lng_view_button_group()
|
||||||
: tr::lng_profile_view_channel(), [=] { _openRequests.fire({}); });
|
: tr::lng_profile_view_channel(), [=] { _openRequests.fire({}); });
|
||||||
|
|
||||||
|
prepareRows();
|
||||||
|
|
||||||
setNoContentMargin(true);
|
setNoContentMargin(true);
|
||||||
setDimensions(st::shortInfoWidth, st::shortInfoWidth);
|
|
||||||
|
_rows->move(0, 0);
|
||||||
|
_rows->heightValue(
|
||||||
|
) | rpl::start_with_next([=](int height) {
|
||||||
|
setDimensions(st::shortInfoWidth, st::shortInfoWidth + height);
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerShortInfoBox::prepareRows() {
|
||||||
|
using namespace Info::Profile;
|
||||||
|
|
||||||
|
auto addInfoLineGeneric = [&](
|
||||||
|
rpl::producer<QString> &&label,
|
||||||
|
rpl::producer<TextWithEntities> &&text,
|
||||||
|
const style::FlatLabel &textSt = st::infoLabeled) {
|
||||||
|
auto line = CreateTextWithLabel(
|
||||||
|
_rows,
|
||||||
|
rpl::duplicate(label) | Ui::Text::ToWithEntities(),
|
||||||
|
rpl::duplicate(text),
|
||||||
|
textSt,
|
||||||
|
st::shortInfoLabeledPadding);
|
||||||
|
_rows->add(std::move(line.wrap));
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
std::move(label),
|
||||||
|
std::move(text)
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_rows->resizeToWidth(st::shortInfoWidth);
|
||||||
|
}, _rows->lifetime());
|
||||||
|
|
||||||
|
//line.text->setClickHandlerFilter(infoClickFilter);
|
||||||
|
return line.text;
|
||||||
|
};
|
||||||
|
auto addInfoLine = [&](
|
||||||
|
rpl::producer<QString> &&label,
|
||||||
|
rpl::producer<TextWithEntities> &&text,
|
||||||
|
const style::FlatLabel &textSt = st::infoLabeled) {
|
||||||
|
return addInfoLineGeneric(
|
||||||
|
std::move(label),
|
||||||
|
std::move(text),
|
||||||
|
textSt);
|
||||||
|
};
|
||||||
|
auto addInfoOneLine = [&](
|
||||||
|
rpl::producer<QString> &&label,
|
||||||
|
rpl::producer<TextWithEntities> &&text,
|
||||||
|
const QString &contextCopyText) {
|
||||||
|
auto result = addInfoLine(
|
||||||
|
std::move(label),
|
||||||
|
std::move(text),
|
||||||
|
st::infoLabeledOneLine);
|
||||||
|
result->setDoubleClickSelectsParagraph(true);
|
||||||
|
result->setContextCopyText(contextCopyText);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
addInfoOneLine(
|
||||||
|
tr::lng_info_link_label(),
|
||||||
|
linkValue(),
|
||||||
|
tr::lng_context_copy_link(tr::now));
|
||||||
|
addInfoOneLine(
|
||||||
|
tr::lng_info_mobile_label(),
|
||||||
|
phoneValue() | Ui::Text::ToWithEntities(),
|
||||||
|
tr::lng_profile_copy_phone(tr::now));
|
||||||
|
auto label = _fields.current().isBio
|
||||||
|
? tr::lng_info_bio_label()
|
||||||
|
: tr::lng_info_about_label();
|
||||||
|
addInfoLine(std::move(label), aboutValue());
|
||||||
|
addInfoOneLine(
|
||||||
|
tr::lng_info_username_label(),
|
||||||
|
usernameValue() | Ui::Text::ToWithEntities(),
|
||||||
|
tr::lng_context_copy_mention(tr::now));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RectParts PeerShortInfoBox::customCornersFilling() {
|
RectParts PeerShortInfoBox::customCornersFilling() {
|
||||||
|
@ -64,6 +152,20 @@ RectParts PeerShortInfoBox::customCornersFilling() {
|
||||||
|
|
||||||
void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
|
void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
|
||||||
BoxContent::resizeEvent(e);
|
BoxContent::resizeEvent(e);
|
||||||
|
|
||||||
|
_name->moveToLeft(
|
||||||
|
st::shortInfoNamePosition.x(),
|
||||||
|
st::shortInfoWidth - st::shortInfoNamePosition.y() - _name->height(),
|
||||||
|
width());
|
||||||
|
_status->moveToLeft(
|
||||||
|
st::shortInfoStatusPosition.x(),
|
||||||
|
(st::shortInfoWidth
|
||||||
|
- st::shortInfoStatusPosition.y()
|
||||||
|
- _status->height()),
|
||||||
|
height());
|
||||||
|
_rows->resizeToWidth(st::shortInfoWidth);
|
||||||
|
_scroll->resize(st::shortInfoWidth, height() - st::shortInfoWidth);
|
||||||
|
_scroll->move(0, st::shortInfoWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerShortInfoBox::paintEvent(QPaintEvent *e) {
|
void PeerShortInfoBox::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -89,6 +191,27 @@ void PeerShortInfoBox::paintEvent(QPaintEvent *e) {
|
||||||
if (_videoInstance && _videoInstance->ready() && !paused) {
|
if (_videoInstance && _videoInstance->ready() && !paused) {
|
||||||
_videoInstance->markFrameShown();
|
_videoInstance->markFrameShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_shadow.isNull()) {
|
||||||
|
_shadow = Images::GenerateShadow(
|
||||||
|
st::shortInfoShadowHeight,
|
||||||
|
0,
|
||||||
|
kShadowMaxAlpha);
|
||||||
|
}
|
||||||
|
const auto shadowRect = QRect(
|
||||||
|
0,
|
||||||
|
st::shortInfoWidth - st::shortInfoShadowHeight,
|
||||||
|
st::shortInfoWidth,
|
||||||
|
st::shortInfoShadowHeight);
|
||||||
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
p.drawImage(
|
||||||
|
shadowRect,
|
||||||
|
_shadow,
|
||||||
|
QRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
_shadow.width(),
|
||||||
|
st::shortInfoShadowHeight * factor));
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage PeerShortInfoBox::currentVideoFrame() const {
|
QImage PeerShortInfoBox::currentVideoFrame() const {
|
||||||
|
@ -113,6 +236,30 @@ rpl::producer<QString> PeerShortInfoBox::nameValue() const {
|
||||||
}) | rpl::distinct_until_changed();
|
}) | rpl::distinct_until_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<TextWithEntities> PeerShortInfoBox::linkValue() const {
|
||||||
|
return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
|
||||||
|
return Ui::Text::Link(fields.link, fields.link);
|
||||||
|
}) | rpl::distinct_until_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> PeerShortInfoBox::phoneValue() const {
|
||||||
|
return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
|
||||||
|
return fields.phone;
|
||||||
|
}) | rpl::distinct_until_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> PeerShortInfoBox::usernameValue() const {
|
||||||
|
return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
|
||||||
|
return fields.username;
|
||||||
|
}) | rpl::distinct_until_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<TextWithEntities> PeerShortInfoBox::aboutValue() const {
|
||||||
|
return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
|
||||||
|
return fields.about;
|
||||||
|
}) | rpl::distinct_until_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void PeerShortInfoBox::applyUserpic(PeerShortInfoUserpic &&value) {
|
void PeerShortInfoBox::applyUserpic(PeerShortInfoUserpic &&value) {
|
||||||
if (!value.photo.isNull()
|
if (!value.photo.isNull()
|
||||||
&& _userpicImage.cacheKey() != value.photo.cacheKey()) {
|
&& _userpicImage.cacheKey() != value.photo.cacheKey()) {
|
||||||
|
|
|
@ -17,6 +17,10 @@ enum class Error;
|
||||||
struct Information;
|
struct Information;
|
||||||
} // namespace Media::Streaming
|
} // namespace Media::Streaming
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class VerticalLayout;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
enum class PeerShortInfoType {
|
enum class PeerShortInfoType {
|
||||||
User,
|
User,
|
||||||
Group,
|
Group,
|
||||||
|
@ -29,6 +33,7 @@ struct PeerShortInfoFields {
|
||||||
QString link;
|
QString link;
|
||||||
TextWithEntities about;
|
TextWithEntities about;
|
||||||
QString username;
|
QString username;
|
||||||
|
bool isBio = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PeerShortInfoUserpic {
|
struct PeerShortInfoUserpic {
|
||||||
|
@ -56,6 +61,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
void prepareRows();
|
||||||
RectParts customCornersFilling() override;
|
RectParts customCornersFilling() override;
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
@ -64,6 +70,10 @@ private:
|
||||||
[[nodiscard]] QImage currentVideoFrame() const;
|
[[nodiscard]] QImage currentVideoFrame() const;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<QString> nameValue() const;
|
[[nodiscard]] rpl::producer<QString> nameValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<TextWithEntities> linkValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<QString> phoneValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<QString> usernameValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<TextWithEntities> aboutValue() const;
|
||||||
void applyUserpic(PeerShortInfoUserpic &&value);
|
void applyUserpic(PeerShortInfoUserpic &&value);
|
||||||
QRect coverRect() const;
|
QRect coverRect() const;
|
||||||
QRect radialRect() const;
|
QRect radialRect() const;
|
||||||
|
@ -80,11 +90,14 @@ private:
|
||||||
|
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
|
not_null<Ui::VerticalLayout*> _rows;
|
||||||
|
|
||||||
QImage _userpicImage;
|
QImage _userpicImage;
|
||||||
std::unique_ptr<Media::Streaming::Instance> _videoInstance;
|
std::unique_ptr<Media::Streaming::Instance> _videoInstance;
|
||||||
crl::time _videoStartPosition = 0;
|
crl::time _videoStartPosition = 0;
|
||||||
Fn<bool()> _videoPaused;
|
Fn<bool()> _videoPaused;
|
||||||
|
QImage _shadow;
|
||||||
|
|
||||||
rpl::event_stream<> _openRequests;
|
rpl::event_stream<> _openRequests;
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,8 @@ void ProcessFullPhoto(
|
||||||
.about = Info::Profile::AboutWithEntities(peer, peer->about()),
|
.about = Info::Profile::AboutWithEntities(peer, peer->about()),
|
||||||
.username = ((user && !username.isEmpty())
|
.username = ((user && !username.isEmpty())
|
||||||
? ('@' + username)
|
? ('@' + username)
|
||||||
: QString())
|
: QString()),
|
||||||
|
.isBio = (user && !user->isBot()),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/toasts/common_toasts.h"
|
#include "ui/toasts/common_toasts.h"
|
||||||
|
#include "ui/image/image_prepare.h"
|
||||||
#include "ui/round_rect.h"
|
#include "ui/round_rect.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
|
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
|
||||||
|
@ -1549,7 +1550,7 @@ void Panel::setupControlsBackgroundNarrow() {
|
||||||
bottom - (st::groupCallMembersFadeHeight * factor),
|
bottom - (st::groupCallMembersFadeHeight * factor),
|
||||||
full->width(),
|
full->width(),
|
||||||
st::groupCallMembersFadeHeight * factor),
|
st::groupCallMembersFadeHeight * factor),
|
||||||
GenerateShadow(
|
Images::GenerateShadow(
|
||||||
st::groupCallMembersFadeHeight,
|
st::groupCallMembersFadeHeight,
|
||||||
0,
|
0,
|
||||||
255,
|
255,
|
||||||
|
@ -1560,7 +1561,7 @@ void Panel::setupControlsBackgroundNarrow() {
|
||||||
(height - st::groupCallMembersShadowHeight) * factor,
|
(height - st::groupCallMembersShadowHeight) * factor,
|
||||||
full->width(),
|
full->width(),
|
||||||
st::groupCallMembersShadowHeight * factor),
|
st::groupCallMembersShadowHeight * factor),
|
||||||
GenerateShadow(
|
Images::GenerateShadow(
|
||||||
st::groupCallMembersShadowHeight,
|
st::groupCallMembersShadowHeight,
|
||||||
0,
|
0,
|
||||||
255,
|
255,
|
||||||
|
|
|
@ -847,53 +847,6 @@ rpl::lifetime &Viewport::lifetime() {
|
||||||
return _content->lifetime();
|
return _content->lifetime();
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage GenerateShadow(
|
|
||||||
int height,
|
|
||||||
int topAlpha,
|
|
||||||
int bottomAlpha,
|
|
||||||
QColor color) {
|
|
||||||
Expects(topAlpha >= 0 && topAlpha < 256);
|
|
||||||
Expects(bottomAlpha >= 0 && bottomAlpha < 256);
|
|
||||||
Expects(height * style::DevicePixelRatio() < 65536);
|
|
||||||
|
|
||||||
const auto base = (uint32(color.red()) << 16)
|
|
||||||
| (uint32(color.green()) << 8)
|
|
||||||
| uint32(color.blue());
|
|
||||||
const auto premultiplied = (topAlpha == bottomAlpha) || !base;
|
|
||||||
auto result = QImage(
|
|
||||||
QSize(1, height * style::DevicePixelRatio()),
|
|
||||||
(premultiplied
|
|
||||||
? QImage::Format_ARGB32_Premultiplied
|
|
||||||
: QImage::Format_ARGB32));
|
|
||||||
if (topAlpha == bottomAlpha) {
|
|
||||||
color.setAlpha(topAlpha);
|
|
||||||
result.fill(color);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
constexpr auto kShift = 16;
|
|
||||||
constexpr auto kMultiply = (1U << kShift);
|
|
||||||
const auto values = std::abs(topAlpha - bottomAlpha);
|
|
||||||
const auto rows = uint32(result.height());
|
|
||||||
const auto step = (values * kMultiply) / (rows - 1);
|
|
||||||
const auto till = rows * uint32(step);
|
|
||||||
Assert(result.bytesPerLine() == sizeof(uint32));
|
|
||||||
auto ints = reinterpret_cast<uint32*>(result.bits());
|
|
||||||
if (topAlpha < bottomAlpha) {
|
|
||||||
for (auto i = uint32(0); i != till; i += step) {
|
|
||||||
*ints++ = base | ((topAlpha + (i >> kShift)) << 24);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto i = uint32(0); i != till; i += step) {
|
|
||||||
*ints++ = base | ((topAlpha - (i >> kShift)) << 24);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!premultiplied) {
|
|
||||||
result = std::move(result).convertToFormat(
|
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
rpl::producer<QString> MuteButtonTooltip(not_null<GroupCall*> call) {
|
rpl::producer<QString> MuteButtonTooltip(not_null<GroupCall*> call) {
|
||||||
//return rpl::single(std::make_tuple(
|
//return rpl::single(std::make_tuple(
|
||||||
// (Data::GroupCall*)nullptr,
|
// (Data::GroupCall*)nullptr,
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "media/view/media_view_pip.h"
|
#include "media/view/media_view_pip.h"
|
||||||
#include "webrtc/webrtc_video_track.h"
|
#include "webrtc/webrtc_video_track.h"
|
||||||
|
#include "ui/image/image_prepare.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_calls.h"
|
#include "styles/style_calls.h"
|
||||||
#include "styles/palette.h"
|
#include "styles/palette.h"
|
||||||
|
@ -277,7 +278,10 @@ void Viewport::RendererSW::paintTileControls(
|
||||||
|
|
||||||
// Shadow.
|
// Shadow.
|
||||||
if (_shadow.isNull()) {
|
if (_shadow.isNull()) {
|
||||||
_shadow = GenerateShadow(st.shadowHeight, 0, kShadowMaxAlpha);
|
_shadow = Images::GenerateShadow(
|
||||||
|
st.shadowHeight,
|
||||||
|
0,
|
||||||
|
kShadowMaxAlpha);
|
||||||
}
|
}
|
||||||
const auto shadowRect = QRect(
|
const auto shadowRect = QRect(
|
||||||
x,
|
x,
|
||||||
|
|
|
@ -954,7 +954,20 @@ infoScrollDateHideTimeout: historyScrollDateHideTimeout;
|
||||||
infoDateFadeDuration: historyDateFadeDuration;
|
infoDateFadeDuration: historyDateFadeDuration;
|
||||||
|
|
||||||
shortInfoName: FlatLabel(defaultFlatLabel) {
|
shortInfoName: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: groupCallVideoTextFg;
|
||||||
|
maxHeight: 19px;
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(15px semibold);
|
||||||
|
linkFont: font(15px semibold);
|
||||||
|
linkFontOver: font(15px semibold underline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
shortInfoStatus: FlatLabel(defaultFlatLabel) {
|
shortInfoStatus: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: groupCallVideoSubTextFg;
|
||||||
|
maxHeight: 18px;
|
||||||
}
|
}
|
||||||
shortInfoWidth: 336px;
|
shortInfoWidth: 304px;
|
||||||
|
shortInfoNamePosition: point(25px, 37px);
|
||||||
|
shortInfoStatusPosition: point(25px, 14px);
|
||||||
|
shortInfoShadowHeight: 80px;
|
||||||
|
shortInfoLabeledPadding: margins(20px, 16px, 20px, 0px);
|
||||||
|
|
Loading…
Add table
Reference in a new issue