Stickers box redesigned.

This commit is contained in:
John Preston 2016-11-22 12:48:13 +03:00
parent 38d20c506e
commit 6e0394dd42
49 changed files with 1525 additions and 1204 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

View File

@ -708,7 +708,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_box_remove" = "Remove";
"lng_custom_stickers" = "Custom stickers";
"lng_stickers_installed_tab" = "Stickers";
"lng_stickers_featured_tab" = "Trending";
"lng_stickers_archived_tab" = "Archived";
"lng_stickers_remove_pack" = "Remove «{sticker_pack}»?";
"lng_stickers_add_pack" = "Add stickers";
"lng_stickers_share_pack" = "Share Stickers";
@ -718,14 +720,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
"lng_stickers_default_set" = "Great Minds";
"lng_stickers_you_have" = "Manage and reorder sticker packs";
"lng_stickers_packs" = "Sticker Packs";
"lng_stickers_reorder" = "Click and drag to reorder sticker packs";
"lng_stickers_featured" = "Trending Stickers";
"lng_stickers_clear_recent" = "Clear";
"lng_stickers_clear_recent_sure" = "Are you sure you want to clear your frequently used stickers list?";
"lng_stickers_remove" = "Delete";
"lng_stickers_return" = "Undo";
"lng_stickers_restore" = "Restore";
"lng_stickers_count" = "{count:Loading...|# sticker|# stickers}";
"lng_stickers_masks_pack" = "This is a pack of mask stickers. You can use them in the photo editor on our mobile apps.";

View File

@ -692,6 +692,109 @@ void ApiWrap::requestStickerSets() {
}
}
void ApiWrap::saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved) {
for (auto requestId : base::take(_stickerSetDisenableRequests)) {
MTP::cancel(requestId);
}
MTP::cancel(base::take(_stickersReorderRequestId));
MTP::cancel(base::take(_stickersClearRecentRequestId));
auto writeInstalled = true, writeRecent = false, writeCloudRecent = false, writeArchived = false;
auto &recent = cGetRecentStickers();
auto &sets = Global::RefStickerSets();
_stickersOrder = localOrder;
for_const (auto removedSetId, localRemoved) {
if (removedSetId == Stickers::CloudRecentSetId) {
if (sets.remove(Stickers::CloudRecentSetId) != 0) {
writeCloudRecent = true;
}
if (sets.remove(Stickers::CustomSetId)) {
writeInstalled = true;
}
if (!recent.isEmpty()) {
recent.clear();
writeRecent = true;
}
MTPmessages_ClearRecentStickers::Flags flags = 0;
_stickersClearRecentRequestId = MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags)), rpcDone(&ApiWrap::stickersClearRecentDone), rpcFail(&ApiWrap::stickersClearRecentFail));
continue;
}
auto it = sets.find(removedSetId);
if (it != sets.cend()) {
for (auto i = recent.begin(); i != recent.cend();) {
if (it->stickers.indexOf(i->first) >= 0) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
if (!(it->flags & MTPDstickerSet::Flag::f_archived)) {
MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName));
_stickerSetDisenableRequests.insert(MTP::send(MTPmessages_UninstallStickerSet(setId), rpcDone(&ApiWrap::stickerSetDisenableDone), rpcFail(&ApiWrap::stickerSetDisenableFail), 0, 5));
int removeIndex = Global::StickerSetsOrder().indexOf(it->id);
if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex);
if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) {
sets.erase(it);
} else {
if (it->flags & MTPDstickerSet::Flag::f_archived) {
writeArchived = true;
}
it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_archived);
}
}
}
}
// Clear all installed flags, set only for sets from order.
for (auto &set : sets) {
if (!(set.flags & MTPDstickerSet::Flag::f_archived)) {
set.flags &= ~MTPDstickerSet::Flag::f_installed;
}
}
auto &order(Global::RefStickerSetsOrder());
order.clear();
for_const (auto setId, _stickersOrder) {
auto it = sets.find(setId);
if (it != sets.cend()) {
if ((it->flags & MTPDstickerSet::Flag::f_archived) && !localRemoved.contains(it->id)) {
MTPInputStickerSet mtpSetId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName));
_stickerSetDisenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(mtpSetId, MTP_boolFalse()), rpcDone(&ApiWrap::stickerSetDisenableDone), rpcFail(&ApiWrap::stickerSetDisenableFail), 0, 5));
it->flags &= ~MTPDstickerSet::Flag::f_archived;
writeArchived = true;
}
order.push_back(setId);
it->flags |= MTPDstickerSet::Flag::f_installed;
}
}
for (auto it = sets.begin(); it != sets.cend();) {
if ((it->flags & MTPDstickerSet_ClientFlag::f_featured)
|| (it->flags & MTPDstickerSet::Flag::f_installed)
|| (it->flags & MTPDstickerSet::Flag::f_archived)
|| (it->flags & MTPDstickerSet_ClientFlag::f_special)) {
++it;
} else {
it = sets.erase(it);
}
}
if (writeInstalled) Local::writeInstalledStickers();
if (writeRecent) Local::writeUserSettings();
if (writeArchived) Local::writeArchivedStickers();
if (writeCloudRecent) Local::writeRecentStickers();
emit App::main()->stickersUpdated();
if (_stickerSetDisenableRequests.isEmpty()) {
stickersSaveOrder();
} else {
MTP::sendAnything();
}
}
void ApiWrap::joinChannel(ChannelData *channel) {
if (channel->amIn()) {
channelAmInUpdated(channel);
@ -1180,3 +1283,57 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
ApiWrap::~ApiWrap() {
App::clearHistories();
}
void ApiWrap::stickerSetDisenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) {
_stickerSetDisenableRequests.remove(req);
if (_stickerSetDisenableRequests.isEmpty()) {
stickersSaveOrder();
}
}
bool ApiWrap::stickerSetDisenableFail(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
_stickerSetDisenableRequests.remove(req);
if (_stickerSetDisenableRequests.isEmpty()) {
stickersSaveOrder();
}
return true;
}
void ApiWrap::stickersSaveOrder() {
if (_stickersOrder.size() > 1) {
QVector<MTPlong> mtpOrder;
mtpOrder.reserve(_stickersOrder.size());
for_const (auto setId, _stickersOrder) {
mtpOrder.push_back(MTP_long(setId));
}
MTPmessages_ReorderStickerSets::Flags flags = 0;
_stickersReorderRequestId = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector<MTPlong>(mtpOrder)), rpcDone(&ApiWrap::stickersReorderDone), rpcFail(&ApiWrap::stickersReorderFail));
} else {
stickersReorderDone(MTP_boolTrue());
}
}
void ApiWrap::stickersReorderDone(const MTPBool &result) {
_stickersReorderRequestId = 0;
}
bool ApiWrap::stickersReorderFail(const RPCError &result) {
if (MTP::isDefaultHandledError(result)) return false;
_stickersReorderRequestId = 0;
Global::SetLastStickersUpdate(0);
App::main()->updateStickers();
return true;
}
void ApiWrap::stickersClearRecentDone(const MTPBool &result) {
_stickersClearRecentRequestId = 0;
}
bool ApiWrap::stickersClearRecentFail(const RPCError &result) {
if (MTP::isDefaultHandledError(result)) return false;
_stickersClearRecentRequestId = 0;
return true;
}

View File

