/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "iv/iv_search_bar.h" #include "dialogs/ui/dialogs_pill.h" #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/multi_select.h" #include "ui/painter.h" #include "styles/palette.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" #include "styles/style_dialogs.h" #include "styles/style_iv.h" #include "styles/style_widgets.h" #include namespace Iv { SearchBar::SearchBar( not_null parent, rpl::producer width, SearchBarMode mode) : _mode(mode) , _wrap(parent, object_ptr(parent.get())) { if (_mode == SearchBarMode::EditorPill) { _wrap.setDirectionUp(true); _pillShadow.emplace(st::ivEditorPillShadow); _pillShadowMargins = _pillShadow->extend(); } else { _shadow = std::make_unique(parent.get()); } setup(std::move(width)); _wrap.hide(anim::type::instant); if (_shadow) { _shadow->hide(); } } void SearchBar::setup(rpl::producer width) { const auto inner = _wrap.entity(); const auto pill = (_mode == SearchBarMode::EditorPill); inner->resize(inner->width(), pill ? (st::ivEditorToolbarPadding.top() + _pillShadowMargins.top() + pillHeight() + _pillShadowMargins.bottom()) : st::ivSearchBarHeight); const auto &selectSt = pill ? st::ivEditorSearchMultiSelect : st::searchInChatMultiSelect; _select = Ui::CreateChild( inner, selectSt, tr::lng_dlg_filter()); _select->setCancelButtonShown(false); _counter = Ui::CreateChild( inner, QString(), st::ivSearchCounterLabel); _counter->setAttribute(Qt::WA_TransparentForMouseEvents); _counter->hide(); const auto &upSt = pill ? st::ivEditorSearchPrevious : st::calendarPrevious; const auto &downSt = pill ? st::ivEditorSearchNext : st::calendarNext; const auto &closeSt = pill ? st::ivEditorSearchCancel : st::defaultMultiSelectSearchCancel; _up = Ui::CreateChild(inner, upSt); _down = Ui::CreateChild(inner, downSt); _close = Ui::CreateChild(inner, closeSt); _close->show(anim::type::instant); inner->paintRequest( ) | rpl::on_next([=](QRect clip) { auto p = QPainter(inner); if (_mode == SearchBarMode::EditorPill) { paintPill(p); } else { p.fillRect(clip, st::windowBg); } }, inner->lifetime()); std::move(width) | rpl::on_next([=](int width) { _wrap.resizeToWidth(width); }, _wrap.lifetime()); inner->sizeValue( ) | rpl::on_next([=] { updateControlsGeometry(); }, inner->lifetime()); if (_shadow) { _wrap.geometryValue( ) | rpl::on_next([=](QRect geometry) { _shadow->setGeometry( geometry.x(), geometry.y() + geometry.height(), geometry.width(), st::lineWidth); }, _shadow->lifetime()); _shadow->showOn(rpl::combine( _wrap.shownValue(), _wrap.heightValue(), rpl::mappers::_1 && rpl::mappers::_2 > 0 ) | rpl::filter([=](bool shown) { return (shown == _shadow->isHidden()); })); } _select->setQueryChangedCallback([=](const QString &query) { _queryChanges.fire_copy(query); }); _select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) { _navigates.fire((modifiers & Qt::ShiftModifier) ? -1 : 1); }); _select->setCancelledCallback([=] { _closeRequests.fire({}); }); _select->setFocusedChangedCallback([=](bool focused) { _focusChanges.fire_copy(focused); }); setResults(0, 0); } int SearchBar::pillHeight() const { return st::ivEditorToolbarButtonSize + 2 * st::ivEditorPillPadding; } QRect SearchBar::pillRect() const { const auto inner = _wrap.entity(); return QRect( _pillShadowMargins.left(), st::ivEditorToolbarPadding.top() + _pillShadowMargins.top(), (inner->width() - _pillShadowMargins.left() - _pillShadowMargins.right()), pillHeight()); } void SearchBar::paintPill(QPainter &p) const { auto hq = PainterHighQualityEnabler(p); const auto pill = pillRect(); const auto radius = pill.height() / 2; _pillShadow->paint(p, pill, radius); p.setBrush(st::dialogsBg); p.setPen(Qt::NoPen); p.drawRoundedRect(pill, radius, radius); Dialogs::PaintPillOutline(p, pill, radius); } void SearchBar::updateControlsGeometry() { const auto inner = _wrap.entity(); const auto width = inner->width(); const auto height = inner->height(); if (_mode == SearchBarMode::EditorPill) { const auto pill = pillRect(); const auto pad = st::ivEditorPillPadding; const auto skip = st::ivEditorPillButtonSkip; const auto centered = [&](int controlHeight) { return pill.y() + (pill.height() - controlHeight) / 2; }; auto right = _pillShadowMargins.right() + pad; _close->moveToRight(right, centered(_close->height())); right += _close->width() + skip; _down->moveToRight(right, centered(_down->height())); right += _down->width() + skip; _up->moveToRight(right, centered(_up->height())); right += _up->width(); if (!_counter->isHidden()) { right += st::ivSearchBarCounterSkip; _counter->moveToRight(right, centered(_counter->height())); right += _counter->width() + st::ivSearchBarCounterSkip; } const auto left = _pillShadowMargins.left() + pad; _select->resizeToWidth(width - left - right); _select->moveToLeft(left, centered(_select->height())); return; } _close->moveToRight(0, (height - _close->height()) / 2); _down->moveToRight(_close->width(), (height - _down->height()) / 2); _up->moveToRight( _close->width() + _down->width(), (height - _up->height()) / 2); auto right = _close->width() + _down->width() + _up->width(); if (!_counter->isHidden()) { right += st::ivSearchBarCounterSkip; _counter->moveToRight(right, (height - _counter->height()) / 2); right += _counter->width() + st::ivSearchBarCounterSkip; } _select->resizeToWidth(width - right); _select->moveToLeft(0, (height - _select->height()) / 2); } void SearchBar::setResults(int current, int total) { if (total > 0) { _counter->setText(u"%1 / %2"_q.arg(current).arg(total)); _counter->show(); } else { _counter->hide(); } const auto disabled = (total <= 0); _up->setIconOverride(disabled ? &st::calendarPreviousDisabled : nullptr); _down->setIconOverride(disabled ? &st::calendarNextDisabled : nullptr); _up->setAttribute(Qt::WA_TransparentForMouseEvents, disabled); _down->setAttribute(Qt::WA_TransparentForMouseEvents, disabled); updateControlsGeometry(); } void SearchBar::toggle(bool shown, anim::type animated) { _wrap.toggle(shown, animated); } void SearchBar::show(anim::type animated) { toggle(true, animated); } void SearchBar::hide(anim::type animated) { toggle(false, animated); } bool SearchBar::shown() const { return _wrap.toggled(); } void SearchBar::setInnerFocus() { _select->setInnerFocus(); } void SearchBar::raise() { _wrap.raise(); if (_shadow) { _shadow->raise(); } } void SearchBar::move(int x, int y) { _wrap.move(x, y); } rpl::producer SearchBar::queryChanges() const { return _queryChanges.events(); } rpl::producer SearchBar::navigateRequests() const { return rpl::merge( _navigates.events(), _up->clicks() | rpl::map_to(-1), _down->clicks() | rpl::map_to(1)); } rpl::producer<> SearchBar::closeRequests() const { return rpl::merge( _closeRequests.events(), _close->clicks() | rpl::to_empty); } rpl::producer SearchBar::focusChanges() const { return _focusChanges.events(); } rpl::producer SearchBar::heightValue() const { if (_mode != SearchBarMode::EditorPill) { return _wrap.heightValue(); } return _wrap.heightValue() | rpl::map([=](int height) { const auto slide = st::ivEditorToolbarPadding.top() + pillHeight(); const auto full = slide + _pillShadowMargins.top() + _pillShadowMargins.bottom(); return (height * slide) / full; }); } rpl::lifetime &SearchBar::lifetime() { return _wrap.lifetime(); } } // namespace Iv