Fix possible crash in CalendarBox.

If month change notification was posted async there was a possibility
to get a mousePressEvent() with already new Context field values, but
with old _selected value. Those two could be inconsistent leading to
an assert violation in (_selected + _context->daysShift() >= 0).
This commit is contained in:
John Preston 2017-12-08 16:44:52 +04:00
parent 80bb6b65a7
commit de8de84a33
1 changed files with 39 additions and 17 deletions

View File

@ -117,11 +117,6 @@ void CalendarBox::Context::showMonth(QDate month) {
} }
void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { void CalendarBox::Context::applyMonth(const QDate &month, bool forced) {
if (forced) {
_month.setForced(month);
} else {
_month.set(month);
}
_daysCount = month.daysInMonth(); _daysCount = month.daysInMonth();
_daysShift = daysShiftForMonth(month); _daysShift = daysShiftForMonth(month);
_rowsCount = rowsCountForMonth(month); _rowsCount = rowsCountForMonth(month);
@ -130,6 +125,11 @@ void CalendarBox::Context::applyMonth(const QDate &month, bool forced) {
_highlightedIndex = month.daysTo(_highlighted); _highlightedIndex = month.daysTo(_highlighted);
_minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min); _minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min);
_maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max); _maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max);
if (forced) {
_month.setForced(month, true);
} else {
_month.set(month, true);
}
} }
void CalendarBox::Context::skipMonth(int skip) { void CalendarBox::Context::skipMonth(int skip) {
@ -219,6 +219,7 @@ protected:
private: private:
void monthChanged(QDate month); void monthChanged(QDate month);
void setSelected(int selected);
void setPressed(int pressed); void setPressed(int pressed);
int rowsLeft() const; int rowsLeft() const;
@ -239,16 +240,21 @@ private:
}; };
CalendarBox::Inner::Inner(QWidget *parent, Context *context) : TWidget(parent) CalendarBox::Inner::Inner(QWidget *parent, Context *context)
: TWidget(parent)
, _context(context) { , _context(context) {
setMouseTracking(true); setMouseTracking(true);
subscribe(context->month(), [this](QDate month) { monthChanged(month); }); subscribe(context->month(), [this](QDate month) {
monthChanged(month);
});
} }
void CalendarBox::Inner::monthChanged(QDate month) { void CalendarBox::Inner::monthChanged(QDate month) {
setSelected(kEmptySelection);
_ripples.clear(); _ripples.clear();
resizeToCurrent(); resizeToCurrent();
update(); update();
sendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
} }
void CalendarBox::Inner::resizeToCurrent() { void CalendarBox::Inner::resizeToCurrent() {
@ -343,19 +349,33 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
} }
void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) { void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) {
auto point = e->pos(); const auto size = st::calendarCellSize;
auto row = floorclamp(point.y() - rowsTop(), st::calendarCellSize.height(), 0, _context->rowsCount()); const auto point = e->pos();
auto col = floorclamp(point.x() - rowsLeft(), st::calendarCellSize.width(), 0, kDaysInWeek); const auto inner = QRect(
auto index = row * kDaysInWeek + col - _context->daysShift(); rowsLeft(),
if (_context->isEnabled(index)) { rowsTop(),
_selected = index; kDaysInWeek * size.width(),
setCursor(style::cur_pointer); _context->rowsCount() * size.height());
if (inner.contains(point)) {
const auto row = (point.y() - rowsTop()) / size.height();
const auto col = (point.x() - rowsLeft()) / size.width();
const auto index = row * kDaysInWeek + col - _context->daysShift();
setSelected(index);
} else { } else {
_selected = kEmptySelection; setSelected(kEmptySelection);
setCursor(style::cur_default);
} }
} }
void CalendarBox::Inner::setSelected(int selected) {
if (selected != kEmptySelection && !_context->isEnabled(selected)) {
selected = kEmptySelection;
}
_selected = selected;
setCursor((_selected == kEmptySelection)
? style::cur_default
: style::cur_pointer);
}
void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) {
setPressed(_selected); setPressed(_selected);
if (_selected != kEmptySelection) { if (_selected != kEmptySelection) {
@ -400,7 +420,9 @@ CalendarBox::Inner::~Inner() = default;
class CalendarBox::Title : public TWidget, private base::Subscriber { class CalendarBox::Title : public TWidget, private base::Subscriber {
public: public:
Title(QWidget *parent, Context *context) : TWidget(parent), _context(context) { Title(QWidget *parent, Context *context)
: TWidget(parent)
, _context(context) {
subscribe(_context->month(), [this](QDate date) { monthChanged(date); }); subscribe(_context->month(), [this](QDate date) { monthChanged(date); });
} }