Some macOS improvements for animations and retina support.

This commit is contained in:
John Preston 2016-12-05 11:45:56 +03:00
parent 3e6d483939
commit a3c406dd00
14 changed files with 142 additions and 70 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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));
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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());

View File

@ -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);

View File

@ -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 &section) {
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);

View File

@ -90,7 +90,7 @@ private:
SectionActivatedCallback _callback;
bool _pressed = false;
int _pressed = -1;
int _selected = 0;
FloatAnimation _a_left;

View File

@ -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() {

View File

@ -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 {

View File

@ -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();