@ -26,7 +26,6 @@ class ApiWrap : public QObject, public RPCSender {
Q_OBJECT
public:
ApiWrap(QObject *parent);
void init();
@ -51,6 +50,7 @@ public:
void scheduleStickerSetRequest(uint64 setId, uint64 access);
void requestStickerSets();
void saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved);
void joinChannel(ChannelData *channel);
void leaveChannel(ChannelData *channel);
@ -67,11 +67,9 @@ public:
~ApiWrap();
signals:
void fullPeerUpdated(PeerData *peer);
public slots:
void resolveMessageDatas();
void resolveWebPages();
@ -79,7 +77,6 @@ public slots:
void saveDraftsToCloud();
private:
void updatesReceived(const MTPUpdates &updates);
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
@ -158,4 +155,16 @@ private:
void saveCloudDraftDone(History *history, const MTPBool &result, mtpRequestId requestId);
bool saveCloudDraftFail(History *history, const RPCError &error, mtpRequestId requestId);
OrderedSet<mtpRequestId> _stickerSetDisenableRequests;
void stickerSetDisenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req);
bool stickerSetDisenableFail(const RPCError &error, mtpRequestId req);
Stickers::Order _stickersOrder;
mtpRequestId _stickersReorderRequestId = 0;
void stickersSaveOrder();
void stickersReorderDone(const MTPBool &result);
bool stickersReorderFail(const RPCError &result);
mtpRequestId _stickersClearRecentRequestId = 0;
void stickersClearRecentDone(const MTPBool &result);
bool stickersClearRecentFail(const RPCError &result);
};

View File

@ -2093,7 +2093,10 @@ namespace {
Global::SetLastStickersUpdate(0);
Global::SetLastRecentStickersUpdate(0);
Global::SetFeaturedStickerSetsOrder(Stickers::Order());
Global::SetFeaturedStickerSetsUnreadCount(0);
if (Global::FeaturedStickerSetsUnreadCount() != 0) {
Global::SetFeaturedStickerSetsUnreadCount(0);
Global::RefFeaturedStickerSetsUnreadCountChanged().notify();
}
Global::SetLastFeaturedStickersUpdate(0);
Global::SetArchivedStickerSetsOrder(Stickers::Order());
cSetSavedGifs(SavedGifs());

View File

@ -60,7 +60,7 @@ void AboutBox::resizeEvent(QResizeEvent *e) {
void AboutBox::onVersion() {
if (cRealBetaVersion()) {
QString url = qsl("https://tdesktop.com/");
auto url = qsl("https://tdesktop.com/");
switch (cPlatform()) {
case dbipWindows: url += qsl("win/%1.zip"); break;
case dbipMac: url += qsl("mac/%1.zip"); break;

View File

@ -58,13 +58,17 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) {
}
void AbstractBox::resizeEvent(QResizeEvent *e) {
updateBlockTitleGeometry();
LayerWidget::resizeEvent(e);
}
void AbstractBox::updateBlockTitleGeometry() {
if (_blockClose) {
_blockClose->moveToRight(0, 0);
}
if (_blockShadow) {
_blockShadow->setGeometry(0, st::boxBlockTitleHeight, width(), st::boxBlockTitleShadow.height());
}
LayerWidget::resizeEvent(e);
}
void AbstractBox::parentResized() {
@ -143,11 +147,22 @@ void AbstractBox::onClose() {
emit closed(this);
}
void AbstractBox::setBlockTitle(bool block) {
void AbstractBox::setBlockTitle(bool block, bool withClose, bool withShadow) {
_blockTitle = block;
_blockShadow.create(this, st::boxBlockTitleShadow);
_blockClose.create(this, st::boxBlockTitleClose);
_blockClose->setClickedCallback([this] { onClose(); });
if (withClose) {
_blockClose.create(this, st::boxBlockTitleClose);
_blockClose->setClickedCallback([this] { onClose(); });
_blockClose->show();
} else {
_blockClose.destroy();
}
if (withShadow) {
_blockShadow.create(this, st::boxBlockTitleShadow);
_blockShadow->show();
} else {
_blockShadow.destroy();
}
updateBlockTitleGeometry();
}
void AbstractBox::raiseShadow() {

View File

@ -38,7 +38,7 @@ public:
void setTitleText(const QString &title);
void setAdditionalTitle(const QString &additionalTitle);
void setBlockTitle(bool block);
void setBlockTitle(bool block, bool withClose = true, bool withShadow = true);
void raiseShadow();
public slots:
@ -59,9 +59,11 @@ protected:
}
private:
int _maxHeight = 0;
void updateBlockTitleGeometry();
int countHeight() const;
int _maxHeight = 0;
bool _closed = false;
QString _title;

View File

@ -470,7 +470,7 @@ confirmCaptionArea: InputArea(defaultInputArea) {
textMargins: margins(1px, 6px, 1px, 4px);
heightMax: 56px;
}
confirmBg: #f2f2f2;
confirmBg: windowBgOver;
confirmMaxHeight: 245px;
confirmCompressedSkip: 10px;

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/discrete_slider.h"
#include "ui/widgets/discrete_sliders.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "styles/style_window.h"

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class RoundButton;
class LinkButton;
class DiscreteSlider;
class SettingsSlider;
} // namespace Ui
class NotificationsBox : public AbstractBox {
@ -70,7 +70,7 @@ private:
ScreenCorner _downCorner = ScreenCorner::TopLeft;
int _oldCount;
ChildWidget<Ui::DiscreteSlider> _countSlider;
ChildWidget<Ui::SettingsSlider> _countSlider;
ChildWidget<Ui::RoundButton> _done;
QVector<SampleWidget*> _cornerSamples[4];

File diff suppressed because it is too large Load Diff

View File

@ -24,10 +24,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class ConfirmBox;
namespace style {
struct RippleAnimation;
} // namespace style
namespace Ui {
class PlainShadow;
class RoundButton;
class RippleAnimation;
class SettingsSlider;
} // namespace Ui
class StickersBox : public ItemListBox, public RPCSender {
@ -43,8 +48,6 @@ public:
StickersBox(Section section = Section::Installed);
StickersBox(const Stickers::Order &archivedIds);
~StickersBox();
public slots:
void onStickersUpdated();
@ -52,8 +55,6 @@ public slots:
void onNoDraggingScroll();
void onScrollTimer();
void onSave();
private slots:
void onScroll();
@ -64,31 +65,56 @@ protected:
void closePressed() override;
private:
void refreshTabs();
void setup();
int32 countHeight() const;
void rebuildList();
void updateTabsGeometry();
void switchTab();
void installSet(uint64 setId);
void disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req);
bool disenableFail(const RPCError &error, mtpRequestId req);
void reorderDone(const MTPBool &result);
bool reorderFail(const RPCError &result);
void saveOrder();
QPixmap grabContentCache();
void installDone(const MTPmessages_StickerSetInstallResult &result);
bool installFail(uint64 setId, const RPCError &error);
void updateVisibleTopBottom();
void preloadArchivedSets();
void requestArchivedSets();
void checkLoadMoreArchived();
void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result);
ChildWidget<Ui::PlainShadow> _topShadow;
ChildWidget<Ui::SettingsSlider> _tabs = { nullptr };
QList<Section> _tabIndices;
class CounterWidget;
ChildWidget<CounterWidget> _unreadBadge = { nullptr };
Section _section;
class Inner;
ChildWidget<Inner> _inner;
ChildWidget<Ui::RoundButton> _save = { nullptr };
ChildWidget<Ui::RoundButton> _cancel = { nullptr };
OrderedSet<mtpRequestId> _disenableRequests;
mtpRequestId _reorderRequest = 0;
ChildWidget<Ui::PlainShadow> _topShadow = { nullptr };
struct Tab {
Tab() : widget(nullptr) {
}
template <typename ...Args>
Tab(int index, Args&&... args) : index(index), widget(std_::forward<Args>(args)...) {
}
int index = 0;
ChildWidget<Inner> widget = { nullptr };
int scrollTop = 0;
};
Tab _installed;
Tab _featured;
Tab _archived;
Tab *_tab = nullptr;
ChildWidget<Ui::RoundButton> _done = { nullptr };
ChildWidget<ScrollableBoxShadow> _bottomShadow = { nullptr };
FloatAnimation _a_slide;
bool _slideLeft = false;
QPixmap _leftCache, _rightCache;
QTimer _scrollTimer;
int32 _scrollDelta = 0;
@ -97,14 +123,19 @@ private:
int _aboutHeight = 0;
mtpRequestId _archivedRequestId = 0;
bool _archivedLoaded = false;
bool _allArchivedLoaded = false;
bool _someArchivedLoaded = false;
Stickers::Order _localOrder;
Stickers::Order _localRemoved;
};
int32 stickerPacksCount(bool includeDisabledOfficial = false);
int stickerPacksCount(bool includeArchivedOfficial = false);
// This class is hold in header because it requires Qt preprocessing.
class StickersBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
class StickersBox::Inner : public TWidget, private base::Subscriber {
Q_OBJECT
public:
@ -116,14 +147,17 @@ public:
void updateSize();
void updateRows(); // refresh only pack cover stickers
bool appendSet(const Stickers::Set &set);
bool savingStart() {
if (_saving) return false;
_saving = true;
return true;
}
Stickers::Order getOrder() const;
Stickers::Order getDisabledSets() const;
Stickers::Order getFullOrder() const;
Stickers::Order getRemovedSets() const;
void setFullOrder(const Stickers::Order &order);
void setRemovedSets(const Stickers::Order &removed);
void setInstallSetCallback(base::lambda<void(uint64 setId)> &&callback) {
_installSetCallback = std_::move(callback);
}
void setVisibleScrollbar(int32 width);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
@ -143,37 +177,34 @@ signals:
public slots:
void onUpdateSelected();
void onClearRecent();
void onClearBoxDestroyed(QObject *box);
private slots:
void onImageLoaded();
private:
template <typename Check>
Stickers::Order collectSets(Check check) const;
int getRowIndex(uint64 setId) const;
void setRowRemoved(int index, bool removed);
void setActionDown(int newActionDown);
void setPressed(int newPressed);
void setup();
QRect relativeAddButtonRect() const;
void paintButton(Painter &p, int y, bool selected, std_::unique_ptr<Ui::RippleAnimation> &ripple, const QString &text, int badgeCounter) const;
QRect relativeButtonRect(bool removeButton) const;
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
void step_shifting(uint64 ms, bool timer);
void paintRow(Painter &p, int32 index, uint64 ms);
void paintRow(Painter &p, int index, uint64 ms);
void paintFakeButton(Painter &p, int index, uint64 ms);
void clear();
void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const;
void readVisibleSets();
void installSet(uint64 setId);
void installDone(const MTPmessages_StickerSetInstallResult &result);
bool installFail(uint64 setId, const RPCError &error);
Section _section;
Stickers::Order _archivedIds;
int32 _rowHeight;
struct StickerSetRow {
StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id)
struct Row {
Row(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool archived, bool removed, int32 pixw, int32 pixh) : id(id)
, sticker(sticker)
, count(count)
, title(title)
@ -181,66 +212,66 @@ private:
, installed(installed)
, official(official)
, unread(unread)
, disabled(disabled)
, recent(recent)
, archived(archived)
, removed(removed)
, pixw(pixw)
, pixh(pixh)
, yadd(0, 0) {
}
bool isRecentSet() const {
return (id == Stickers::CloudRecentSetId);
}
uint64 id;
DocumentData *sticker;
int32 count;
QString title;
int titleWidth;
bool installed, official, unread, disabled, recent;
bool installed, official, unread, archived, removed;
int32 pixw, pixh;
anim::ivalue yadd;
QSharedPointer<Ui::RippleAnimation> ripple;
};
using StickerSetRows = QList<StickerSetRow*>;
using Rows = QList<Row*>;
void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth);
void fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(const Stickers::Set &set) const;
QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const;
void fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled);
void fillSetFlags(const Stickers::Set &set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
int countMaxNameWidth() const;
StickerSetRows _rows;
Rows _rows;
QList<uint64> _animStartTimes;
uint64 _aboveShadowFadeStart = 0;
anim::fvalue _aboveShadowFadeOpacity = { 0., 0. };
Animation _a_shifting;
base::lambda<void(uint64 setId)> _installSetCallback;
int _visibleTop = 0;
int _visibleBottom = 0;
int _itemsTop = 0;
bool _saving = false;
int _actionSel = -1;
int _actionDown = -1;
int _clearWidth, _removeWidth, _returnWidth, _restoreWidth;
ConfirmBox *_clearBox = nullptr;
QString _addText;
int _addWidth = 0;
QString _undoText;
int _undoWidth = 0;
int _buttonHeight = 0;
bool _hasFeaturedButton = false;
bool _hasArchivedButton = false;
QPoint _mouse;
int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button
int _pressed = -2;
bool _inDragArea = false;
int _selected = -1;
int _pressed = -1;
QPoint _dragStart;
int _started = -1;
int _dragging = -1;
int _above = -1;
std_::unique_ptr<Ui::RippleAnimation> _archivedRipple;
std_::unique_ptr<Ui::RippleAnimation> _featuredRipple;
Ui::RectShadow _aboveShadow;
int _scrollbar = 0;

View File

@ -100,7 +100,6 @@ dialogsUnlockIconOver: icon {{ "dialogs_unlock", dialogsMenuIconFgOver }};
dialogsFilter: FlatInput(defaultFlatInput) {
font: font(fsize);
bgColor: #f2f2f2;
phColor: #949494;
phFocusColor: #a4a4a4;

View File

@ -214,13 +214,14 @@ UnreadBadgeStyle::UnreadBadgeStyle()
, selected(false)
, muted(false)
, size(st::dialogsUnreadHeight)
, padding(st::dialogsUnreadPadding)
, sizeId(UnreadBadgeInDialogs)
, font(st::dialogsUnreadFont) {
}
void paintUnreadCount(Painter &p, const QString &text, int x, int y, const UnreadBadgeStyle &st, int *outUnreadWidth) {
int unreadWidth = st.font->width(text);
int unreadRectWidth = unreadWidth + 2 * st::dialogsUnreadPadding;
int unreadRectWidth = unreadWidth + 2 * st.padding;
int unreadRectHeight = st.size;
accumulate_max(unreadRectWidth, unreadRectHeight);
@ -237,9 +238,10 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea
paintUnreadBadge(p, QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight), st);
auto textTop = st.textTop ? st.textTop : (unreadRectHeight - st.font->height) / 2;
p.setFont(st.font);
p.setPen(st.active ? st::dialogsUnreadFgActive : (st.selected ? st::dialogsUnreadFgOver : st::dialogsUnreadFg));
p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + (unreadRectHeight - st.font->height) / 2 + st.font->ascent, text);
p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st.font->ascent, text);
}
void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground) {
@ -282,7 +284,7 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele
st.active = active;
st.muted = history->mute();
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
availableWidth -= unreadWidth + st::dialogsUnreadPadding;
availableWidth -= unreadWidth + st.padding;
}
if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height), active, selected, history->textCachedFor, history->lastItemTextCache);

