mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-31 15:59:54 +00:00
Some macOS improvements for animations and retina support.
This commit is contained in:
parent
3e6d483939
commit
a3c406dd00
@ -299,6 +299,13 @@ void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool
|
||||
});
|
||||
}
|
||||
|
||||
QRect RowPainter::sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated) {
|
||||
auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding;
|
||||
auto namewidth = fullWidth - nameleft - st::dialogsPadding.x();
|
||||
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip;
|
||||
return QRect(nameleft, texttop, textUpdated ? namewidth : animationWidth, animationHeight);
|
||||
}
|
||||
|
||||
void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground) {
|
||||
p.fillRect(0, 0, w, st::dialogsImportantBarHeight, selected ? st::dialogsBgOver : st::dialogsBg);
|
||||
if (onlyBackground) {
|
||||
|
@ -33,6 +33,8 @@ class RowPainter {
|
||||
public:
|
||||
static void paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms);
|
||||
static void paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms);
|
||||
static QRect sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated);
|
||||
|
||||
};
|
||||
|
||||
void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground);
|
||||
|
@ -64,6 +64,10 @@ DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(pare
|
||||
subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) {
|
||||
itemRemoved(item);
|
||||
});
|
||||
subscribe(App::histories().sendActionAnimationUpdated(), [this](const Histories::SendActionAnimationUpdate &update) {
|
||||
auto updateRect = Dialogs::Layout::RowPainter::sendActionAnimationRect(update.width, update.height, fullWidth(), update.textUpdated);
|
||||
updateDialogRow(update.history, MsgId(0), updateRect, UpdateRowSection::Default | UpdateRowSection::Filtered);
|
||||
});
|
||||
|
||||
refresh();
|
||||
}
|
||||
@ -524,37 +528,48 @@ void DialogsInner::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) {
|
||||
}
|
||||
|
||||
void DialogsInner::dlgUpdated(History *history, MsgId msgId) {
|
||||
updateDialogRow(history, msgId, QRect(0, 0, fullWidth(), st::dialogsRowHeight));
|
||||
}
|
||||
|
||||
void DialogsInner::updateDialogRow(History *history, MsgId msgId, QRect updateRect, UpdateRowSections sections) {
|
||||
auto updateRow = [this, updateRect](int rowTop) {
|
||||
rtlupdate(updateRect.x(), rowTop + updateRect.y(), updateRect.width(), updateRect.height());
|
||||
};
|
||||
if (_state == DefaultState) {
|
||||
if (auto row = shownDialogs()->getRow(history->peer->id)) {
|
||||
update(0, dialogsOffset() + row->pos() * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight);
|
||||
if (sections & UpdateRowSection::Default) {
|
||||
if (auto row = shownDialogs()->getRow(history->peer->id)) {
|
||||
updateRow(dialogsOffset() + row->pos() * st::dialogsRowHeight);
|
||||
}
|
||||
}
|
||||
} else if (_state == FilteredState || _state == SearchedState) {
|
||||
int32 cnt = 0, add = filteredOffset();
|
||||
for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) {
|
||||
if ((*i)->history() == history) {
|
||||
update(0, add + cnt * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight);
|
||||
break;
|
||||
}
|
||||
++cnt;
|
||||
}
|
||||
if (!_peopleResults.isEmpty()) {
|
||||
int32 cnt = 0, add = peopleOffset();
|
||||
for (PeopleResults::const_iterator i = _peopleResults.cbegin(), e = _peopleResults.cend(); i != e; ++i) {
|
||||
if ((*i) == history->peer) {
|
||||
update(0, add + cnt * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight);
|
||||
if ((sections & UpdateRowSection::Filtered) && !_filterResults.isEmpty()) {
|
||||
auto index = 0, add = filteredOffset();
|
||||
for_const (auto row, _filterResults) {
|
||||
if (row->history() == history) {
|
||||
updateRow(add + index * st::dialogsRowHeight);
|
||||
break;
|
||||
}
|
||||
++cnt;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
if (!_searchResults.isEmpty()) {
|
||||
int32 cnt = 0, add = searchedOffset();
|
||||
for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) {
|
||||
if ((*i)->item()->history() == history && (*i)->item()->id == msgId) {
|
||||
update(0, add + cnt * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight);
|
||||
if ((sections & UpdateRowSection::GlobalSearch) && !_peopleResults.isEmpty()) {
|
||||
auto index = 0, add = peopleOffset();
|
||||
for_const (auto peer, _peopleResults) {
|
||||
if (peer == history->peer) {
|
||||
updateRow(add + index * st::dialogsRowHeight);
|
||||
break;
|
||||
}
|
||||
++cnt;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
if ((sections & UpdateRowSection::MessageSearch) && !_searchResults.isEmpty()) {
|
||||
auto index = 0, add = searchedOffset();
|
||||
for_const (auto fakeRow, _searchResults) {
|
||||
if (fakeRow->item()->history() == history && fakeRow->item()->id == msgId) {
|
||||
updateRow(add + index * st::dialogsRowHeight);
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,16 @@ protected:
|
||||
|
||||
private:
|
||||
void itemRemoved(HistoryItem *item);
|
||||
enum class UpdateRowSection {
|
||||
Default = 0x01,
|
||||
Filtered = 0x02,
|
||||
GlobalSearch = 0x04,
|
||||
MessageSearch = 0x08,
|
||||
All = 0x0F,
|
||||
};
|
||||
Q_DECLARE_FLAGS(UpdateRowSections, UpdateRowSection);
|
||||
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(UpdateRowSections);
|
||||
void updateDialogRow(History *history, MsgId msgId, QRect updateRect, UpdateRowSections sections = UpdateRowSection::All);
|
||||
|
||||
int dialogsOffset() const;
|
||||
int filteredOffset() const;
|
||||
@ -225,6 +235,8 @@ private:
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(DialogsInner::UpdateRowSections);
|
||||
|
||||
class DialogsWidget : public TWidget, public RPCSender, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -251,7 +251,12 @@ bool History::paintSendAction(Painter &p, int x, int y, int availableWidth, int
|
||||
availableWidth -= animationWidth;
|
||||
p.setPen(color);
|
||||
_sendActionText.drawElided(p, x, y, availableWidth);
|
||||
updateSendActionAnimationAreas();
|
||||
// App::histories().sendActionAnimationUpdated().notify({
|
||||
// this,
|
||||
// animationWidth,
|
||||
// st::normalFont->height,
|
||||
// false
|
||||
// });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -334,18 +339,16 @@ bool History::updateSendActionNeedsAnimating(TimeMs ms, bool force) {
|
||||
}
|
||||
auto result = (!_typing.isEmpty() || !_sendActions.isEmpty());
|
||||
if (changed || result) {
|
||||
updateSendActionAnimationAreas();
|
||||
App::histories().sendActionAnimationUpdated().notify({
|
||||
this,
|
||||
_sendActionAnimation.width(),
|
||||
st::normalFont->height,
|
||||
changed
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void History::updateSendActionAnimationAreas() {
|
||||
updateChatListEntry();
|
||||
if (App::main()->historyPeer() == peer) {
|
||||
App::main()->topBar()->update();
|
||||
}
|
||||
}
|
||||
|
||||
ChannelHistory::ChannelHistory(const PeerId &peer) : History(peer)
|
||||
, _joinedMessage(nullptr) {
|
||||
}
|
||||
@ -2066,11 +2069,11 @@ void History::addChatListEntryByLetter(Dialogs::Mode list, QChar letter, Dialogs
|
||||
}
|
||||
|
||||
void History::updateChatListEntry() const {
|
||||
if (MainWidget *m = App::main()) {
|
||||
if (auto main = App::main()) {
|
||||
if (inChatList(Dialogs::Mode::All)) {
|
||||
m->dlgUpdated(Dialogs::Mode::All, mainChatListLink(Dialogs::Mode::All));
|
||||
main->dlgUpdated(Dialogs::Mode::All, mainChatListLink(Dialogs::Mode::All));
|
||||
if (inChatList(Dialogs::Mode::Important)) {
|
||||
m->dlgUpdated(Dialogs::Mode::Important, mainChatListLink(Dialogs::Mode::Important));
|
||||
main->dlgUpdated(Dialogs::Mode::Important, mainChatListLink(Dialogs::Mode::Important));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ enum NewMessageType {
|
||||
class History;
|
||||
class Histories {
|
||||
public:
|
||||
typedef QHash<PeerId, History*> Map;
|
||||
using Map = QHash<PeerId, History*>;
|
||||
Map map;
|
||||
|
||||
Histories() : _a_typings(animation(this, &Histories::step_typings)), _unreadFull(0), _unreadMuted(0) {
|
||||
@ -82,8 +82,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct SendActionAnimationUpdate {
|
||||
History *history = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
bool textUpdated = 0;
|
||||
};
|
||||
base::Observable<SendActionAnimationUpdate> &sendActionAnimationUpdated() {
|
||||
return _sendActionAnimationUpdated;
|
||||
}
|
||||
|
||||
private:
|
||||
int _unreadFull, _unreadMuted;
|
||||
base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
|
||||
|
||||
};
|
||||
|
||||
@ -314,7 +325,6 @@ public:
|
||||
bool updateSendActionNeedsAnimating(TimeMs ms, bool force = false);
|
||||
void unregSendAction(UserData *from);
|
||||
bool updateSendActionNeedsAnimating(UserData *user, const MTPSendMessageAction &action);
|
||||
void updateSendActionAnimationAreas();
|
||||
bool mySendActionUpdated(SendAction::Type type, bool doing);
|
||||
bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, const style::color &color, TimeMs ms);
|
||||
|
||||
|
@ -378,24 +378,25 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
|
||||
}
|
||||
bool radial = isRadialAnimation(ms);
|
||||
|
||||
if (bubble) {
|
||||
skipx = st::mediaPadding.left();
|
||||
skipy = st::mediaPadding.top();
|
||||
|
||||
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
||||
height -= skipy + st::mediaPadding.bottom();
|
||||
if (!_caption.isEmpty()) {
|
||||
height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
height -= st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
||||
}
|
||||
QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
|
||||
auto rthumb = rtlrect(skipx, skipy, width, height, _width);
|
||||
QPixmap pix;
|
||||
if (_parent->toHistoryMessage()) {
|
||||
if (bubble) {
|
||||
skipx = st::mediaPadding.left();
|
||||
skipy = st::mediaPadding.top();
|
||||
|
||||
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
||||
height -= skipy + st::mediaPadding.bottom();
|
||||
if (!_caption.isEmpty()) {
|
||||
height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
height -= st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
rthumb = rtlrect(skipx, skipy, width, height, _width);
|
||||
} else {
|
||||
App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
||||
}
|
||||
auto inWebPage = (_parent->getMedia() != this);
|
||||
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
|
||||
auto roundCorners = inWebPage ? ImageRoundCorner::All : ((isBubbleTop() ? (ImageRoundCorner::TopLeft | ImageRoundCorner::TopRight) : ImageRoundCorner::None)
|
||||
|
@ -6217,17 +6217,20 @@ bool HistoryWidget::paintTopBar(Painter &p, int decreaseWidth, TimeMs ms) {
|
||||
|
||||
if (!_history) return false;
|
||||
|
||||
int increaseLeft = (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) ? (st::topBarArrowPadding.left() - st::topBarArrowPadding.right()) : 0;
|
||||
auto increaseLeft = (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) ? (st::topBarArrowPadding.left() - st::topBarArrowPadding.right()) : 0;
|
||||
decreaseWidth += increaseLeft;
|
||||
QRect rectForName(st::topBarArrowPadding.right() + increaseLeft, st::topBarArrowPadding.top(), width() - decreaseWidth - st::topBarArrowPadding.left() - st::topBarArrowPadding.right(), st::msgNameFont->height);
|
||||
auto nameleft = st::topBarArrowPadding.right() + increaseLeft;
|
||||
auto nametop = st::topBarArrowPadding.top();
|
||||
auto statustop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
|
||||
auto namewidth = width() - decreaseWidth - st::topBarArrowPadding.left() - st::topBarArrowPadding.right();
|
||||
p.setFont(st::dialogsTextFont);
|
||||
if (!_history->paintSendAction(p, rectForName.x(), st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height, rectForName.width(), width(), st::statusFgActive, ms)) {
|
||||
if (!_history->paintSendAction(p, nameleft, statustop, namewidth, width(), st::statusFgActive, ms)) {
|
||||
p.setPen(_titlePeerTextOnline ? st::statusFgActive : st::statusFg);
|
||||
p.drawText(rectForName.x(), st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height + st::dialogsTextFont->ascent, _titlePeerText);
|
||||
p.drawText(nameleft, st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height + st::dialogsTextFont->ascent, _titlePeerText);
|
||||
}
|
||||
|
||||
p.setPen(st::dialogsNameFg);
|
||||
_peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
||||
_peer->dialogName().drawElided(p, nameleft, nametop, namewidth);
|
||||
|
||||
if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) {
|
||||
st::topBarBackward.paint(p, (st::topBarArrowPadding.left() - st::topBarBackward.width()) / 2, (st::topBarHeight - st::topBarBackward.height()) / 2, width());
|
||||
|
@ -197,6 +197,7 @@ void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, TimeMs ms
|
||||
|
||||
QImage RippleAnimation::maskByDrawer(QSize size, bool filled, base::lambda<void(QPainter &p)> &&drawer) {
|
||||
auto result = QImage(size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(filled ? QColor(255, 255, 255) : Qt::transparent);
|
||||
if (drawer) {
|
||||
Painter p(&result);
|
||||
|
@ -111,25 +111,29 @@ void DiscreteSlider::mousePressEvent(QMouseEvent *e) {
|
||||
setSelectedSection(index);
|
||||
}
|
||||
startRipple(index);
|
||||
_pressed = true;
|
||||
_pressed = index;
|
||||
}
|
||||
|
||||
void DiscreteSlider::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (!_pressed) return;
|
||||
if (_pressed < 0) return;
|
||||
if (_selectOnPress) {
|
||||
setSelectedSection(getIndexFromPosition(e->pos()));
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (!base::take(_pressed)) return;
|
||||
auto pressed = base::take(_pressed, -1);
|
||||
if (pressed < 0) return;
|
||||
|
||||
auto index = getIndexFromPosition(e->pos());
|
||||
if (index >= 0 && index < _sections.size()) {
|
||||
if (_sections[index].ripple) {
|
||||
_sections[index].ripple->lastStop();
|
||||
if (pressed < _sections.size()) {
|
||||
if (_sections[pressed].ripple) {
|
||||
_sections[pressed].ripple->lastStop();
|
||||
}
|
||||
}
|
||||
setActiveSection(index);
|
||||
if (_selectOnPress || index == pressed) {
|
||||
setActiveSection(index);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteSlider::setSelectedSection(int index) {
|
||||
@ -219,8 +223,10 @@ void SettingsSlider::paintEvent(QPaintEvent *e) {
|
||||
|
||||
p.setFont(_st.labelFont);
|
||||
enumerateSections([this, &p, activeLeft, ms](Section §ion) {
|
||||
auto active = 1. - snap(qAbs(activeLeft - section.left) / float64(section.width), 0., 1.);
|
||||
if (section.ripple) {
|
||||
section.ripple->paint(p, section.left, 0, width(), ms);
|
||||
auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active);
|
||||
section.ripple->paint(p, section.left, 0, width(), ms, &color);
|
||||
if (section.ripple->empty()) {
|
||||
section.ripple.reset();
|
||||
}
|
||||
@ -232,7 +238,6 @@ void SettingsSlider::paintEvent(QPaintEvent *e) {
|
||||
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);
|
||||
|
@ -90,7 +90,7 @@ private:
|
||||
|
||||
SectionActivatedCallback _callback;
|
||||
|
||||
bool _pressed = false;
|
||||
int _pressed = -1;
|
||||
int _selected = 0;
|
||||
FloatAnimation _a_left;
|
||||
|
||||
|
@ -122,6 +122,12 @@ public:
|
||||
}
|
||||
void update(const QRect&);
|
||||
void update(const QRegion&);
|
||||
void rtlupdate(const QRect &r) {
|
||||
update(myrtlrect(r));
|
||||
}
|
||||
void rtlupdate(int x, int y, int w, int h) {
|
||||
update(myrtlrect(x, y, w, h));
|
||||
}
|
||||
|
||||
public slots:
|
||||
void update() {
|
||||
|
@ -707,6 +707,8 @@ SettingsSlider {
|
||||
labelFgActive: color;
|
||||
duration: int;
|
||||
rippleBottomSkip: pixels;
|
||||
rippleBg: color;
|
||||
rippleBgActive: color;
|
||||
ripple: RippleAnimation;
|
||||
}
|
||||
|
||||
@ -734,9 +736,9 @@ defaultTabsSlider: SettingsSlider(defaultSettingsSlider) {
|
||||
labelFg: #999999;
|
||||
labelFgActive: lightButtonFg;
|
||||
rippleBottomSkip: 1px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
rippleBg: windowBgOver;
|
||||
rippleBgActive: #e0f2fa;
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
|
||||
defaultRoundShadow: Shadow {
|
||||
|
@ -66,6 +66,11 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w)
|
||||
rtlupdate(0, 0, st::titleUnreadCounterRight, st::titleUnreadCounterTop);
|
||||
});
|
||||
}
|
||||
subscribe(App::histories().sendActionAnimationUpdated(), [this](const Histories::SendActionAnimationUpdate &update) {
|
||||
if (App::main() && update.history->peer == App::main()->historyPeer()) {
|
||||
rtlupdate(0, 0, width(), height());
|
||||
}
|
||||
});
|
||||
|
||||
setCursor(style::cur_pointer);
|
||||
showAll();
|
||||
|
Loading…
Reference in New Issue
Block a user