New cancel search icon animation.

This commit is contained in:
John Preston 2016-11-21 20:46:29 +03:00
parent 9fa284a6d1
commit 9155591e8a
21 changed files with 301 additions and 79 deletions

View File

@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/window_theme.h"
#include "styles/style_overview.h"
#include "styles/style_boxes.h"
#include "ui/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll)
, _inner(this) {

View File

@ -268,8 +268,12 @@ contactsMultiSelect: MultiSelect {
textActiveBg: activeButtonBg;
textActiveFg: activeButtonFg;
deleteFg: activeButtonFg;
deleteLeft: 10px;
deleteStroke: 2px;
deleteCross: CrossAnimation {
size: 32px;
skip: 10px;
stroke: 2px;
minScale: 0.3;
}
duration: 150;
minScale: 0.3;
}
@ -295,7 +299,25 @@ contactsMultiSelect: MultiSelect {
fieldIcon: boxFieldSearchIcon;
fieldIconSkip: 36px;
fieldCancel: boxBlockTitleClose;
fieldCancel: CrossButton {
width: boxBlockTitleHeight;
height: boxBlockTitleHeight;
cross: CrossAnimation {
size: 40px;
skip: 14px;
stroke: 2px;
minScale: 0.3;
}
crossFg: boxBlockTitleCloseFg;
crossFgOver: boxBlockTitleCloseFgOver;
crossPosition: point(4px, 4px);
duration: 150;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
fieldCancelSkip: 40px;
}
contactsPhotoCheckbox: RoundImageCheckbox {

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/abstractbox.h"
#include "core/single_timer.h"
#include "ui/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
#include "boxes/members_box.h"
namespace Dialogs {

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/abstractbox.h"
#include "core/single_timer.h"
#include "ui/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
#include "ui/widgets/buttons.h"
class ContactsBox;

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/abstractbox.h"
#include "core/observer.h"
#include "core/vector_of_moveable.h"
#include "ui/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
namespace Dialogs {
class Row;

View File

@ -113,11 +113,21 @@ dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) {
iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }};
iconPosition: point(11px, 11px);
}
dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) {
dialogsCancelSearch: CrossButton {
width: 32px;
height: 32px;
iconPosition: point(7px, 7px);
cross: CrossAnimation {
size: 32px;
skip: 10px;
stroke: 2px;
minScale: 0.3;
}
crossFg: dialogsMenuIconFg;
crossFgOver: dialogsMenuIconFgOver;
crossPosition: point(0px, 0px);
duration: 150;
ripple: emptyRippleAnimation;
}

View File

@ -1719,7 +1719,6 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent)
_filter->setFocusPolicy(Qt::StrongFocus);
_filter->customUpDown(true);
_cancelSearch->hide();
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
@ -1790,7 +1789,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window:
_mainMenuToggle->hide();
if (_forwardCancel) _forwardCancel->hide();
_filter->hide();
_cancelSearch->hide();
_cancelSearch->hideFast();
_lockUnlock->hide();
int delta = st::slideShift;
@ -2294,9 +2293,9 @@ void DialogsWidget::onFilterUpdate(bool force) {
_searchCache.clear();
_searchQueries.clear();
_searchQuery = QString();
_cancelSearch->hide();
} else if (_cancelSearch->isHidden()) {
_cancelSearch->show();
_cancelSearch->hideAnimated();
} else {
_cancelSearch->showAnimated();
}
if (filterText.size() < MinUsernameLength) {
_peopleCache.clear();

View File

@ -36,6 +36,7 @@ class DropdownMenu;
class FlatButton;
class LinkButton;
class FlatInput;
class CrossButton;
} // namespace Ui
enum DialogsSearchRequestType {
@ -334,7 +335,7 @@ private:
ChildWidget<Ui::IconButton> _forwardCancel = { nullptr };
ChildWidget<Ui::IconButton> _mainMenuToggle;
ChildWidget<Ui::FlatInput> _filter;
ChildWidget<Ui::IconButton> _cancelSearch;
ChildWidget<Ui::CrossButton> _cancelSearch;
ChildWidget<Ui::IconButton> _lockUnlock;
ChildWidget<Ui::ScrollArea> _scroll;
ChildWidget<DialogsInner> _inner;

View File

@ -35,7 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_instance.h"
#include "localstorage.h"
#include "history/history_media_types.h"
#include "ui/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
namespace Overview {
namespace Layout {

View File

@ -85,7 +85,6 @@ OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, P
_searchTimer.setSingleShot(true);
connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages()));
_cancelSearch->hide();
if (_type == OverviewLinks || _type == OverviewFiles) {
_search->show();
} else {
@ -1346,7 +1345,7 @@ void OverviewInner::switchType(MediaOverviewType type) {
_search->updatePlaceholder();
onSearchUpdate();
}
_cancelSearch->hide();
_cancelSearch->hideFast();
resizeToWidth(_width, 0, _minHeight, true);
}
@ -1475,9 +1474,9 @@ void OverviewInner::onSearchUpdate() {
_searchQueries.clear();
_searchQuery = QString();
_searchResults.clear();
_cancelSearch->hide();
} else if (_cancelSearch->isHidden()) {
_cancelSearch->show();
_cancelSearch->hideAnimated();
} else {
_cancelSearch->showAnimated();
}
if (changed) {
@ -1499,7 +1498,7 @@ void OverviewInner::onCancel() {
bool OverviewInner::onCancelSearch() {
if (_search->isHidden()) return false;
bool clearing = !_search->text().isEmpty();
_cancelSearch->hide();
_cancelSearch->hideAnimated();
_search->clear();
_search->updatePlaceholder();
onSearchUpdate();

View File

@ -37,6 +37,7 @@ class PlainShadow;
class PopupMenu;
class IconButton;
class FlatInput;
class CrossButton;
} // namespace Ui
class OverviewWidget;
@ -182,7 +183,7 @@ private:
int32 setLayoutItem(int32 index, Overview::Layout::AbstractItem *item, int32 top);
ChildWidget<Ui::FlatInput> _search;
ChildWidget<Ui::IconButton> _cancelSearch;
ChildWidget<Ui::CrossButton> _cancelSearch;
QVector<MsgId> _results;
int32 _itemsToBeLoaded;

View File

@ -0,0 +1,74 @@
/*
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/effects/cross_animation.h"
namespace Ui {
void CrossAnimation::paint(QPainter &p, const style::CrossAnimation &st, const style::color &color, int x, int y, int outerWidth, float64 shown) {
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto deleteScale = shown + st.minScale * (1. - shown);
auto deleteSkip = deleteScale * st.skip + (1. - deleteScale) * (st.size / 2);
auto sqrt2 = sqrt(2.);
auto deleteLeft = rtlpoint(x + deleteSkip, 0, outerWidth).x() + 0.;
auto deleteTop = y + deleteSkip + 0.;
auto deleteWidth = st.size - 2 * deleteSkip;
auto deleteHeight = st.size - 2 * deleteSkip;
auto deleteStroke = st.stroke / sqrt2;
QPointF pathDelete[] = {
{ deleteLeft, deleteTop + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop },
{ deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) - deleteStroke },
{ deleteLeft + deleteWidth - deleteStroke, deleteTop },
{ deleteLeft + deleteWidth, deleteTop + deleteStroke },
{ deleteLeft + (deleteWidth / 2.) + deleteStroke, deleteTop + (deleteHeight / 2.) },
{ deleteLeft + deleteWidth, deleteTop + deleteHeight - deleteStroke },
{ deleteLeft + deleteWidth - deleteStroke, deleteTop + deleteHeight },
{ deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop + deleteHeight },
{ deleteLeft, deleteTop + deleteHeight - deleteStroke },
{ deleteLeft + (deleteWidth / 2.) - deleteStroke, deleteTop + (deleteHeight / 2.) },
};
if (shown < 1.) {
auto alpha = -(shown - 1.) * M_PI_2;
auto cosalpha = cos(alpha);
auto sinalpha = sin(alpha);
auto shiftx = deleteLeft + (deleteWidth / 2.);
auto shifty = deleteTop + (deleteHeight / 2.);
for (auto &point : pathDelete) {
auto x = point.x() - shiftx;
auto y = point.y() - shifty;
point.setX(shiftx + x * cosalpha - y * sinalpha);
point.setY(shifty + y * cosalpha + x * sinalpha);
}
}
QPainterPath path;
path.moveTo(pathDelete[0]);
for (int i = 1; i != base::array_size(pathDelete); ++i) {
path.lineTo(pathDelete[i]);
}
p.fillPath(path, color);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
} // namespace Ui

View File

@ -0,0 +1,33 @@
/*
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 "styles/style_widgets.h"
namespace Ui {
class CrossAnimation {
public:
static void paint(QPainter &p, const style::CrossAnimation &st, const style::color &color, int x, int y, int outerWidth, float64 shown);
};
} // namespace Ui

View File

@ -19,7 +19,7 @@ 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/effects/round_image_checkbox.h"
#include "ui/effects/round_checkbox.h"
namespace Ui {
namespace {

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/cross_animation.h"
namespace Ui {
@ -449,4 +450,68 @@ void LeftOutlineButton::paintEvent(QPaintEvent *e) {
p.drawTextLeft(_st.padding.left(), _st.padding.top(), width(), _text, _textWidth);
}
CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : RippleButton(parent, st.ripple)
, _st(st) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer);
hide();
}
void CrossButton::hideAnimated() {
startAnimation(false);
}
void CrossButton::showAnimated() {
startAnimation(true);
}
void CrossButton::hideFast() {
hideAnimated();
_a_show.finish();
}
void CrossButton::startAnimation(bool shown) {
if (_shown == shown) return;
_shown = shown;
if (isHidden()) show();
_a_show.start([this] { animationCallback(); }, _shown ? 0. : 1., _shown ? 1. : 0., _st.duration);
}
void CrossButton::animationCallback() {
update();
if (!_shown && !_a_show.animating()) {
hide();
}
}
void CrossButton::paintEvent(QPaintEvent *e) {
Painter p(this);
auto ms = getms();
auto over = (_state & StateOver);
auto shown = _a_show.current(ms, _shown ? 1. : 0.);
p.setOpacity(shown);
paintRipple(p, _st.crossPosition.x(), _st.crossPosition.y(), ms);
CrossAnimation::paint(p, _st.cross, over ? _st.crossFgOver : _st.crossFg, _st.crossPosition.x(), _st.crossPosition.y(), width(), shown);
}
void CrossButton::onStateChanged(int oldState, StateChangeSource source) {
RippleButton::onStateChanged(oldState, source);
auto over = (_state & StateOver);
if (over != (oldState & StateOver)) {
update();
}
}
QPoint CrossButton::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos()) - _st.crossPosition;
}
QImage CrossButton::prepareRippleMask() const {
return RippleAnimation::ellipseMask(QSize(_st.cross.size, _st.cross.size));
}
} // namespace Ui

View File

@ -197,4 +197,35 @@ private:
};
class CrossButton : public RippleButton {
public:
CrossButton(QWidget *parent, const style::CrossButton &st);
void showAnimated();
void hideAnimated();
void hideFast();
bool isShown() const {
return _shown;
}
protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override;
QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
private:
void startAnimation(bool shown);
void animationCallback();
const style::CrossButton &_st;
bool _shown = false;
FloatAnimation _a_show;
};
} // namespace Ui

View File

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/cross_animation.h"
#include "lang.h"
namespace Ui {
@ -199,54 +200,15 @@ void MultiSelect::Inner::Item::paintOnce(Painter &p, int x, int y, int outerWidt
void MultiSelect::Inner::Item::paintDeleteButton(Painter &p, int x, int y, int outerWidth, float64 overOpacity) {
p.setOpacity(overOpacity);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(Qt::NoPen);
p.setBrush(_color);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.drawEllipse(rtlrect(x, y, _st.height, _st.height, outerWidth));
auto deleteScale = overOpacity + _st.minScale * (1. - overOpacity);
auto deleteSkip = deleteScale * _st.deleteLeft + (1. - deleteScale) * (_st.height / 2);
auto sqrt2 = sqrt(2.);
auto deleteLeft = rtlpoint(x + deleteSkip, 0, outerWidth).x() + 0.;
auto deleteTop = y + deleteSkip + 0.;
auto deleteWidth = _st.height - 2 * deleteSkip;
auto deleteHeight = _st.height - 2 * deleteSkip;
auto deleteStroke = _st.deleteStroke / sqrt2;
QPointF pathDelete[] = {
{ deleteLeft, deleteTop + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop },
{ deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) - deleteStroke },
{ deleteLeft + deleteWidth - deleteStroke, deleteTop },
{ deleteLeft + deleteWidth, deleteTop + deleteStroke },
{ deleteLeft + (deleteWidth / 2.) + deleteStroke, deleteTop + (deleteHeight / 2.) },
{ deleteLeft + deleteWidth, deleteTop + deleteHeight - deleteStroke },
{ deleteLeft + deleteWidth - deleteStroke, deleteTop + deleteHeight },
{ deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop + deleteHeight },
{ deleteLeft, deleteTop + deleteHeight - deleteStroke },
{ deleteLeft + (deleteWidth / 2.) - deleteStroke, deleteTop + (deleteHeight / 2.) },
};
if (overOpacity < 1.) {
auto alpha = -(overOpacity - 1.) * M_PI_2;
auto cosalpha = cos(alpha);
auto sinalpha = sin(alpha);
auto shiftx = deleteLeft + (deleteWidth / 2.);
auto shifty = deleteTop + (deleteHeight / 2.);
for (auto &point : pathDelete) {
auto x = point.x() - shiftx;
auto y = point.y() - shifty;
point.setX(shiftx + x * cosalpha - y * sinalpha);
point.setY(shifty + y * cosalpha + x * sinalpha);
}
}
QPainterPath path;
path.moveTo(pathDelete[0]);
for (int i = 1; i != base::array_size(pathDelete); ++i) {
path.lineTo(pathDelete[i]);
}
p.fillPath(path, _st.deleteFg);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
CrossAnimation::paint(p, _st.deleteCross, _st.deleteFg, x, y, outerWidth, overOpacity);
p.setOpacity(1.);
}
@ -465,7 +427,6 @@ MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const Q
connect(_field, SIGNAL(focused()), this, SLOT(onFieldFocused()));
connect(_field, SIGNAL(changed()), this, SLOT(onQueryChanged()));
connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSubmitted(bool)));
_cancel->hide();
_cancel->setClickedCallback([this] {
clearQuery();
_field->setFocus();
@ -475,7 +436,11 @@ MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const Q
void MultiSelect::Inner::onQueryChanged() {
auto query = getQuery();
_cancel->setVisible(!query.isEmpty());
if (query.isEmpty()) {
_cancel->hideAnimated();
} else {
_cancel->showAnimated();
}
updateFieldGeometry();
if (_queryChangedCallback) {
_queryChangedCallback(query);
@ -510,7 +475,7 @@ void MultiSelect::Inner::setSubmittedCallback(base::lambda<void(bool ctrlShiftEn
void MultiSelect::Inner::updateFieldGeometry() {
auto fieldFinalWidth = _fieldWidth;
if (!_cancel->isHidden()) {
if (_cancel->isShown()) {
fieldFinalWidth -= _st.fieldCancelSkip;
}
_field->resizeToWidth(fieldFinalWidth);

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class InputField;
class IconButton;
class CrossButton;
class ScrollArea;
class MultiSelect : public TWidget {
@ -154,7 +154,7 @@ private:
int _fieldTop = 0;
int _fieldWidth = 0;
ChildWidget<Ui::InputField> _field;
ChildWidget<Ui::IconButton> _cancel;
ChildWidget<Ui::CrossButton> _cancel;
int _newHeight = 0;
IntAnimation _height;

View File

@ -339,6 +339,27 @@ RoundImageCheckbox {
check: RoundCheckbox;
}
CrossAnimation {
fg: color;
size: pixels;
skip: pixels;
stroke: pixels;
minScale: double;
}
CrossButton {
width: pixels;
height: pixels;
cross: CrossAnimation;
crossFg: color;
crossFgOver:color;
crossPosition: point;
duration: int;
ripple: RippleAnimation;
}
MultiSelectItem {
padding: margins;
maxWidth: pixels;
@ -348,10 +369,9 @@ MultiSelectItem {
textFg: color;
textActiveBg: color;
textActiveFg: color;
deleteFg: color;
deleteLeft: pixels;
deleteStroke: pixels;
duration: int;
deleteFg: color;
deleteCross: CrossAnimation;
minScale: double;
}
@ -368,7 +388,7 @@ MultiSelect {
fieldMinWidth: pixels;
fieldIcon: icon;
fieldIconSkip: pixels;
fieldCancel: IconButton;
fieldCancel: CrossButton;
fieldCancelSkip: pixels;
}

View File

@ -454,6 +454,8 @@
'<(src_loc)/ui/buttons/history_down_button.h',
'<(src_loc)/ui/buttons/peer_avatar_button.cpp',
'<(src_loc)/ui/buttons/peer_avatar_button.h',
'<(src_loc)/ui/effects/cross_animation.cpp',
'<(src_loc)/ui/effects/cross_animation.h',
'<(src_loc)/ui/effects/panel_animation.cpp',
'<(src_loc)/ui/effects/panel_animation.h',
'<(src_loc)/ui/effects/radial_animation.cpp',
@ -462,8 +464,8 @@
'<(src_loc)/ui/effects/rect_shadow.h',
'<(src_loc)/ui/effects/ripple_animation.cpp',
'<(src_loc)/ui/effects/ripple_animation.h',
'<(src_loc)/ui/effects/round_image_checkbox.cpp',
'<(src_loc)/ui/effects/round_image_checkbox.h',
'<(src_loc)/ui/effects/round_checkbox.cpp',
'<(src_loc)/ui/effects/round_checkbox.h',
'<(src_loc)/ui/effects/widget_fade_wrap.cpp',
'<(src_loc)/ui/effects/widget_fade_wrap.h',
'<(src_loc)/ui/effects/widget_slide_wrap.cpp',