View File

@ -52,7 +52,9 @@ struct UnreadBadgeStyle {
bool active;
bool selected;
bool muted;
int textTop = 0;
int size;
int padding;
UnreadBadgeSize sizeId;
style::font font;
};

View File

@ -637,6 +637,7 @@ struct Data {
uint64 LastRecentStickersUpdate = 0;
Stickers::Order FeaturedStickerSetsOrder;
int FeaturedStickerSetsUnreadCount = 0;
base::Observable<void> FeaturedStickerSetsUnreadCountChanged;
uint64 LastFeaturedStickersUpdate = 0;
Stickers::Order ArchivedStickerSetsOrder;
@ -754,6 +755,7 @@ DefineVar(Global, uint64, LastStickersUpdate);
DefineVar(Global, uint64, LastRecentStickersUpdate);
DefineVar(Global, Stickers::Order, FeaturedStickerSetsOrder);
DefineVar(Global, int, FeaturedStickerSetsUnreadCount);
DefineRefVar(Global, base::Observable<void>, FeaturedStickerSetsUnreadCountChanged);
DefineVar(Global, uint64, LastFeaturedStickersUpdate);
DefineVar(Global, Stickers::Order, ArchivedStickerSetsOrder);

View File

@ -317,6 +317,7 @@ DeclareVar(uint64, LastStickersUpdate);
DeclareVar(uint64, LastRecentStickersUpdate);
DeclareVar(Stickers::Order, FeaturedStickerSetsOrder);
DeclareVar(int, FeaturedStickerSetsUnreadCount);
DeclareRefVar(base::Observable<void>, FeaturedStickerSetsUnreadCountChanged);
DeclareVar(uint64, LastFeaturedStickersUpdate);
DeclareVar(Stickers::Order, ArchivedStickerSetsOrder);

View File

@ -701,10 +701,10 @@ void HistoryItem::nextItemChanged() {
void HistoryItem::recountAttachToPrevious() {
bool attach = false;
if (auto previous = previousItem()) {
if (!isPost() && !Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) {
attach = !previous->isPost()
&& !previous->serviceMsg()
&& !previous->isEmpty()
if (!Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) {
attach = !isPost() && !previous->isPost()
&& !serviceMsg() && !previous->serviceMsg()
&& !isEmpty() && !previous->isEmpty()
&& previous->from() == from()
&& (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta);
}

View File

@ -3992,7 +3992,10 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic
it = sets.erase(it);
}
}
Global::SetFeaturedStickerSetsUnreadCount(unreadCount);
if (Global::FeaturedStickerSetsUnreadCount() != unreadCount) {
Global::SetFeaturedStickerSetsUnreadCount(unreadCount);
Global::RefFeaturedStickerSetsUnreadCountChanged().notify();
}
if (Local::countFeaturedStickersHash() != d.vhash.v) {
LOG(("API Error: received featured stickers hash %1 while counted hash is %2").arg(d.vhash.v).arg(Local::countFeaturedStickersHash()));

View File

@ -36,7 +36,7 @@ introCountry: countryInput {
width: 300px;
height: 41px;
top: 33px;
bgColor: #f2f2f2;
bgColor: windowBgOver;
ptrSize: size(15px, 8px);
textMrg: margins(16px, 5px, 16px, 15px);
font: defaultInputFont;

View File

@ -3224,7 +3224,7 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
outOrder->push_front(setId);
}
} else if (setId == Stickers::CustomSetId) {
setTitle = lang(lng_custom_stickers);
setTitle = qsl("Custom stickers");
setFlags |= qFlags(MTPDstickerSet_ClientFlag::f_special);
} else if (setId == Stickers::CloudRecentSetId) {
setTitle = lang(lng_recent_stickers);
@ -3404,7 +3404,7 @@ void importOldRecentStickers() {
recent.clear();
auto &def = sets.insert(Stickers::DefaultSetId, Stickers::Set(Stickers::DefaultSetId, 0, lang(lng_stickers_default_set), QString(), 0, 0, MTPDstickerSet::Flag::f_official | MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value();
auto &custom = sets.insert(Stickers::CustomSetId, Stickers::Set(Stickers::CustomSetId, 0, lang(lng_custom_stickers), QString(), 0, 0, MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value();
auto &custom = sets.insert(Stickers::CustomSetId, Stickers::Set(Stickers::CustomSetId, 0, qsl("Custom stickers"), QString(), 0, 0, MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value();
QMap<uint64, bool> read;
while (!stickers.stream.atEnd()) {
@ -3480,7 +3480,10 @@ void readFeaturedStickers() {
++unreadCount;
}
}
Global::SetFeaturedStickerSetsUnreadCount(unreadCount);
if (Global::FeaturedStickerSetsUnreadCount() != unreadCount) {
Global::SetFeaturedStickerSetsUnreadCount(unreadCount);
Global::RefFeaturedStickerSetsUnreadCountChanged().notify();
}
}
void readRecentStickers() {

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_cover.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/media_slider.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/buttons.h"
#include "media/media_audio.h"
#include "media/view/media_clip_playback.h"

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/media_audio.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/media_slider.h"
#include "ui/widgets/continuous_sliders.h"
#include "styles/style_media_player.h"
#include "styles/style_widgets.h"
#include "mainwindow.h"

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_widget.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/filled_slider.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
#include "media/media_audio.h"

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/view/media_clip_volume_controller.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/media_slider.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/effects/widget_fade_wrap.h"
#include "ui/widgets/buttons.h"
#include "media/media_audio.h"

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/continuous_slider.h"
#include "ui/widgets/continuous_sliders.h"
struct AudioPlaybackState;

View File

@ -94,7 +94,7 @@ overviewLoader: size(34px, 14px);
overviewLoaderPoint: size(4px, 4px);
overviewLoaderSkip: 4px;
playlistHoverBg: #f2f2f2;
playlistHoverBg: windowBgOver;
playlistPadding: 10px;
linksSearchMargin: margins(20px, 20px, 20px, 0px);
@ -121,7 +121,6 @@ overviewLinksChecked: icon {
overviewFilter: FlatInput(defaultFlatInput) {
font: font(fsize);
bgColor: #f2f2f2;
phColor: #949494;
phFocusColor: #a4a4a4;
icon: fieldSearchIcon;

View File

@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "boxes/confirmbox.h"
#include "application.h"
#include "ui/widgets/discrete_slider.h"
#include "ui/widgets/discrete_sliders.h"
namespace Settings {
namespace {
@ -52,7 +52,7 @@ ScaleWidget::ScaleWidget(QWidget *parent, UserData *self) : BlockWidget(parent,
void ScaleWidget::createControls() {
style::margins margin(0, 0, 0, st::settingsSmallSkip);
addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), SLOT(onAutoChosen()), (cConfigScale() == dbisAuto));
addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), SLOT(onAutoChanged()), (cConfigScale() == dbisAuto));
addChildRow(_scale, style::margins(0, 0, 0, 0));
_scale->addSection(scaleLabel(dbisOne));
@ -63,7 +63,7 @@ void ScaleWidget::createControls() {
_scale->setSectionActivatedCallback([this] { scaleChanged(); });
}
void ScaleWidget::onAutoChosen() {
void ScaleWidget::onAutoChanged() {
auto newScale = _auto->checked() ? dbisAuto : cEvalScale(cConfigScale());
if (newScale == cScreenScale()) {
if (newScale != cScale()) {
@ -81,6 +81,11 @@ void ScaleWidget::onAutoChosen() {
}
void ScaleWidget::setScale(DBIScale newScale) {
if (_inSetScale) return;
_inSetScale = true;
auto guard = base::scope_guard([this] { _inSetScale = false; });
if (newScale == cScreenScale()) newScale = dbisAuto;
if (newScale == dbisAuto && !_auto->checked()) {
_auto->setChecked(true);
} else if (newScale != dbisAuto && _auto->checked()) {
@ -111,9 +116,6 @@ void ScaleWidget::scaleChanged() {
case 2: newScale = dbisOneAndHalf; break;
case 3: newScale = dbisTwo; break;
}
if (newScale == cScreenScale()) {
newScale = dbisAuto;
}
setScale(newScale);
}

View File

@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class Checkbox;
class DiscreteSlider;
class SettingsSlider;
} // namespace Ui
namespace Settings {
@ -36,7 +36,7 @@ public:
ScaleWidget(QWidget *parent, UserData *self);
private slots:
void onAutoChosen();
void onAutoChanged();
void onRestartNow();
void onCancel();
@ -46,9 +46,10 @@ private:
void setScale(DBIScale newScale);
ChildWidget<Ui::Checkbox> _auto = { nullptr };
ChildWidget<Ui::DiscreteSlider> _scale = { nullptr };
ChildWidget<Ui::SettingsSlider> _scale = { nullptr };
DBIScale _newScale = dbisAuto;
bool _inSetScale = false;
};

View File

@ -2530,7 +2530,6 @@ void EmojiPan::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 opaci
t_assert(started());
t_assert(dt >= 0.);
auto &transition = anim::easeOutCirc;
_frameAlpha = anim::interpolate(1, 256, opacity);
auto frameInts = _frameInts + _innerLeft + _innerTop * _frameIntsPerLine;

View File

@ -83,6 +83,26 @@ void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) {
emit App::main()->stickersUpdated();
}
// For testing: Just apply random subset or your sticker sets as archived.
bool applyArchivedResultFake() {
if (rand_value<uint32>() % 128 < 64) {
return false;
}
auto sets = QVector<MTPStickerSetCovered>();
for (auto &set : Global::RefStickerSets()) {
if ((set.flags & MTPDstickerSet::Flag::f_installed) && !(set.flags & MTPDstickerSet_ClientFlag::f_special)) {
if (rand_value<uint32>() % 128 < 64) {
auto data = MTP_stickerSet(MTP_flags(set.flags | MTPDstickerSet::Flag::f_archived), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash));
sets.push_back(MTP_stickerSetCovered(data, MTP_documentEmpty(MTP_long(0))));
}
}
}
if (sets.size() > 3) sets = sets.mid(0, 3);
auto fakeResult = MTP_messages_stickerSetInstallResultArchive(MTP_vector<MTPStickerSetCovered>(sets));
applyArchivedResult(fakeResult.c_messages_stickerSetInstallResultArchive());
return true;
}
void installLocally(uint64 setId) {
auto &sets = Global::RefStickerSets();
auto it = sets.find(setId);
@ -199,7 +219,10 @@ void FeaturedReader::onReadSets() {
if (!wrappedIds.empty()) {
MTP::send(MTPmessages_ReadFeaturedStickers(MTP_vector<MTPlong>(wrappedIds)), rpcDone(&readFeaturedDone));
Global::SetFeaturedStickerSetsUnreadCount(count);
if (Global::FeaturedStickerSetsUnreadCount() != count) {
Global::SetFeaturedStickerSetsUnreadCount(count);
Global::RefFeaturedStickerSetsUnreadCountChanged().notify();
}
}
}

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Stickers {
void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d);
bool applyArchivedResultFake(); // For testing.
void installLocally(uint64 setId);
void undoInstallLocally(uint64 setId);
void markFeaturedAsRead(uint64 setId);

View File

@ -41,27 +41,46 @@ stickersTrendingSubheaderTop: 20px;
stickersTrendingAddTop: 3px;
stickersTrendingAdd: RoundButton(defaultActiveButton) {
width: -17px;
width: -16px;
height: 26px;
textTop: 4px;
}
stickersRemove: IconButton(defaultIconButton) {
width: 40px;
height: 40px;
icon: icon {{ "stickers_remove", menuIconFg }};
iconOver: icon {{ "stickers_remove", menuIconFgOver }};
rippleAreaSize: 40px;
rippleAreaPosition: point(0px, 0px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
stickersUndoRemove: RoundButton(defaultLightButton) {
width: -16px;
height: 26px;
textTop: 4px;
}
stickersRemoveSkip: 4px;
stickersReorderIcon: icon {{ "stickers_reorder", menuIconFg }};
stickersReorderSkip: 13px;
stickerEmojiSkip: 5px;
stickersAddIcon: icon {{ "stickers_add", #ffffff }};
stickersAddSize: size(30px, 24px);
stickersFeaturedHeight: 32px;
stickersFeaturedFont: contactsNameFont;
stickersFeaturedPosition: point(16px, 6px);
stickersFeaturedBadgeFont: semiboldFont;
stickersFeaturedBadgeSize: 21px;
stickersFeaturedBadgeFont: font(12px bold);
stickersFeaturedBadgeSize: 15px;
stickersFeaturedBadgeTextTop: -1px;
stickersFeaturedBadgePadding: 4px;
stickersFeaturedBadgeSkip: 4px;
stickersFeaturedBadgeTop: 9px;
stickersFeaturedPen: lightButtonFg;
stickersFeaturedUnreadBg: msgFileInBg;
stickersFeaturedUnreadSize: 5px;
stickersFeaturedUnreadSkip: 5px;
stickersFeaturedUnreadTop: 7px;
stickersFeaturedInstalled: icon {{ "mediaview_save_check", lightButtonFg }};
stickersFeaturedInstalled: icon {{ "send_control_save", lightButtonFg }};
stickersMaxHeight: 440px;
stickersPadding: margins(19px, 17px, 19px, 17px);

View File

@ -744,7 +744,7 @@ public:
return flags & MTPDchannel::Flag::f_verified;
}
bool canAddMembers() const {
return amCreator() || amEditor() || (flags & MTPDchannel::Flag::f_democracy);
return amCreator() || amEditor() || (amIn() && (flags & MTPDchannel::Flag::f_democracy));
}
bool canEditPhoto() const {
return amCreator() || (amEditor() && isMegagroup());

View File

@ -1,175 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/continuous_slider.h"
namespace Ui {
namespace {
constexpr auto kByWheelFinishedTimeout = 1000;
} // namespace
ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent)
, _a_value(animation(this, &ContinuousSlider::step_value)) {
setCursor(style::cur_pointer);
}
float64 ContinuousSlider::value() const {
return a_value.current();
}
void ContinuousSlider::setDisabled(bool disabled) {
if (_disabled != disabled) {
_disabled = disabled;
setCursor(_disabled ? style::cur_default : style::cur_pointer);
update();
}
}
void ContinuousSlider::setMoveByWheel(bool move) {
if (move != moveByWheel()) {
if (move) {
_byWheelFinished = std_::make_unique<SingleTimer>();
_byWheelFinished->setTimeoutHandler([this] {
if (_changeFinishedCallback) {
_changeFinishedCallback(getCurrentValue(getms()));
}
});
} else {
_byWheelFinished.reset();
}
}
}
void ContinuousSlider::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::fvalue(value, value);
_a_value.stop();
}
update();
}
void ContinuousSlider::setFadeOpacity(float64 opacity) {
_fadeOpacity = opacity;
update();
}
void ContinuousSlider::step_value(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
updateDownValueFromPos(e->pos());
}
}
float64 ContinuousSlider::computeValue(const QPoint &pos) const {
auto seekRect = myrtlrect(getSeekRect());
auto result = isHorizontal() ?
(pos.x() - seekRect.x()) / float64(seekRect.width()) :
(1. - (pos.y() - seekRect.y()) / float64(seekRect.height()));
return snap(result, 0., 1.);
}
void ContinuousSlider::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
_downValue = computeValue(e->pos());
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) {
if (_mouseDown) {
_mouseDown = false;
if (_changeFinishedCallback) {
_changeFinishedCallback(_downValue);
}
a_value = anim::fvalue(_downValue, _downValue);
_a_value.stop();
update();
}
}
void ContinuousSlider::wheelEvent(QWheelEvent *e) {
if (_mouseDown || !moveByWheel()) {
return;
}
#ifdef OS_MAC_OLD
constexpr auto step = 120;
#else // OS_MAC_OLD
constexpr auto step = static_cast<int>(QWheelEvent::DefaultDeltasPerStep);
#endif // OS_MAC_OLD
constexpr auto coef = 1. / (step * 10.);
auto deltaX = e->angleDelta().x(), deltaY = e->angleDelta().y();
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
deltaY *= -1;
} else {
deltaX *= -1;
}
auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY;
auto finalValue = snap(a_value.to() + delta * coef, 0., 1.);
setValue(finalValue, false);
if (_changeProgressCallback) {
_changeProgressCallback(finalValue);
}
_byWheelFinished->start(kByWheelFinishedTimeout);
}
void ContinuousSlider::updateDownValueFromPos(const QPoint &pos) {
_downValue = computeValue(pos);
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::enterEvent(QEvent *e) {
setOver(true);
}
void ContinuousSlider::leaveEvent(QEvent *e) {
setOver(false);
}
void ContinuousSlider::setOver(bool over) {
if (_over == over) return;
_over = over;
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, getOverDuration());
}
} // namespace Ui

View File

@ -0,0 +1,293 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/continuous_sliders.h"
namespace Ui {
namespace {
constexpr auto kByWheelFinishedTimeout = 1000;
} // namespace
ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent)
, _a_value(animation(this, &ContinuousSlider::step_value)) {
setCursor(style::cur_pointer);
}
float64 ContinuousSlider::value() const {
return a_value.current();
}
void ContinuousSlider::setDisabled(bool disabled) {
if (_disabled != disabled) {
_disabled = disabled;
setCursor(_disabled ? style::cur_default : style::cur_pointer);
update();
}
}
void ContinuousSlider::setMoveByWheel(bool move) {
if (move != moveByWheel()) {
if (move) {
_byWheelFinished = std_::make_unique<SingleTimer>();
_byWheelFinished->setTimeoutHandler([this] {
if (_changeFinishedCallback) {
_changeFinishedCallback(getCurrentValue(getms()));
}
});
} else {
_byWheelFinished.reset();
}
}
}
void ContinuousSlider::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::fvalue(value, value);
_a_value.stop();
}
update();
}
void ContinuousSlider::setFadeOpacity(float64 opacity) {
_fadeOpacity = opacity;
update();
}
void ContinuousSlider::step_value(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
updateDownValueFromPos(e->pos());
}
}
float64 ContinuousSlider::computeValue(const QPoint &pos) const {
auto seekRect = myrtlrect(getSeekRect());
auto result = isHorizontal() ?
(pos.x() - seekRect.x()) / float64(seekRect.width()) :
(1. - (pos.y() - seekRect.y()) / float64(seekRect.height()));
return snap(result, 0., 1.);
}
void ContinuousSlider::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
_downValue = computeValue(e->pos());
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) {
if (_mouseDown) {
_mouseDown = false;
if (_changeFinishedCallback) {
_changeFinishedCallback(_downValue);
}
a_value = anim::fvalue(_downValue, _downValue);
_a_value.stop();
update();
}
}
void ContinuousSlider::wheelEvent(QWheelEvent *e) {
if (_mouseDown || !moveByWheel()) {
return;
}
#ifdef OS_MAC_OLD
constexpr auto step = 120;
#else // OS_MAC_OLD
constexpr auto step = static_cast<int>(QWheelEvent::DefaultDeltasPerStep);
#endif // OS_MAC_OLD
constexpr auto coef = 1. / (step * 10.);
auto deltaX = e->angleDelta().x(), deltaY = e->angleDelta().y();
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
deltaY *= -1;
} else {
deltaX *= -1;
}
auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY;
auto finalValue = snap(a_value.to() + delta * coef, 0., 1.);
setValue(finalValue, false);
if (_changeProgressCallback) {
_changeProgressCallback(finalValue);
}
_byWheelFinished->start(kByWheelFinishedTimeout);
}
void ContinuousSlider::updateDownValueFromPos(const QPoint &pos) {
_downValue = computeValue(pos);
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::enterEvent(QEvent *e) {
setOver(true);
}
void ContinuousSlider::leaveEvent(QEvent *e) {
setOver(false);
}
void ContinuousSlider::setOver(bool over) {
if (_over == over) return;
_over = over;
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, getOverDuration());
}
FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
QRect FilledSlider::getSeekRect() const {
return QRect(0, 0, width(), height());
}
float64 FilledSlider::getOverDuration() const {
return _st.duration;
}
void FilledSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto masterOpacity = fadeOpacity();
auto ms = getms();
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over);
auto lineWidthRounded = qFloor(lineWidth);
auto lineWidthPartial = lineWidth - lineWidthRounded;
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width();
if (mid > from) {
p.setOpacity(masterOpacity);
p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, disabled ? _st.disabledFg : _st.activeFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * lineWidthPartial);
p.fillRect(from, height() - lineWidthRounded - 1, (mid - from), 1, disabled ? _st.disabledFg : _st.activeFg);
}
}
if (end > mid && over > 0) {
p.setOpacity(masterOpacity * over);
p.fillRect(mid, height() - lineWidthRounded, (end - mid), lineWidthRounded, _st.inactiveFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * over * lineWidthPartial);
p.fillRect(mid, height() - lineWidthRounded - 1, (end - mid), 1, _st.inactiveFg);
}
}
}
MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
QRect MediaSlider::getSeekRect() const {
return isHorizontal()
? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height())
: QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width());
}
float64 MediaSlider::getOverDuration() const {
return _st.duration;
}
void MediaSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setOpacity(fadeOpacity());
auto horizontal = isHorizontal();
auto ms = getms();
auto radius = _st.width / 2;
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
// invert colors and value for vertical
if (!horizontal) value = 1. - value;
auto markerFrom = (horizontal ? seekRect.x() : seekRect.y());
auto markerLength = (horizontal ? seekRect.width() : seekRect.height());
auto from = _alwaysDisplayMarker ? 0 : markerFrom;
auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength;
auto mid = qRound(from + value * length);
auto end = from + length;
auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over);
auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over);
if (mid > from) {
auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid);
auto fromRect = horizontal
? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width)
: QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from);
p.setClipRect(fromClipRect);
p.setBrush(horizontal ? activeFg : inactiveFg);
p.drawRoundedRect(fromRect, radius, radius);
}
if (end > mid) {
auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid);
auto endRect = horizontal
? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width)
: QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius));
p.setClipRect(endClipRect);
p.setBrush(horizontal ? inactiveFg : activeFg);
p.drawRoundedRect(endRect, radius, radius);
}
auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over);
if (markerSizeRatio > 0) {
auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y());
auto seekButton = horizontal
? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height())
: QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height());
auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height();
auto remove = static_cast<int>(((1. - markerSizeRatio) * size) / 2.);
if (remove * 2 < size) {
p.setClipRect(rect());
p.setBrush(activeFg);
p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove)));
}
}
}
} // namespace Ui

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "styles/style_widgets.h"
namespace Ui {
class ContinuousSlider : public TWidget {
@ -115,4 +117,40 @@ private:
};
class FilledSlider : public ContinuousSlider {
public:
FilledSlider(QWidget *parent, const style::FilledSlider &st);
protected:
void paintEvent(QPaintEvent *e) override;
private:
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::FilledSlider &_st;
};
class MediaSlider : public ContinuousSlider {
public:
MediaSlider(QWidget *parent, const style::MediaSlider &st);
void setAlwaysDisplayMarker(bool alwaysDisplayMarker) {
_alwaysDisplayMarker = alwaysDisplayMarker;
update();
}
protected:
void paintEvent(QPaintEvent *e) override;
private:
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::MediaSlider &_st;
bool _alwaysDisplayMarker = false;
};
} // namespace Ui

