diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index 161838cf4..70587628b 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "base/optional.h" + namespace base { template @@ -378,7 +380,6 @@ public: using iterator = typename parent::iterator; using const_iterator = typename parent::const_iterator; using value_type = typename parent::value_type; - using reference = typename parent::reference; iterator insert(const value_type &value) { if (this->empty() || (value.first < this->front().first)) { @@ -424,19 +425,29 @@ public: return this->findFirst(key); } - reference operator[](const Key &key) { + Type &operator[](const Key &key) { if (this->empty() || (key < this->front().first)) { - this->_impl.push_front(Type()); - return this->front(); + this->_impl.push_front({ key, Type() }); + return this->front().second; } else if (this->back().first < key) { - this->_impl.push_back(Type()); - return this->back(); + this->_impl.push_back({ key, Type() }); + return this->back().second; } auto where = this->getLowerBound(key); if (key < where->first) { - return *this->_impl.insert(where, { key, Type() }); + return this->_impl.insert(where, { key, Type() })->second; } - return *where; + return where->second; + } + + optional take(const Key &key) { + auto it = find(key); + if (it == this->end()) { + return base::none; + } + auto result = std::move(it->second); + this->erase(it); + return std::move(result); } }; diff --git a/Telegram/SourceFiles/base/functors.h b/Telegram/SourceFiles/base/functors.h new file mode 100644 index 000000000..bdf905e1e --- /dev/null +++ b/Telegram/SourceFiles/base/functors.h @@ -0,0 +1,42 @@ +/* +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-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace base { +namespace functors { + +struct abs_helper { + template ()), + typename = decltype(-std::declval())> + constexpr Type operator()(Type value) const { + return (0 < value) ? value : (-value); + } +}; +constexpr auto abs = abs_helper {}; + +template +inline auto add(Type a) { + return [a](auto b) { return a + b; }; +}; + +} // namespace functors +} // namespace base diff --git a/Telegram/SourceFiles/base/optional.h b/Telegram/SourceFiles/base/optional.h new file mode 100644 index 000000000..bfb6ec8a0 --- /dev/null +++ b/Telegram/SourceFiles/base/optional.h @@ -0,0 +1,214 @@ +/* +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-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "base/variant.h" + +namespace base { + +struct none_type { + bool operator==(none_type other) const { + return true; + } + bool operator!=(none_type other) const { + return false; + } + bool operator<(none_type other) const { + return false; + } + bool operator<=(none_type other) const { + return true; + } + bool operator>(none_type other) const { + return false; + } + bool operator>=(none_type other) const { + return true; + } + +}; + +constexpr none_type none = {}; + +template +class optional_variant { +public: + optional_variant() : _impl(none) { + } + optional_variant(const optional_variant &other) : _impl(other._impl) { + } + optional_variant(optional_variant &&other) : _impl(std::move(other._impl)) { + } + template >::value>> + optional_variant(T &&value) : _impl(std::forward(value)) { + } + optional_variant &operator=(const optional_variant &other) { + _impl = other._impl; + return *this; + } + optional_variant &operator=(optional_variant &&other) { + _impl = std::move(other._impl); + return *this; + } + template >::value>> + optional_variant &operator=(T &&value) { + _impl = std::forward(value); + return *this; + } + + explicit operator bool() const { + return (get_if(&_impl) == nullptr); + } + bool operator==(const optional_variant &other) const { + return _impl == other._impl; + } + bool operator!=(const optional_variant &other) const { + return _impl != other._impl; + } + bool operator<(const optional_variant &other) const { + return _impl < other._impl; + } + bool operator<=(const optional_variant &other) const { + return _impl <= other._impl; + } + bool operator>(const optional_variant &other) const { + return _impl > other._impl; + } + bool operator>=(const optional_variant &other) const { + return _impl >= other._impl; + } + + template + decltype(auto) is() const { + return _impl.template is(); + } + template + decltype(auto) get_unchecked() { + return _impl.template get_unchecked(); + } + template + decltype(auto) get_unchecked() const { + return _impl.template get_unchecked(); + } + +private: + variant _impl; + +}; + +template +inline T *get_if(optional_variant *v) { + return (v && v->template is()) ? &v->template get_unchecked() : nullptr; +} + +template +inline const T *get_if(const optional_variant *v) { + return (v && v->template is()) ? &v->template get_unchecked() : nullptr; +} + +template +class optional; + +template +struct optional_wrap_once { + using type = optional; +}; + +template +struct optional_wrap_once> { + using type = optional; +}; + +template +using optional_wrap_once_t = typename optional_wrap_once>::type; + +template +struct optional_chain_result { + using type = optional_wrap_once_t; +}; + +template <> +struct optional_chain_result { + using type = bool; +}; + +template +using optional_chain_result_t = typename optional_chain_result::type; + +template +class optional : public optional_variant { +public: + using optional_variant::optional_variant; + + Type &operator*() { + auto result = get_if(this); + Expects(result != nullptr); + return *result; + } + const Type &operator*() const { + auto result = get_if(this); + Expects(result != nullptr); + return *result; + } + Type *operator->() { + auto result = get_if(this); + Expects(result != nullptr); + return result; + } + const Type *operator->() const { + auto result = get_if(this); + Expects(result != nullptr); + return result; + } + +}; + +template +optional_wrap_once_t make_optional(Type &&value) { + return optional_wrap_once_t { std::forward(value) }; +} + +template +inline auto optional_chain( + const optional &value, + Method &method, + std::false_type) +-> optional_chain_result_t { + return value ? make_optional(method(*value)) : none; +} + +template +inline auto optional_chain( + const optional &value, + Method &method, + std::true_type) +-> optional_chain_result_t { + return value ? (method(*value), true) : false; +} + +template +inline auto operator|(const optional &value, Method method) +-> optional_chain_result_t { + using is_void_return = std::is_same; + return optional_chain(value, method, is_void_return {}); +} + +} // namespace base diff --git a/Telegram/SourceFiles/base/variant.h b/Telegram/SourceFiles/base/variant.h index 7e40bba4c..02a005c19 100644 --- a/Telegram/SourceFiles/base/variant.h +++ b/Telegram/SourceFiles/base/variant.h @@ -25,21 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org // We use base::variant<> alias and base::get_if() helper while we don't have std::variant<>. namespace base { -struct null_variant_type { -}; - -inline constexpr null_variant_type null_variant() { - return null_variant_type {}; -} - -inline bool operator<(null_variant_type a, null_variant_type b) { - return false; -} - -inline bool operator==(null_variant_type a, null_variant_type b) { - return true; -} - template using variant = mapbox::util::variant; @@ -53,36 +38,4 @@ inline const T *get_if(const variant *v) { return (v && v->template is()) ? &v->template get_unchecked() : nullptr; } -template -using optional_variant = variant; - -template -inline bool is_null_variant(const optional_variant &variant) { - return get_if(&variant) != nullptr; -} - -template -using optional = optional_variant; - -using null_optional_type = null_variant_type; - -template -inline Type *get_if(optional *v) { - return (v && v->template is()) ? &v->template get_unchecked() : nullptr; -} - -template -inline const Type *get_if(const optional *v) { - return (v && v->template is()) ? &v->template get_unchecked() : nullptr; -} - -template -inline bool is_null_optional(const optional &optional) { - return is_null_variant(optional); -} - -inline constexpr null_optional_type null_optional() { - return null_optional_type {}; -} - } // namespace base diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 65ae4869f..7f175cea0 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -635,7 +635,7 @@ void StickersListWidget::paintFeaturedStickers(Painter &p, QRect clip) { auto &sets = shownSets(); auto selectedSticker = base::get_if(&_selected); - auto selectedButton = base::get_if(base::is_null_variant(_pressed) ? &_selected : &_pressed); + auto selectedButton = base::get_if(_pressed ? &_pressed : &_selected); auto tilly = st::stickerPanPadding; auto ms = getms(); @@ -727,7 +727,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { auto ms = getms(); auto &sets = shownSets(); auto selectedSticker = base::get_if(&_selected); - auto selectedButton = base::get_if(base::is_null_variant(_pressed) ? &_selected : &_pressed); + auto selectedButton = base::get_if(_pressed ? &_pressed : &_selected); enumerateSections([this, &p, clip, fromColumn, toColumn, selectedSticker, selectedButton, ms](const SectionInfo &info) { if (clip.top() >= info.rowsBottom) { return true; @@ -979,7 +979,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { _previewTimer.stop(); auto pressed = _pressed; - setPressed(base::null_variant()); + setPressed(base::none); if (pressed != _selected) { update(); } @@ -994,7 +994,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { updateSelected(); auto &sets = shownSets(); - if (!base::is_null_variant(pressed) && pressed == _selected) { + if (pressed && pressed == _selected) { if (auto sticker = base::get_if(&pressed)) { t_assert(sticker->section >= 0 && sticker->section < sets.size()); auto &set = sets[sticker->section]; @@ -1104,8 +1104,8 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) { } void StickersListWidget::clearSelection() { - setPressed(base::null_variant()); - setSelected(base::null_variant()); + setPressed(base::none); + setSelected(base::none); update(); } @@ -1394,11 +1394,11 @@ bool StickersListWidget::preventAutoHide() { } void StickersListWidget::updateSelected() { - if (!base::is_null_variant(_pressed) && !_previewShown) { + if (_pressed && !_previewShown) { return; } - auto newSelected = OverState { base::null_variant() }; + auto newSelected = OverState { base::none }; auto p = mapFromGlobal(_lastMousePosition); if (!rect().contains(p) || p.y() < getVisibleTop() || p.y() >= getVisibleBottom() @@ -1487,7 +1487,7 @@ bool StickersListWidget::stickerHasDeleteButton(const Set &set, int index) const void StickersListWidget::setSelected(OverState newSelected) { if (_selected != newSelected) { - setCursor(base::is_null_variant(newSelected) ? style::cur_default : style::cur_pointer); + setCursor(newSelected ? style::cur_pointer : style::cur_default); auto &sets = shownSets(); auto updateSelected = [this, &sets]() { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 62c502994..c92ab2cb8 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -226,8 +226,8 @@ private: Footer *_footer = nullptr; - OverState _selected = base::null_variant(); - OverState _pressed = base::null_variant(); + OverState _selected; + OverState _pressed; QPoint _lastMousePosition; Text _megagroupSetAbout; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 8b165454e..9f644c3f5 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1174,11 +1174,10 @@ void History::setUnreadMentionsCount(int count) { } bool History::addToUnreadMentions(MsgId msgId, AddToOverviewMethod method) { - auto count = base::get_if(&_unreadMentionsCount); - auto allLoaded = count ? (_unreadMentions.size() >= *count) : false; + auto allLoaded = _unreadMentionsCount ? (_unreadMentions.size() >= *_unreadMentionsCount) : false; if (allLoaded) { if (method == AddToOverviewNew) { - ++*count; + ++*_unreadMentionsCount; _unreadMentions.insert(msgId); return true; } @@ -1191,9 +1190,9 @@ bool History::addToUnreadMentions(MsgId msgId, AddToOverviewMethod method) { void History::eraseFromUnreadMentions(MsgId msgId) { _unreadMentions.remove(msgId); - if (auto count = base::get_if(&_unreadMentionsCount)) { - if (*count > 0) { - --*count; + if (_unreadMentionsCount) { + if (*_unreadMentionsCount > 0) { + --*_unreadMentionsCount; } } Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 1089f811e..d44294aaa 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -361,7 +361,7 @@ public: return _unreadMentions.empty() ? 0 : _unreadMentions.back(); } int getUnreadMentionsCount(int notLoadedValue = -1) const { - return base::is_null_optional(_unreadMentionsCount) ? notLoadedValue : *base::get_if(&_unreadMentionsCount); + return _unreadMentionsCount ? *_unreadMentionsCount : notLoadedValue; } bool hasUnreadMentions() const { return (getUnreadMentionsCount() > 0); @@ -559,7 +559,7 @@ private: bool _mute = false; int _unreadCount = 0; - base::optional _unreadMentionsCount = base::null_optional(); + base::optional _unreadMentionsCount; base::flat_set _unreadMentions; Dialogs::RowsByLetter _chatListLinks[2]; diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index b8d80a520..12937bcfc 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -65,6 +65,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include "base/variant.h" +#include "base/optional.h" #include "base/algorithm.h" #include "core/basic_types.h" diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index c2e5a3e87..7200b3321 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -7,6 +7,7 @@ <(src_loc)/base/observer.h <(src_loc)/base/ordered_set.h <(src_loc)/base/openssl_help.h +<(src_loc)/base/optional.h <(src_loc)/base/parse_helper.cpp <(src_loc)/base/parse_helper.h <(src_loc)/base/qthelp_regex.h