View File

@ -19,14 +19,13 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/discrete_slider.h"
#include "ui/widgets/discrete_sliders.h"
#include "styles/style_widgets.h"
namespace Ui {
DiscreteSlider::DiscreteSlider(QWidget *parent) : TWidget(parent)
, _a_left(animation(this, &DiscreteSlider::step_left)) {
DiscreteSlider::DiscreteSlider(QWidget *parent) : TWidget(parent) {
setCursor(style::cur_pointer);
}
@ -35,54 +34,70 @@ void DiscreteSlider::setSectionActivatedCallback(SectionActivatedCallback &&call
}
void DiscreteSlider::setActiveSection(int index) {
setSelectedSection(index);
if (_activeIndex != index) {
_activeIndex = index;
if (_callback) {
_callback();
}
}
setSelectedSection(index);
}
void DiscreteSlider::setActiveSectionFast(int index) {
setActiveSection(index);
a_left.finish();
_a_left.stop();
_a_left.finish();
update();
}
void DiscreteSlider::addSection(const QString &label) {
auto section = Section(label);
_sections.push_back(section);
void DiscreteSlider::setSelectOnPress(bool selectOnPress) {
_selectOnPress = selectOnPress;
}
void DiscreteSlider::resizeSections(int newWidth) {
auto count = _sections.size();
if (!count) return;
void DiscreteSlider::addSection(const QString &label) {
_sections.push_back(Section(label, getLabelFont()));
resizeToWidth(width());
}
auto skips = count - 1;
auto sectionsWidth = newWidth - skips * st::discreteSliderSkip;
auto sectionWidth = sectionsWidth / float64(count);
auto x = 0.;
for (int i = 0; i != count; ++i) {
auto &section = _sections[i];
auto skip = i * st::discreteSliderThickness;
section.left = qFloor(x) + skip;
x += sectionWidth;
section.width = qRound(x) - (section.left - skip);
void DiscreteSlider::setSections(const QStringList &labels) {
t_assert(!labels.isEmpty());
_sections.clear();
for_const (auto &label, labels) {
_sections.push_back(Section(label, getLabelFont()));
}
stopAnimation();
if (_activeIndex >= _sections.size()) {
_activeIndex = 0;
}
if (_selected >= _sections.size()) {
_selected = 0;
}
resizeToWidth(width());
}
int DiscreteSlider::getCurrentActiveLeft(uint64 ms) {
return _a_left.current(ms, _sections.isEmpty() ? 0 : _sections[_selected].left);
}
template <typename Lambda>
void DiscreteSlider::enumerateSections(Lambda callback) {
for (auto &section : _sections) {
callback(section);
}
a_left = anim::ivalue(_sections[_activeIndex].left, _sections[_activeIndex].left);
_a_left.stop();
}
void DiscreteSlider::mousePressEvent(QMouseEvent *e) {
setSelectedSection(getIndexFromPosition(e->pos()));
if (_selectOnPress) {
setSelectedSection(getIndexFromPosition(e->pos()));
}
_pressed = true;
}
void DiscreteSlider::mouseMoveEvent(QMouseEvent *e) {
if (!_pressed) return;
setSelectedSection(getIndexFromPosition(e->pos()));
if (_selectOnPress) {
setSelectedSection(getIndexFromPosition(e->pos()));
}
}
void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) {
@ -92,50 +107,16 @@ void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) {
}
void DiscreteSlider::setSelectedSection(int index) {
if (index < 0) return;
if (index < 0 || index >= _sections.size()) return;
if (_selected != index) {
auto from = _sections[_selected].left;
_selected = index;
a_left.start(_sections[_selected].left);
_a_left.start();
auto to = _sections[_selected].left;
_a_left.start([this] { update(); }, from, to, getAnimationDuration());
}
}
void DiscreteSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
int activeLeft = a_left.current();
p.setFont(st::discreteSliderLabelFont);
p.setPen(st::discreteSliderLabelFg);
for (int i = 0, count = _sections.size(); i != count; ++i) {
auto &section = _sections.at(i);
auto from = section.left, tofill = section.width;
if (activeLeft > from) {
auto fill = qMin(tofill, activeLeft - from);
p.fillRect(myrtlrect(from, st::discreteSliderTop, fill, st::discreteSliderThickness), st::discreteSliderInactiveFg);
from += fill;
tofill -= fill;
}
if (activeLeft + section.width > from) {
if (auto fill = qMin(tofill, activeLeft + section.width - from)) {
p.fillRect(myrtlrect(from, st::discreteSliderTop, fill, st::discreteSliderThickness), st::discreteSliderActiveFg);
from += fill;
tofill -= fill;
}
}
if (tofill) {
p.fillRect(myrtlrect(from, st::discreteSliderTop, tofill, st::discreteSliderThickness), st::discreteSliderInactiveFg);
}
p.drawTextLeft(section.left + (section.width - section.labelWidth) / 2, st::discreteSliderLabelTop, width(), section.label, section.labelWidth);
}
}
int DiscreteSlider::resizeGetHeight(int newWidth) {
resizeSections(newWidth);
return st::discreteSliderHeight;
}
int DiscreteSlider::getIndexFromPosition(QPoint pos) {
int count = _sections.size();
for (int i = 0; i != count; ++i) {
@ -146,22 +127,73 @@ int DiscreteSlider::getIndexFromPosition(QPoint pos) {
return count - 1;
}
void DiscreteSlider::step_left(float64 ms, bool timer) {
auto dt = ms / st::discreteSliderDuration;
if (dt >= 1) {
a_left.finish();
_a_left.stop();
} else {
a_left.update(dt, anim::linear);
}
if (timer) {
update();
}
DiscreteSlider::Section::Section(const QString &label, const style::font &font)
: label(label)
, labelWidth(font->width(label)) {
}
DiscreteSlider::Section::Section(const QString &label)
: label(label)
, labelWidth(st::discreteSliderLabelFont->width(label)) {
SettingsSlider::SettingsSlider(QWidget *parent, const style::SettingsSlider &st) : DiscreteSlider(parent)
, _st(st) {
}
const style::font &SettingsSlider::getLabelFont() const {
return _st.labelFont;
}
int SettingsSlider::getAnimationDuration() const {
return _st.duration;
}
void SettingsSlider::resizeSections(int newWidth) {
auto count = getSectionsCount();
if (!count) return;
auto sectionsWidth = newWidth - (count - 1) * _st.barSkip;
auto sectionWidth = sectionsWidth / float64(count);
auto skip = 0;
auto x = 0.;
enumerateSections([this, &x, &skip, sectionWidth](Section &section) {
section.left = qFloor(x) + skip;
x += sectionWidth;
section.width = qRound(x) - (section.left - skip);
skip += _st.barSkip;
});
stopAnimation();
}
int SettingsSlider::resizeGetHeight(int newWidth) {
resizeSections(newWidth);
return _st.height;
}
void SettingsSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
auto activeLeft = getCurrentActiveLeft(getms());
p.setFont(_st.labelFont);
enumerateSections([this, &p, activeLeft](Section &section) {
auto from = section.left, tofill = section.width;
if (activeLeft > from) {
auto fill = qMin(tofill, activeLeft - from);
p.fillRect(myrtlrect(from, _st.barTop, fill, _st.barStroke), _st.barFg);
from += fill;
tofill -= fill;
}
auto active = 1. - snap(qAbs(activeLeft - section.left) / float64(section.width), 0., 1.);
if (activeLeft + section.width > from) {
if (auto fill = qMin(tofill, activeLeft + section.width - from)) {
p.fillRect(myrtlrect(from, _st.barTop, fill, _st.barStroke), _st.barFgActive);
from += fill;
tofill -= fill;
}
}
if (tofill) {
p.fillRect(myrtlrect(from, _st.barTop, tofill, _st.barStroke), _st.barFg);
}
p.setPen(anim::pen(_st.labelFg, _st.labelFgActive, active));
p.drawTextLeft(section.left + (section.width - section.labelWidth) / 2, _st.labelTop, width(), section.label, section.labelWidth);
});
}
} // namespace Ui

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "styles/style_widgets.h"
namespace Ui {
class DiscreteSlider : public TWidget {
@ -27,46 +29,80 @@ public:
DiscreteSlider(QWidget *parent);
void addSection(const QString &label);
void setSections(const QStringList &labels);
int activeSection() const {
return _activeIndex;
}
void setActiveSection(int index);
void setActiveSectionFast(int index);
void setSelectOnPress(bool selectOnPress);
using SectionActivatedCallback = base::lambda<void()>;
void setSectionActivatedCallback(SectionActivatedCallback &&callback);
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
void resizeSections(int newWidth);
int getIndexFromPosition(QPoint pos);
void setSelectedSection(int index);
void step_left(float64 ms, bool timer);
int resizeGetHeight(int newWidth) override = 0;
struct Section {
Section(const QString &label);
Section(const QString &label, const style::font &font);
int left, width;
QString label;
int labelWidth;
};
int getCurrentActiveLeft(uint64 ms);
int getSectionsCount() const {
return _sections.size();
}
template <typename Lambda>
void enumerateSections(Lambda callback);
void stopAnimation() {
_a_left.finish();
}
private:
virtual const style::font &getLabelFont() const = 0;
virtual int getAnimationDuration() const = 0;
int getIndexFromPosition(QPoint pos);
void setSelectedSection(int index);
QList<Section> _sections;
int _activeIndex = 0;
bool _selectOnPress = true;
SectionActivatedCallback _callback;
bool _pressed = false;
int _selected = 0;
anim::ivalue a_left = { 0 };
Animation _a_left;
FloatAnimation _a_left;
};
class SettingsSlider : public DiscreteSlider {
public:
SettingsSlider(QWidget *parent, const style::SettingsSlider &st = st::defaultSettingsSlider);
protected:
void paintEvent(QPaintEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
const style::font &getLabelFont() const override;
int getAnimationDuration() const override;
void resizeSections(int newWidth);
const style::SettingsSlider &_st;
};

View File

@ -1,73 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/filled_slider.h"
#include "styles/style_widgets.h"
namespace Ui {
FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
QRect FilledSlider::getSeekRect() const {
return QRect(0, 0, width(), height());
}
float64 FilledSlider::getOverDuration() const {
return _st.duration;
}
void FilledSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto masterOpacity = fadeOpacity();
auto ms = getms();
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over);
auto lineWidthRounded = qFloor(lineWidth);
auto lineWidthPartial = lineWidth - lineWidthRounded;
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width();
if (mid > from) {
p.setOpacity(masterOpacity);
p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, disabled ? _st.disabledFg : _st.activeFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * lineWidthPartial);
p.fillRect(from, height() - lineWidthRounded - 1, (mid - from), 1, disabled ? _st.disabledFg : _st.activeFg);
}
}
if (end > mid && over > 0) {
p.setOpacity(masterOpacity * over);
p.fillRect(mid, height() - lineWidthRounded, (end - mid), lineWidthRounded, _st.inactiveFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * over * lineWidthPartial);
p.fillRect(mid, height() - lineWidthRounded - 1, (end - mid), 1, _st.inactiveFg);
}
}
}
} // namespace Ui

View File

@ -1,43 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/continuous_slider.h"
#include "styles/style_widgets.h"
namespace Ui {
class FilledSlider : public ContinuousSlider {
public:
FilledSlider(QWidget *parent, const style::FilledSlider &st);
protected:
void paintEvent(QPaintEvent *e) override;
private:
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::FilledSlider &_st;
};
} // namespace Ui

View File

@ -1,101 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/media_slider.h"
#include "styles/style_widgets.h"
namespace Ui {
MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
QRect MediaSlider::getSeekRect() const {
return isHorizontal()
? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height())
: QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width());
}
float64 MediaSlider::getOverDuration() const {
return _st.duration;
}
void MediaSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setOpacity(fadeOpacity());
auto horizontal = isHorizontal();
auto ms = getms();
auto radius = _st.width / 2;
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
// invert colors and value for vertical
if (!horizontal) value = 1. - value;
auto markerFrom = (horizontal ? seekRect.x() : seekRect.y());
auto markerLength = (horizontal ? seekRect.width() : seekRect.height());
auto from = _alwaysDisplayMarker ? 0 : markerFrom;
auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength;
auto mid = qRound(from + value * length);
auto end = from + length;
auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over);
auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over);
if (mid > from) {
auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid);
auto fromRect = horizontal
? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width)
: QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from);
p.setClipRect(fromClipRect);
p.setBrush(horizontal ? activeFg : inactiveFg);
p.drawRoundedRect(fromRect, radius, radius);
}
if (end > mid) {
auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid);
auto endRect = horizontal
? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width)
: QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius));
p.setClipRect(endClipRect);
p.setBrush(horizontal ? inactiveFg : activeFg);
p.drawRoundedRect(endRect, radius, radius);
}
auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over);
if (markerSizeRatio > 0) {
auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y());
auto seekButton = horizontal
? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height())
: QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height());
auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height();
auto remove = static_cast<int>(((1. - markerSizeRatio) * size) / 2.);
if (remove * 2 < size) {
p.setClipRect(rect());
p.setBrush(activeFg);
p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove)));
}
}
}
} // namespace Ui

View File

@ -1,49 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/continuous_slider.h"
#include "styles/style_widgets.h"
namespace Ui {
class MediaSlider : public ContinuousSlider {
public:
MediaSlider(QWidget *parent, const style::MediaSlider &st);
void setAlwaysDisplayMarker(bool alwaysDisplayMarker) {
_alwaysDisplayMarker = alwaysDisplayMarker;
update();
}
protected:
void paintEvent(QPaintEvent *e) override;
private:
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::MediaSlider &_st;
bool _alwaysDisplayMarker = false;
};
} // namespace Ui

View File

@ -566,7 +566,7 @@ defaultSolidScroll: FlatScroll(defaultFlatScroll) {
defaultInputFont: font(17px);
defaultFlatInput: FlatInput {
textColor: #000000;
bgColor: #f2f2f2;
bgColor: windowBgOver;
bgActive: #ffffff;
width: 210px;
height: 40px;
@ -575,7 +575,7 @@ defaultFlatInput: FlatInput {
font: defaultInputFont;
borderWidth: 2px;
borderColor: #f2f2f2;
borderColor: windowBgOver;
borderActive: #54c3f3;
borderError: #ed8080;
@ -724,16 +724,47 @@ widgetFadeDuration: 200;
fieldSearchIcon: icon {{ "box_search", #aaaaaa, point(9px, 8px) }};
boxFieldSearchIcon: icon {{ "box_search", #aaaaaa, point(10px, 9px) }};
discreteSliderHeight: 39px;
discreteSliderTop: 5px;
discreteSliderSkip: 3px;
discreteSliderThickness: 3px;
discreteSliderActiveFg: #4bb5e7;
discreteSliderInactiveFg: #e1eaef;
discreteSliderLabelTop: 17px;
discreteSliderLabelFont: normalFont;
discreteSliderLabelFg: #1485c2;
discreteSliderDuration: 200;
SettingsSlider {
height: pixels;
barTop: pixels;
barSkip: pixels;
barStroke: pixels;
barFg: color;
barFgActive: color;
labelTop: pixels;
labelFont: font;
labelFg: color;
labelFgActive: color;
duration: int;
}
defaultSettingsSlider: SettingsSlider {
height: 39px;
barTop: 5px;
barSkip: 3px;
barStroke: 3px;
barFg: #e1eaef;
barFgActive: windowBgActive;
labelTop: 17px;
labelFont: semiboldFont;
// labelFont: normalFont;
labelFg: #999999;
labelFgActive: lightButtonFg;
// labelFg: #1485c2;
// labelFgActive: #1485c2;
duration: 150;
}
defaultTabsSlider: SettingsSlider(defaultSettingsSlider) {
height: 49px;
barTop: 46px;
barSkip: 0px;
barFg: transparent;
labelTop: 16px;
labelFont: semiboldFont;
labelFg: #999999;
labelFgActive: lightButtonFg;
}
defaultRoundShadow: Shadow {
left: icon {{ "round_shadow_left", windowShadowFg }};

View File

@ -496,22 +496,18 @@
'<(src_loc)/ui/widgets/buttons.h',
'<(src_loc)/ui/widgets/checkbox.cpp',
'<(src_loc)/ui/widgets/checkbox.h',
'<(src_loc)/ui/widgets/continuous_slider.cpp',
'<(src_loc)/ui/widgets/continuous_slider.h',
'<(src_loc)/ui/widgets/discrete_slider.cpp',
'<(src_loc)/ui/widgets/discrete_slider.h',
'<(src_loc)/ui/widgets/continuous_sliders.cpp',
'<(src_loc)/ui/widgets/continuous_sliders.h',
'<(src_loc)/ui/widgets/discrete_sliders.cpp',
'<(src_loc)/ui/widgets/discrete_sliders.h',
'<(src_loc)/ui/widgets/dropdown_menu.cpp',
'<(src_loc)/ui/widgets/dropdown_menu.h',
'<(src_loc)/ui/widgets/filled_slider.cpp',
'<(src_loc)/ui/widgets/filled_slider.h',
'<(src_loc)/ui/widgets/inner_dropdown.cpp',
'<(src_loc)/ui/widgets/inner_dropdown.h',
'<(src_loc)/ui/widgets/input_fields.cpp',
'<(src_loc)/ui/widgets/input_fields.h',
'<(src_loc)/ui/widgets/labels.cpp',
'<(src_loc)/ui/widgets/labels.h',
'<(src_loc)/ui/widgets/media_slider.cpp',
'<(src_loc)/ui/widgets/media_slider.h',
'<(src_loc)/ui/widgets/menu.cpp',
'<(src_loc)/ui/widgets/menu.h',
'<(src_loc)/ui/widgets/multi_select.cpp',