From daa33a572695176283a29613f02f88739153351b Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 23 Sep 2019 14:06:27 +0300 Subject: [PATCH 01/59] Update API scheme to layer 106. --- Telegram/Resources/tl/api.tl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index e3c12c904..5baf29966 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1078,6 +1078,10 @@ theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_ account.themesNotModified#f41eb622 = account.Themes; account.themes#7f676421 hash:int themes:Vector = account.Themes; +wallet.liteResponse#764386d7 response:bytes = wallet.LiteResponse; + +wallet.secretSalt#dd484d64 salt:bytes = wallet.KeySecretSalt; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1415,4 +1419,7 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates; folders.deleteFolder#1c295881 folder_id:int = Updates; -// LAYER 105 +wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse; +wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt; + +// LAYER 106 From be9398b05ab45272c496e7242405ec10cd583e3c Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 17 Sep 2019 13:44:59 +0300 Subject: [PATCH 02/59] Add several submodules from desktop-app. --- .gitmodules | 15 ++++-- Telegram/ThirdParty/crl | 1 - Telegram/ThirdParty/gyp_helpers | 1 + Telegram/ThirdParty/lib_base | 1 + Telegram/ThirdParty/lib_crl | 1 + Telegram/ThirdParty/lib_rpl | 1 + Telegram/gyp/crl.gyp | 78 ----------------------------- Telegram/gyp/lib_base.gyp | 6 ++- Telegram/gyp/lib_export.gyp | 4 +- Telegram/gyp/lib_ffmpeg.gyp | 3 -- Telegram/gyp/lib_lottie.gyp | 3 -- Telegram/gyp/lib_mtproto.gyp | 4 +- Telegram/gyp/lib_storage.gyp | 3 -- Telegram/gyp/lib_ui.gyp | 3 +- Telegram/gyp/telegram/telegram.gypi | 1 - 15 files changed, 25 insertions(+), 100 deletions(-) delete mode 160000 Telegram/ThirdParty/crl create mode 160000 Telegram/ThirdParty/gyp_helpers create mode 160000 Telegram/ThirdParty/lib_base create mode 160000 Telegram/ThirdParty/lib_crl create mode 160000 Telegram/ThirdParty/lib_rpl delete mode 100644 Telegram/gyp/crl.gyp diff --git a/.gitmodules b/.gitmodules index e292cfd38..2e9eb629e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,6 @@ [submodule "Telegram/ThirdParty/Catch"] path = Telegram/ThirdParty/Catch url = https://github.com/philsquared/Catch -[submodule "Telegram/ThirdParty/crl"] - path = Telegram/ThirdParty/crl - url = https://github.com/telegramdesktop/crl.git [submodule "Telegram/ThirdParty/xxHash"] path = Telegram/ThirdParty/xxHash url = https://github.com/Cyan4973/xxHash.git @@ -22,3 +19,15 @@ [submodule "Telegram/ThirdParty/lz4"] path = Telegram/ThirdParty/lz4 url = https://github.com/lz4/lz4.git +[submodule "Telegram/ThirdParty/lib_crl"] + path = Telegram/ThirdParty/lib_crl + url = https://github.com/desktop-app/lib_crl.git +[submodule "Telegram/ThirdParty/lib_rpl"] + path = Telegram/ThirdParty/lib_rpl + url = https://github.com/desktop-app/lib_rpl.git +[submodule "Telegram/ThirdParty/lib_base"] + path = Telegram/ThirdParty/lib_base + url = https://github.com/desktop-app/lib_base.git +[submodule "Telegram/ThirdParty/gyp_helpers"] + path = Telegram/ThirdParty/gyp_helpers + url = https://github.com/desktop-app/gyp_helpers.git diff --git a/Telegram/ThirdParty/crl b/Telegram/ThirdParty/crl deleted file mode 160000 index 52baf11aa..000000000 --- a/Telegram/ThirdParty/crl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 52baf11aaeb7f5ea6955a438abaa1aee4c4308d8 diff --git a/Telegram/ThirdParty/gyp_helpers b/Telegram/ThirdParty/gyp_helpers new file mode 160000 index 000000000..ac609eccb --- /dev/null +++ b/Telegram/ThirdParty/gyp_helpers @@ -0,0 +1 @@ +Subproject commit ac609eccb9de963b64821a9aacdc481eca12360f diff --git a/Telegram/ThirdParty/lib_base b/Telegram/ThirdParty/lib_base new file mode 160000 index 000000000..7a5fe257d --- /dev/null +++ b/Telegram/ThirdParty/lib_base @@ -0,0 +1 @@ +Subproject commit 7a5fe257d9bffb813598913b5fc08f1725042099 diff --git a/Telegram/ThirdParty/lib_crl b/Telegram/ThirdParty/lib_crl new file mode 160000 index 000000000..83ce4916d --- /dev/null +++ b/Telegram/ThirdParty/lib_crl @@ -0,0 +1 @@ +Subproject commit 83ce4916d4f712300b7550900c59d3d0d420178b diff --git a/Telegram/ThirdParty/lib_rpl b/Telegram/ThirdParty/lib_rpl new file mode 160000 index 000000000..93465f785 --- /dev/null +++ b/Telegram/ThirdParty/lib_rpl @@ -0,0 +1 @@ +Subproject commit 93465f7854012f2237452f5eaddc5edcc7de2d48 diff --git a/Telegram/gyp/crl.gyp b/Telegram/gyp/crl.gyp deleted file mode 100644 index e7b0c84ee..000000000 --- a/Telegram/gyp/crl.gyp +++ /dev/null @@ -1,78 +0,0 @@ -# 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 - -{ - 'includes': [ - 'common/common.gypi', - ], - 'targets': [{ - 'target_name': 'crl', - 'dependencies': [ - ], - 'includes': [ - 'common/library.gypi', - 'modules/qt.gypi', - ], - 'defines': [ - ], - 'conditions': [[ 'build_macold', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], - }, - 'include_dirs': [ - '/usr/local/macold/include/c++/v1', - ], - }]], - 'variables': { - 'crl_src_loc': '../ThirdParty/crl/src/crl', - 'official_build_target%': '', - }, - 'include_dirs': [ - '../ThirdParty/crl/src', - ], - 'sources': [ - '<(crl_src_loc)/common/crl_common_config.h', - '<(crl_src_loc)/common/crl_common_list.cpp', - '<(crl_src_loc)/common/crl_common_list.h', - '<(crl_src_loc)/common/crl_common_on_main.cpp', - '<(crl_src_loc)/common/crl_common_on_main.h', - '<(crl_src_loc)/common/crl_common_on_main_guarded.h', - '<(crl_src_loc)/common/crl_common_queue.cpp', - '<(crl_src_loc)/common/crl_common_queue.h', - '<(crl_src_loc)/common/crl_common_sync.h', - '<(crl_src_loc)/common/crl_common_utils.h', - '<(crl_src_loc)/dispatch/crl_dispatch_async.cpp', - '<(crl_src_loc)/dispatch/crl_dispatch_async.h', - '<(crl_src_loc)/dispatch/crl_dispatch_on_main.h', - '<(crl_src_loc)/dispatch/crl_dispatch_queue.cpp', - '<(crl_src_loc)/dispatch/crl_dispatch_queue.h', - '<(crl_src_loc)/dispatch/crl_dispatch_semaphore.cpp', - '<(crl_src_loc)/dispatch/crl_dispatch_semaphore.h', - '<(crl_src_loc)/mac/crl_mac_time.cpp', - '<(crl_src_loc)/linux/crl_linux_time.cpp', - '<(crl_src_loc)/qt/crl_qt_async.cpp', - '<(crl_src_loc)/qt/crl_qt_async.h', - '<(crl_src_loc)/qt/crl_qt_semaphore.cpp', - '<(crl_src_loc)/qt/crl_qt_semaphore.h', - '<(crl_src_loc)/winapi/crl_winapi_async.cpp', - '<(crl_src_loc)/winapi/crl_winapi_async.h', - '<(crl_src_loc)/winapi/crl_winapi_dll.h', - '<(crl_src_loc)/winapi/crl_winapi_list.cpp', - '<(crl_src_loc)/winapi/crl_winapi_list.h', - '<(crl_src_loc)/winapi/crl_winapi_semaphore.cpp', - '<(crl_src_loc)/winapi/crl_winapi_semaphore.h', - '<(crl_src_loc)/winapi/crl_winapi_time.cpp', - '<(crl_src_loc)/crl.h', - '<(crl_src_loc)/crl_async.h', - '<(crl_src_loc)/crl_object_on_queue.h', - '<(crl_src_loc)/crl_on_main.h', - '<(crl_src_loc)/crl_queue.h', - '<(crl_src_loc)/crl_semaphore.h', - '<(crl_src_loc)/crl_time.cpp', - '<(crl_src_loc)/crl_time.h', - ], - }], -} diff --git a/Telegram/gyp/lib_base.gyp b/Telegram/gyp/lib_base.gyp index 98c0d885b..768f794f0 100644 --- a/Telegram/gyp/lib_base.gyp +++ b/Telegram/gyp/lib_base.gyp @@ -28,7 +28,10 @@ 'XXH_INLINE_ALL', ], 'dependencies': [ - 'crl.gyp:crl', + '../ThirdParty/crl/crl.gyp:crl', + ], + 'export_dependent_settings': [ + '../ThirdParty/crl/crl.gyp:crl', ], 'include_dirs': [ '<(src_loc)', @@ -36,7 +39,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', '<(submodules_loc)/xxHash', ], 'sources': [ diff --git a/Telegram/gyp/lib_export.gyp b/Telegram/gyp/lib_export.gyp index 140ea6b98..dd8dd4894 100644 --- a/Telegram/gyp/lib_export.gyp +++ b/Telegram/gyp/lib_export.gyp @@ -28,10 +28,11 @@ ], 'dependencies': [ 'lib_scheme.gyp:lib_scheme', - 'crl.gyp:crl', + 'lib_base.gyp:lib_base', ], 'export_dependent_settings': [ 'lib_scheme.gyp:lib_scheme', + 'lib_base.gyp:lib_base', ], 'conditions': [[ 'build_macold', { 'xcode_settings': { @@ -47,7 +48,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', ], 'sources': [ '<(src_loc)/export/export_api_wrap.cpp', diff --git a/Telegram/gyp/lib_ffmpeg.gyp b/Telegram/gyp/lib_ffmpeg.gyp index 22a2a9661..1d77a63e2 100644 --- a/Telegram/gyp/lib_ffmpeg.gyp +++ b/Telegram/gyp/lib_ffmpeg.gyp @@ -21,11 +21,9 @@ 'submodules_loc': '../ThirdParty', }, 'dependencies': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', ], 'export_dependent_settings': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', ], 'defines': [ @@ -37,7 +35,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', ], 'sources': [ '<(src_loc)/ffmpeg/ffmpeg_utility.cpp', diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp index ee2bb0f04..5c14edb69 100644 --- a/Telegram/gyp/lib_lottie.gyp +++ b/Telegram/gyp/lib_lottie.gyp @@ -24,14 +24,12 @@ 'lz4_loc': '<(submodules_loc)/lz4/lib', }, 'dependencies': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', 'lib_ffmpeg.gyp:lib_ffmpeg', 'lib_lz4.gyp:lib_lz4', ], 'export_dependent_settings': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', 'lib_ffmpeg.gyp:lib_ffmpeg', @@ -50,7 +48,6 @@ '<(lz4_loc)', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', ], 'sources': [ '<(src_loc)/lottie/lottie_animation.cpp', diff --git a/Telegram/gyp/lib_mtproto.gyp b/Telegram/gyp/lib_mtproto.gyp index 6adcd346f..45c029f24 100644 --- a/Telegram/gyp/lib_mtproto.gyp +++ b/Telegram/gyp/lib_mtproto.gyp @@ -28,10 +28,11 @@ ], 'dependencies': [ 'lib_scheme.gyp:lib_scheme', - 'crl.gyp:crl', + 'lib_base.gyp:lib_base', ], 'export_dependent_settings': [ 'lib_scheme.gyp:lib_scheme', + 'lib_base.gyp:lib_base', ], 'conditions': [[ 'build_macold', { 'xcode_settings': { @@ -47,7 +48,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', ], 'sources': [ '<(src_loc)/mtproto/mtp_abstract_socket.cpp', diff --git a/Telegram/gyp/lib_storage.gyp b/Telegram/gyp/lib_storage.gyp index cd3b6d30c..d3154c591 100644 --- a/Telegram/gyp/lib_storage.gyp +++ b/Telegram/gyp/lib_storage.gyp @@ -28,11 +28,9 @@ 'XXH_INLINE_ALL', ], 'dependencies': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', ], 'export_dependent_settings': [ - 'crl.gyp:crl', 'lib_base.gyp:lib_base', ], 'include_dirs': [ @@ -41,7 +39,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', '<(submodules_loc)/xxHash', ], 'sources': [ diff --git a/Telegram/gyp/lib_ui.gyp b/Telegram/gyp/lib_ui.gyp index fa6bd4ea3..e5bba8299 100644 --- a/Telegram/gyp/lib_ui.gyp +++ b/Telegram/gyp/lib_ui.gyp @@ -23,7 +23,7 @@ 'dependencies': [ 'codegen.gyp:codegen_emoji', 'codegen.gyp:codegen_style', - 'crl.gyp:crl', + 'lib_base.gyp:lib_base', ], 'variables': { 'src_loc': '../SourceFiles', @@ -59,7 +59,6 @@ '<(libs_loc)/range-v3/include', '<(submodules_loc)/GSL/include', '<(submodules_loc)/variant/include', - '<(submodules_loc)/crl/src', '<(emoji_suggestions_loc)', ], 'sources': [ diff --git a/Telegram/gyp/telegram/telegram.gypi b/Telegram/gyp/telegram/telegram.gypi index c207db637..6abbe852e 100644 --- a/Telegram/gyp/telegram/telegram.gypi +++ b/Telegram/gyp/telegram/telegram.gypi @@ -74,7 +74,6 @@ 'tests/tests.gyp:tests', 'utils.gyp:Updater', '../ThirdParty/libtgvoip/libtgvoip.gyp:libtgvoip', - 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_export.gyp:lib_export', 'lib_storage.gyp:lib_storage', From 1b89348d89a305fe979a90b0a58ad37b0d58adc8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 17 Sep 2019 16:11:23 +0300 Subject: [PATCH 03/59] Use lib_rpl / lib_base from submodules. --- Telegram/SourceFiles/base/algorithm.h | 112 -- Telegram/SourceFiles/base/algorithm_tests.cpp | 50 - Telegram/SourceFiles/base/assertion.h | 101 -- Telegram/SourceFiles/base/base_integration.h | 18 - Telegram/SourceFiles/base/base_pch.cpp | 10 - Telegram/SourceFiles/base/base_pch.h | 29 - Telegram/SourceFiles/base/basic_types.h | 67 -- Telegram/SourceFiles/base/binary_guard.h | 101 -- Telegram/SourceFiles/base/build_config.h | 67 -- Telegram/SourceFiles/base/bytes.h | 164 --- .../SourceFiles/base/concurrent_timer.cpp | 364 ------ Telegram/SourceFiles/base/concurrent_timer.h | 146 --- Telegram/SourceFiles/base/crc32hash.cpp | 61 - Telegram/SourceFiles/base/crc32hash.h | 16 - Telegram/SourceFiles/base/enum_mask.h | 47 - Telegram/SourceFiles/base/flags.h | 351 ------ Telegram/SourceFiles/base/flags_tests.cpp | 129 --- Telegram/SourceFiles/base/flat_map.h | 917 --------------- Telegram/SourceFiles/base/flat_map_tests.cpp | 121 -- Telegram/SourceFiles/base/flat_set.h | 720 ------------ Telegram/SourceFiles/base/flat_set_tests.cpp | 84 -- Telegram/SourceFiles/base/functors.h | 41 - .../SourceFiles/base/index_based_iterator.h | 130 --- Telegram/SourceFiles/base/invoke_queued.h | 62 - Telegram/SourceFiles/base/last_used_cache.h | 69 -- Telegram/SourceFiles/base/match_method.h | 52 - Telegram/SourceFiles/base/object_ptr.h | 121 -- Telegram/SourceFiles/base/observer.cpp | 85 -- Telegram/SourceFiles/base/observer.h | 476 -------- Telegram/SourceFiles/base/openssl_help.h | 577 ---------- Telegram/SourceFiles/base/optional.h | 171 --- Telegram/SourceFiles/base/ordered_set.h | 153 --- Telegram/SourceFiles/base/overload.h | 89 -- Telegram/SourceFiles/base/parse_helper.cpp | 101 -- Telegram/SourceFiles/base/parse_helper.h | 42 - Telegram/SourceFiles/base/qt_connection.h | 49 - .../SourceFiles/base/qt_signal_producer.h | 82 -- Telegram/SourceFiles/base/qthelp_regex.h | 81 -- Telegram/SourceFiles/base/qthelp_url.cpp | 125 -- Telegram/SourceFiles/base/qthelp_url.h | 43 - .../SourceFiles/base/runtime_composer.cpp | 32 - Telegram/SourceFiles/base/runtime_composer.h | 279 ----- Telegram/SourceFiles/base/tests_main.cpp | 104 -- Telegram/SourceFiles/base/thread_safe_wrap.h | 60 - Telegram/SourceFiles/base/timer.cpp | 152 --- Telegram/SourceFiles/base/timer.h | 114 -- Telegram/SourceFiles/base/type_traits.h | 117 -- Telegram/SourceFiles/base/unique_any.h | 230 ---- Telegram/SourceFiles/base/unique_function.h | 178 --- Telegram/SourceFiles/base/unique_qptr.h | 121 -- Telegram/SourceFiles/base/unixtime.cpp | 174 --- Telegram/SourceFiles/base/unixtime.h | 33 - Telegram/SourceFiles/base/value_ordering.h | 144 --- Telegram/SourceFiles/base/variant.h | 127 -- Telegram/SourceFiles/base/virtual_method.h | 704 ----------- Telegram/SourceFiles/base/weak_ptr.h | 325 ------ Telegram/SourceFiles/base/zlib_help.h | 410 ------- Telegram/SourceFiles/mtproto/core_types.h | 1 - Telegram/SourceFiles/rpl/after_next.h | 59 - Telegram/SourceFiles/rpl/before_next.h | 24 - Telegram/SourceFiles/rpl/combine.h | 349 ------ Telegram/SourceFiles/rpl/combine_previous.h | 107 -- Telegram/SourceFiles/rpl/complete.h | 22 - Telegram/SourceFiles/rpl/conditional.h | 52 - Telegram/SourceFiles/rpl/consumer.h | 636 ---------- Telegram/SourceFiles/rpl/deferred.h | 26 - Telegram/SourceFiles/rpl/details/callable.h | 145 --- .../SourceFiles/rpl/details/superset_type.h | 23 - Telegram/SourceFiles/rpl/details/type_list.h | 180 --- .../SourceFiles/rpl/distinct_until_changed.h | 50 - Telegram/SourceFiles/rpl/event_stream.h | 293 ----- Telegram/SourceFiles/rpl/fail.h | 24 - Telegram/SourceFiles/rpl/filter.h | 144 --- Telegram/SourceFiles/rpl/flatten_latest.h | 74 -- Telegram/SourceFiles/rpl/lifetime.h | 90 -- Telegram/SourceFiles/rpl/map.h | 211 ---- Telegram/SourceFiles/rpl/mappers.h | 474 -------- Telegram/SourceFiles/rpl/merge.h | 149 --- Telegram/SourceFiles/rpl/never.h | 21 - Telegram/SourceFiles/rpl/operators_tests.cpp | 508 -------- Telegram/SourceFiles/rpl/producer.h | 1025 ----------------- Telegram/SourceFiles/rpl/producer_tests.cpp | 441 ------- Telegram/SourceFiles/rpl/range.h | 87 -- Telegram/SourceFiles/rpl/rpl.h | 39 - Telegram/SourceFiles/rpl/skip.h | 61 - Telegram/SourceFiles/rpl/take.h | 64 - Telegram/SourceFiles/rpl/then.h | 72 -- Telegram/SourceFiles/rpl/type_erased.h | 32 - Telegram/SourceFiles/rpl/variable.h | 181 --- Telegram/SourceFiles/rpl/variable_tests.cpp | 32 - Telegram/gyp/Telegram.gyp | 2 +- Telegram/gyp/codegen.gyp | 20 +- Telegram/gyp/common/common.gypi | 115 -- Telegram/gyp/common/executable.gypi | 21 - Telegram/gyp/common/library.gypi | 12 - Telegram/gyp/common/linux.gypi | 112 -- Telegram/gyp/common/mac.gypi | 117 -- Telegram/gyp/common/win.gypi | 118 -- Telegram/gyp/lib_base.gyp | 105 -- Telegram/gyp/lib_export.gyp | 17 +- Telegram/gyp/lib_ffmpeg.gyp | 16 +- Telegram/gyp/lib_lottie.gyp | 18 +- Telegram/gyp/lib_lz4.gyp | 5 +- Telegram/gyp/lib_mtproto.gyp | 18 +- Telegram/gyp/lib_rlottie.gyp | 6 +- Telegram/gyp/lib_scheme.gyp | 19 +- Telegram/gyp/lib_storage.gyp | 20 +- Telegram/gyp/lib_ui.gyp | 19 +- Telegram/gyp/linux_glibc_wraps.gyp | 2 +- Telegram/gyp/modules/openssl.gypi | 55 - Telegram/gyp/modules/pch.gypi | 20 - Telegram/gyp/modules/qt.gypi | 246 ---- Telegram/gyp/modules/qt_moc.gypi | 31 - Telegram/gyp/modules/qt_rcc.gypi | 27 - Telegram/gyp/telegram/telegram.gypi | 19 +- Telegram/gyp/tests/common_test.gypi | 13 +- Telegram/gyp/tests/tests.gyp | 89 +- Telegram/gyp/utils.gyp | 8 +- 118 files changed, 128 insertions(+), 16366 deletions(-) delete mode 100644 Telegram/SourceFiles/base/algorithm.h delete mode 100644 Telegram/SourceFiles/base/algorithm_tests.cpp delete mode 100644 Telegram/SourceFiles/base/assertion.h delete mode 100644 Telegram/SourceFiles/base/base_integration.h delete mode 100644 Telegram/SourceFiles/base/base_pch.cpp delete mode 100644 Telegram/SourceFiles/base/base_pch.h delete mode 100644 Telegram/SourceFiles/base/basic_types.h delete mode 100644 Telegram/SourceFiles/base/binary_guard.h delete mode 100644 Telegram/SourceFiles/base/build_config.h delete mode 100644 Telegram/SourceFiles/base/bytes.h delete mode 100644 Telegram/SourceFiles/base/concurrent_timer.cpp delete mode 100644 Telegram/SourceFiles/base/concurrent_timer.h delete mode 100644 Telegram/SourceFiles/base/crc32hash.cpp delete mode 100644 Telegram/SourceFiles/base/crc32hash.h delete mode 100644 Telegram/SourceFiles/base/enum_mask.h delete mode 100644 Telegram/SourceFiles/base/flags.h delete mode 100644 Telegram/SourceFiles/base/flags_tests.cpp delete mode 100644 Telegram/SourceFiles/base/flat_map.h delete mode 100644 Telegram/SourceFiles/base/flat_map_tests.cpp delete mode 100644 Telegram/SourceFiles/base/flat_set.h delete mode 100644 Telegram/SourceFiles/base/flat_set_tests.cpp delete mode 100644 Telegram/SourceFiles/base/functors.h delete mode 100644 Telegram/SourceFiles/base/index_based_iterator.h delete mode 100644 Telegram/SourceFiles/base/invoke_queued.h delete mode 100644 Telegram/SourceFiles/base/last_used_cache.h delete mode 100644 Telegram/SourceFiles/base/match_method.h delete mode 100644 Telegram/SourceFiles/base/object_ptr.h delete mode 100644 Telegram/SourceFiles/base/observer.cpp delete mode 100644 Telegram/SourceFiles/base/observer.h delete mode 100644 Telegram/SourceFiles/base/openssl_help.h delete mode 100644 Telegram/SourceFiles/base/optional.h delete mode 100644 Telegram/SourceFiles/base/ordered_set.h delete mode 100644 Telegram/SourceFiles/base/overload.h delete mode 100644 Telegram/SourceFiles/base/parse_helper.cpp delete mode 100644 Telegram/SourceFiles/base/parse_helper.h delete mode 100644 Telegram/SourceFiles/base/qt_connection.h delete mode 100644 Telegram/SourceFiles/base/qt_signal_producer.h delete mode 100644 Telegram/SourceFiles/base/qthelp_regex.h delete mode 100644 Telegram/SourceFiles/base/qthelp_url.cpp delete mode 100644 Telegram/SourceFiles/base/qthelp_url.h delete mode 100644 Telegram/SourceFiles/base/runtime_composer.cpp delete mode 100644 Telegram/SourceFiles/base/runtime_composer.h delete mode 100644 Telegram/SourceFiles/base/tests_main.cpp delete mode 100644 Telegram/SourceFiles/base/thread_safe_wrap.h delete mode 100644 Telegram/SourceFiles/base/timer.cpp delete mode 100644 Telegram/SourceFiles/base/timer.h delete mode 100644 Telegram/SourceFiles/base/type_traits.h delete mode 100644 Telegram/SourceFiles/base/unique_any.h delete mode 100644 Telegram/SourceFiles/base/unique_function.h delete mode 100644 Telegram/SourceFiles/base/unique_qptr.h delete mode 100644 Telegram/SourceFiles/base/unixtime.cpp delete mode 100644 Telegram/SourceFiles/base/unixtime.h delete mode 100644 Telegram/SourceFiles/base/value_ordering.h delete mode 100644 Telegram/SourceFiles/base/variant.h delete mode 100644 Telegram/SourceFiles/base/virtual_method.h delete mode 100644 Telegram/SourceFiles/base/weak_ptr.h delete mode 100644 Telegram/SourceFiles/base/zlib_help.h delete mode 100644 Telegram/SourceFiles/rpl/after_next.h delete mode 100644 Telegram/SourceFiles/rpl/before_next.h delete mode 100644 Telegram/SourceFiles/rpl/combine.h delete mode 100644 Telegram/SourceFiles/rpl/combine_previous.h delete mode 100644 Telegram/SourceFiles/rpl/complete.h delete mode 100644 Telegram/SourceFiles/rpl/conditional.h delete mode 100644 Telegram/SourceFiles/rpl/consumer.h delete mode 100644 Telegram/SourceFiles/rpl/deferred.h delete mode 100644 Telegram/SourceFiles/rpl/details/callable.h delete mode 100644 Telegram/SourceFiles/rpl/details/superset_type.h delete mode 100644 Telegram/SourceFiles/rpl/details/type_list.h delete mode 100644 Telegram/SourceFiles/rpl/distinct_until_changed.h delete mode 100644 Telegram/SourceFiles/rpl/event_stream.h delete mode 100644 Telegram/SourceFiles/rpl/fail.h delete mode 100644 Telegram/SourceFiles/rpl/filter.h delete mode 100644 Telegram/SourceFiles/rpl/flatten_latest.h delete mode 100644 Telegram/SourceFiles/rpl/lifetime.h delete mode 100644 Telegram/SourceFiles/rpl/map.h delete mode 100644 Telegram/SourceFiles/rpl/mappers.h delete mode 100644 Telegram/SourceFiles/rpl/merge.h delete mode 100644 Telegram/SourceFiles/rpl/never.h delete mode 100644 Telegram/SourceFiles/rpl/operators_tests.cpp delete mode 100644 Telegram/SourceFiles/rpl/producer.h delete mode 100644 Telegram/SourceFiles/rpl/producer_tests.cpp delete mode 100644 Telegram/SourceFiles/rpl/range.h delete mode 100644 Telegram/SourceFiles/rpl/rpl.h delete mode 100644 Telegram/SourceFiles/rpl/skip.h delete mode 100644 Telegram/SourceFiles/rpl/take.h delete mode 100644 Telegram/SourceFiles/rpl/then.h delete mode 100644 Telegram/SourceFiles/rpl/type_erased.h delete mode 100644 Telegram/SourceFiles/rpl/variable.h delete mode 100644 Telegram/SourceFiles/rpl/variable_tests.cpp delete mode 100644 Telegram/gyp/common/common.gypi delete mode 100644 Telegram/gyp/common/executable.gypi delete mode 100644 Telegram/gyp/common/library.gypi delete mode 100644 Telegram/gyp/common/linux.gypi delete mode 100644 Telegram/gyp/common/mac.gypi delete mode 100644 Telegram/gyp/common/win.gypi delete mode 100644 Telegram/gyp/lib_base.gyp delete mode 100644 Telegram/gyp/modules/openssl.gypi delete mode 100644 Telegram/gyp/modules/pch.gypi delete mode 100644 Telegram/gyp/modules/qt.gypi delete mode 100644 Telegram/gyp/modules/qt_moc.gypi delete mode 100644 Telegram/gyp/modules/qt_rcc.gypi diff --git a/Telegram/SourceFiles/base/algorithm.h b/Telegram/SourceFiles/base/algorithm.h deleted file mode 100644 index fbf53e94c..000000000 --- a/Telegram/SourceFiles/base/algorithm.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -template -inline Type take(Type &value) { - return std::exchange(value, Type {}); -} - -template -inline Type duplicate(const Type &value) { - return value; -} - -template -inline constexpr size_t array_size(const Type(&)[Size]) { - return Size; -} - -template -inline bool contains(const Container &container, const T &value) { - const auto end = std::end(container); - return std::find(std::begin(container), end, value) != end; -} - -template -inline constexpr D up_cast(T object) { - using DV = std::decay_t; - using TV = std::decay_t; - if constexpr (std::is_base_of_v) { - return object; - } else { - return nullptr; - } -} - -// We need a custom comparator for set>::find to work with pointers. -// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs -template -struct pointer_comparator { - using is_transparent = std::true_type; - - // helper does some magic in order to reduce the number of - // pairs of types we need to know how to compare: it turns - // everything into a pointer, and then uses `std::less` - // to do the comparison: - struct helper { - const T *ptr = nullptr; - helper() = default; - helper(const helper &other) = default; - helper(const T *p) : ptr(p) { - } - template - helper(const std::shared_ptr &other) : ptr(other.get()) { - } - template - helper(const std::unique_ptr &other) : ptr(other.get()) { - } - bool operator<(helper other) const { - return std::less()(ptr, other.ptr); - } - }; - - // without helper, we'd need 2^n different overloads, where - // n is the number of types we want to support (so, 8 with - // raw pointers, unique pointers, and shared pointers). That - // seems silly. - // && helps enforce rvalue use only - bool operator()(const helper &&lhs, const helper &&rhs) const { - return lhs < rhs; - } - -}; - -inline QString FromUtf8Safe(const char *string, int size = -1) { - if (!string || !size) { - return QString(); - } else if (size < 0) { - size = strlen(string); - } - const auto result = QString::fromUtf8(string, size); - const auto back = result.toUtf8(); - return (back.size() != size || memcmp(back.constData(), string, size)) - ? QString::fromLocal8Bit(string, size) - : result; -} - -inline QString FromUtf8Safe(const QByteArray &string) { - return FromUtf8Safe(string.constData(), string.size()); -} - -} // namespace base - -template -inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; } - -template -inline void accumulate_min(T &a, const T &b) { if (a > b) a = b; } - -template -QLatin1String qstr(const char(&string)[Size]) { - return QLatin1String(string, Size - 1); -} diff --git a/Telegram/SourceFiles/base/algorithm_tests.cpp b/Telegram/SourceFiles/base/algorithm_tests.cpp deleted file mode 100644 index 9e3df829f..000000000 --- a/Telegram/SourceFiles/base/algorithm_tests.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -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 "catch.hpp" - -#include "base/index_based_iterator.h" - -TEST_CASE("index_based_iterator tests", "[base::algorithm]") { - auto v = std::vector(); - - v.insert(v.end(), { 1, 2, 3, 4, 5, 4, 3, 2, 1 }); - auto push_back_safe_remove_if = [](auto &v, auto predicate) { - auto begin = base::index_based_begin(v); - auto end = base::index_based_end(v); - auto from = std::remove_if(begin, end, predicate); - if (from != end) { - auto newEnd = base::index_based_end(v); - if (newEnd != end) { - REQUIRE(newEnd > end); - while (end != newEnd) { - *from++ = *end++; - } - } - v.erase(from.base(), newEnd.base()); - } - }; - SECTION("allows to push_back from predicate") { - push_back_safe_remove_if(v, [&v](int value) { - v.push_back(value); - return (value % 2) == 1; - }); - auto expected = std::vector { 2, 4, 4, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1 }; - REQUIRE(v == expected); - } - - SECTION("allows to push_back while removing all") { - push_back_safe_remove_if(v, [&v](int value) { - if (value == 5) { - v.push_back(value); - } - return true; - }); - auto expected = std::vector { 5 }; - REQUIRE(v == expected); - } -} \ No newline at end of file diff --git a/Telegram/SourceFiles/base/assertion.h b/Telegram/SourceFiles/base/assertion.h deleted file mode 100644 index d7907a583..000000000 --- a/Telegram/SourceFiles/base/assertion.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -// Ensures/Expects. -#include - -namespace base { -namespace assertion { - -// Client must define that method. -void log(const char *message, const char *file, int line); - -// Release build assertions. -inline constexpr void noop() { -} - -[[noreturn]] inline void fail( - const char *message, - const char *file, - int line) { - log(message, file, line); - - // Crash with access violation and generate crash report. - volatile auto nullptr_value = (int*)nullptr; - *nullptr_value = 0; - - // Silent the possible failure to comply noreturn warning. - std::abort(); -} - -constexpr const char* extract_basename(const char* path, size_t size) { - while (size != 0 && path[size - 1] != '/' && path[size - 1] != '\\') { - --size; - } - return path + size; -} - -} // namespace assertion -} // namespace base - -#if defined(__clang__) || defined(__GNUC__) -#define AssertUnlikelyHelper(x) __builtin_expect(!!(x), 0) -#else -#define AssertUnlikelyHelper(x) (!!(x)) -#endif - -#define AssertValidationCondition(condition, message, file, line)\ - ((AssertUnlikelyHelper(!(condition)))\ - ? ::base::assertion::fail(message, file, line)\ - : ::base::assertion::noop()) - -#define SOURCE_FILE_BASENAME (::base::assertion::extract_basename(\ - __FILE__,\ - sizeof(__FILE__))) - -#define AssertCustom(condition, message) (AssertValidationCondition(\ - condition,\ - message,\ - SOURCE_FILE_BASENAME,\ - __LINE__)) -#define Assert(condition) AssertCustom(condition, "\"" #condition "\"") - -// Define our own versions of Expects() and Ensures(). -// Let them crash with reports and logging. -#ifdef Expects -#undef Expects -#endif // Expects -#define Expects(condition) (AssertValidationCondition(\ - condition,\ - "\"" #condition "\"",\ - SOURCE_FILE_BASENAME,\ - __LINE__)) - -#ifdef Ensures -#undef Ensures -#endif // Ensures -#define Ensures(condition) (AssertValidationCondition(\ - condition,\ - "\"" #condition "\"",\ - SOURCE_FILE_BASENAME,\ - __LINE__)) - -#ifdef Unexpected -#undef Unexpected -#endif // Unexpected -#define Unexpected(message) (::base::assertion::fail(\ - "Unexpected: " message,\ - SOURCE_FILE_BASENAME,\ - __LINE__)) - -#ifdef _DEBUG -#define AssertIsDebug(...) -#endif // _DEBUG diff --git a/Telegram/SourceFiles/base/base_integration.h b/Telegram/SourceFiles/base/base_integration.h deleted file mode 100644 index 397e4736c..000000000 --- a/Telegram/SourceFiles/base/base_integration.h +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" - -// Methods that must be implemented outside lib_base. - -namespace base { - -void EnterFromEventLoop(FnMut &&method); - -} // namespace diff --git a/Telegram/SourceFiles/base/base_pch.cpp b/Telegram/SourceFiles/base/base_pch.cpp deleted file mode 100644 index 33c7d3279..000000000 --- a/Telegram/SourceFiles/base/base_pch.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* -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 "base/base_pch.h" - -// Precompiled header helper. diff --git a/Telegram/SourceFiles/base/base_pch.h b/Telegram/SourceFiles/base/base_pch.h deleted file mode 100644 index 933cf5b25..000000000 --- a/Telegram/SourceFiles/base/base_pch.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -#include "base/flat_map.h" -#include "base/flat_set.h" -#include "base/optional.h" -#include "base/openssl_help.h" diff --git a/Telegram/SourceFiles/base/basic_types.h b/Telegram/SourceFiles/base/basic_types.h deleted file mode 100644 index bd5ec4059..000000000 --- a/Telegram/SourceFiles/base/basic_types.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/build_config.h" -#include "base/ordered_set.h" -#include "base/unique_function.h" -#include "base/functors.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace func = base::functors; - -using gsl::not_null; -using index_type = gsl::index; -using size_type = gsl::index; - -template -using Fn = std::function; - -template -using FnMut = base::unique_function; - -//using uchar = unsigned char; // Qt has uchar -using int8 = qint8; -using uint8 = quint8; -using int16 = qint16; -using uint16 = quint16; -using int32 = qint32; -using uint32 = quint32; -using int64 = qint64; -using uint64 = quint64; -using float32 = float; -using float64 = double; - -using TimeId = int32; - -// Define specializations for QByteArray for Qt 5.3.2, because -// QByteArray in Qt 5.3.2 doesn't declare "pointer" subtype. -#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) -namespace gsl { - -template <> -inline span make_span(QByteArray &cont) { - return span(cont.data(), cont.size()); -} - -template <> -inline span make_span(const QByteArray &cont) { - return span(cont.constData(), cont.size()); -} - -} // namespace gsl -#endif // OS_MAC_OLD diff --git a/Telegram/SourceFiles/base/binary_guard.h b/Telegram/SourceFiles/base/binary_guard.h deleted file mode 100644 index 2f0978999..000000000 --- a/Telegram/SourceFiles/base/binary_guard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/algorithm.h" - -#include - -namespace base { - -class binary_guard { -public: - binary_guard() = default; - binary_guard(binary_guard &&other); - binary_guard &operator=(binary_guard &&other); - ~binary_guard(); - - binary_guard &operator=(std::nullptr_t); - - bool alive() const; - binary_guard make_guard(); - - explicit operator bool() const; - -private: - void destroy(); - - std::atomic *_bothAlive = nullptr; - -}; - -inline binary_guard::binary_guard(binary_guard &&other) -: _bothAlive(base::take(other._bothAlive)) { -} - -inline binary_guard &binary_guard::operator=(binary_guard &&other) { - if (this != &other) { - destroy(); - _bothAlive = base::take(other._bothAlive); - } - return *this; -} - -inline binary_guard::~binary_guard() { - destroy(); -} - -inline binary_guard &binary_guard::operator=(std::nullptr_t) { - destroy(); - return *this; -} - -inline binary_guard::operator bool() const { - return alive(); -} - -inline bool binary_guard::alive() const { - return _bothAlive && _bothAlive->load(); -} - -inline void binary_guard::destroy() { - if (const auto both = base::take(_bothAlive)) { - auto old = true; - if (!both->compare_exchange_strong(old, false)) { - delete both; - } - } -} - -inline binary_guard binary_guard::make_guard() { - destroy(); - - auto result = binary_guard(); - _bothAlive = result._bothAlive = new std::atomic(true); - return result; -} - -} // namespace base - -namespace crl { - -template -struct guard_traits; - -template <> -struct guard_traits { - static base::binary_guard create(base::binary_guard value) { - return value; - } - static bool check(const base::binary_guard &guard) { - return guard.alive(); - } - -}; - -} // namespace crl diff --git a/Telegram/SourceFiles/base/build_config.h b/Telegram/SourceFiles/base/build_config.h deleted file mode 100644 index 3bcb5952a..000000000 --- a/Telegram/SourceFiles/base/build_config.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -// thanks Chromium - -#if defined(__APPLE__) -#define OS_MAC 1 -#elif defined(__linux__) // __APPLE__ -#define OS_LINUX 1 -#elif defined(_WIN32) // __APPLE__ || __linux__ -#define OS_WIN 1 -#else // __APPLE__ || __linux__ || _WIN32 -#error Please add support for your platform in base/build_config.h -#endif // else for __APPLE__ || __linux__ || _WIN32 - -// For access to standard POSIXish features, use OS_POSIX instead of a -// more specific macro. -#if defined(OS_MAC) || defined(OS_LINUX) -#define OS_POSIX 1 -#endif // OS_MAC || OS_LINUX - -// Compiler detection. -#if defined(__clang__) -#define COMPILER_CLANG 1 -#elif defined(__GNUC__) // __clang__ -#define COMPILER_GCC 1 -#elif defined(_MSC_VER) // __clang__ || __GNUC__ -#define COMPILER_MSVC 1 -#else // _MSC_VER || __clang__ || __GNUC__ -#error Please add support for your compiler in base/build_config.h -#endif // else for _MSC_VER || __clang__ || __GNUC__ - -// Processor architecture detection. -#if defined(_M_X64) || defined(__x86_64__) -#define ARCH_CPU_X86_FAMILY 1 -#define ARCH_CPU_X86_64 1 -#define ARCH_CPU_64_BITS 1 -#elif defined(_M_IX86) || defined(__i386__) -#define ARCH_CPU_X86_FAMILY 1 -#define ARCH_CPU_X86 1 -#define ARCH_CPU_32_BITS 1 -#elif defined(__aarch64__) -#define ARCH_CPU_64_BITS 1 -#elif defined(_M_ARM) || defined(__arm__) -#define ARCH_CPU_32_BITS 1 -#else -#error Please add support for your architecture in base/build_config.h -#endif - -#if defined(__GNUC__) -#define TG_FORCE_INLINE inline __attribute__((always_inline)) -#elif defined(_MSC_VER) -#define TG_FORCE_INLINE __forceinline -#else -#define TG_FORCE_INLINE inline -#endif - -#include -static_assert(CHAR_BIT == 8, "Not supported char size."); diff --git a/Telegram/SourceFiles/base/bytes.h b/Telegram/SourceFiles/base/bytes.h deleted file mode 100644 index 3810e1b4a..000000000 --- a/Telegram/SourceFiles/base/bytes.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" -#include -#include -#include -#include - -namespace bytes { - -using type = gsl::byte; -using span = gsl::span; -using const_span = gsl::span; -using vector = std::vector; - -template -using array = std::array; - -inline span make_detached_span(QByteArray &container) { - return gsl::as_writeable_bytes(gsl::make_span(container)); -} - -template < - typename Container, - typename = std::enable_if_t< - !std::is_const_v - && !std::is_same_v>> -inline span make_span(Container &container) { - return gsl::as_writeable_bytes(gsl::make_span(container)); -} - -template -inline const_span make_span(const Container &container) { - return gsl::as_bytes(gsl::make_span(container)); -} - -template -inline span make_span(gsl::span container) { - return gsl::as_writeable_bytes(container); -} - -template -inline const_span make_span(gsl::span container) { - return gsl::as_bytes(container); -} - -template -inline span make_span(Type *value, std::size_t count) { - return gsl::as_writeable_bytes(gsl::make_span(value, count)); -} - -template -inline const_span make_span(const Type *value, std::size_t count) { - return gsl::as_bytes(gsl::make_span(value, count)); -} - -template -inline span object_as_span(Type *value) { - return bytes::make_span(value, 1); -} - -template -inline const_span object_as_span(const Type *value) { - return bytes::make_span(value, 1); -} - -template -inline vector make_vector(const Container &container) { - const auto buffer = bytes::make_span(container); - return { buffer.begin(), buffer.end() }; -} - -inline void copy(span destination, const_span source) { - Expects(destination.size() >= source.size()); - - memcpy(destination.data(), source.data(), source.size()); -} - -inline void move(span destination, const_span source) { - Expects(destination.size() >= source.size()); - - memmove(destination.data(), source.data(), source.size()); -} - -inline void set_with_const(span destination, type value) { - memset( - destination.data(), - gsl::to_integer(value), - destination.size()); -} - -inline int compare(const_span a, const_span b) { - const auto aSize = a.size(), bSize = b.size(); - return (aSize > bSize) - ? 1 - : (aSize < bSize) - ? -1 - : memcmp(a.data(), b.data(), aSize); -} - -namespace details { - -template -std::size_t spansLength(Arg &&arg) { - return bytes::make_span(arg).size(); -} - -template -std::size_t spansLength(Arg &&arg, Args &&...args) { - return bytes::make_span(arg).size() + spansLength(args...); -} - -template -void spansAppend(span destination, Arg &&arg) { - bytes::copy(destination, bytes::make_span(arg)); -} - -template -void spansAppend(span destination, Arg &&arg, Args &&...args) { - const auto data = bytes::make_span(arg); - bytes::copy(destination, data); - spansAppend(destination.subspan(data.size()), args...); -} - -} // namespace details - -template < - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > 1)>> -vector concatenate(Args &&...args) { - const auto size = details::spansLength(args...); - auto result = vector(size); - details::spansAppend(make_span(result), args...); - return result; -} - -template < - typename SpanRange> -vector concatenate(SpanRange args) { - auto size = std::size_t(0); - for (const auto &arg : args) { - size += bytes::make_span(arg).size(); - } - auto result = vector(size); - auto buffer = make_span(result); - for (const auto &arg : args) { - const auto part = bytes::make_span(arg); - bytes::copy(buffer, part); - buffer = buffer.subspan(part.size()); - } - return result; -} - -// Implemented in base/openssl_help.h -void set_random(span destination); - -} // namespace bytes diff --git a/Telegram/SourceFiles/base/concurrent_timer.cpp b/Telegram/SourceFiles/base/concurrent_timer.cpp deleted file mode 100644 index 7e523a0d1..000000000 --- a/Telegram/SourceFiles/base/concurrent_timer.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* -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 "base/concurrent_timer.h" - -#include -#include - -using namespace base::details; - -namespace base { -namespace details { -namespace { - -constexpr auto kCallDelayedEvent = QEvent::Type(QEvent::User + 1); -constexpr auto kCancelTimerEvent = QEvent::Type(QEvent::User + 2); -static_assert(kCancelTimerEvent < QEvent::MaxUser); - -ConcurrentTimerEnvironment *Environment/* = nullptr*/; -QMutex EnvironmentMutex; - -class CallDelayedEvent : public QEvent { -public: - CallDelayedEvent( - crl::time timeout, - Qt::TimerType type, - FnMut method); - - crl::time timeout() const; - Qt::TimerType type() const; - FnMut takeMethod(); - -private: - crl::time _timeout = 0; - Qt::TimerType _type = Qt::PreciseTimer; - FnMut _method; - -}; - -class CancelTimerEvent : public QEvent { -public: - CancelTimerEvent(); - -}; - -CallDelayedEvent::CallDelayedEvent( - crl::time timeout, - Qt::TimerType type, - FnMut method) -: QEvent(kCallDelayedEvent) -, _timeout(timeout) -, _type(type) -, _method(std::move(method)) { - Expects(_timeout >= 0 && _timeout < std::numeric_limits::max()); -} - -crl::time CallDelayedEvent::timeout() const { - return _timeout; -} - -Qt::TimerType CallDelayedEvent::type() const { - return _type; -} - -FnMut CallDelayedEvent::takeMethod() { - return base::take(_method); -} - -CancelTimerEvent::CancelTimerEvent() : QEvent(kCancelTimerEvent) { -} - -} // namespace - -class TimerObject : public QObject { -public: - TimerObject( - not_null thread, - not_null adjuster, - Fn adjust); - -protected: - bool event(QEvent *e) override; - -private: - void callDelayed(not_null e); - void callNow(); - void cancel(); - void adjust(); - - FnMut _next; - Fn _adjust; - int _timerId = 0; - -}; - -TimerObject::TimerObject( - not_null thread, - not_null adjuster, - Fn adjust) -: _adjust(std::move(adjust)) { - moveToThread(thread); - connect( - adjuster, - &QObject::destroyed, - this, - &TimerObject::adjust, - Qt::DirectConnection); -} - -bool TimerObject::event(QEvent *e) { - const auto type = e->type(); - switch (type) { - case kCallDelayedEvent: - callDelayed(static_cast(e)); - return true; - case kCancelTimerEvent: - cancel(); - return true; - case QEvent::Timer: - callNow(); - return true; - } - return QObject::event(e); -} - -void TimerObject::callDelayed(not_null e) { - cancel(); - - const auto timeout = e->timeout(); - const auto type = e->type(); - _next = e->takeMethod(); - if (timeout > 0) { - _timerId = startTimer(timeout, type); - } else { - base::take(_next)(); - } -} - -void TimerObject::cancel() { - if (const auto id = base::take(_timerId)) { - killTimer(id); - } - _next = nullptr; -} - -void TimerObject::callNow() { - auto next = base::take(_next); - cancel(); - next(); -} - -void TimerObject::adjust() { - if (_adjust) { - _adjust(); - } -} - -TimerObjectWrap::TimerObjectWrap(Fn adjust) { - QMutexLocker lock(&EnvironmentMutex); - - if (Environment) { - _value = Environment->createTimer(std::move(adjust)); - } -} - -TimerObjectWrap::~TimerObjectWrap() { - if (_value) { - QMutexLocker lock(&EnvironmentMutex); - - if (Environment) { - _value.release()->deleteLater(); - } - } -} - -void TimerObjectWrap::call( - crl::time timeout, - Qt::TimerType type, - FnMut method) { - sendEvent(std::make_unique( - timeout, - type, - std::move(method))); -} - -void TimerObjectWrap::cancel() { - sendEvent(std::make_unique()); -} - -void TimerObjectWrap::sendEvent(std::unique_ptr event) { - if (!_value) { - return; - } - QCoreApplication::postEvent( - _value.get(), - event.release(), - Qt::HighEventPriority); -} - -} // namespace details - -ConcurrentTimerEnvironment::ConcurrentTimerEnvironment() { - _thread.start(); - _adjuster.moveToThread(&_thread); - - acquire(); -} - -ConcurrentTimerEnvironment::~ConcurrentTimerEnvironment() { - _thread.quit(); - release(); - _thread.wait(); - QObject::disconnect(&_adjuster, &QObject::destroyed, nullptr, nullptr); -} - -std::unique_ptr ConcurrentTimerEnvironment::createTimer( - Fn adjust) { - return std::make_unique( - &_thread, - &_adjuster, - std::move(adjust)); -} - -void ConcurrentTimerEnvironment::Adjust() { - QMutexLocker lock(&EnvironmentMutex); - if (Environment) { - Environment->adjustTimers(); - } -} - -void ConcurrentTimerEnvironment::adjustTimers() { - QObject emitter; - QObject::connect( - &emitter, - &QObject::destroyed, - &_adjuster, - &QObject::destroyed, - Qt::QueuedConnection); -} - -void ConcurrentTimerEnvironment::acquire() { - Expects(Environment == nullptr); - - QMutexLocker lock(&EnvironmentMutex); - Environment = this; -} - -void ConcurrentTimerEnvironment::release() { - Expects(Environment == this); - - QMutexLocker lock(&EnvironmentMutex); - Environment = nullptr; -} - -ConcurrentTimer::ConcurrentTimer( - Fn)> runner, - Fn callback) -: _runner(std::move(runner)) -, _object(createAdjuster()) -, _callback(std::move(callback)) -, _type(Qt::PreciseTimer) -, _adjusted(false) { - setRepeat(Repeat::Interval); -} - -Fn ConcurrentTimer::createAdjuster() { - _guard = std::make_shared(true); - return [=, runner = _runner, guard = std::weak_ptr(_guard)] { - runner([=] { - if (!guard.lock()) { - return; - } - adjust(); - }); - }; -} - -void ConcurrentTimer::start( - crl::time timeout, - Qt::TimerType type, - Repeat repeat) { - _type = type; - setRepeat(repeat); - _adjusted = false; - setTimeout(timeout); - - cancelAndSchedule(_timeout); - _next = crl::now() + _timeout; -} - -void ConcurrentTimer::cancelAndSchedule(int timeout) { - auto method = [ - =, - runner = _runner, - guard = _running.make_guard() - ]() mutable { - if (!guard) { - return; - } - runner([=, guard = std::move(guard)] { - if (!guard) { - return; - } - timerEvent(); - }); - }; - _object.call(timeout, _type, std::move(method)); -} - -void ConcurrentTimer::timerEvent() { - if (repeat() == Repeat::Interval) { - if (_adjusted) { - start(_timeout, _type, repeat()); - } else { - _next = crl::now() + _timeout; - } - } else { - cancel(); - } - - if (_callback) { - _callback(); - } -} - -void ConcurrentTimer::cancel() { - _running = {}; - if (isActive()) { - _running = base::binary_guard(); - _object.cancel(); - } -} - -crl::time ConcurrentTimer::remainingTime() const { - if (!isActive()) { - return -1; - } - const auto now = crl::now(); - return (_next > now) ? (_next - now) : crl::time(0); -} - -void ConcurrentTimer::adjust() { - auto remaining = remainingTime(); - if (remaining >= 0) { - cancelAndSchedule(remaining); - _adjusted = true; - } -} - -void ConcurrentTimer::setTimeout(crl::time timeout) { - Expects(timeout >= 0 && timeout <= std::numeric_limits::max()); - - _timeout = static_cast(timeout); -} - -int ConcurrentTimer::timeout() const { - return _timeout; -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/concurrent_timer.h b/Telegram/SourceFiles/base/concurrent_timer.h deleted file mode 100644 index 4020ee6ae..000000000 --- a/Telegram/SourceFiles/base/concurrent_timer.h +++ /dev/null @@ -1,146 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/binary_guard.h" -#include -#include -#include - -namespace base { -namespace details { - -class TimerObject; - -class TimerObjectWrap { -public: - explicit TimerObjectWrap(Fn adjust); - ~TimerObjectWrap(); - - void call( - crl::time timeout, - Qt::TimerType type, - FnMut method); - void cancel(); - -private: - void sendEvent(std::unique_ptr event); - - std::unique_ptr _value; - -}; - -} // namespace details - -class ConcurrentTimerEnvironment { -public: - ConcurrentTimerEnvironment(); - ~ConcurrentTimerEnvironment(); - - std::unique_ptr createTimer(Fn adjust); - - static void Adjust(); - -private: - void acquire(); - void release(); - void adjustTimers(); - - QThread _thread; - QObject _adjuster; - -}; - -class ConcurrentTimer { -public: - explicit ConcurrentTimer( - Fn)> runner, - Fn callback = nullptr); - - template - explicit ConcurrentTimer( - crl::weak_on_queue weak, - Fn callback = nullptr); - - static Qt::TimerType DefaultType(crl::time timeout) { - constexpr auto kThreshold = crl::time(1000); - return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer; - } - - void setCallback(Fn callback) { - _callback = std::move(callback); - } - - void callOnce(crl::time timeout) { - callOnce(timeout, DefaultType(timeout)); - } - - void callEach(crl::time timeout) { - callEach(timeout, DefaultType(timeout)); - } - - void callOnce(crl::time timeout, Qt::TimerType type) { - start(timeout, type, Repeat::SingleShot); - } - - void callEach(crl::time timeout, Qt::TimerType type) { - start(timeout, type, Repeat::Interval); - } - - bool isActive() const { - return _running.alive(); - } - - void cancel(); - crl::time remainingTime() const; - -private: - enum class Repeat : unsigned { - Interval = 0, - SingleShot = 1, - }; - Fn createAdjuster(); - void start(crl::time timeout, Qt::TimerType type, Repeat repeat); - void adjust(); - - void cancelAndSchedule(int timeout); - - void setTimeout(crl::time timeout); - int timeout() const; - - void timerEvent(); - - void setRepeat(Repeat repeat) { - _repeat = static_cast(repeat); - } - Repeat repeat() const { - return static_cast(_repeat); - } - - Fn)> _runner; - std::shared_ptr _guard; // Must be before _object. - details::TimerObjectWrap _object; - Fn _callback; - base::binary_guard _running; - crl::time _next = 0; - int _timeout = 0; - - Qt::TimerType _type : 2; - bool _adjusted : 1; - unsigned _repeat : 1; - -}; - -template -ConcurrentTimer::ConcurrentTimer( - crl::weak_on_queue weak, - Fn callback) -: ConcurrentTimer(weak.runner(), std::move(callback)) { -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/crc32hash.cpp b/Telegram/SourceFiles/base/crc32hash.cpp deleted file mode 100644 index 91cbf9920..000000000 --- a/Telegram/SourceFiles/base/crc32hash.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -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 "base/crc32hash.h" - -namespace base { -namespace { - -class Crc32Table { -public: - Crc32Table() { - auto poly = std::uint32_t(0x04c11db7); - for (auto i = 0; i != 256; ++i) { - _data[i] = reflect(i, 8) << 24; - for (auto j = 0; j != 8; ++j) { - _data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0); - } - _data[i] = reflect(_data[i], 32); - } - } - - std::uint32_t operator[](int index) const { - return _data[index]; - } - -private: - std::uint32_t reflect(std::uint32_t val, char ch) { - auto result = std::uint32_t(0); - for (int i = 1; i < (ch + 1); ++i) { - if (val & 1) { - result |= 1 << (ch - i); - } - val >>= 1; - } - return result; - } - - std::uint32_t _data[256]; - -}; - -} // namespace - -std::int32_t crc32(const void *data, int len) { - static const auto kTable = Crc32Table(); - - const auto buffer = static_cast(data); - - auto crc = std::uint32_t(0xffffffff); - for (auto i = 0; i != len; ++i) { - crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]]; - } - - return static_cast(crc ^ 0xffffffff); -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/crc32hash.h b/Telegram/SourceFiles/base/crc32hash.h deleted file mode 100644 index dac0b5886..000000000 --- a/Telegram/SourceFiles/base/crc32hash.h +++ /dev/null @@ -1,16 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -std::int32_t crc32(const void *data, int len); - -} // namespace base diff --git a/Telegram/SourceFiles/base/enum_mask.h b/Telegram/SourceFiles/base/enum_mask.h deleted file mode 100644 index f943646c7..000000000 --- a/Telegram/SourceFiles/base/enum_mask.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { - -template -class enum_mask { - using Type = std::uint32_t; - -public: - static_assert(static_cast(Enum::kCount) <= 32, "We have only 32 bit."); - - enum_mask() = default; - enum_mask(Enum value) : _value(ToBit(value)) { - } - - enum_mask added(enum_mask other) const { - auto result = *this; - result.set(other); - return result; - } - void set(enum_mask other) { - _value |= other._value; - } - bool test(Enum value) const { - return _value & ToBit(value); - } - - explicit operator bool() const { - return _value != 0; - } - -private: - inline static Type ToBit(Enum value) { - return 1 << static_cast(value); - } - Type _value = 0; - -}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/flags.h b/Telegram/SourceFiles/base/flags.h deleted file mode 100644 index e3116a334..000000000 --- a/Telegram/SourceFiles/base/flags.h +++ /dev/null @@ -1,351 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -template -class flags; - -template -struct extended_flags; - -template -using extended_flags_t = typename extended_flags::type; - -namespace details { - -struct flags_zero_helper_struct { -}; - -using flags_zero_helper = void(base::details::flags_zero_helper_struct::*)(); - -template ::type> -inline constexpr auto extended_flag_convert(ExtendedEnum value) { - return static_cast(value); -} - -template ::type> -inline constexpr auto extended_flags_convert(ExtendedEnum value) { - return flags(extended_flag_convert(value)); -} - -} // namespace details - -template -class flags { -public: - using Enum = EnumType; - using Type = std::underlying_type_t; - - constexpr flags() = default; - constexpr flags(details::flags_zero_helper) noexcept { - } - constexpr flags(Enum value) noexcept - : _value(static_cast(value)) { - } - static constexpr flags from_raw(Type value) noexcept { - return flags(static_cast(value)); - } - - constexpr auto value() const noexcept { - return _value; - } - constexpr operator Type() const noexcept { - return value(); - } - - constexpr auto &operator|=(flags b) noexcept { - _value |= b.value(); - return *this; - } - constexpr auto &operator&=(flags b) noexcept { - _value &= b.value(); - return *this; - } - constexpr auto &operator^=(flags b) noexcept { - _value ^= b.value(); - return *this; - } - - constexpr auto operator~() const noexcept { - return from_raw(~value()); - } - - constexpr auto operator|(flags b) const noexcept { - return (flags(*this) |= b); - } - constexpr auto operator&(flags b) const noexcept { - return (flags(*this) &= b); - } - constexpr auto operator^(flags b) const noexcept { - return (flags(*this) ^= b); - } - - constexpr auto operator|(Enum b) const noexcept { - return (flags(*this) |= b); - } - constexpr auto operator&(Enum b) const noexcept { - return (flags(*this) &= b); - } - constexpr auto operator^(Enum b) const noexcept { - return (flags(*this) ^= b); - } - - constexpr auto operator==(Enum b) const noexcept { - return (value() == static_cast(b)); - } - constexpr auto operator!=(Enum b) const noexcept { - return !(*this == b); - } - constexpr auto operator<(Enum b) const noexcept { - return value() < static_cast(b); - } - constexpr auto operator>(Enum b) const noexcept { - return (b < *this); - } - constexpr auto operator<=(Enum b) const noexcept { - return !(b < *this); - } - constexpr auto operator>=(Enum b) const noexcept { - return !(*this < b); - } - -private: - Type _value = 0; - -}; - -template -constexpr auto make_flags(Enum value) noexcept { - return flags(value); -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator|(Enum a, flags b) noexcept { - return b | a; -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator&(Enum a, flags b) noexcept { - return b & a; -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator^(Enum a, flags b) noexcept { - return b ^ a; -} - -template ::type> -inline constexpr auto operator|(flags> a, ExtendedEnum b) { - return a | details::extended_flags_convert(b); -} - -template ::type> -inline constexpr auto operator|(ExtendedEnum a, flags> b) { - return b | a; -} - -template > -inline constexpr auto operator&(flags> a, ExtendedEnum b) { - return a & details::extended_flags_convert(b); -} - -template ::type> -inline constexpr auto operator&(ExtendedEnum a, flags> b) { - return b & a; -} - -template > -inline constexpr auto operator^(flags> a, ExtendedEnum b) { - return a ^ details::extended_flags_convert(b); -} - -template ::type> -inline constexpr auto operator^(ExtendedEnum a, flags> b) { - return b ^ a; -} - -template ::type> -inline constexpr auto &operator&=(flags> &a, ExtendedEnum b) { - return (a &= details::extended_flags_convert(b)); -} - -template ::type> -inline constexpr auto &operator|=(flags> &a, ExtendedEnum b) { - return (a |= details::extended_flags_convert(b)); -} - -template ::type> -inline constexpr auto &operator^=(flags> &a, ExtendedEnum b) { - return (a ^= details::extended_flags_convert(b)); -} - -template ::type> -inline constexpr auto operator==(flags> a, ExtendedEnum b) { - return a == details::extended_flags_convert(b); -} - -template ::type> -inline constexpr auto operator==(ExtendedEnum a, flags> b) { - return (b == a); -} - -template ::type> -inline constexpr auto operator!=(flags> a, ExtendedEnum b) { - return !(a == b); -} - -template ::type> -inline constexpr auto operator!=(ExtendedEnum a, flags> b) { - return !(a == b); -} - -template ::type> -inline constexpr auto operator<(flags> a, ExtendedEnum b) { - return a < details::extended_flags_convert(b); -} - -template ::type> -inline constexpr auto operator<(ExtendedEnum a, flags> b) { - return details::extended_flags_convert(a) < b; -} - -template ::type> -inline constexpr auto operator>(flags> a, ExtendedEnum b) { - return (b < a); -} - -template ::type> -inline constexpr auto operator>(ExtendedEnum a, flags> b) { - return (b < a); -} - -template ::type> -inline constexpr auto operator<=(flags> a, ExtendedEnum b) { - return !(b < a); -} - -template ::type> -inline constexpr auto operator<=(ExtendedEnum a, flags> b) { - return !(b < a); -} - -template ::type> -inline constexpr auto operator>=(flags> a, ExtendedEnum b) { - return !(a < b); -} - -template ::type> -inline constexpr auto operator>=(ExtendedEnum a, flags> b) { - return !(a < b); -} - -} // namespace base - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator!(Enum a) noexcept { - return !base::make_flags(a); -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator~(Enum a) noexcept { - return ~base::make_flags(a); -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator|(Enum a, Enum b) noexcept { - return base::make_flags(a) | b; -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator|(Enum a, base::details::flags_zero_helper) noexcept { - return base::make_flags(a); -} - -template ::value>, - typename = std::enable_if_t> -inline constexpr auto operator|(base::details::flags_zero_helper, Enum b) noexcept { - return base::make_flags(b); -} - -template ::type> -inline constexpr auto operator|(ExtendedEnum a, ExtendedEnum b) { - return base::details::extended_flags_convert(a) | b; -} - -template ::type> -inline constexpr auto operator|(ExtendedEnum a, typename base::extended_flags::type b) { - return base::details::extended_flags_convert(a) | b; -} - -template ::type> -inline constexpr auto operator|(typename base::extended_flags::type a, ExtendedEnum b) { - return b | a; -} - -template ::type> -inline constexpr auto operator|(base::details::flags_zero_helper, ExtendedEnum b) { - return 0 | base::details::extended_flag_convert(b); -} - -template ::type> -inline constexpr auto operator|(ExtendedEnum a, base::details::flags_zero_helper) { - return base::details::extended_flag_convert(a) | 0; -} - -template ::type> -inline constexpr auto operator~(ExtendedEnum b) { - return ~base::details::extended_flags_convert(b); -} diff --git a/Telegram/SourceFiles/base/flags_tests.cpp b/Telegram/SourceFiles/base/flags_tests.cpp deleted file mode 100644 index 786dc49c2..000000000 --- a/Telegram/SourceFiles/base/flags_tests.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* -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 "catch.hpp" - -#include "base/flags.h" - -namespace MethodNamespace { - -template -void TestFlags(Enum a, Enum b, Enum c) { - auto abc = a | b; - abc |= c; - auto test = abc != a; - CHECK(abc != a); - CHECK(abc != (a | b)); - CHECK((abc & a) == a); - CHECK((abc & b) == b); - CHECK((abc & c) == c); - CHECK((abc & ~a) == (b | c)); - CHECK((abc & ~(b | c)) == a); - CHECK((abc ^ a) == (abc & ~a)); - - auto another = a | b; - another |= c; - CHECK(abc == another); - another &= ~b; - CHECK(another == (a | c)); - another ^= a; - CHECK(another == c); - another = 0; - another = nullptr; - auto is_zero = ((another & abc) == 0); - CHECK(is_zero); - CHECK(!(another & abc)); - auto more = a | another; - auto just = a | 0; - CHECK(more == just); - CHECK(just); -} - -} // namespace MethodNamespace - -namespace FlagsNamespace { - -enum class Flag : int { - one = (1 << 0), - two = (1 << 1), - three = (1 << 2), -}; -inline constexpr auto is_flag_type(Flag) { return true; } - -class Class { -public: - enum class Public : long { - one = (1 << 2), - two = (1 << 1), - three = (1 << 0), - }; - friend inline constexpr auto is_flag_type(Public) { return true; } - - static void TestPrivate(); - -private: - enum class Private : long { - one = (1 << 0), - two = (1 << 1), - three = (1 << 2), - }; - friend inline constexpr auto is_flag_type(Private) { return true; } - -}; - -void Class::TestPrivate() { - MethodNamespace::TestFlags(Private::one, Private::two, Private::three); -} - -} // namespace FlagsNamespace - -namespace ExtendedNamespace { - -enum class Flag : int { - one = (1 << 3), - two = (1 << 4), - three = (1 << 5), -}; - -} // namespace ExtendedNamespace - -namespace base { - -template<> -struct extended_flags { - using type = FlagsNamespace::Flag; -}; - -} // namespace base - -TEST_CASE("flags operators on scoped enums", "[flags]") { - SECTION("testing non-member flags") { - MethodNamespace::TestFlags( - FlagsNamespace::Flag::one, - FlagsNamespace::Flag::two, - FlagsNamespace::Flag::three); - } - SECTION("testing public member flags") { - MethodNamespace::TestFlags( - FlagsNamespace::Class::Public::one, - FlagsNamespace::Class::Public::two, - FlagsNamespace::Class::Public::three); - } - SECTION("testing private member flags") { - FlagsNamespace::Class::TestPrivate(); - } - SECTION("testing extended flags") { - MethodNamespace::TestFlags( - ExtendedNamespace::Flag::one, - ExtendedNamespace::Flag::two, - ExtendedNamespace::Flag::three); - - auto onetwo = FlagsNamespace::Flag::one | ExtendedNamespace::Flag::two; - auto twoone = ExtendedNamespace::Flag::two | FlagsNamespace::Flag::one; - CHECK(onetwo == twoone); - } -} diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h deleted file mode 100644 index 9c8a9bcd7..000000000 --- a/Telegram/SourceFiles/base/flat_map.h +++ /dev/null @@ -1,917 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "base/optional.h" - -namespace base { - -using std::begin; -using std::end; - -template < - typename Key, - typename Type, - typename Compare = std::less<>> -class flat_map; - -template < - typename Key, - typename Type, - typename Compare = std::less<>> -class flat_multi_map; - -template < - typename Me, - typename Key, - typename Type, - typename iterator_impl, - typename pointer_impl, - typename reference_impl> -class flat_multi_map_iterator_base_impl; - -template -struct flat_multi_map_pair_type { - using first_type = const Key; - using second_type = Value; - - constexpr flat_multi_map_pair_type() - : first() - , second() { - } - - template - constexpr flat_multi_map_pair_type(OtherKey &&key, OtherValue &&value) - : first(std::forward(key)) - , second(std::forward(value)) { - } - - flat_multi_map_pair_type(const flat_multi_map_pair_type &pair) - : first(pair.first) - , second(pair.second) { - } - - flat_multi_map_pair_type(flat_multi_map_pair_type &&pair) - : first(std::move(const_cast(pair.first))) - , second(std::move(pair.second)) { - } - - flat_multi_map_pair_type &operator=(const flat_multi_map_pair_type&) = delete; - flat_multi_map_pair_type &operator=(flat_multi_map_pair_type &&other) { - const_cast(first) = std::move(const_cast(other.first)); - second = std::move(other.second); - return *this; - } - - void swap(flat_multi_map_pair_type &other) { - using std::swap; - - if (this != &other) { - std::swap( - const_cast(first), - const_cast(other.first)); - std::swap(second, other.second); - } - } - - const Key first; - Value second; - -}; - -template < - typename Me, - typename Key, - typename Type, - typename iterator_impl, - typename pointer_impl, - typename reference_impl> -class flat_multi_map_iterator_base_impl { -public: - using iterator_category = typename iterator_impl::iterator_category; - - using pair_type = flat_multi_map_pair_type; - using value_type = pair_type; - using difference_type = typename iterator_impl::difference_type; - using pointer = pointer_impl; - using reference = reference_impl; - - flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl()) - : _impl(impl) { - } - - reference operator*() const { - return *_impl; - } - pointer operator->() const { - return std::addressof(**this); - } - Me &operator++() { - ++_impl; - return static_cast(*this); - } - Me operator++(int) { - return _impl++; - } - Me &operator--() { - --_impl; - return static_cast(*this); - } - Me operator--(int) { - return _impl--; - } - Me &operator+=(difference_type offset) { - _impl += offset; - return static_cast(*this); - } - Me operator+(difference_type offset) const { - return _impl + offset; - } - Me &operator-=(difference_type offset) { - _impl -= offset; - return static_cast(*this); - } - Me operator-(difference_type offset) const { - return _impl - offset; - } - template < - typename other_me, - typename other_iterator_impl, - typename other_pointer_impl, - typename other_reference_impl> - difference_type operator-( - const flat_multi_map_iterator_base_impl< - other_me, - Key, - Type, - other_iterator_impl, - other_pointer_impl, - other_reference_impl> &right) const { - return _impl - right._impl; - } - reference operator[](difference_type offset) const { - return _impl[offset]; - } - - template < - typename other_me, - typename other_iterator_impl, - typename other_pointer_impl, - typename other_reference_impl> - bool operator==( - const flat_multi_map_iterator_base_impl< - other_me, - Key, - Type, - other_iterator_impl, - other_pointer_impl, - other_reference_impl> &right) const { - return _impl == right._impl; - } - template < - typename other_me, - typename other_iterator_impl, - typename other_pointer_impl, - typename other_reference_impl> - bool operator!=( - const flat_multi_map_iterator_base_impl< - other_me, - Key, - Type, - other_iterator_impl, - other_pointer_impl, - other_reference_impl> &right) const { - return _impl != right._impl; - } - template < - typename other_me, - typename other_iterator_impl, - typename other_pointer_impl, - typename other_reference_impl> - bool operator<( - const flat_multi_map_iterator_base_impl< - other_me, - Key, - Type, - other_iterator_impl, - other_pointer_impl, - other_reference_impl> &right) const { - return _impl < right._impl; - } - -private: - iterator_impl _impl; - - template < - typename OtherKey, - typename OtherType, - typename OtherCompare> - friend class flat_multi_map; - - template < - typename OtherMe, - typename OtherKey, - typename OtherType, - typename other_iterator_impl, - typename other_pointer_impl, - typename other_reference_impl> - friend class flat_multi_map_iterator_base_impl; - -}; - -template -class flat_multi_map { -public: - class iterator; - class const_iterator; - class reverse_iterator; - class const_reverse_iterator; - -private: - using pair_type = flat_multi_map_pair_type; - using impl_t = std::deque; - - using iterator_base = flat_multi_map_iterator_base_impl< - iterator, - Key, - Type, - typename impl_t::iterator, - pair_type*, - pair_type&>; - using const_iterator_base = flat_multi_map_iterator_base_impl< - const_iterator, - Key, - Type, - typename impl_t::const_iterator, - const pair_type*, - const pair_type&>; - using reverse_iterator_base = flat_multi_map_iterator_base_impl< - reverse_iterator, - Key, - Type, - typename impl_t::reverse_iterator, - pair_type*, - pair_type&>; - using const_reverse_iterator_base = flat_multi_map_iterator_base_impl< - const_reverse_iterator, - Key, - Type, - typename impl_t::const_reverse_iterator, - const pair_type*, - const pair_type&>; - -public: - using value_type = pair_type; - using size_type = typename impl_t::size_type; - using difference_type = typename impl_t::difference_type; - using pointer = pair_type*; - using const_pointer = const pair_type*; - using reference = pair_type&; - using const_reference = const pair_type&; - - class iterator : public iterator_base { - public: - using iterator_base::iterator_base; - iterator() = default; - iterator(const iterator_base &other) : iterator_base(other) { - } - friend class const_iterator; - - }; - class const_iterator : public const_iterator_base { - public: - using const_iterator_base::const_iterator_base; - const_iterator() = default; - const_iterator(const_iterator_base other) : const_iterator_base(other) { - } - const_iterator(const iterator &other) : const_iterator_base(other._impl) { - } - - }; - class reverse_iterator : public reverse_iterator_base { - public: - using reverse_iterator_base::reverse_iterator_base; - reverse_iterator() = default; - reverse_iterator(reverse_iterator_base other) : reverse_iterator_base(other) { - } - friend class const_reverse_iterator; - - }; - class const_reverse_iterator : public const_reverse_iterator_base { - public: - using const_reverse_iterator_base::const_reverse_iterator_base; - const_reverse_iterator() = default; - const_reverse_iterator(const_reverse_iterator_base other) : const_reverse_iterator_base(other) { - } - const_reverse_iterator(const reverse_iterator &other) : const_reverse_iterator_base(other._impl) { - } - - }; - - flat_multi_map() = default; - flat_multi_map(const flat_multi_map &other) = default; - flat_multi_map(flat_multi_map &&other) = default; - flat_multi_map &operator=(const flat_multi_map &other) { - auto copy = other; - return (*this = std::move(copy)); - } - flat_multi_map &operator=(flat_multi_map &&other) = default; - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category> - flat_multi_map(Iterator first, Iterator last) - : _data(first, last) { - std::sort(std::begin(impl()), std::end(impl()), compare()); - } - - flat_multi_map(std::initializer_list iter) - : flat_multi_map(iter.begin(), iter.end()) { - } - - size_type size() const { - return impl().size(); - } - bool empty() const { - return impl().empty(); - } - void clear() { - impl().clear(); - } - - iterator begin() { - return impl().begin(); - } - iterator end() { - return impl().end(); - } - const_iterator begin() const { - return impl().begin(); - } - const_iterator end() const { - return impl().end(); - } - const_iterator cbegin() const { - return impl().cbegin(); - } - const_iterator cend() const { - return impl().cend(); - } - reverse_iterator rbegin() { - return impl().rbegin(); - } - reverse_iterator rend() { - return impl().rend(); - } - const_reverse_iterator rbegin() const { - return impl().rbegin(); - } - const_reverse_iterator rend() const { - return impl().rend(); - } - const_reverse_iterator crbegin() const { - return impl().crbegin(); - } - const_reverse_iterator crend() const { - return impl().crend(); - } - - reference front() { - return *begin(); - } - const_reference front() const { - return *begin(); - } - reference back() { - return *(end() - 1); - } - const_reference back() const { - return *(end() - 1); - } - - iterator insert(const value_type &value) { - if (empty() || compare()(value.first, front().first)) { - impl().push_front(value); - return begin(); - } else if (!compare()(value.first, back().first)) { - impl().push_back(value); - return (end() - 1); - } - auto where = getUpperBound(value.first); - return impl().insert(where, value); - } - iterator insert(value_type &&value) { - if (empty() || compare()(value.first, front().first)) { - impl().push_front(std::move(value)); - return begin(); - } else if (!compare()(value.first, back().first)) { - impl().push_back(std::move(value)); - return (end() - 1); - } - auto where = getUpperBound(value.first); - return impl().insert(where, std::move(value)); - } - template - iterator emplace(Args&&... args) { - return insert(value_type(std::forward(args)...)); - } - - bool removeOne(const Key &key) { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return false; - } - auto where = getLowerBound(key); - if (compare()(key, where->first)) { - return false; - } - impl().erase(where); - return true; - } - int removeAll(const Key &key) { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return 0; - } - auto range = getEqualRange(key); - if (range.first == range.second) { - return 0; - } - const auto result = (range.second - range.first); - impl().erase(range.first, range.second); - return result; - } - - iterator erase(const_iterator where) { - return impl().erase(where._impl); - } - iterator erase(const_iterator from, const_iterator till) { - return impl().erase(from._impl, till._impl); - } - int erase(const Key &key) { - return removeAll(key); - } - - iterator findFirst(const Key &key) { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return end(); - } - auto where = getLowerBound(key); - return compare()(key, where->first) ? impl().end() : where; - } - - const_iterator findFirst(const Key &key) const { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return end(); - } - auto where = getLowerBound(key); - return compare()(key, where->first) ? impl().end() : where; - } - - template - iterator findFirst(const OtherKey &key) { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return end(); - } - auto where = getLowerBound(key); - return compare()(key, where->first) ? impl().end() : where; - } - - template - const_iterator findFirst(const OtherKey &key) const { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return end(); - } - auto where = getLowerBound(key); - return compare()(key, where->first) ? impl().end() : where; - } - - bool contains(const Key &key) const { - return findFirst(key) != end(); - } - int count(const Key &key) const { - if (empty() - || compare()(key, front().first) - || compare()(back().first, key)) { - return 0; - } - auto range = getEqualRange(key); - return (range.second - range.first); - } - -private: - friend class flat_map; - - struct transparent_compare : Compare { - inline constexpr const Compare &initial() const noexcept { - return *this; - } - - template < - typename OtherType1, - typename OtherType2, - typename = std::enable_if_t< - !std::is_same_v, pair_type> && - !std::is_same_v, pair_type>>> - inline constexpr auto operator()( - OtherType1 &&a, - OtherType2 &&b) const { - return initial()( - std::forward(a), - std::forward(b)); - } - template < - typename OtherType1, - typename OtherType2> - inline constexpr auto operator()( - OtherType1 &&a, - OtherType2 &&b) const -> std::enable_if_t< - std::is_same_v, pair_type> && - std::is_same_v, pair_type>, bool> { - return initial()(a.first, b.first); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, pair_type>>> - inline constexpr auto operator()( - const pair_type &a, - OtherType &&b) const { - return operator()(a.first, std::forward(b)); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, pair_type>>> - inline constexpr auto operator()( - OtherType &&a, - const pair_type &b) const { - return operator()(std::forward(a), b.first); - } - - }; - struct Data : transparent_compare { - template - Data(Args &&...args) - : elements(std::forward(args)...) { - } - - impl_t elements; - }; - - Data _data; - const transparent_compare &compare() const noexcept { - return _data; - } - const impl_t &impl() const noexcept { - return _data.elements; - } - impl_t &impl() noexcept { - return _data.elements; - } - - template - typename impl_t::iterator getLowerBound(const OtherKey &key) { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - template - typename impl_t::const_iterator getLowerBound(const OtherKey &key) const { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - template - typename impl_t::iterator getUpperBound(const OtherKey &key) { - return std::upper_bound( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - template - typename impl_t::const_iterator getUpperBound(const OtherKey &key) const { - return std::upper_bound( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - template - std::pair< - typename impl_t::iterator, - typename impl_t::iterator - > getEqualRange(const OtherKey &key) { - return std::equal_range( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - template - std::pair< - typename impl_t::const_iterator, - typename impl_t::const_iterator - > getEqualRange(const OtherKey &key) const { - return std::equal_range( - std::begin(impl()), - std::end(impl()), - key, - compare()); - } - -}; - -template -class flat_map : private flat_multi_map { - using parent = flat_multi_map; - using pair_type = typename parent::pair_type; - -public: - using value_type = typename parent::value_type; - using size_type = typename parent::size_type; - using difference_type = typename parent::difference_type; - using pointer = typename parent::pointer; - using const_pointer = typename parent::const_pointer; - using reference = typename parent::reference; - using const_reference = typename parent::const_reference; - using iterator = typename parent::iterator; - using const_iterator = typename parent::const_iterator; - using reverse_iterator = typename parent::reverse_iterator; - using const_reverse_iterator = typename parent::const_reverse_iterator; - - flat_map() = default; - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category - > - flat_map(Iterator first, Iterator last) : parent(first, last) { - finalize(); - } - - flat_map(std::initializer_list iter) : parent(iter.begin(), iter.end()) { - finalize(); - } - - using parent::parent; - using parent::size; - using parent::empty; - using parent::clear; - using parent::begin; - using parent::end; - using parent::cbegin; - using parent::cend; - using parent::rbegin; - using parent::rend; - using parent::crbegin; - using parent::crend; - using parent::front; - using parent::back; - using parent::erase; - using parent::contains; - - std::pair insert(const value_type &value) { - if (this->empty() || this->compare()(value.first, this->front().first)) { - this->impl().push_front(value); - return { this->begin(), true }; - } else if (this->compare()(this->back().first, value.first)) { - this->impl().push_back(value); - return { this->end() - 1, true }; - } - auto where = this->getLowerBound(value.first); - if (this->compare()(value.first, where->first)) { - return { this->impl().insert(where, value), true }; - } - return { where, false }; - } - std::pair insert(value_type &&value) { - if (this->empty() || this->compare()(value.first, this->front().first)) { - this->impl().push_front(std::move(value)); - return { this->begin(), true }; - } else if (this->compare()(this->back().first, value.first)) { - this->impl().push_back(std::move(value)); - return { this->end() - 1, true }; - } - auto where = this->getLowerBound(value.first); - if (this->compare()(value.first, where->first)) { - return { this->impl().insert(where, std::move(value)), true }; - } - return { where, false }; - } - std::pair insert_or_assign( - const Key &key, - const Type &value) { - if (this->empty() || this->compare()(key, this->front().first)) { - this->impl().emplace_front(key, value); - return { this->begin(), true }; - } else if (this->compare()(this->back().first, key)) { - this->impl().emplace_back(key, value); - return { this->end() - 1, true }; - } - auto where = this->getLowerBound(key); - if (this->compare()(key, where->first)) { - return { this->impl().insert(where, value_type(key, value)), true }; - } - where->second = value; - return { where, false }; - } - std::pair insert_or_assign( - const Key &key, - Type &&value) { - if (this->empty() || this->compare()(key, this->front().first)) { - this->impl().emplace_front(key, std::move(value)); - return { this->begin(), true }; - } else if (this->compare()(this->back().first, key)) { - this->impl().emplace_back(key, std::move(value)); - return { this->end() - 1, true }; - } - auto where = this->getLowerBound(key); - if (this->compare()(key, where->first)) { - return { this->impl().insert(where, value_type(key, std::move(value))), true }; - } - where->second = std::move(value); - return { where, false }; - } - template - std::pair emplace( - OtherKey &&key, - Args&&... args) { - return this->insert(value_type( - std::forward(key), - Type(std::forward(args)...))); - } - template - std::pair emplace_or_assign( - const Key &key, - Args&&... args) { - return this->insert_or_assign( - key, - Type(std::forward(args)...)); - } - template - std::pair try_emplace( - const Key &key, - Args&&... args) { - if (this->empty() || this->compare()(key, this->front().first)) { - this->impl().push_front(value_type( - key, - Type(std::forward(args)...))); - return { this->begin(), true }; - } else if (this->compare()(this->back().first, key)) { - this->impl().push_back(value_type( - key, - Type(std::forward(args)...))); - return { this->end() - 1, true }; - } - auto where = this->getLowerBound(key); - if (this->compare()(key, where->first)) { - return { - this->impl().insert( - where, - value_type( - key, - Type(std::forward(args)...))), - true - }; - } - return { where, false }; - } - - bool remove(const Key &key) { - return this->removeOne(key); - } - - iterator find(const Key &key) { - return this->findFirst(key); - } - const_iterator find(const Key &key) const { - return this->findFirst(key); - } - template - iterator find(const OtherKey &key) { - return this->template findFirst(key); - } - template - const_iterator find(const OtherKey &key) const { - return this->template findFirst(key); - } - - Type &operator[](const Key &key) { - if (this->empty() || this->compare()(key, this->front().first)) { - this->impl().push_front({ key, Type() }); - return this->front().second; - } else if (this->compare()(this->back().first, key)) { - this->impl().push_back({ key, Type() }); - return this->back().second; - } - auto where = this->getLowerBound(key); - if (this->compare()(key, where->first)) { - return this->impl().insert(where, { key, Type() })->second; - } - return where->second; - } - - std::optional take(const Key &key) { - auto it = find(key); - if (it == this->end()) { - return std::nullopt; - } - auto result = std::move(it->second); - this->erase(it); - return std::move(result); - } - -private: - void finalize() { - this->impl().erase( - std::unique( - std::begin(this->impl()), - std::end(this->impl()), - [&](auto &&a, auto &&b) { - return !this->compare()(a, b); - } - ), - std::end(this->impl())); - } - -}; - -} // namespace base - -// Structured bindings support. -namespace std { - -template -class tuple_size> -: public integral_constant { -}; - -template -class tuple_element<0, base::flat_multi_map_pair_type> { -public: - using type = const Key; -}; - -template -class tuple_element<1, base::flat_multi_map_pair_type> { -public: - using type = Value; -}; - -} // namespace std - -// Structured bindings support. -namespace base { -namespace details { - -template -using flat_multi_map_pair_element = std::tuple_element_t< - N, - flat_multi_map_pair_type>; - -} // namespace details - -template -auto get(base::flat_multi_map_pair_type &value) --> details::flat_multi_map_pair_element & { - if constexpr (N == 0) { - return value.first; - } else { - return value.second; - } -} - -template -auto get(const base::flat_multi_map_pair_type &value) --> const details::flat_multi_map_pair_element & { - if constexpr (N == 0) { - return value.first; - } else { - return value.second; - } -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/flat_map_tests.cpp b/Telegram/SourceFiles/base/flat_map_tests.cpp deleted file mode 100644 index e1372c4ac..000000000 --- a/Telegram/SourceFiles/base/flat_map_tests.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* -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 "catch.hpp" - -#include "base/flat_map.h" -#include - -struct int_wrap { - int value; -}; -struct int_wrap_comparator { - inline bool operator()(const int_wrap &a, const int_wrap &b) const { - return a.value < b.value; - } -}; - -using namespace std; - -TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") { - base::flat_map v; - v.emplace(0, "a"); - v.emplace(5, "b"); - v.emplace(4, "d"); - v.emplace(2, "e"); - - auto checkSorted = [&] { - auto prev = v.begin(); - REQUIRE(prev != v.end()); - for (auto i = prev + 1; i != v.end(); prev = i, ++i) { - REQUIRE(prev->first < i->first); - } - }; - REQUIRE(v.size() == 4); - checkSorted(); - - SECTION("adding item puts it in the right position") { - v.emplace(3, "c"); - REQUIRE(v.size() == 5); - REQUIRE(v.find(3) != v.end()); - checkSorted(); - } -} - -TEST_CASE("simple flat_maps tests", "[flat_map]") { - SECTION("copy constructor") { - base::flat_map v; - v.emplace(0, "a"); - v.emplace(2, "b"); - auto u = v; - REQUIRE(u.size() == 2); - REQUIRE(u.find(0) == u.begin()); - REQUIRE(u.find(2) == u.end() - 1); - } - SECTION("assignment") { - base::flat_map v, u; - v.emplace(0, "a"); - v.emplace(2, "b"); - u = v; - REQUIRE(u.size() == 2); - REQUIRE(u.find(0) == u.begin()); - REQUIRE(u.find(2) == u.end() - 1); - } -} - -TEST_CASE("flat_maps custom comparator", "[flat_map]") { - base::flat_map v; - v.emplace(int_wrap{ 0 }, "a"); - v.emplace(int_wrap{ 5 }, "b"); - v.emplace(int_wrap{ 4 }, "d"); - v.emplace(int_wrap{ 2 }, "e"); - - auto checkSorted = [&] { - auto prev = v.begin(); - REQUIRE(prev != v.end()); - for (auto i = prev + 1; i != v.end(); prev = i, ++i) { - REQUIRE(int_wrap_comparator()(prev->first, i->first)); - } - }; - REQUIRE(v.size() == 4); - checkSorted(); - - SECTION("adding item puts it in the right position") { - v.emplace(int_wrap{ 3 }, "c"); - REQUIRE(v.size() == 5); - REQUIRE(v.find({ 3 }) != v.end()); - checkSorted(); - } -} - -TEST_CASE("flat_maps structured bindings", "[flat_map]") { - base::flat_map> v; - v.emplace(0, std::make_unique(0.)); - v.emplace(1, std::make_unique(1.)); - - SECTION("structred binded range-based for loop") { - for (const auto &[key, value] : v) { - REQUIRE(key == int(std::round(*value))); - } - } - - SECTION("non-const structured binded range-based for loop") { - base::flat_map second = { - { 1, 1 }, - { 2, 2 }, - { 2, 3 }, - { 3, 3 }, - }; - REQUIRE(second.size() == 3); - //for (auto [a, b] : second) { // #MSVC Bug, reported - // REQUIRE(a == b); - //} - for (const auto [a, b] : second) { - REQUIRE(a == b); - } - } -} diff --git a/Telegram/SourceFiles/base/flat_set.h b/Telegram/SourceFiles/base/flat_set.h deleted file mode 100644 index b72814c2e..000000000 --- a/Telegram/SourceFiles/base/flat_set.h +++ /dev/null @@ -1,720 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace base { - -using std::begin; -using std::end; - -template > -class flat_set; - -template > -class flat_multi_set; - -template -class flat_multi_set_iterator_impl; - -template -class flat_multi_set_iterator_impl { -public: - using iterator_category = typename iterator_impl::iterator_category; - - using value_type = Type; - using difference_type = typename iterator_impl::difference_type; - using pointer = const Type*; - using reference = const Type&; - - flat_multi_set_iterator_impl( - iterator_impl impl = iterator_impl()) - : _impl(impl) { - } - template - flat_multi_set_iterator_impl( - const flat_multi_set_iterator_impl< - Type, - other_iterator_impl> &other) - : _impl(other._impl) { - } - - reference operator*() const { - return *_impl; - } - pointer operator->() const { - return std::addressof(**this); - } - flat_multi_set_iterator_impl &operator++() { - ++_impl; - return *this; - } - flat_multi_set_iterator_impl operator++(int) { - return _impl++; - } - flat_multi_set_iterator_impl &operator--() { - --_impl; - return *this; - } - flat_multi_set_iterator_impl operator--(int) { - return _impl--; - } - flat_multi_set_iterator_impl &operator+=(difference_type offset) { - _impl += offset; - return *this; - } - flat_multi_set_iterator_impl operator+(difference_type offset) const { - return _impl + offset; - } - flat_multi_set_iterator_impl &operator-=(difference_type offset) { - _impl -= offset; - return *this; - } - flat_multi_set_iterator_impl operator-(difference_type offset) const { - return _impl - offset; - } - template - difference_type operator-( - const flat_multi_set_iterator_impl< - Type, - other_iterator_impl> &right) const { - return _impl - right._impl; - } - reference operator[](difference_type offset) const { - return _impl[offset]; - } - - template - bool operator==( - const flat_multi_set_iterator_impl< - Type, - other_iterator_impl> &right) const { - return _impl == right._impl; - } - template - bool operator!=( - const flat_multi_set_iterator_impl< - Type, - other_iterator_impl> &right) const { - return _impl != right._impl; - } - template - bool operator<( - const flat_multi_set_iterator_impl< - Type, - other_iterator_impl> &right) const { - return _impl < right._impl; - } - -private: - iterator_impl _impl; - - template - friend class flat_multi_set; - - template - friend class flat_set; - - template < - typename OtherType, - typename other_iterator_impl> - friend class flat_multi_set_iterator_impl; - - Type &wrapped() { - return _impl->wrapped(); - } - -}; - -template -class flat_multi_set_const_wrap { -public: - constexpr flat_multi_set_const_wrap(const Type &value) - : _value(value) { - } - constexpr flat_multi_set_const_wrap(Type &&value) - : _value(std::move(value)) { - } - inline constexpr operator const Type&() const { - return _value; - } - constexpr Type &wrapped() { - return _value; - } - -private: - Type _value; - -}; - -template -class flat_multi_set { - using const_wrap = flat_multi_set_const_wrap; - using impl_t = std::deque; - -public: - using value_type = Type; - using size_type = typename impl_t::size_type; - using difference_type = typename impl_t::difference_type; - using pointer = const Type*; - using reference = const Type&; - - using iterator = flat_multi_set_iterator_impl< - Type, - typename impl_t::iterator>; - using const_iterator = flat_multi_set_iterator_impl< - Type, - typename impl_t::const_iterator>; - using reverse_iterator = flat_multi_set_iterator_impl< - Type, - typename impl_t::reverse_iterator>; - using const_reverse_iterator = flat_multi_set_iterator_impl< - Type, - typename impl_t::const_reverse_iterator>; - - flat_multi_set() = default; - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category> - flat_multi_set(Iterator first, Iterator last) - : _data(first, last) { - std::sort(std::begin(impl()), std::end(impl()), compare()); - } - - flat_multi_set(std::initializer_list iter) - : flat_multi_set(iter.begin(), iter.end()) { - } - - size_type size() const { - return impl().size(); - } - bool empty() const { - return impl().empty(); - } - void clear() { - impl().clear(); - } - - iterator begin() { - return impl().begin(); - } - iterator end() { - return impl().end(); - } - const_iterator begin() const { - return impl().begin(); - } - const_iterator end() const { - return impl().end(); - } - const_iterator cbegin() const { - return impl().cbegin(); - } - const_iterator cend() const { - return impl().cend(); - } - reverse_iterator rbegin() { - return impl().rbegin(); - } - reverse_iterator rend() { - return impl().rend(); - } - const_reverse_iterator rbegin() const { - return impl().rbegin(); - } - const_reverse_iterator rend() const { - return impl().rend(); - } - const_reverse_iterator crbegin() const { - return impl().crbegin(); - } - const_reverse_iterator crend() const { - return impl().crend(); - } - - reference front() const { - return *begin(); - } - reference back() const { - return *(end() - 1); - } - - iterator insert(const Type &value) { - if (empty() || compare()(value, front())) { - impl().push_front(value); - return begin(); - } else if (!compare()(value, back())) { - impl().push_back(value); - return (end() - 1); - } - auto where = getUpperBound(value); - return impl().insert(where, value); - } - iterator insert(Type &&value) { - if (empty() || compare()(value, front())) { - impl().push_front(std::move(value)); - return begin(); - } else if (!compare()(value, back())) { - impl().push_back(std::move(value)); - return (end() - 1); - } - auto where = getUpperBound(value); - return impl().insert(where, std::move(value)); - } - template - iterator emplace(Args&&... args) { - return insert(Type(std::forward(args)...)); - } - - bool removeOne(const Type &value) { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return false; - } - auto where = getLowerBound(value); - if (compare()(value, *where)) { - return false; - } - impl().erase(where); - return true; - } - int removeAll(const Type &value) { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return 0; - } - auto range = getEqualRange(value); - if (range.first == range.second) { - return 0; - } - const auto result = (range.second - range.first); - impl().erase(range.first, range.second); - return result; - } - - iterator erase(const_iterator where) { - return impl().erase(where._impl); - } - iterator erase(const_iterator from, const_iterator till) { - return impl().erase(from._impl, till._impl); - } - int erase(const Type &value) { - return removeAll(value); - } - - iterator findFirst(const Type &value) { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return end(); - } - auto where = getLowerBound(value); - return compare()(value, *where) ? impl().end() : where; - } - - const_iterator findFirst(const Type &value) const { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return end(); - } - auto where = getLowerBound(value); - return compare()(value, *where) ? impl().end() : where; - } - - template < - typename OtherType, - typename = typename Compare::is_transparent> - iterator findFirst(const OtherType &value) { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return end(); - } - auto where = getLowerBound(value); - return compare()(value, *where) ? impl().end() : where; - } - - template < - typename OtherType, - typename = typename Compare::is_transparent> - const_iterator findFirst(const OtherType &value) const { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return end(); - } - auto where = getLowerBound(value); - return compare()(value, *where) ? impl().end() : where; - } - - bool contains(const Type &value) const { - return findFirst(value) != end(); - } - int count(const Type &value) const { - if (empty() - || compare()(value, front()) - || compare()(back(), value)) { - return 0; - } - auto range = getEqualRange(value); - return (range.second - range.first); - } - - template - auto modify(iterator which, Action action) { - auto result = action(which.wrapped()); - for (auto i = which + 1, e = end(); i != e; ++i) { - if (compare()(*i, *which)) { - std::swap(i.wrapped(), which.wrapped()); - } else { - break; - } - } - for (auto i = which, b = begin(); i != b;) { - --i; - if (compare()(*which, *i)) { - std::swap(i.wrapped(), which.wrapped()); - } else { - break; - } - } - return result; - } - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category> - void merge(Iterator first, Iterator last) { - impl().insert(impl().end(), first, last); - std::sort(std::begin(impl()), std::end(impl()), compare()); - } - - void merge(const flat_multi_set &other) { - merge(other.begin(), other.end()); - } - - void merge(std::initializer_list list) { - merge(list.begin(), list.end()); - } - -private: - friend class flat_set; - - struct transparent_compare : Compare { - inline constexpr const Compare &initial() const noexcept { - return *this; - } - - template < - typename OtherType1, - typename OtherType2, - typename = std::enable_if_t< - !std::is_same_v, const_wrap> && - !std::is_same_v, const_wrap>>> - inline constexpr auto operator()( - OtherType1 &&a, - OtherType2 &&b) const { - return initial()( - std::forward(a), - std::forward(b)); - } - template < - typename OtherType1, - typename OtherType2> - inline constexpr auto operator()( - OtherType1 &&a, - OtherType2 &&b) const -> std::enable_if_t< - std::is_same_v, const_wrap> && - std::is_same_v, const_wrap>, bool> { - return initial()( - static_cast(a), - static_cast(b)); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, const_wrap>>> - inline constexpr auto operator()( - const const_wrap &a, - OtherType &&b) const { - return initial()( - static_cast(a), - std::forward(b)); - } - template < - typename OtherType, - typename = std::enable_if_t< - !std::is_same_v, const_wrap>>> - inline constexpr auto operator()( - OtherType &&a, - const const_wrap &b) const { - return initial()( - std::forward(a), - static_cast(b)); - } - - }; - struct Data : transparent_compare { - template - Data(Args &&...args) - : elements(std::forward(args)...) { - } - - impl_t elements; - }; - - Data _data; - const transparent_compare &compare() const { - return _data; - } - const impl_t &impl() const { - return _data.elements; - } - impl_t &impl() { - return _data.elements; - } - - typename impl_t::iterator getLowerBound(const Type &value) { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - typename impl_t::const_iterator getLowerBound(const Type &value) const { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - template < - typename OtherType, - typename = typename Compare::is_transparent> - typename impl_t::iterator getLowerBound(const OtherType &value) { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - template < - typename OtherType, - typename = typename Compare::is_transparent> - typename impl_t::const_iterator getLowerBound(const OtherType &value) const { - return std::lower_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - typename impl_t::iterator getUpperBound(const Type &value) { - return std::upper_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - typename impl_t::const_iterator getUpperBound(const Type &value) const { - return std::upper_bound( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - std::pair< - typename impl_t::iterator, - typename impl_t::iterator - > getEqualRange(const Type &value) { - return std::equal_range( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - std::pair< - typename impl_t::const_iterator, - typename impl_t::const_iterator - > getEqualRange(const Type &value) const { - return std::equal_range( - std::begin(impl()), - std::end(impl()), - value, - compare()); - } - -}; - -template -class flat_set : private flat_multi_set { - using parent = flat_multi_set; - -public: - using iterator = typename parent::iterator; - using const_iterator = typename parent::const_iterator; - using reverse_iterator = typename parent::reverse_iterator; - using const_reverse_iterator = typename parent::const_reverse_iterator; - using value_type = typename parent::value_type; - using size_type = typename parent::size_type; - using difference_type = typename parent::difference_type; - using pointer = typename parent::pointer; - using reference = typename parent::reference; - - flat_set() = default; - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category - > - flat_set(Iterator first, Iterator last) : parent(first, last) { - finalize(); - } - - flat_set(std::initializer_list iter) : parent(iter.begin(), iter.end()) { - finalize(); - } - - using parent::parent; - using parent::size; - using parent::empty; - using parent::clear; - using parent::begin; - using parent::end; - using parent::cbegin; - using parent::cend; - using parent::rbegin; - using parent::rend; - using parent::crbegin; - using parent::crend; - using parent::front; - using parent::back; - using parent::contains; - using parent::erase; - - std::pair insert(const Type &value) { - if (this->empty() || this->compare()(value, this->front())) { - this->impl().push_front(value); - return std::make_pair(this->begin(), true); - } else if (this->compare()(this->back(), value)) { - this->impl().push_back(value); - return std::make_pair(this->end() - 1, true); - } - auto where = this->getLowerBound(value); - if (this->compare()(value, *where)) { - return std::make_pair(this->impl().insert(where, value), true); - } - return std::make_pair(where, false); - } - std::pair insert(Type &&value) { - if (this->empty() || this->compare()(value, this->front())) { - this->impl().push_front(std::move(value)); - return std::make_pair(this->begin(), true); - } else if (this->compare()(this->back(), value)) { - this->impl().push_back(std::move(value)); - return std::make_pair(this->end() - 1, true); - } - auto where = this->getLowerBound(value); - if (this->compare()(value, *where)) { - return std::make_pair( - this->impl().insert(where, std::move(value)), - true); - } - return std::make_pair(where, false); - } - template - std::pair emplace(Args&&... args) { - return this->insert(Type(std::forward(args)...)); - } - - bool remove(const Type &value) { - return this->removeOne(value); - } - - iterator find(const Type &value) { - return this->findFirst(value); - } - const_iterator find(const Type &value) const { - return this->findFirst(value); - } - template < - typename OtherType, - typename = typename Compare::is_transparent> - iterator find(const OtherType &value) { - return this->findFirst(value); - } - template < - typename OtherType, - typename = typename Compare::is_transparent> - const_iterator find(const OtherType &value) const { - return this->findFirst(value); - } - - template - void modify(iterator which, Action action) { - action(which.wrapped()); - for (auto i = iterator(which + 1), e = end(); i != e; ++i) { - if (this->compare()(*i, *which)) { - std::swap(i.wrapped(), which.wrapped()); - } else if (!this->compare()(*which, *i)) { - erase(which); - return; - } else{ - break; - } - } - for (auto i = which, b = begin(); i != b;) { - --i; - if (this->compare()(*which, *i)) { - std::swap(i.wrapped(), which.wrapped()); - } else if (!this->compare()(*i, *which)) { - erase(which); - return; - } else { - break; - } - } - } - - template < - typename Iterator, - typename = typename std::iterator_traits::iterator_category> - void merge(Iterator first, Iterator last) { - parent::merge(first, last); - finalize(); - } - - void merge(const flat_multi_set &other) { - merge(other.begin(), other.end()); - } - - void merge(std::initializer_list list) { - merge(list.begin(), list.end()); - } - -private: - void finalize() { - this->impl().erase( - std::unique( - std::begin(this->impl()), - std::end(this->impl()), - [&](auto &&a, auto &&b) { - return !this->compare()(a, b); - } - ), - std::end(this->impl())); - } - -}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/flat_set_tests.cpp b/Telegram/SourceFiles/base/flat_set_tests.cpp deleted file mode 100644 index a73f1d226..000000000 --- a/Telegram/SourceFiles/base/flat_set_tests.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -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 "catch.hpp" - -#include "base/flat_set.h" - -struct int_wrap { - int value; -}; -struct int_wrap_comparator { - using is_transparent = void; - inline bool operator()(const int &a, const int_wrap &b) const { - return a < b.value; - } - inline bool operator()(const int_wrap &a, const int_wrap &b) const { - return a.value < b.value; - } - inline bool operator()(const int_wrap &a, const int &b) const { - return a.value < b; - } - inline bool operator()(const int &a, const int &b) const { - return a < b; - } -}; - -TEST_CASE("flat_sets should keep items sorted", "[flat_set]") { - - base::flat_set v; - v.insert(0); - v.insert(5); - v.insert(4); - v.insert(2); - - REQUIRE(v.contains(4)); - - auto checkSorted = [&] { - auto prev = v.begin(); - REQUIRE(prev != v.end()); - for (auto i = prev + 1; i != v.end(); prev = i, ++i) { - REQUIRE(*prev < *i); - } - }; - REQUIRE(v.size() == 4); - checkSorted(); - - SECTION("adding item puts it in the right position") { - v.insert(3); - REQUIRE(v.size() == 5); - REQUIRE(v.find(3) != v.end()); - checkSorted(); - } -} - -TEST_CASE("flat_sets with custom comparators", "[flat_set]") { - base::flat_set v; - v.insert({ 0 }); - v.insert({ 5 }); - v.insert({ 4 }); - v.insert({ 2 }); - - REQUIRE(v.find(4) != v.end()); - - auto checkSorted = [&] { - auto prev = v.begin(); - REQUIRE(prev != v.end()); - for (auto i = prev + 1; i != v.end(); prev = i, ++i) { - REQUIRE(prev->value < i->value); - } - }; - REQUIRE(v.size() == 4); - checkSorted(); - - SECTION("adding item puts it in the right position") { - v.insert({ 3 }); - REQUIRE(v.size() == 5); - REQUIRE(v.find(3) != v.end()); - checkSorted(); - } -} diff --git a/Telegram/SourceFiles/base/functors.h b/Telegram/SourceFiles/base/functors.h deleted file mode 100644 index 2a778a75b..000000000 --- a/Telegram/SourceFiles/base/functors.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { -namespace functors { - -struct abs_helper { - template < - typename Type, - typename = decltype(0 < std::declval()), - typename = decltype(-std::declval())> - constexpr Type operator()(Type value) const { - return (0 < value) ? value : (-value); - } -}; -constexpr auto abs = abs_helper{}; - -constexpr auto add = [](auto value) { - return [value](auto other) { - return value + other; - }; -}; - -struct negate_helper { - template < - typename Type, - typename = decltype(-std::declval())> - constexpr Type operator()(Type value) const { - return -value; - } -}; -constexpr auto negate = negate_helper{}; - -} // namespace functors -} // namespace base diff --git a/Telegram/SourceFiles/base/index_based_iterator.h b/Telegram/SourceFiles/base/index_based_iterator.h deleted file mode 100644 index a4db272d0..000000000 --- a/Telegram/SourceFiles/base/index_based_iterator.h +++ /dev/null @@ -1,130 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/assertion.h" - -namespace base { - -template -class index_based_iterator { -public: - using iterator_category = std::random_access_iterator_tag; - - using value_type = typename Container::value_type; - using difference_type = typename Container::difference_type; - using pointer = std::conditional_t< - std::is_const_v, - typename Container::const_pointer, - typename Container::pointer>; - using reference = std::conditional_t< - std::is_const_v, - typename Container::const_reference, - typename Container::reference>; - using base_type = std::conditional_t< - std::is_const_v, - typename Container::const_iterator, - typename Container::iterator>; - - index_based_iterator(Container *container, base_type impl) - : _container(container) - , _index(impl - _container->begin()) { - } - - reference operator*() const { - return *(_container->begin() + _index); - } - pointer operator->() const { - return std::addressof(**this); - } - index_based_iterator &operator++() { - ++_index; - return *this; - } - index_based_iterator operator++(int) { - auto copy = *this; - ++*this; - return copy; - } - index_based_iterator &operator--() { - --_index; - return *this; - } - index_based_iterator operator--(int) { - auto copy = *this; - --*this; - return copy; - } - index_based_iterator &operator+=(difference_type offset) { - _index += offset; - return *this; - } - index_based_iterator operator+(difference_type offset) const { - auto copy = *this; - copy += offset; - return copy; - } - index_based_iterator &operator-=(difference_type offset) { - _index -= offset; - return *this; - } - index_based_iterator operator-(difference_type offset) const { - auto copy = *this; - copy -= offset; - return copy; - } - difference_type operator-(const index_based_iterator &other) const { - return _index - other._index; - } - reference operator[](difference_type offset) const { - return *(*this + offset); - } - - bool operator==(const index_based_iterator &other) const { - Expects(_container == other._container); - return _index == other._index; - } - bool operator!=(const index_based_iterator &other) const { - Expects(_container == other._container); - return _index != other._index; - } - bool operator<(const index_based_iterator &other) const { - Expects(_container == other._container); - return _index < other._index; - } - bool operator>(const index_based_iterator &other) const { - return other < *this; - } - bool operator<=(const index_based_iterator &other) const { - return !(other < *this); - } - bool operator>=(const index_based_iterator &other) const { - return !(*this < other); - } - - base_type base() const { - return _container->begin() + _index; - } - -private: - Container *_container = nullptr; - difference_type _index = 0; - -}; - -template -index_based_iterator index_based_begin(Container &container) { - return { &container, std::begin(container) }; -} - -template -index_based_iterator index_based_end(Container &container) { - return { &container, std::end(container) }; -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/invoke_queued.h b/Telegram/SourceFiles/base/invoke_queued.h deleted file mode 100644 index a94efe527..000000000 --- a/Telegram/SourceFiles/base/invoke_queued.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -#include "base/basic_types.h" - -namespace base { - -class InvokeQueuedEvent : public QEvent { -public: - static constexpr auto kType = QEvent::Type(60666); - - explicit InvokeQueuedEvent(FnMut &&method) - : QEvent(kType) - , _method(std::move(method)) { - } - - void invoke() { - _method(); - } - -private: - FnMut _method; - -}; - -} // namespace base - -template -inline void InvokeQueued(const QObject *context, Lambda &&lambda) { - QCoreApplication::postEvent( - const_cast(context), - new base::InvokeQueuedEvent(std::forward(lambda))); -} - -class SingleQueuedInvokation : public QObject { -public: - SingleQueuedInvokation(Fn callback) : _callback(callback) { - } - void call() { - if (_pending.testAndSetAcquire(0, 1)) { - InvokeQueued(this, [this] { - if (_pending.testAndSetRelease(1, 0)) { - _callback(); - } - }); - } - } - -private: - Fn _callback; - QAtomicInt _pending = { 0 }; - -}; diff --git a/Telegram/SourceFiles/base/last_used_cache.h b/Telegram/SourceFiles/base/last_used_cache.h deleted file mode 100644 index 65d5fb4a3..000000000 --- a/Telegram/SourceFiles/base/last_used_cache.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace base { - -template -class last_used_cache { -public: - void up(Entry entry); - void remove(Entry entry); - void clear(); - - Entry take_lowest(); - -private: - std::list _queue; - std::unordered_map::iterator> _map; - -}; - -template -void last_used_cache::up(Entry entry) { - if (!_queue.empty() && _queue.back() == entry) { - return; - } - const auto i = _map.find(entry); - if (i != end(_map)) { - _queue.splice(end(_queue), _queue, i->second); - } else { - _map.emplace(entry, _queue.insert(end(_queue), entry)); - } -} - -template -void last_used_cache::remove(Entry entry) { - const auto i = _map.find(entry); - if (i != end(_map)) { - _queue.erase(i->second); - _map.erase(i); - } -} - -template -void last_used_cache::clear() { - _queue.clear(); - _map.clear(); -} - -template -Entry last_used_cache::take_lowest() { - if (_queue.empty()) { - return Entry(); - } - auto result = std::move(_queue.front()); - _queue.erase(begin(_queue)); - _map.erase(result); - return result; -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/match_method.h b/Telegram/SourceFiles/base/match_method.h deleted file mode 100644 index 209494939..000000000 --- a/Telegram/SourceFiles/base/match_method.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -template -inline decltype(auto) match_method( - Data &&data, - Method &&method, - Methods &&...methods) { - using namespace rpl::details; - if constexpr (is_callable_plain_v) { - return std::forward(method)(std::forward(data)); - } else { - return match_method( - std::forward(data), - std::forward(methods)...); - } -} - -template < - typename Data1, - typename Data2, - typename Method, - typename ...Methods> -inline decltype(auto) match_method2( - Data1 &&data1, - Data2 &&data2, - Method &&method, - Methods &&...methods) { - using namespace rpl::details; - if constexpr (is_callable_plain_v) { - return std::forward(method)( - std::forward(data1), - std::forward(data2)); - } else { - return match_method2( - std::forward(data1), - std::forward(data2), - std::forward(methods)...); - } -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/object_ptr.h b/Telegram/SourceFiles/base/object_ptr.h deleted file mode 100644 index 714ee600a..000000000 --- a/Telegram/SourceFiles/base/object_ptr.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -// Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent. -template -class object_ptr { -public: - object_ptr(std::nullptr_t) noexcept { - } - - // No default constructor, but constructors with at least - // one argument are simply make functions. - template - explicit object_ptr(Parent &&parent, Args&&... args) - : _object(new Object(std::forward(parent), std::forward(args)...)) { - } - static object_ptr fromRaw(Object *value) noexcept { - object_ptr result = { nullptr }; - result._object = value; - return result; - } - Object *release() noexcept { - return static_cast(base::take(_object).data()); - } - - object_ptr(const object_ptr &other) = delete; - object_ptr &operator=(const object_ptr &other) = delete; - object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) { - } - object_ptr &operator=(object_ptr &&other) noexcept { - auto temp = std::move(other); - destroy(); - std::swap(_object, temp._object); - return *this; - } - - template < - typename OtherObject, - typename = std::enable_if_t< - std::is_base_of_v>> - object_ptr(object_ptr &&other) noexcept - : _object(base::take(other._object)) { - } - - template < - typename OtherObject, - typename = std::enable_if_t< - std::is_base_of_v>> - object_ptr &operator=(object_ptr &&other) noexcept { - _object = base::take(other._object); - return *this; - } - - object_ptr &operator=(std::nullptr_t) noexcept { - _object = nullptr; - return *this; - } - - // So we can pass this pointer to methods like connect(). - Object *data() const noexcept { - return static_cast(_object.data()); - } - operator Object*() const noexcept { - return data(); - } - - explicit operator bool() const noexcept { - return _object != nullptr; - } - - Object *operator->() const noexcept { - return data(); - } - Object &operator*() const noexcept { - return *data(); - } - - // Use that instead "= new Object(parent, ...)" - template - Object *create(Parent &&parent, Args&&... args) { - destroy(); - _object = new Object( - std::forward(parent), - std::forward(args)...); - return data(); - } - void destroy() noexcept { - delete base::take(_object); - } - void destroyDelayed() { - if (_object) { - if (auto widget = base::up_cast(data())) { - widget->hide(); - } - base::take(_object)->deleteLater(); - } - } - - ~object_ptr() noexcept { - if (auto pointer = _object) { - if (!pointer->parent()) { - destroy(); - } - } - } - -private: - template - friend class object_ptr; - - QPointer _object; - -}; diff --git a/Telegram/SourceFiles/base/observer.cpp b/Telegram/SourceFiles/base/observer.cpp deleted file mode 100644 index 1c1245071..000000000 --- a/Telegram/SourceFiles/base/observer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* -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 "base/observer.h" - -namespace base { -namespace internal { -namespace { - -bool CantUseObservables = false; -void (*HandleDelayedMethod)() = nullptr; - -struct ObservableListWrap { - ~ObservableListWrap() { - CantUseObservables = true; - } - OrderedSet list; -}; - -ObservableListWrap &PendingObservables() { - static ObservableListWrap result; - return result; -} - -ObservableListWrap &ActiveObservables() { - static ObservableListWrap result; - return result; -} - -} // namespace - -void RegisterPendingObservable(ObservableCallHandlers *handlers) { - if (CantUseObservables) return; - PendingObservables().list.insert(handlers); - if (HandleDelayedMethod) { - HandleDelayedMethod(); - } -} - -void UnregisterActiveObservable(ObservableCallHandlers *handlers) { - if (CantUseObservables) return; - ActiveObservables().list.remove(handlers); -} - -void UnregisterObservable(ObservableCallHandlers *handlers) { - if (CantUseObservables) return; - PendingObservables().list.remove(handlers); - ActiveObservables().list.remove(handlers); -} - -} // namespace internal - -void InitObservables(void(*HandleDelayed)()) { - internal::HandleDelayedMethod = HandleDelayed; -} - -void HandleObservables() { - if (internal::CantUseObservables) return; - auto &active = internal::ActiveObservables().list; - qSwap(active, internal::PendingObservables().list); - while (!active.empty()) { - auto first = *active.begin(); - (*first)(); - if (!active.empty() && *active.begin() == first) { - active.erase(active.begin()); - } - } -} - -rpl::producer<> ObservableViewer(base::Observable &observable) { - return [&observable](const auto &consumer) { - auto lifetime = rpl::lifetime(); - lifetime.make_state( - observable.add_subscription([consumer]() { - consumer.put_next({}); - })); - return lifetime; - }; -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/observer.h b/Telegram/SourceFiles/base/observer.h deleted file mode 100644 index 75bc0e722..000000000 --- a/Telegram/SourceFiles/base/observer.h +++ /dev/null @@ -1,476 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include "base/type_traits.h" - -namespace base { -namespace internal { - -using ObservableCallHandlers = Fn; -void RegisterPendingObservable(ObservableCallHandlers *handlers); -void UnregisterActiveObservable(ObservableCallHandlers *handlers); -void UnregisterObservable(ObservableCallHandlers *handlers); - -template -struct SubscriptionHandlerHelper { - using type = Fn)>; -}; - -template <> -struct SubscriptionHandlerHelper { - using type = Fn; -}; - -template -using SubscriptionHandler = typename SubscriptionHandlerHelper::type; - -class BaseObservableData { -}; - -template -class CommonObservableData; - -template -class ObservableData; - -} // namespace internal - -class Subscription { -public: - Subscription() = default; - Subscription(const Subscription &) = delete; - Subscription &operator=(const Subscription &) = delete; - Subscription(Subscription &&other) : _node(base::take(other._node)), _removeAndDestroyMethod(other._removeAndDestroyMethod) { - } - Subscription &operator=(Subscription &&other) { - qSwap(_node, other._node); - qSwap(_removeAndDestroyMethod, other._removeAndDestroyMethod); - return *this; - } - explicit operator bool() const { - return (_node != nullptr); - } - void destroy() { - if (_node) { - (*_removeAndDestroyMethod)(base::take(_node)); - } - } - ~Subscription() { - destroy(); - } - -private: - struct Node { - Node(const std::shared_ptr &observable) - : observable(observable) { - } - Node *next = nullptr; - Node *prev = nullptr; - std::weak_ptr observable; - }; - using RemoveAndDestroyMethod = void(*)(Node*); - Subscription(Node *node, RemoveAndDestroyMethod removeAndDestroyMethod) - : _node(node) - , _removeAndDestroyMethod(removeAndDestroyMethod) { - } - - Node *_node = nullptr; - RemoveAndDestroyMethod _removeAndDestroyMethod; - - template - friend class internal::CommonObservableData; - - template - friend class internal::ObservableData; - -}; - -namespace internal { - -template -class BaseObservable; - -template -class CommonObservable { -public: - Subscription add_subscription(Handler &&handler) { - if (!_data) { - _data = std::make_shared>(this); - } - return _data->append(std::move(handler)); - } - -private: - std::shared_ptr> _data; - - friend class CommonObservableData; - friend class BaseObservable::is_fast_copy_type::value>; - -}; - -template -class BaseObservable : public internal::CommonObservable { -public: - void notify(EventType event, bool sync = false) { - if (this->_data) { - this->_data->notify(std::move(event), sync); - } - } - -}; - -template -class BaseObservable : public internal::CommonObservable { -public: - void notify(EventType &&event, bool sync = false) { - if (this->_data) { - this->_data->notify(std::move(event), sync); - } - } - void notify(const EventType &event, bool sync = false) { - if (this->_data) { - auto event_copy = event; - this->_data->notify(std::move(event_copy), sync); - } - } - -}; - -} // namespace internal - -namespace internal { - -template -class CommonObservableData : public BaseObservableData { -public: - CommonObservableData(CommonObservable *observable) : _observable(observable) { - } - - Subscription append(Handler &&handler) { - auto node = new Node(_observable->_data, std::move(handler)); - if (_begin) { - _end->next = node; - node->prev = _end; - _end = node; - } else { - _begin = _end = node; - } - return { _end, &CommonObservableData::removeAndDestroyNode }; - } - - bool empty() const { - return !_begin; - } - -private: - struct Node : public Subscription::Node { - Node( - const std::shared_ptr &observer, - Handler &&handler) - : Subscription::Node(observer) - , handler(std::move(handler)) { - } - Handler handler; - }; - - void remove(Subscription::Node *node) { - if (node->prev) { - node->prev->next = node->next; - } - if (node->next) { - node->next->prev = node->prev; - } - if (_begin == node) { - _begin = static_cast(node->next); - } - if (_end == node) { - _end = static_cast(node->prev); - } - if (_current == node) { - _current = static_cast(node->prev); - } else if (!_begin) { - _observable->_data.reset(); - } - } - - static void removeAndDestroyNode(Subscription::Node *node) { - if (const auto that = node->observable.lock()) { - static_cast(that.get())->remove(node); - } - delete static_cast(node); - } - - template - void notifyEnumerate(CallCurrent callCurrent) { - _current = _begin; - do { - callCurrent(); - if (_current) { - _current = static_cast(_current->next); - } else if (_begin) { - _current = _begin; - } else { - break; - } - } while (_current); - } - - bool destroyMeIfEmpty() const { - if (empty()) { - _observable->_data.reset(); - return true; - } - return false; - } - - CommonObservable *_observable = nullptr; - Node *_begin = nullptr; - Node *_current = nullptr; - Node *_end = nullptr; - ObservableCallHandlers _callHandlers; - - friend class ObservableData; - -}; - -template -class ObservableData : public CommonObservableData { -public: - using CommonObservableData::CommonObservableData; - - void notify(EventType &&event, bool sync) { - if (_handling) { - sync = false; - } - if (sync) { - _events.push_back(std::move(event)); - callHandlers(); - } else { - if (!this->_callHandlers) { - this->_callHandlers = [this]() { - callHandlers(); - }; - } - if (_events.empty()) { - RegisterPendingObservable(&this->_callHandlers); - } - _events.push_back(std::move(event)); - } - } - - ~ObservableData() { - UnregisterObservable(&this->_callHandlers); - } - -private: - void callHandlers() { - _handling = true; - auto events = base::take(_events); - for (auto &event : events) { - this->notifyEnumerate([this, &event]() { - this->_current->handler(event); - }); - if (this->destroyMeIfEmpty()) { - return; - } - } - _handling = false; - UnregisterActiveObservable(&this->_callHandlers); - } - - std::deque _events; - bool _handling = false; - -}; - -template -class ObservableData : public CommonObservableData { -public: - using CommonObservableData::CommonObservableData; - - void notify(bool sync) { - if (_handling) { - sync = false; - } - if (sync) { - ++_eventsCount; - callHandlers(); - } else { - if (!this->_callHandlers) { - this->_callHandlers = [this]() { - callHandlers(); - }; - } - if (!_eventsCount) { - RegisterPendingObservable(&this->_callHandlers); - } - ++_eventsCount; - } - } - - ~ObservableData() { - UnregisterObservable(&this->_callHandlers); - } - -private: - void callHandlers() { - _handling = true; - auto eventsCount = base::take(_eventsCount); - for (int i = 0; i != eventsCount; ++i) { - this->notifyEnumerate([this]() { - this->_current->handler(); - }); - if (this->destroyMeIfEmpty()) { - return; - } - } - _handling = false; - UnregisterActiveObservable(&this->_callHandlers); - } - - int _eventsCount = 0; - bool _handling = false; - -}; - -template -class BaseObservable::is_fast_copy_type::value> : public internal::CommonObservable { -public: - void notify(bool sync = false) { - if (this->_data) { - this->_data->notify(sync); - } - } - -}; - -} // namespace internal - -template > -class Observable : public internal::BaseObservable::is_fast_copy_type::value> { -public: - Observable() = default; - Observable(const Observable &other) = delete; - Observable(Observable &&other) = default; - Observable &operator=(const Observable &other) = delete; - Observable &operator=(Observable &&other) = default; - -}; - -template -class Variable { -public: - Variable(parameter_type startValue = Type()) : _value(startValue) { - } - Variable(Variable &&other) = default; - Variable &operator=(Variable &&other) = default; - - parameter_type value() const { - return _value; - } - - void setForced(parameter_type newValue, bool sync = false) { - _value = newValue; - changed().notify(_value, sync); - } - - void set(parameter_type newValue, bool sync = false) { - if (_value != newValue) { - setForced(newValue, sync); - } - } - - template - void process(Callback callback, bool sync = false) { - callback(_value); - changed().notify(_value, sync); - } - - Observable &changed() const { - return _changed; - } - -private: - Type _value; - mutable Observable _changed; - -}; - -class Subscriber { -protected: - template - int subscribe(base::Observable &observable, Lambda &&handler) { - _subscriptions.push_back(observable.add_subscription(std::forward(handler))); - return _subscriptions.size(); - } - - template - int subscribe(base::Observable *observable, Lambda &&handler) { - return subscribe(*observable, std::forward(handler)); - } - - template - int subscribe(const base::Variable &variable, Lambda &&handler) { - return subscribe(variable.changed(), std::forward(handler)); - } - - template - int subscribe(const base::Variable *variable, Lambda &&handler) { - return subscribe(variable->changed(), std::forward(handler)); - } - - void unsubscribe(int index) { - if (!index) return; - auto count = static_cast(_subscriptions.size()); - Assert(index > 0 && index <= count); - _subscriptions[index - 1].destroy(); - if (index == count) { - while (index > 0 && !_subscriptions[--index]) { - _subscriptions.pop_back(); - } - } - } - - ~Subscriber() { - auto subscriptions = base::take(_subscriptions); - for (auto &subscription : subscriptions) { - subscription.destroy(); - } - } - -private: - std::vector _subscriptions; - -}; - -void InitObservables(void(*HandleDelayed)()); -void HandleObservables(); - -template < - typename Type, - typename = std::enable_if_t>> -inline auto ObservableViewer(base::Observable &observable) { - return rpl::make_producer([&observable]( - const auto &consumer) { - auto lifetime = rpl::lifetime(); - lifetime.make_state( - observable.add_subscription([consumer](auto &&update) { - consumer.put_next_forward( - std::forward(update)); - })); - return lifetime; - }); -} - -rpl::producer<> ObservableViewer(base::Observable &observable); - -} // namespace base diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h deleted file mode 100644 index f9ca3ccfb..000000000 --- a/Telegram/SourceFiles/base/openssl_help.h +++ /dev/null @@ -1,577 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/bytes.h" -#include "base/algorithm.h" -#include "base/basic_types.h" - -extern "C" { -#include -#include -#include -#include -#include -#include -#include -#include -} // extern "C" - -#ifdef small -#undef small -#endif // small - -namespace openssl { - -class Context { -public: - Context() : _data(BN_CTX_new()) { - } - Context(const Context &other) = delete; - Context(Context &&other) : _data(base::take(other._data)) { - } - Context &operator=(const Context &other) = delete; - Context &operator=(Context &&other) { - _data = base::take(other._data); - return *this; - } - ~Context() { - if (_data) { - BN_CTX_free(_data); - } - } - - BN_CTX *raw() const { - return _data; - } - -private: - BN_CTX *_data = nullptr; - -}; - -class BigNum { -public: - BigNum() = default; - BigNum(const BigNum &other) - : _data((other.failed() || other.isZero()) - ? nullptr - : BN_dup(other.raw())) - , _failed(other._failed) { - } - BigNum(BigNum &&other) - : _data(std::exchange(other._data, nullptr)) - , _failed(std::exchange(other._failed, false)) { - } - BigNum &operator=(const BigNum &other) { - if (other.failed()) { - _failed = true; - } else if (other.isZero()) { - clear(); - _failed = false; - } else if (!_data) { - _data = BN_dup(other.raw()); - _failed = false; - } else { - _failed = !BN_copy(raw(), other.raw()); - } - return *this; - } - BigNum &operator=(BigNum &&other) { - std::swap(_data, other._data); - std::swap(_failed, other._failed); - return *this; - } - ~BigNum() { - clear(); - } - - explicit BigNum(unsigned int word) : BigNum() { - setWord(word); - } - explicit BigNum(bytes::const_span bytes) : BigNum() { - setBytes(bytes); - } - - BigNum &setWord(unsigned int word) { - if (!word) { - clear(); - _failed = false; - } else { - _failed = !BN_set_word(raw(), word); - } - return *this; - } - BigNum &setBytes(bytes::const_span bytes) { - if (bytes.empty()) { - clear(); - _failed = false; - } else { - _failed = !BN_bin2bn( - reinterpret_cast(bytes.data()), - bytes.size(), - raw()); - } - return *this; - } - - BigNum &setAdd(const BigNum &a, const BigNum &b) { - if (a.failed() || b.failed()) { - _failed = true; - } else { - _failed = !BN_add(raw(), a.raw(), b.raw()); - } - return *this; - } - BigNum &setSub(const BigNum &a, const BigNum &b) { - if (a.failed() || b.failed()) { - _failed = true; - } else { - _failed = !BN_sub(raw(), a.raw(), b.raw()); - } - return *this; - } - BigNum &setMul( - const BigNum &a, - const BigNum &b, - const Context &context = Context()) { - if (a.failed() || b.failed()) { - _failed = true; - } else { - _failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw()); - } - return *this; - } - BigNum &setModAdd( - const BigNum &a, - const BigNum &b, - const BigNum &m, - const Context &context = Context()) { - if (a.failed() || b.failed() || m.failed()) { - _failed = true; - } else if (a.isNegative() || b.isNegative() || m.isNegative()) { - _failed = true; - } else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) { - _failed = true; - } else if (isNegative()) { - _failed = true; - } else { - _failed = false; - } - return *this; - } - BigNum &setModSub( - const BigNum &a, - const BigNum &b, - const BigNum &m, - const Context &context = Context()) { - if (a.failed() || b.failed() || m.failed()) { - _failed = true; - } else if (a.isNegative() || b.isNegative() || m.isNegative()) { - _failed = true; - } else if (!BN_mod_sub(raw(), a.raw(), b.raw(), m.raw(), context.raw())) { - _failed = true; - } else if (isNegative()) { - _failed = true; - } else { - _failed = false; - } - return *this; - } - BigNum &setModMul( - const BigNum &a, - const BigNum &b, - const BigNum &m, - const Context &context = Context()) { - if (a.failed() || b.failed() || m.failed()) { - _failed = true; - } else if (a.isNegative() || b.isNegative() || m.isNegative()) { - _failed = true; - } else if (!BN_mod_mul(raw(), a.raw(), b.raw(), m.raw(), context.raw())) { - _failed = true; - } else if (isNegative()) { - _failed = true; - } else { - _failed = false; - } - return *this; - } - BigNum &setModInverse( - const BigNum &a, - const BigNum &m, - const Context &context = Context()) { - if (a.failed() || m.failed()) { - _failed = true; - } else if (a.isNegative() || m.isNegative()) { - _failed = true; - } else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) { - _failed = true; - } else if (isNegative()) { - _failed = true; - } else { - _failed = false; - } - return *this; - } - BigNum &setModExp( - const BigNum &base, - const BigNum &power, - const BigNum &m, - const Context &context = Context()) { - if (base.failed() || power.failed() || m.failed()) { - _failed = true; - } else if (base.isNegative() || power.isNegative() || m.isNegative()) { - _failed = true; - } else if (!BN_mod_exp(raw(), base.raw(), power.raw(), m.raw(), context.raw())) { - _failed = true; - } else if (isNegative()) { - _failed = true; - } else { - _failed = false; - } - return *this; - } - - [[nodiscard]] bool isZero() const { - return !failed() && (!_data || BN_is_zero(raw())); - } - - [[nodiscard]] bool isOne() const { - return !failed() && _data && BN_is_one(raw()); - } - - [[nodiscard]] bool isNegative() const { - return !failed() && _data && BN_is_negative(raw()); - } - - [[nodiscard]] bool isPrime(const Context &context = Context()) const { - if (failed() || !_data) { - return false; - } - constexpr auto kMillerRabinIterationCount = 30; - const auto result = BN_is_prime_ex( - raw(), - kMillerRabinIterationCount, - context.raw(), - nullptr); - if (result == 1) { - return true; - } else if (result != 0) { - _failed = true; - } - return false; - } - - BigNum &subWord(unsigned int word) { - if (failed()) { - return *this; - } else if (!BN_sub_word(raw(), word)) { - _failed = true; - } - return *this; - } - BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) { - Expects(word != 0); - - const auto result = failed() - ? (BN_ULONG)-1 - : BN_div_word(raw(), word); - if (result == (BN_ULONG)-1) { - _failed = true; - } - if (mod) { - *mod = result; - } - return *this; - } - [[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const { - Expects(word != 0); - - return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word); - } - - [[nodiscard]] int bitsSize() const { - return failed() ? 0 : BN_num_bits(raw()); - } - [[nodiscard]] int bytesSize() const { - return failed() ? 0 : BN_num_bytes(raw()); - } - - [[nodiscard]] bytes::vector getBytes() const { - if (failed()) { - return {}; - } - auto length = BN_num_bytes(raw()); - auto result = bytes::vector(length); - auto resultSize = BN_bn2bin( - raw(), - reinterpret_cast(result.data())); - Assert(resultSize == length); - return result; - } - - [[nodiscard]] BIGNUM *raw() { - if (!_data) _data = BN_new(); - return _data; - } - [[nodiscard]] const BIGNUM *raw() const { - if (!_data) _data = BN_new(); - return _data; - } - [[nodiscard]] BIGNUM *takeRaw() { - return _failed - ? nullptr - : _data - ? std::exchange(_data, nullptr) - : BN_new(); - } - - [[nodiscard]] bool failed() const { - return _failed; - } - - [[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) { - return BigNum().setAdd(a, b); - } - [[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) { - return BigNum().setSub(a, b); - } - [[nodiscard]] static BigNum Mul( - const BigNum &a, - const BigNum &b, - const Context &context = Context()) { - return BigNum().setMul(a, b, context); - } - [[nodiscard]] static BigNum ModAdd( - const BigNum &a, - const BigNum &b, - const BigNum &mod, - const Context &context = Context()) { - return BigNum().setModAdd(a, b, mod, context); - } - [[nodiscard]] static BigNum ModSub( - const BigNum &a, - const BigNum &b, - const BigNum &mod, - const Context &context = Context()) { - return BigNum().setModSub(a, b, mod, context); - } - [[nodiscard]] static BigNum ModMul( - const BigNum &a, - const BigNum &b, - const BigNum &mod, - const Context &context = Context()) { - return BigNum().setModMul(a, b, mod, context); - } - [[nodiscard]] static BigNum ModInverse( - const BigNum &a, - const BigNum &mod, - const Context &context = Context()) { - return BigNum().setModInverse(a, mod, context); - } - [[nodiscard]] static BigNum ModExp( - const BigNum &base, - const BigNum &power, - const BigNum &mod, - const Context &context = Context()) { - return BigNum().setModExp(base, power, mod, context); - } - [[nodiscard]] static BigNum Failed() { - auto result = BigNum(); - result._failed = true; - return result; - } - -private: - void clear() { - BN_clear_free(std::exchange(_data, nullptr)); - } - - mutable BIGNUM *_data = nullptr; - mutable bool _failed = false; - -}; - -namespace details { - -template -inline void ShaUpdate(Context context, Method method, Arg &&arg) { - const auto span = bytes::make_span(arg); - method(context, span.data(), span.size()); -} - -template -inline void ShaUpdate(Context context, Method method, Arg &&arg, Args &&...args) { - const auto span = bytes::make_span(arg); - method(context, span.data(), span.size()); - ShaUpdate(context, method, args...); -} - -template -inline bytes::vector Sha(Method method, bytes::const_span data) { - auto result = bytes::vector(Size); - method( - reinterpret_cast(data.data()), - data.size(), - reinterpret_cast(result.data())); - return result; -} - -template < - size_type Size, - typename Context, - typename Init, - typename Update, - typename Finalize, - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > 1)>> -bytes::vector Sha( - Context context, - Init init, - Update update, - Finalize finalize, - Args &&...args) { - auto result = bytes::vector(Size); - - init(&context); - ShaUpdate(&context, update, args...); - finalize(reinterpret_cast(result.data()), &context); - - return result; -} - -template < - size_type Size, - typename Evp> -bytes::vector Pbkdf2( - bytes::const_span password, - bytes::const_span salt, - int iterations, - Evp evp) { - auto result = bytes::vector(Size); - PKCS5_PBKDF2_HMAC( - reinterpret_cast(password.data()), - password.size(), - reinterpret_cast(salt.data()), - salt.size(), - iterations, - evp, - result.size(), - reinterpret_cast(result.data())); - return result; -} - -} // namespace details - -constexpr auto kSha1Size = size_type(SHA_DIGEST_LENGTH); -constexpr auto kSha256Size = size_type(SHA256_DIGEST_LENGTH); -constexpr auto kSha512Size = size_type(SHA512_DIGEST_LENGTH); - -inline bytes::vector Sha1(bytes::const_span data) { - return details::Sha(SHA1, data); -} - -template < - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > 1)>> -inline bytes::vector Sha1(Args &&...args) { - return details::Sha( - SHA_CTX(), - SHA1_Init, - SHA1_Update, - SHA1_Final, - args...); -} - -inline bytes::vector Sha256(bytes::const_span data) { - return details::Sha(SHA256, data); -} - -template < - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > 1)>> -inline bytes::vector Sha256(Args &&...args) { - return details::Sha( - SHA256_CTX(), - SHA256_Init, - SHA256_Update, - SHA256_Final, - args...); -} - -inline bytes::vector Sha512(bytes::const_span data) { - return details::Sha(SHA512, data); -} - -template < - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > 1)>> -inline bytes::vector Sha512(Args &&...args) { - return details::Sha( - SHA512_CTX(), - SHA512_Init, - SHA512_Update, - SHA512_Final, - args...); -} - -inline void AddRandomSeed(bytes::const_span data) { - RAND_seed(data.data(), data.size()); -} - -template >> -[[nodiscard]] inline T RandomValue() { - unsigned char buffer[sizeof(T)]; - if (!RAND_bytes(buffer, sizeof(T))) { - Unexpected("Could not generate random bytes!"); - } - auto result = T(); - memcpy(&result, buffer, sizeof(T)); - return result; -} - -inline bytes::vector Pbkdf2Sha512( - bytes::const_span password, - bytes::const_span salt, - int iterations) { - return details::Pbkdf2( - password, - salt, - iterations, - EVP_sha512()); -} - -inline bytes::vector HmacSha256( - bytes::const_span key, - bytes::const_span data) { - auto result = bytes::vector(kSha256Size); - auto length = (unsigned int)kSha256Size; - - HMAC( - EVP_sha256(), - key.data(), - key.size(), - reinterpret_cast(data.data()), - data.size(), - reinterpret_cast(result.data()), - &length); - - return result; -} - -} // namespace openssl - -namespace bytes { - -inline void set_random(span destination) { - RAND_bytes( - reinterpret_cast(destination.data()), - destination.size()); -} - -} // namespace bytes diff --git a/Telegram/SourceFiles/base/optional.h b/Telegram/SourceFiles/base/optional.h deleted file mode 100644 index 2628ee5bb..000000000 --- a/Telegram/SourceFiles/base/optional.h +++ /dev/null @@ -1,171 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "base/variant.h" - -namespace base { - -template -class optional_variant { -public: - optional_variant() : _impl(std::nullopt) { - } - 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; - } - - bool has_value() const { - return !is(); - } - explicit operator bool() const { - return has_value(); - } - 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 - T &set(Args &&...args) { - _impl.template set(std::forward(args)...); - return get_unchecked(); - } - void clear() { - _impl.template set(); - } - - 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(); - } - - template - decltype(auto) match(Methods &&...methods) { - return base::match(_impl, std::forward(methods)...); - } - template - decltype(auto) match(Methods &&...methods) const { - return base::match(_impl, std::forward(methods)...); - } - -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 -inline decltype(auto) match( - optional_variant &value, - Methods &&...methods) { - return value.match(std::forward(methods)...); -} - -template -inline decltype(auto) match( - const optional_variant &value, - Methods &&...methods) { - return value.match(std::forward(methods)...); -} - -template -struct optional_wrap_once { - using type = std::optional; -}; - -template -struct optional_wrap_once> { - using type = std::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 -optional_wrap_once_t make_optional(Type &&value) { - return optional_wrap_once_t { std::forward(value) }; -} - -} // namespace base - -template -inline auto operator|(const std::optional &value, Method method) --> base::optional_chain_result_t { - if constexpr (std::is_same_v) { - return value ? (method(*value), true) : false; - } else { - return value - ? base::optional_chain_result_t( - method(*value)) - : std::nullopt; - } -} diff --git a/Telegram/SourceFiles/base/ordered_set.h b/Telegram/SourceFiles/base/ordered_set.h deleted file mode 100644 index f9ea222d7..000000000 --- a/Telegram/SourceFiles/base/ordered_set.h +++ /dev/null @@ -1,153 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -// ordered set template based on QMap -template -class OrderedSet { - struct NullType { - }; - using Self = OrderedSet; - using Impl = QMap; - using IteratorImpl = typename Impl::iterator; - using ConstIteratorImpl = typename Impl::const_iterator; - Impl impl_; - -public: - OrderedSet() = default; - OrderedSet(const OrderedSet &other) = default; - OrderedSet(OrderedSet &&other) = default; - OrderedSet &operator=(const OrderedSet &other) = default; - OrderedSet &operator=(OrderedSet &&other) = default; - ~OrderedSet() = default; - - inline bool operator==(const Self &other) const { return impl_ == other.impl_; } - inline bool operator!=(const Self &other) const { return impl_ != other.impl_; } - inline int size() const { return impl_.size(); } - inline bool isEmpty() const { return impl_.isEmpty(); } - inline void detach() { return impl_.detach(); } - inline bool isDetached() const { return impl_.isDetached(); } - inline void clear() { return impl_.clear(); } - inline QList values() const { return impl_.keys(); } - inline const T &first() const { return impl_.firstKey(); } - inline const T &last() const { return impl_.lastKey(); } - - class const_iterator; - class iterator { - public: - typedef typename IteratorImpl::iterator_category iterator_category; - typedef typename IteratorImpl::difference_type difference_type; - typedef T value_type; - typedef T *pointer; - typedef T &reference; - - iterator() = default; - iterator(const iterator &other) = default; - iterator &operator=(const iterator &other) = default; - inline const T &operator*() const { return impl_.key(); } - inline const T *operator->() const { return &impl_.key(); } - inline bool operator==(const iterator &other) const { return impl_ == other.impl_; } - inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; } - inline iterator &operator++() { ++impl_; return *this; } - inline iterator operator++(int) { return iterator(impl_++); } - inline iterator &operator--() { --impl_; return *this; } - inline iterator operator--(int) { return iterator(impl_--); } - inline iterator operator+(int j) const { return iterator(impl_ + j); } - inline iterator operator-(int j) const { return iterator(impl_ - j); } - inline iterator &operator+=(int j) { impl_ += j; return *this; } - inline iterator &operator-=(int j) { impl_ -= j; return *this; } - - friend class const_iterator; - inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; } - inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; } - - private: - explicit iterator(const IteratorImpl &impl) : impl_(impl) { - } - IteratorImpl impl_; - friend class OrderedSet; - - }; - friend class iterator; - - class const_iterator { - public: - typedef typename IteratorImpl::iterator_category iterator_category; - typedef typename IteratorImpl::difference_type difference_type; - typedef T value_type; - typedef T *pointer; - typedef T &reference; - - const_iterator() = default; - const_iterator(const const_iterator &other) = default; - const_iterator &operator=(const const_iterator &other) = default; - const_iterator(const iterator &other) : impl_(other.impl_) { - } - const_iterator &operator=(const iterator &other) { - impl_ = other.impl_; - return *this; - } - inline const T &operator*() const { return impl_.key(); } - inline const T *operator->() const { return &impl_.key(); } - inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; } - inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; } - inline const_iterator &operator++() { ++impl_; return *this; } - inline const_iterator operator++(int) { return const_iterator(impl_++); } - inline const_iterator &operator--() { --impl_; return *this; } - inline const_iterator operator--(int) { return const_iterator(impl_--); } - inline const_iterator operator+(int j) const { return const_iterator(impl_ + j); } - inline const_iterator operator-(int j) const { return const_iterator(impl_ - j); } - inline const_iterator &operator+=(int j) { impl_ += j; return *this; } - inline const_iterator &operator-=(int j) { impl_ -= j; return *this; } - - friend class iterator; - inline bool operator==(const iterator &other) const { return impl_ == other.impl_; } - inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; } - - private: - explicit const_iterator(const ConstIteratorImpl &impl) : impl_(impl) { - } - ConstIteratorImpl impl_; - friend class OrderedSet; - - }; - friend class const_iterator; - - // STL style - inline iterator begin() { return iterator(impl_.begin()); } - inline const_iterator begin() const { return const_iterator(impl_.cbegin()); } - inline const_iterator constBegin() const { return const_iterator(impl_.cbegin()); } - inline const_iterator cbegin() const { return const_iterator(impl_.cbegin()); } - inline iterator end() { detach(); return iterator(impl_.end()); } - inline const_iterator end() const { return const_iterator(impl_.cend()); } - inline const_iterator constEnd() const { return const_iterator(impl_.cend()); } - inline const_iterator cend() const { return const_iterator(impl_.cend()); } - inline iterator erase(iterator it) { return iterator(impl_.erase(it.impl_)); } - - inline iterator insert(const T &value) { return iterator(impl_.insert(value, NullType())); } - inline iterator insert(const_iterator pos, const T &value) { return iterator(impl_.insert(pos.impl_, value, NullType())); } - inline int remove(const T &value) { return impl_.remove(value); } - inline bool contains(const T &value) const { return impl_.contains(value); } - - // more Qt - typedef iterator Iterator; - typedef const_iterator ConstIterator; - inline int count() const { return impl_.count(); } - inline iterator find(const T &value) { return iterator(impl_.find(value)); } - inline const_iterator find(const T &value) const { return const_iterator(impl_.constFind(value)); } - inline const_iterator constFind(const T &value) const { return const_iterator(impl_.constFind(value)); } - inline Self &unite(const Self &other) { impl_.unite(other.impl_); return *this; } - - // STL compatibility - typedef typename Impl::difference_type difference_type; - typedef typename Impl::size_type size_type; - inline bool empty() const { return impl_.empty(); } - -}; diff --git a/Telegram/SourceFiles/base/overload.h b/Telegram/SourceFiles/base/overload.h deleted file mode 100644 index 33e3acae3..000000000 --- a/Telegram/SourceFiles/base/overload.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { -namespace details { - -// This implementation was taken from range-v3 library. -// It was modified so that more than one of passed function objects -// could be called with an argument list and the first one that -// matches gets used instead of a compile-time ambiguity error. -// -// This allows to write "default" visitor handlers with [](auto&&) syntax. - -template -constexpr bool is_callable_v = rpl::details::is_callable_plain_v; - -template -struct overloaded; - -template <> -struct overloaded<> { -}; - -template -struct overloaded - : private First - , private overloaded { - -private: - using Others = overloaded; - -public: - overloaded() = default; - - constexpr overloaded(First first, Rest... rest) - : First(std::move(first)) - , Others(std::move(rest)...) { - } - - template - auto operator()(Args&&... args) - -> decltype(std::declval()(std::forward(args)...)) { - return static_cast(*this)(std::forward(args)...); - } - - template - auto operator()(Args&&... args) const - -> decltype(std::declval()(std::forward(args)...)) { - return static_cast(*this)(std::forward(args)...); - } - - template < - typename... Args, - typename = std::enable_if_t>> - auto operator()(Args&&... args) - -> decltype(std::declval()(std::forward(args)...)) { - return static_cast(*this)(std::forward(args)...); - } - - template < - typename... Args, - typename = std::enable_if_t>> - auto operator()(Args&&... args) const - -> decltype(std::declval()(std::forward(args)...)) { - return static_cast(*this)(std::forward(args)...); - } - -}; - -} // namespace details - -template -Function overload(Function &&function) { - return std::forward(function); -} - -template 1)>> -auto overload(Functions ...functions) { - return details::overloaded(std::move(functions)...); -} - -} // namespace base - diff --git a/Telegram/SourceFiles/base/parse_helper.cpp b/Telegram/SourceFiles/base/parse_helper.cpp deleted file mode 100644 index 52f1b12c0..000000000 --- a/Telegram/SourceFiles/base/parse_helper.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 "base/parse_helper.h" - -namespace base { -namespace parse { - -// inspired by https://github.com/sindresorhus/strip-json-comments -QByteArray stripComments(const QByteArray &content) { - enum class InsideComment { - None, - SingleLine, - MultiLine, - }; - auto insideComment = InsideComment::None; - auto insideString = false; - - QByteArray result; - auto begin = content.cbegin(), end = content.cend(), offset = begin; - auto feedContent = [&result, &offset, end](const char *ch) { - if (ch > offset) { - if (result.isEmpty()) result.reserve(end - offset - 2); - result.append(offset, ch - offset); - offset = ch; - } - }; - auto feedComment = [&result, &offset, end](const char *ch) { - if (ch > offset) { - if (result.isEmpty()) result.reserve(end - offset - 2); - result.append(' '); - offset = ch; - } - }; - for (auto ch = offset; ch != end;) { - auto currentChar = *ch; - auto nextChar = (ch + 1 == end) ? 0 : *(ch + 1); - - if (insideComment == InsideComment::None && currentChar == '"') { - auto escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\'); - if (!escaped) { - insideString = !insideString; - } - } - if (insideString) { - ++ch; - continue; - } - - if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') { - feedContent(ch); - insideComment = InsideComment::SingleLine; - ch += 2; - } else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') { - feedComment(ch); - ch += 2; - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::SingleLine && currentChar == '\n') { - feedComment(ch); - ++ch; - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') { - feedContent(ch); - ch += 2; - insideComment = InsideComment::MultiLine; - } else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') { - ch += 2; - feedComment(ch); - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') { - feedComment(ch); - ch += 2; - feedContent(ch); - } else if (insideComment == InsideComment::MultiLine && currentChar == '\n') { - feedComment(ch); - ++ch; - feedContent(ch); - } else { - ++ch; - } - } - - if (insideComment == InsideComment::MultiLine) { - // unexpected end of content - } - if (insideComment == InsideComment::None && end > offset) { - if (result.isEmpty()) { - return content; - } else { - result.append(offset, end - offset); - } - } - return result; -} - -} // namespace parse -} // namespace base diff --git a/Telegram/SourceFiles/base/parse_helper.h b/Telegram/SourceFiles/base/parse_helper.h deleted file mode 100644 index ce96690b0..000000000 --- a/Telegram/SourceFiles/base/parse_helper.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { -namespace parse { - -// Strip all C-style comments. -QByteArray stripComments(const QByteArray &content); - -inline bool skipWhitespaces(const char *&from, const char *end) { - Assert(from <= end); - while (from != end && ( - (*from == ' ') || - (*from == '\n') || - (*from == '\t') || - (*from == '\r'))) { - ++from; - } - return (from != end); -} - -inline QLatin1String readName(const char *&from, const char *end) { - Assert(from <= end); - auto start = from; - while (from != end && ( - (*from >= 'a' && *from <= 'z') || - (*from >= 'A' && *from <= 'Z') || - (*from >= '0' && *from <= '9') || - (*from == '_'))) { - ++from; - } - return QLatin1String(start, from - start); -} - -} // namespace parse -} // namespace base diff --git a/Telegram/SourceFiles/base/qt_connection.h b/Telegram/SourceFiles/base/qt_connection.h deleted file mode 100644 index 3fd5610c9..000000000 --- a/Telegram/SourceFiles/base/qt_connection.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/algorithm.h" - -#include - -namespace base { - -class qt_connection final { -public: - qt_connection(QMetaObject::Connection data = {}) : _data(data) { - } - qt_connection(qt_connection &&other) : _data(base::take(other._data)) { - } - qt_connection &operator=(qt_connection &&other) { - reset(base::take(other._data)); - return *this; - } - ~qt_connection() { - disconnect(); - } - - void release() { - _data = QMetaObject::Connection(); - } - void reset(QMetaObject::Connection data = {}) { - disconnect(); - _data = data; - } - -private: - void disconnect() { - if (_data) { - QObject::disconnect(base::take(_data)); - } - } - - QMetaObject::Connection _data; - -}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/qt_signal_producer.h b/Telegram/SourceFiles/base/qt_signal_producer.h deleted file mode 100644 index 29a12a016..000000000 --- a/Telegram/SourceFiles/base/qt_signal_producer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/base_integration.h" - -namespace base { - -// This method allows to create an rpl::producer from a Qt object -// and a signal with none or one reported value. -// -// QtSignalProducer(qtWindow, &QWindow::activeChanged) | rpl::start_ -// -// This producer values construct a custom event loop leave point. -// This means that all postponeCall's will be invoked right after -// the value processing by the current consumer finishes. -template -auto qt_signal_producer(Object *object, Signal signal); - -namespace details { - -template -struct qt_signal_argument; - -template -struct qt_signal_argument { - using type = Value; -}; - -template -struct qt_signal_argument { - using type = void; -}; - -} // namespace details - -template -auto qt_signal_producer(Object *object, Signal signal) { - using Value = typename details::qt_signal_argument::type; - static constexpr auto NoArgument = std::is_same_v; - using Produced = std::conditional_t< - NoArgument, - rpl::empty_value, - std::remove_const_t>>; - const auto guarded = QPointer(object); - return rpl::make_producer([=](auto consumer) { - if (!guarded) { - return rpl::lifetime(); - } - const auto connect = [&](auto &&handler) { - const auto listener = new QObject(guarded.data()); - QObject::connect( - guarded, - signal, - listener, - std::forward(handler)); - const auto weak = QPointer(listener); - return rpl::lifetime([=] { - if (weak) { - delete weak; - } - }); - }; - auto put = [=](const Produced &value) { - EnterFromEventLoop([&] { - consumer.put_next_copy(value); - }); - }; - if constexpr (NoArgument) { - return connect([put = std::move(put)] { put({}); }); - } else { - return connect(std::move(put)); - } - }); -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/qthelp_regex.h b/Telegram/SourceFiles/base/qthelp_regex.h deleted file mode 100644 index 09441a74e..000000000 --- a/Telegram/SourceFiles/base/qthelp_regex.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/flags.h" - -namespace qthelp { - -class RegularExpressionMatch { -public: - RegularExpressionMatch(const QRegularExpressionMatch &other) = delete; - RegularExpressionMatch(const RegularExpressionMatch &other) = delete; - RegularExpressionMatch(QRegularExpressionMatch &&match) : data_(std::move(match)) { - } - RegularExpressionMatch(RegularExpressionMatch &&other) : data_(std::move(other.data_)) { - } - RegularExpressionMatch &operator=(const QRegularExpressionMatch &match) = delete; - RegularExpressionMatch &operator=(const RegularExpressionMatch &other) = delete; - RegularExpressionMatch &operator=(QRegularExpressionMatch &&match) { - data_ = std::move(match); - return *this; - } - RegularExpressionMatch &operator=(RegularExpressionMatch &&other) { - data_ = std::move(other.data_); - return *this; - } - QRegularExpressionMatch *operator->() { - return &data_; - } - const QRegularExpressionMatch *operator->() const { - return &data_; - } - bool valid() const { - return data_.hasMatch(); - } - explicit operator bool() const { - return valid(); - } - -private: - QRegularExpressionMatch data_; - -}; - -enum class RegExOption { - None = QRegularExpression::NoPatternOption, - CaseInsensitive = QRegularExpression::CaseInsensitiveOption, - DotMatchesEverything = QRegularExpression::DotMatchesEverythingOption, - Multiline = QRegularExpression::MultilineOption, - ExtendedSyntax = QRegularExpression::ExtendedPatternSyntaxOption, - InvertedGreediness = QRegularExpression::InvertedGreedinessOption, - DontCapture = QRegularExpression::DontCaptureOption, - UseUnicodeProperties = QRegularExpression::UseUnicodePropertiesOption, -#ifndef OS_MAC_OLD - OptimizeOnFirstUsage = QRegularExpression::OptimizeOnFirstUsageOption, - DontAutomaticallyOptimize = QRegularExpression::DontAutomaticallyOptimizeOption, -#endif // OS_MAC_OLD -}; -using RegExOptions = base::flags; -inline constexpr auto is_flag_type(RegExOption) { return true; }; - -inline RegularExpressionMatch regex_match(const QString &string, const QString &subject, RegExOptions options = 0) { - auto qtOptions = QRegularExpression::PatternOptions(static_cast(options)); - return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subject)); -} - -inline RegularExpressionMatch regex_match(const QString &string, const QStringRef &subjectRef, RegExOptions options = 0) { - auto qtOptions = QRegularExpression::PatternOptions(static_cast(options)); -#ifndef OS_MAC_OLD - return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subjectRef)); -#else // OS_MAC_OLD - return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subjectRef.toString())); -#endif // OS_MAC_OLD -} - -} // namespace qthelp diff --git a/Telegram/SourceFiles/base/qthelp_url.cpp b/Telegram/SourceFiles/base/qthelp_url.cpp deleted file mode 100644 index 0cdc486f8..000000000 --- a/Telegram/SourceFiles/base/qthelp_url.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* -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 "base/qthelp_url.h" - -namespace qthelp { -namespace { - -QRegularExpression CreateRegExp(const QString &expression) { - auto result = QRegularExpression( - expression, - QRegularExpression::UseUnicodePropertiesOption); -#ifndef OS_MAC_OLD - result.optimize(); -#endif // OS_MAC_OLD - return result; -} - -QString ExpressionDomain() { - // Matches any domain name, containing at least one '.', including "file.txt". - return QString::fromUtf8("(? url_parse_params( - const QString ¶ms, - UrlParamNameTransform transform) { - auto result = QMap(); - - const auto transformParamName = [transform](const QString &name) { - if (transform == UrlParamNameTransform::ToLower) { - return name.toLower(); - } - return name; - }; - for (const auto ¶m : params.split('&')) { - // Skip params without a name (starting with '='). - if (auto separatorPosition = param.indexOf('=')) { - const auto paramName = transformParamName( - (separatorPosition > 0) - ? param.mid(0, separatorPosition) - : param); - const auto paramValue = (separatorPosition > 0) - ? url_decode(param.mid(separatorPosition + 1)) - : QString(); - if (!result.contains(paramName)) { - result.insert(paramName, paramValue); - } - } - } - return result; -} - -bool is_ipv6(const QString &ip) { - //static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$"); - //return regexp.match(ip).hasMatch(); - return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0; -} - -QString url_append_query_or_hash(const QString &url, const QString &add) { - const auto query = url.lastIndexOf('?'); - if (query < 0) { - return url + '?' + add; - } - const auto hash = url.lastIndexOf('#'); - return url - + (query >= 0 && query > url.lastIndexOf('#') ? '&' : '?') - + add; -} - -QString validate_url(const QString &value) { - const auto trimmed = value.trimmed(); - if (trimmed.isEmpty()) { - return QString(); - } - const auto match = RegExpDomainExplicit().match(trimmed); - if (!match.hasMatch()) { - const auto domain = RegExpDomain().match(trimmed); - if (!domain.hasMatch() || domain.capturedStart() != 0) { - return QString(); - } - return qstr("http://") + trimmed; - } else if (match.capturedStart() != 0) { - return QString(); - } - const auto protocolMatch = RegExpProtocol().match(trimmed); - Assert(protocolMatch.hasMatch()); - return IsGoodProtocol(protocolMatch.captured(1)) ? trimmed : QString(); -} - -} // namespace qthelp diff --git a/Telegram/SourceFiles/base/qthelp_url.h b/Telegram/SourceFiles/base/qthelp_url.h deleted file mode 100644 index fd8e460be..000000000 --- a/Telegram/SourceFiles/base/qthelp_url.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include - -namespace qthelp { - -const QRegularExpression &RegExpDomain(); -const QRegularExpression &RegExpDomainExplicit(); -QRegularExpression RegExpProtocol(); - -inline QString url_encode(const QString &part) { - return QString::fromLatin1(QUrl::toPercentEncoding(part)); -} - -inline QString url_decode(const QString &encoded) { - return QUrl::fromPercentEncoding(encoded.toUtf8()); -} - -enum class UrlParamNameTransform { - NoTransform, - ToLower, -}; -// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map. -QMap url_parse_params( - const QString ¶ms, - UrlParamNameTransform transform = UrlParamNameTransform::NoTransform); - -QString url_append_query_or_hash(const QString &url, const QString &add); - -bool is_ipv6(const QString &ip); - -QString validate_url(const QString &value); - -} // namespace qthelp diff --git a/Telegram/SourceFiles/base/runtime_composer.cpp b/Telegram/SourceFiles/base/runtime_composer.cpp deleted file mode 100644 index eec3cdca3..000000000 --- a/Telegram/SourceFiles/base/runtime_composer.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* -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 "base/runtime_composer.h" - -struct RuntimeComposerMetadatasMap { - std::map> data; - QMutex mutex; -}; - -const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) { - static RuntimeComposerMetadatasMap RuntimeComposerMetadatas; - - QMutexLocker lock(&RuntimeComposerMetadatas.mutex); - auto i = RuntimeComposerMetadatas.data.find(mask); - if (i == end(RuntimeComposerMetadatas.data)) { - i = RuntimeComposerMetadatas.data.emplace( - mask, - std::make_unique(mask)).first; - } - return i->second.get(); -} - -const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0); - -RuntimeComponentWrapStruct RuntimeComponentWraps[64]; - -QAtomicInt RuntimeComponentIndexLast; diff --git a/Telegram/SourceFiles/base/runtime_composer.h b/Telegram/SourceFiles/base/runtime_composer.h deleted file mode 100644 index 461f3888e..000000000 --- a/Telegram/SourceFiles/base/runtime_composer.h +++ /dev/null @@ -1,279 +0,0 @@ -/* -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 -*/ -#pragma once - -template -class RuntimeComposer; - -class RuntimeComposerBase; -typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer); -typedef void(*RuntimeComponentDestruct)(void *location); -typedef void(*RuntimeComponentMove)(void *location, void *waslocation); - -struct RuntimeComponentWrapStruct { - // Don't init any fields, because it is only created in - // global scope, so it will be filled by zeros from the start. - RuntimeComponentWrapStruct() = default; - RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move) - : Size(size) - , Align(align) - , Construct(construct) - , Destruct(destruct) - , Move(move) { - } - std::size_t Size; - std::size_t Align; - RuntimeComponentConstruct Construct; - RuntimeComponentDestruct Destruct; - RuntimeComponentMove Move; -}; - -template -struct CeilDivideMinimumOne { - static constexpr int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0)); -}; - -extern RuntimeComponentWrapStruct RuntimeComponentWraps[64]; -extern QAtomicInt RuntimeComponentIndexLast; - -template -struct RuntimeComponent { - using RuntimeComponentBase = Base; - - RuntimeComponent() { - // While there is no std::aligned_alloc(). - static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!"); - } - RuntimeComponent(const RuntimeComponent &other) = delete; - RuntimeComponent &operator=(const RuntimeComponent &other) = delete; - RuntimeComponent(RuntimeComponent &&other) = delete; - RuntimeComponent &operator=(RuntimeComponent &&other) = default; - - static int Index() { - static QAtomicInt MyIndex(0); - if (auto index = MyIndex.loadAcquire()) { - return index - 1; - } - while (true) { - auto last = RuntimeComponentIndexLast.loadAcquire(); - if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) { - Assert(last < 64); - if (MyIndex.testAndSetOrdered(0, last + 1)) { - RuntimeComponentWraps[last] = RuntimeComponentWrapStruct( - sizeof(Type), - alignof(Type), - Type::RuntimeComponentConstruct, - Type::RuntimeComponentDestruct, - Type::RuntimeComponentMove); - } - break; - } - } - return MyIndex.loadAcquire() - 1; - } - static uint64 Bit() { - return (1ULL << Index()); - } - -protected: - static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) { - new (location) Type(); - } - static void RuntimeComponentDestruct(void *location) { - ((Type*)location)->~Type(); - } - static void RuntimeComponentMove(void *location, void *waslocation) { - *(Type*)location = std::move(*(Type*)waslocation); - } - -}; - -class RuntimeComposerMetadata { -public: - RuntimeComposerMetadata(uint64 mask) : _mask(mask) { - for (int i = 0; i != 64; ++i) { - auto componentBit = (1ULL << i); - if (_mask & componentBit) { - auto componentSize = RuntimeComponentWraps[i].Size; - if (componentSize) { - auto componentAlign = RuntimeComponentWraps[i].Align; - if (auto badAlign = (size % componentAlign)) { - size += (componentAlign - badAlign); - } - offsets[i] = size; - size += componentSize; - accumulate_max(align, componentAlign); - } - } else if (_mask < componentBit) { - last = i; - break; - } - } - } - - // Meta pointer in the start. - std::size_t size = sizeof(const RuntimeComposerMetadata*); - std::size_t align = alignof(const RuntimeComposerMetadata*); - std::size_t offsets[64] = { 0 }; - int last = 64; - - bool equals(uint64 mask) const { - return _mask == mask; - } - uint64 maskadd(uint64 mask) const { - return _mask | mask; - } - uint64 maskremove(uint64 mask) const { - return _mask & (~mask); - } - -private: - uint64 _mask; - -}; - -const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask); - -class RuntimeComposerBase { -public: - RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) { - if (mask) { - auto meta = GetRuntimeComposerMetadata(mask); - - auto data = operator new(meta->size); - Assert(data != nullptr); - - _data = data; - _meta() = meta; - for (int i = 0; i < meta->last; ++i) { - auto offset = meta->offsets[i]; - if (offset >= sizeof(_meta())) { - try { - auto constructAt = _dataptrunsafe(offset); - auto space = RuntimeComponentWraps[i].Size; - auto alignedAt = constructAt; - std::align(RuntimeComponentWraps[i].Align, space, alignedAt, space); - Assert(alignedAt == constructAt); - RuntimeComponentWraps[i].Construct(constructAt, this); - } catch (...) { - while (i > 0) { - --i; - offset = meta->offsets[--i]; - if (offset >= sizeof(_meta())) { - RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset)); - } - } - throw; - } - } - } - } - } - RuntimeComposerBase(const RuntimeComposerBase &other) = delete; - RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete; - ~RuntimeComposerBase() { - if (_data != zerodata()) { - auto meta = _meta(); - for (int i = 0; i < meta->last; ++i) { - auto offset = meta->offsets[i]; - if (offset >= sizeof(_meta())) { - RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset)); - } - } - operator delete(_data); - } - } - -protected: - bool UpdateComponents(uint64 mask = 0) { - if (_meta()->equals(mask)) { - return false; - } - RuntimeComposerBase result(mask); - result.swap(*this); - if (_data != zerodata() && result._data != zerodata()) { - const auto meta = _meta(); - const auto wasmeta = result._meta(); - for (auto i = 0; i != meta->last; ++i) { - const auto offset = meta->offsets[i]; - const auto wasoffset = wasmeta->offsets[i]; - if (offset >= sizeof(_meta()) - && wasoffset >= sizeof(_meta())) { - RuntimeComponentWraps[i].Move( - _dataptrunsafe(offset), - result._dataptrunsafe(wasoffset)); - } - } - } - return true; - } - bool AddComponents(uint64 mask = 0) { - return UpdateComponents(_meta()->maskadd(mask)); - } - bool RemoveComponents(uint64 mask = 0) { - return UpdateComponents(_meta()->maskremove(mask)); - } - -private: - template - friend class RuntimeComposer; - - static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata; - static void *zerodata() { - return &ZeroRuntimeComposerMetadata; - } - - void *_dataptrunsafe(int skip) const { - return (char*)_data + skip; - } - void *_dataptr(int skip) const { - return (skip >= sizeof(_meta())) ? _dataptrunsafe(skip) : nullptr; - } - const RuntimeComposerMetadata *&_meta() const { - return *static_cast(_data); - } - void *_data = nullptr; - - void swap(RuntimeComposerBase &other) { - std::swap(_data, other._data); - } - -}; - -template -class RuntimeComposer : public RuntimeComposerBase { -public: - using RuntimeComposerBase::RuntimeComposerBase; - - template < - typename Type, - typename = std::enable_if_t>> - bool Has() const { - return (_meta()->offsets[Type::Index()] >= sizeof(_meta())); - } - - template < - typename Type, - typename = std::enable_if_t>> - Type *Get() { - return static_cast(_dataptr(_meta()->offsets[Type::Index()])); - } - template < - typename Type, - typename = std::enable_if_t>> - const Type *Get() const { - return static_cast(_dataptr(_meta()->offsets[Type::Index()])); - } - -}; diff --git a/Telegram/SourceFiles/base/tests_main.cpp b/Telegram/SourceFiles/base/tests_main.cpp deleted file mode 100644 index fbdcb56fe..000000000 --- a/Telegram/SourceFiles/base/tests_main.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 -*/ -#define CATCH_CONFIG_RUNNER -#include "catch.hpp" -#include "reporters/catch_reporter_compact.hpp" -#include - -int (*TestForkedMethod)()/* = nullptr*/; - -namespace base { -namespace assertion { - -// For Assert() / Expects() / Ensures() / Unexpected() to work. -void log(const char *message, const char *file, int line) { - std::cout << message << " (" << file << ":" << line << ")" << std::endl; -} - -} // namespace assertion -} // namespace base - -namespace Catch { - - struct MinimalReporter : CompactReporter { - MinimalReporter( ReporterConfig const& _config ) - : CompactReporter( _config ) - {} - - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - } - - private: - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? std::string() : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : std::string(); - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else { - } - } - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "minimal", MinimalReporter ) - -} // end namespace Catch - -int main(int argc, const char *argv[]) { - auto touchFile = QString(); - for (auto i = 0; i != argc; ++i) { - if (argv[i] == QString("--touch") && i + 1 != argc) { - touchFile = QFile::decodeName(argv[++i]); - } else if (argv[i] == QString("--forked") && TestForkedMethod) { - return TestForkedMethod(); - } - } - const char *catch_argv[] = { - argv[0], - touchFile.isEmpty() ? "-b" : "-r", - touchFile.isEmpty() ? "-b" : "minimal" }; - constexpr auto catch_argc = sizeof(catch_argv) / sizeof(catch_argv[0]); - auto result = Catch::Session().run(catch_argc, catch_argv); - if (result == 0 && !touchFile.isEmpty()) { - QFile(touchFile).open(QIODevice::WriteOnly); - } - return (result < 0xff ? result : 0xff); -} - diff --git a/Telegram/SourceFiles/base/thread_safe_wrap.h b/Telegram/SourceFiles/base/thread_safe_wrap.h deleted file mode 100644 index 6d97151c7..000000000 --- a/Telegram/SourceFiles/base/thread_safe_wrap.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -template -class thread_safe_wrap { -public: - template - thread_safe_wrap(Args &&...args) : _value(std::forward(args)...) { - } - - template - auto with(Callback &&callback) { - QMutexLocker lock(&_mutex); - return callback(_value); - } - - template - auto with(Callback &&callback) const { - QMutexLocker lock(&_mutex); - return callback(_value); - } - -private: - T _value; - QMutex _mutex; - -}; - -template typename Container = std::deque> -class thread_safe_queue { -public: - template - void emplace(Args &&...args) { - _wrap.with([&](Container &value) { - value.emplace_back(std::forward(args)...); - }); - } - - Container take() { - return _wrap.with([&](Container &value) { - return std::exchange(value, Container()); - }); - } - -private: - thread_safe_wrap> _wrap; - -}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/timer.cpp b/Telegram/SourceFiles/base/timer.cpp deleted file mode 100644 index 00204ad43..000000000 --- a/Telegram/SourceFiles/base/timer.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -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 "base/timer.h" - -#include - -namespace base { -namespace { - -QObject *TimersAdjuster() { - static QObject adjuster; - return &adjuster; -} - -} // namespace - -Timer::Timer( - not_null thread, - Fn callback) -: Timer(std::move(callback)) { - moveToThread(thread); -} - -Timer::Timer(Fn callback) -: QObject(nullptr) -, _callback(std::move(callback)) -, _type(Qt::PreciseTimer) -, _adjusted(false) { - setRepeat(Repeat::Interval); - connect( - TimersAdjuster(), - &QObject::destroyed, - this, - [this] { adjust(); }, - Qt::QueuedConnection); -} - -void Timer::start(crl::time timeout, Qt::TimerType type, Repeat repeat) { - cancel(); - - _type = type; - setRepeat(repeat); - _adjusted = false; - setTimeout(timeout); - _timerId = startTimer(_timeout, _type); - if (_timerId) { - _next = crl::now() + _timeout; - } else { - _next = 0; - } -} - -void Timer::cancel() { - if (isActive()) { - killTimer(base::take(_timerId)); - } -} - -crl::time Timer::remainingTime() const { - if (!isActive()) { - return -1; - } - const auto now = crl::now(); - return (_next > now) ? (_next - now) : crl::time(0); -} - -void Timer::Adjust() { - QObject emitter; - connect( - &emitter, - &QObject::destroyed, - TimersAdjuster(), - &QObject::destroyed); -} - -void Timer::adjust() { - auto remaining = remainingTime(); - if (remaining >= 0) { - cancel(); - _timerId = startTimer(remaining, _type); - _adjusted = true; - } -} - -void Timer::setTimeout(crl::time timeout) { - Expects(timeout >= 0 && timeout <= std::numeric_limits::max()); - - _timeout = static_cast(timeout); -} - -int Timer::timeout() const { - return _timeout; -} - -void Timer::timerEvent(QTimerEvent *e) { - if (repeat() == Repeat::Interval) { - if (_adjusted) { - start(_timeout, _type, repeat()); - } else { - _next = crl::now() + _timeout; - } - } else { - cancel(); - } - - if (const auto onstack = _callback) { - onstack(); - } -} - -int DelayedCallTimer::call( - crl::time timeout, - FnMut callback, - Qt::TimerType type) { - Expects(timeout >= 0); - - if (!callback) { - return 0; - } - auto timerId = startTimer(static_cast(timeout), type); - if (timerId) { - _callbacks.emplace(timerId, std::move(callback)); - } - return timerId; -} - -void DelayedCallTimer::cancel(int callId) { - if (callId) { - killTimer(callId); - _callbacks.remove(callId); - } -} - -void DelayedCallTimer::timerEvent(QTimerEvent *e) { - auto timerId = e->timerId(); - killTimer(timerId); - - auto it = _callbacks.find(timerId); - if (it != _callbacks.end()) { - auto callback = std::move(it->second); - _callbacks.erase(it); - - callback(); - } -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/timer.h b/Telegram/SourceFiles/base/timer.h deleted file mode 100644 index 8ed2b3bb3..000000000 --- a/Telegram/SourceFiles/base/timer.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "base/flat_map.h" - -#include - -namespace base { - -class Timer final : private QObject { -public: - explicit Timer( - not_null thread, - Fn callback = nullptr); - explicit Timer(Fn callback = nullptr); - - static Qt::TimerType DefaultType(crl::time timeout) { - constexpr auto kThreshold = crl::time(1000); - return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer; - } - - void setCallback(Fn callback) { - _callback = std::move(callback); - } - - void callOnce(crl::time timeout) { - callOnce(timeout, DefaultType(timeout)); - } - - void callEach(crl::time timeout) { - callEach(timeout, DefaultType(timeout)); - } - - void callOnce(crl::time timeout, Qt::TimerType type) { - start(timeout, type, Repeat::SingleShot); - } - - void callEach(crl::time timeout, Qt::TimerType type) { - start(timeout, type, Repeat::Interval); - } - - bool isActive() const { - return (_timerId != 0); - } - - void cancel(); - crl::time remainingTime() const; - - static void Adjust(); - -protected: - void timerEvent(QTimerEvent *e) override; - -private: - enum class Repeat : unsigned { - Interval = 0, - SingleShot = 1, - }; - void start(crl::time timeout, Qt::TimerType type, Repeat repeat); - void adjust(); - - void setTimeout(crl::time timeout); - int timeout() const; - - void setRepeat(Repeat repeat) { - _repeat = static_cast(repeat); - } - Repeat repeat() const { - return static_cast(_repeat); - } - - Fn _callback; - crl::time _next = 0; - int _timeout = 0; - int _timerId = 0; - - Qt::TimerType _type : 2; - bool _adjusted : 1; - unsigned _repeat : 1; - -}; - -class DelayedCallTimer final : private QObject { -public: - int call(crl::time timeout, FnMut callback) { - return call( - timeout, - std::move(callback), - Timer::DefaultType(timeout)); - } - - int call( - crl::time timeout, - FnMut callback, - Qt::TimerType type); - void cancel(int callId); - -protected: - void timerEvent(QTimerEvent *e) override; - -private: - base::flat_map> _callbacks; - -}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/type_traits.h b/Telegram/SourceFiles/base/type_traits.h deleted file mode 100644 index f322d4992..000000000 --- a/Telegram/SourceFiles/base/type_traits.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { - -template -struct custom_is_fast_copy_type : public std::false_type { -}; -// To make your own type a fast copy type just write: -// template <> -// struct base::custom_is_fast_copy_type : public std::true_type { -// }; - -namespace internal { - -template -struct type_list_contains; - -template -struct type_list_contains : public std::false_type { -}; - -template -struct type_list_contains : public std::integral_constant::value || type_list_contains::value> { -}; - -template -using is_std_unsigned_int = type_list_contains; - -template -using is_std_signed_int = type_list_contains; - -template -using is_std_integral = std::integral_constant::value || is_std_signed_int::value || type_list_contains::value>; - -template -using is_std_float = type_list_contains; - -template -using is_std_arith = std::integral_constant::value || is_std_float::value>; - -template -using is_std_fundamental = std::integral_constant::value || std::is_same::value>; - -template -struct is_pointer : public std::false_type { -}; - -template -struct is_pointer : public std::true_type { -}; - -template -struct is_member_pointer : public std::false_type { -}; - -template -struct is_member_pointer : public std::true_type { -}; - -template -using is_fast_copy_type = std::integral_constant::value || is_pointer::value || is_member_pointer::value || custom_is_fast_copy_type::value>; - -template -struct add_const_reference { - using type = const T &; -}; - -template <> -struct add_const_reference { - using type = void; -}; - -template -using add_const_reference_t = typename add_const_reference::type; - -template -struct remove_pointer { - using type = T; -}; - -template -struct remove_pointer { - using type = T; -}; - -template -using remove_pointer_t = typename remove_pointer::type; - -} // namespace internal - -template -struct type_traits { - using is_std_unsigned_int = internal::is_std_unsigned_int; - using is_std_signed_int = internal::is_std_signed_int; - using is_std_integral = internal::is_std_integral; - using is_std_float = internal::is_std_float; - using is_std_arith = internal::is_std_arith; - using is_std_fundamental = internal::is_std_fundamental; - using is_pointer = internal::is_pointer; - using is_member_pointer = internal::is_member_pointer; - using is_fast_copy_type = internal::is_fast_copy_type; - - using parameter_type = std::conditional_t>; - using pointed_type = internal::remove_pointer_t; -}; - -template -using parameter_type = typename type_traits::parameter_type; - -} // namespace base diff --git a/Telegram/SourceFiles/base/unique_any.h b/Telegram/SourceFiles/base/unique_any.h deleted file mode 100644 index c7ff4ea44..000000000 --- a/Telegram/SourceFiles/base/unique_any.h +++ /dev/null @@ -1,230 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { -namespace details { - -template -struct moveable_as_copyable_wrap { - moveable_as_copyable_wrap(Value &&other) - : value(std::move(other)) { - } - moveable_as_copyable_wrap &operator=(Value &&other) { - value = std::move(other); - return *this; - } - moveable_as_copyable_wrap(moveable_as_copyable_wrap &&other) - : value(std::move(other.value)) { - } - moveable_as_copyable_wrap( - const moveable_as_copyable_wrap &other) { - Unexpected("Attempt to copy-construct a move-only type."); - } - moveable_as_copyable_wrap &operator=( - moveable_as_copyable_wrap &&other) { - value = std::move(other.value); - return *this; - } - moveable_as_copyable_wrap &operator=( - const moveable_as_copyable_wrap &other) { - Unexpected("Attempt to copy-assign a move-only type."); - } - - Value value; - -}; - -template < - typename Value, - typename = std::enable_if_t< - std::is_move_constructible_v> - && !std::is_lvalue_reference_v>> -auto wrap_moveable_as_copyable(Value &&value) { - return moveable_as_copyable_wrap(std::move(value)); -} - -} // namespace details - -class unique_any; - -template -Value *any_cast(unique_any *value) noexcept; - -template -const Value *any_cast(const unique_any *value) noexcept; - -class unique_any final { -public: - // Construction and destruction [any.cons] - constexpr unique_any() noexcept { - } - - unique_any(const unique_any &other) = delete; - unique_any &operator=(const unique_any &other) = delete; - - unique_any(unique_any &&other) noexcept - : _impl(std::move(other._impl)) { - } - - unique_any &operator=(unique_any &&other) noexcept { - _impl = std::move(other._impl); - return *this; - } - - template < - typename Value, - typename = std::enable_if_t< - !std::is_same_v, unique_any>>> - unique_any(Value &&other) - : unique_any( - std::forward(other), - std::is_copy_constructible>()) { - } - - template < - typename Value, - typename = std::enable_if_t< - !std::is_same_v, unique_any>>> - unique_any &operator=(Value &&other) { - if constexpr (std::is_copy_constructible_v>) { - _impl = std::forward(other); - } else if constexpr (std::is_move_constructible_v> - && !std::is_lvalue_reference_v) { - _impl = details::wrap_moveable_as_copyable(std::move(other)); - } else { - static_assert( - false_t(Value{}), - "Bad value for base::unique_any."); - } - return *this; - } - - template < - typename Value, - typename ...Args, - typename = std::enable_if_t< - std::is_constructible_v, Args...> - && std::is_copy_constructible_v>>> - std::decay_t &emplace(Args &&...args) { - return _impl.emplace(std::forward(args)...); - } - - void reset() noexcept { - _impl.reset(); - } - - void swap(unique_any &other) noexcept { - _impl.swap(other._impl); - } - - bool has_value() const noexcept { - return _impl.has_value(); - } - - // Should check if it is a moveable_only wrap first. - //const std::type_info &type() const noexcept { - // return _impl.type(); - //} - -private: - template < - typename Value, - typename = std::enable_if_t< - !std::is_same_v, unique_any> - && std::is_copy_constructible_v>>> - unique_any(Value &&other, std::true_type) - : _impl(std::forward(other)) { - } - - template < - typename Value, - typename = std::enable_if_t< - !std::is_same_v, unique_any> - && !std::is_copy_constructible_v> - && std::is_move_constructible_v> - && !std::is_lvalue_reference_v>> - unique_any(Value &&other, std::false_type) - : _impl(details::wrap_moveable_as_copyable(std::move(other))) { - } - - template < - typename Value, - typename ...Args> - friend unique_any make_any(Args &&...args); - - template - friend const Value *any_cast(const unique_any *value) noexcept; - - template - friend Value *any_cast(unique_any *value) noexcept; - - std::any _impl; - -}; - -inline void swap(unique_any &a, unique_any &b) noexcept { - a.swap(b); -} - -template < - typename Value, - typename ...Args> -inline auto make_any(Args &&...args) --> std::enable_if_t< - std::is_copy_constructible_v>, - unique_any> { - return std::make_any(std::forward(args)...); -} - -template < - typename Value, - typename ...Args> -inline auto make_any(Args &&...args) --> std::enable_if_t< - !std::is_copy_constructible_v> - && std::is_move_constructible_v>, - unique_any> { - return Value(std::forward(args)...); -} - -template -inline Value *any_cast(unique_any *value) noexcept { - if constexpr (std::is_copy_constructible_v) { - return std::any_cast(&value->_impl); - } else if constexpr (std::is_move_constructible_v) { - auto wrap = std::any_cast< - details::moveable_as_copyable_wrap - >(&value->_impl); - return wrap ? &wrap->value : nullptr; - } else { - static_assert( - false_t(Value{}), - "Bad type for base::any_cast."); - } -} - -template -inline const Value *any_cast(const unique_any *value) noexcept { - if constexpr (std::is_copy_constructible_v) { - return std::any_cast(&value->_impl); - } else if constexpr (std::is_move_constructible_v) { - auto wrap = std::any_cast< - details::moveable_as_copyable_wrap - >(&value->_impl); - return wrap ? &wrap->value : nullptr; - } else { - static_assert( - false_t(Value{}), - "Bad type for base::any_cast."); - } -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/unique_function.h b/Telegram/SourceFiles/base/unique_function.h deleted file mode 100644 index f1911be27..000000000 --- a/Telegram/SourceFiles/base/unique_function.h +++ /dev/null @@ -1,178 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -#ifndef Unexpected -#define Unexpected(message) std::abort() -#define UniqueFunctionUnexpected -#endif // Unexpected - -namespace base { -namespace details { - -template -class moveable_callable_wrap { -public: - static_assert( - std::is_move_constructible_v, - "Should be at least moveable."); - - moveable_callable_wrap(Callable &&other) - : _value(std::move(other)) { - } - moveable_callable_wrap &operator=(Callable &&other) { - _value = std::move(other); - return *this; - } - moveable_callable_wrap(moveable_callable_wrap &&other) - : _value(std::move(other._value)) { - } - moveable_callable_wrap( - const moveable_callable_wrap &other) - : _value(fail_construct()) { - } - moveable_callable_wrap &operator=( - moveable_callable_wrap &&other) { - _value = std::move(other._value); - return *this; - } - moveable_callable_wrap &operator=( - const moveable_callable_wrap &other) { - return fail_assign(); - } - - template - decltype(auto) operator()(Args &&...args) { - return _value(std::forward(args)...); - } - -private: - [[noreturn]] Callable fail_construct() { - Unexpected("Attempt to copy-construct a move-only type."); - } - [[noreturn]] moveable_callable_wrap &fail_assign() { - Unexpected("Attempt to copy-assign a move-only type."); - } - - Callable _value; - -}; - -} // namespace details - -template -class unique_function; - -template -class unique_function final { -public: - unique_function(std::nullptr_t = nullptr) noexcept { - } - unique_function(const unique_function &other) = delete; - unique_function &operator=(const unique_function &other) = delete; - - // Move construct / assign from the same type. - unique_function(unique_function &&other) - : _impl(std::move(other._impl)) { - } - unique_function &operator=(unique_function &&other) { - _impl = std::move(other._impl); - return *this; - } - - template < - typename Callable, - typename = std::enable_if_t< - std::is_convertible_v< - decltype(std::declval()( - std::declval()...)), - Return>>> - unique_function(Callable &&other) - : unique_function( - std::forward(other), - std::is_copy_constructible>{}) { - } - - template < - typename Callable, - typename = std::enable_if_t< - std::is_convertible_v< - decltype(std::declval()( - std::declval()...)), - Return>>> - unique_function &operator=(Callable &&other) { - using Decayed = std::decay_t; - if constexpr (std::is_copy_constructible_v) { - _impl = std::forward(other); - } else if constexpr (std::is_move_constructible_v) { - _impl = details::moveable_callable_wrap( - std::forward(other)); - } else { - static_assert(false_t(other), "Should be moveable."); - } - return *this; - } - - void swap(unique_function &other) { - _impl.swap(other._impl); - } - - Return operator()(Args ...args) { - return _impl(std::forward(args)...); - } - - explicit operator bool() const { - return _impl.operator bool(); - } - - friend inline bool operator==( - const unique_function &value, - std::nullptr_t) noexcept { - return value._impl == nullptr; - } - friend inline bool operator==( - std::nullptr_t, - const unique_function &value) noexcept { - return value._impl == nullptr; - } - friend inline bool operator!=( - const unique_function &value, - std::nullptr_t) noexcept { - return value._impl != nullptr; - } - friend inline bool operator!=( - std::nullptr_t, - const unique_function &value) noexcept { - return value._impl != nullptr; - } - -private: - template - unique_function(Callable &&other, std::true_type) - : _impl(std::forward(other)) { - } - - template - unique_function(Callable &&other, std::false_type) - : _impl(details::moveable_callable_wrap>( - std::forward(other))) { - } - - std::function _impl; - -}; - -} // namespace base - -#ifdef UniqueFunctionUnexpected -#undef UniqueFunctionUnexpected -#undef Unexpected -#endif // UniqueFunctionUnexpectedb - diff --git a/Telegram/SourceFiles/base/unique_qptr.h b/Telegram/SourceFiles/base/unique_qptr.h deleted file mode 100644 index 89bd2dc58..000000000 --- a/Telegram/SourceFiles/base/unique_qptr.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { - -template -class unique_qptr { -public: - unique_qptr() = default; - unique_qptr(std::nullptr_t) noexcept { - } - explicit unique_qptr(T *pointer) noexcept - : _object(pointer) { - } - - unique_qptr(const unique_qptr &other) = delete; - unique_qptr &operator=(const unique_qptr &other) = delete; - unique_qptr(unique_qptr &&other) noexcept - : _object(base::take(other._object)) { - } - unique_qptr &operator=(unique_qptr &&other) noexcept { - if (_object != other._object) { - destroy(); - _object = base::take(other._object); - } - return *this; - } - - template < - typename U, - typename = std::enable_if_t>> - unique_qptr(unique_qptr &&other) noexcept - : _object(base::take(other._object)) { - } - - template < - typename U, - typename = std::enable_if_t>> - unique_qptr &operator=(unique_qptr &&other) noexcept { - if (_object != other._object) { - destroy(); - _object = base::take(other._object); - } - return *this; - } - - unique_qptr &operator=(std::nullptr_t) noexcept { - destroy(); - return *this; - } - - template - explicit unique_qptr(std::in_place_t, Args &&...args) - : _object(new T(std::forward(args)...)) { - } - - template - T *emplace(Args &&...args) { - reset(new T(std::forward(args)...)); - return get(); - } - - void reset(T *value = nullptr) noexcept { - if (_object != value) { - destroy(); - _object = value; - } - } - - T *get() const noexcept { - return static_cast(_object.data()); - } - operator T*() const noexcept { - return get(); - } - - T *release() noexcept { - return static_cast(base::take(_object).data()); - } - - explicit operator bool() const noexcept { - return _object != nullptr; - } - - T *operator->() const noexcept { - return get(); - } - T &operator*() const noexcept { - return *get(); - } - - ~unique_qptr() noexcept { - destroy(); - } - -private: - void destroy() noexcept { - delete base::take(_object).data(); - } - - template - friend class unique_qptr; - - QPointer _object; - -}; - -template -inline unique_qptr make_unique_q(Args &&...args) { - return unique_qptr(std::in_place, std::forward(args)...); -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/unixtime.cpp b/Telegram/SourceFiles/base/unixtime.cpp deleted file mode 100644 index d3185e190..000000000 --- a/Telegram/SourceFiles/base/unixtime.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* -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 "base/unixtime.h" - -#include -#include - -#ifdef Q_OS_WIN -#include -#elif defined Q_OS_MAC -#include -#else -#include -#endif - -namespace base { -namespace unixtime { -namespace { - -std::atomic ValueUpdated/* = false*/; -std::atomic ValueShift/* = 0*/; -std::atomic HttpValueValid/* = false*/; -std::atomic HttpValueShift/* = 0*/; - -class MsgIdManager { -public: - MsgIdManager(); - - void update(); - [[nodiscard]] uint64 next(); - -private: - void initialize(); - - QReadWriteLock _lock; - uint64 _startId = 0; - std::atomic _incrementedPart = 0; - uint64 _startCounter = 0; - uint64 _randomPart = 0; - float64 _multiplier = 0.; - -}; - -MsgIdManager GlobalMsgIdManager; - -[[nodiscard]] float64 GetMultiplier() { - // 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower, - // than unixtime and we had time to reconfigure. - -#ifdef Q_OS_WIN - LARGE_INTEGER li; - QueryPerformanceFrequency(&li); - return float64(0xFFFF0000L) / float64(li.QuadPart); -#elif defined Q_OS_MAC // Q_OS_WIN - mach_timebase_info_data_t tb = { 0, 0 }; - mach_timebase_info(&tb); - const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.; - return frequency * (float64(0xFFFF0000L) / 1000.); -#else // Q_OS_MAC || Q_OS_WIN - return float64(0xFFFF0000L) / 1000000000.; -#endif // Q_OS_MAC || Q_OS_WIN -} - -[[nodiscard]] uint64 GetCounter() { -#ifdef Q_OS_WIN - LARGE_INTEGER li; - QueryPerformanceCounter(&li); - return li.QuadPart; -#elif defined Q_OS_MAC // Q_OS_WIN - return mach_absolute_time(); -#else // Q_OS_MAC || Q_OS_WIN - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec); -#endif // Q_OS_MAC || Q_OS_WIN -} - -MsgIdManager::MsgIdManager() { - auto generator = std::mt19937(std::random_device()()); - auto distribution = std::uniform_int_distribution(); - _randomPart = distribution(generator); - _multiplier = GetMultiplier(); - initialize(); - - srand(uint32(_startCounter & 0xFFFFFFFFUL)); -} - -void MsgIdManager::update() { - QWriteLocker lock(&_lock); - initialize(); -} - -void MsgIdManager::initialize() { - _startCounter = GetCounter(); - _startId = ((uint64(uint32(now()))) << 32) | _randomPart; -} - -uint64 MsgIdManager::next() { - const auto counter = GetCounter(); - - QReadLocker lock(&_lock); - const auto delta = (counter - _startCounter); - const auto result = _startId + (uint64)floor(delta * _multiplier); - lock.unlock(); - - return (result & ~0x03L) + (_incrementedPart += 4); -} - -TimeId local() { - return (TimeId)time(nullptr); -} - -} // namespace - -TimeId now() { - return local() + ValueShift.load(); -} - -void update(TimeId now, bool force) { - if (force) { - ValueUpdated = true; - } else { - auto expected = false; - if (!ValueUpdated.compare_exchange_strong(expected, true)) { - return; - } - } - const auto shift = now + 1 - local(); - ValueShift = shift; - - HttpValueShift = 0; - HttpValueValid = false; - - GlobalMsgIdManager.update(); -} - -QDateTime parse(TimeId value) { - return (value > 0) - ? QDateTime::fromTime_t(value - ValueShift) - : QDateTime(); -} - -TimeId serialize(const QDateTime &date) { - return date.isNull() ? TimeId(0) : date.toTime_t() + ValueShift; -} - -bool http_valid() { - return HttpValueValid; -} - -TimeId http_now() { - return now() + HttpValueShift; -} - -void http_update(TimeId now) { - HttpValueShift = now - base::unixtime::now(); - HttpValueValid = true; -} - -void http_invalidate() { - HttpValueValid = false; -} - -uint64 mtproto_msg_id() { - return GlobalMsgIdManager.next(); -} - -} // namespace unixtime -} // namespace base diff --git a/Telegram/SourceFiles/base/unixtime.h b/Telegram/SourceFiles/base/unixtime.h deleted file mode 100644 index 9db64ed2a..000000000 --- a/Telegram/SourceFiles/base/unixtime.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" - -class QDateTime; - -namespace base { -namespace unixtime { - -// All functions are thread-safe. - -[[nodiscard]] TimeId now(); -void update(TimeId now, bool force = false); - -[[nodiscard]] QDateTime parse(TimeId value); -[[nodiscard]] TimeId serialize(const QDateTime &date); - -[[nodiscard]] bool http_valid(); -[[nodiscard]] TimeId http_now(); -void http_update(TimeId now); -void http_invalidate(); - -[[nodiscard]] uint64 mtproto_msg_id(); - -} // namespace unixtime -} // namespace base diff --git a/Telegram/SourceFiles/base/value_ordering.h b/Telegram/SourceFiles/base/value_ordering.h deleted file mode 100644 index 941421bfa..000000000 --- a/Telegram/SourceFiles/base/value_ordering.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace base { -namespace details { - -template < - typename Type, - typename Operator, - typename = decltype(Operator{}( - std::declval(), - std::declval()))> -char test_operator(const Type &, const Operator &); -int test_operator(...); - -template typename Operator> -struct has_operator - : std::bool_constant< - sizeof(test_operator( - std::declval(), - std::declval>() - )) == sizeof(char)> { -}; - -template < - typename Type, - template typename Operator> -constexpr bool has_operator_v - = has_operator::value; - -template -constexpr bool has_less_v = has_operator_v; - -template -constexpr bool has_greater_v = has_operator_v; - -template -constexpr bool has_less_equal_v = has_operator_v; - -template -constexpr bool has_greater_equal_v = has_operator_v; - -template -constexpr bool has_equal_to_v = has_operator_v; - -template -constexpr bool has_not_equal_to_v = has_operator_v; - -} // namespace details -} // namespace base - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator<(const ValueType &a, const ValueType &b) --> std::enable_if_t, bool> { - return value_ordering_helper(a) < value_ordering_helper(b); -} - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator>(const ValueType &a, const ValueType &b) --> std::enable_if_t< - base::details::has_greater_v - || base::details::has_less_v, - bool -> { - if constexpr (base::details::has_greater_v) { - return value_ordering_helper(a) > value_ordering_helper(b); - } else { - return value_ordering_helper(b) < value_ordering_helper(a); - } -} - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator<=(const ValueType &a, const ValueType &b) --> std::enable_if_t< - base::details::has_less_equal_v - || base::details::has_less_v, - bool -> { - if constexpr (base::details::has_less_equal_v) { - return value_ordering_helper(a) <= value_ordering_helper(b); - } else { - return !(value_ordering_helper(b) < value_ordering_helper(a)); - } -} - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator>=(const ValueType &a, const ValueType &b) --> std::enable_if_t< - base::details::has_greater_equal_v - || base::details::has_less_v, - bool -> { - if constexpr (base::details::has_greater_equal_v) { - return value_ordering_helper(a) >= value_ordering_helper(b); - } else { - return !(value_ordering_helper(a) < value_ordering_helper(b)); - } -} - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator==(const ValueType &a, const ValueType &b) --> std::enable_if_t, bool> { - return value_ordering_helper(a) == value_ordering_helper(b); -} - -template < - typename ValueType, - typename Helper = decltype( - value_ordering_helper(std::declval()))> -inline auto operator!=(const ValueType &a, const ValueType &b) --> std::enable_if_t< - base::details::has_not_equal_to_v - || base::details::has_equal_to_v, - bool -> { - if constexpr (base::details::has_not_equal_to_v) { - return value_ordering_helper(a) != value_ordering_helper(b); - } else { - return !(value_ordering_helper(a) == value_ordering_helper(b)); - } -} diff --git a/Telegram/SourceFiles/base/variant.h b/Telegram/SourceFiles/base/variant.h deleted file mode 100644 index e8539d4d2..000000000 --- a/Telegram/SourceFiles/base/variant.h +++ /dev/null @@ -1,127 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -inline bool operator<(std::nullopt_t, std::nullopt_t) { - return false; -} -inline bool operator>(std::nullopt_t, std::nullopt_t) { - return false; -} -inline bool operator<=(std::nullopt_t, std::nullopt_t) { - return true; -} -inline bool operator>=(std::nullopt_t, std::nullopt_t) { - return true; -} -inline bool operator==(std::nullopt_t, std::nullopt_t) { - return true; -} -inline bool operator!=(std::nullopt_t, std::nullopt_t) { - return false; -} - -#include -#include -#include "base/match_method.h" -#include "base/assertion.h" - -// We use base::variant<> alias and base::get_if() helper while we don't have std::variant<>. -namespace base { - -template -using variant = mapbox::util::variant; - -template -inline T *get_if(variant *v) { - return (v && v->template is()) ? &v->template get_unchecked() : nullptr; -} - -template -inline const T *get_if(const variant *v) { - return (v && v->template is()) ? &v->template get_unchecked() : nullptr; -} - -namespace type_list = rpl::details::type_list; - -template -struct normalized_variant { - using list = type_list::list; - using distinct = type_list::distinct_t; - using type = std::conditional_t< - type_list::size_v == 1, - type_list::get_t<0, distinct>, - type_list::extract_to_t>; -}; - -template -using normalized_variant_t - = typename normalized_variant::type; - -template -struct match_helper; - -template < - typename Type, - typename ...Types, - typename Variant, - typename ...Methods> -struct match_helper, Variant, Methods...> { - static decltype(auto) call(Variant &value, Methods &&...methods) { - if (const auto v = get_if(&value)) { - return match_method( - *v, - std::forward(methods)...); - } - return match_helper< - type_list::list, - Variant, - Methods...>::call( - value, - std::forward(methods)...); - } -}; - -template < - typename Type, - typename Variant, - typename ...Methods> -struct match_helper, Variant, Methods...> { - static decltype(auto) call(Variant &value, Methods &&...methods) { - if (const auto v = get_if(&value)) { - return match_method( - *v, - std::forward(methods)...); - } - Unexpected("Valueless variant in base::match()."); - } -}; - -template -inline decltype(auto) match( - variant &value, - Methods &&...methods) { - return match_helper< - type_list::list, - variant, - Methods...>::call(value, std::forward(methods)...); -} - -template -inline decltype(auto) match( - const variant &value, - Methods &&...methods) { - return match_helper< - type_list::list, - const variant, - Methods...>::call(value, std::forward(methods)...); -} - -} // namespace base diff --git a/Telegram/SourceFiles/base/virtual_method.h b/Telegram/SourceFiles/base/virtual_method.h deleted file mode 100644 index 9feda79c6..000000000 --- a/Telegram/SourceFiles/base/virtual_method.h +++ /dev/null @@ -1,704 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace base { - -template -class virtual_object; - -template -class virtual_method; - -template -class virtual_override; - -namespace virtual_methods { - -struct child_entry; -using is_parent_check = bool(*)(const child_entry &possible_parent); -struct child_entry { - is_parent_check check_is_parent; - int *table_index; -}; -using child_entries = std::vector; - -// Recursive method to find if some class is a child of some other class. -template -struct is_parent { - static inline bool check(const child_entry &possible_parent) { - // Generate a good error message if ConcreteObject is not a child of virtual_object<>. - using all_objects_must_derive_virtual_object = typename ConcreteObject::virtual_object_parent; - using ConcreteObjectParent = all_objects_must_derive_virtual_object; - return (possible_parent.check_is_parent == &is_parent::check) - || is_parent::check(possible_parent); - } -}; - -template <> -struct is_parent { - static inline bool check(const child_entry &possible_parent) { - return (possible_parent.check_is_parent == &is_parent::check); - } -}; - -// Just force the compiler not to optimize away the object that "enforce" points at. -inline void dont_optimize_away(void *enforce) { - static volatile void *result = nullptr; - if (result) { - result = enforce; - } -} - -template -struct dont_optimize_away_struct { -}; - -inline bool first_dispatch_fired(bool did_fire = false) { - static bool fired = false; - if (did_fire) { - fired = true; - } - return fired; -} - -template -class object_registrator { -public: - inline object_registrator() { - Assert(!first_dispatch_fired()); - Creator(child_entry { - &is_parent::check, - &_index, - }); - } - static inline int &Index() { - return _index; - } - -private: - static int _index; - -}; - -template -int object_registrator::_index = -1; - -class object_base { -protected: - virtual ~object_base() = default; - -}; - -template -struct multi_index_collector; -template -struct override_key_collector_helper; -template -struct table_fill_entry_helper; -template -struct table_count_size; - -} // namespace virtual_methods - -// This should be a base class for every child object in your hierarchy. -// It registers this child in the root virtual_objects classes list. -// Also it holds its own index in the classes list that is used for fast -// invoking of methods from the virtual tables in different virtual_methods. -template -class virtual_object : public ParentObject { -protected: - virtual ~virtual_object() { - virtual_methods::dont_optimize_away(&_virtual_object_registrator); - } - -private: - using virtual_object_parent = ParentObject; - - friend struct virtual_methods::is_parent; - template - friend struct virtual_methods::multi_index_collector; - template - friend struct virtual_methods::override_key_collector_helper; - template - friend class virtual_object; - template - friend class virtual_method; - - static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) { - return ParentObject::virtual_object_register_child(entry); - } - - using virtual_object_registrator = virtual_methods::object_registrator; - static virtual_object_registrator _virtual_object_registrator; - using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; - - static inline int &virtual_object_child_index_static() { - return virtual_object_registrator::Index(); - } - int &virtual_object_child_index() override { - return virtual_object_child_index_static(); - } - -}; - -template -typename virtual_object::virtual_object_registrator virtual_object::_virtual_object_registrator = {}; - -// This should be a base class for the root of the whole hierarchy. -// It holds the table of all child classes in a list. -// This list is used by virtual_methods to generate virtual table. -template -class virtual_object : public virtual_methods::object_base { -protected: - virtual ~virtual_object() { - virtual_methods::dont_optimize_away(&_virtual_object_registrator); - } - -private: - using virtual_object_parent = void; - - friend struct virtual_methods::is_parent; - template - friend struct virtual_methods::table_count_size; - template - friend struct virtual_methods::multi_index_collector; - template - friend struct virtual_methods::override_key_collector_helper; - template - friend struct virtual_methods::table_fill_entry_helper; - template - friend class virtual_object; - template - friend class virtual_method; - - static inline virtual_methods::child_entries &virtual_object_get_child_entries() { - static virtual_methods::child_entries entries; - return entries; - } - - // Registers a new child class. - // After that on the next call to virtual_method::virtual_method_prepare_table() will - // generate a new virtual table for that virtual method. - static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) { - auto &entries = virtual_object_get_child_entries(); - for (auto i = entries.begin(), e = entries.end(); i != e; ++i) { - if (entry.check_is_parent(*i)) { - *entry.table_index = (i - entries.begin()); - i = entries.insert(i, entry); - for (++i, e = entries.end(); i != e; ++i) { - ++*(i->table_index); - } - return; - } - } - *entry.table_index = entries.size(); - entries.push_back(entry); - } - - using virtual_object_registrator = virtual_methods::object_registrator; - static virtual_object_registrator _virtual_object_registrator; - using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; - - static inline int &virtual_object_child_index_static() { - return virtual_object_registrator::Index(); - } - virtual int &virtual_object_child_index() { - return virtual_object_child_index_static(); - } - -}; - -template -typename virtual_object::virtual_object_registrator virtual_object::_virtual_object_registrator = {}; - -namespace virtual_methods { - -template -struct is_virtual_argument : public std::integral_constant::is_pointer::value - ? std::is_base_of::pointed_type>::value - : false> { -}; - -template -class multi_int_wrap { -public: - inline multi_int_wrap(int *indices) : _indices(indices) { - } - inline multi_int_wrap subindex() const { - static_assert(N > 0, "Wrong multi_int_wrap created!"); - return multi_int_wrap(_indices + 1); - } - inline int ¤t() const { - return *_indices; - } - -private: - int *_indices; - -}; - -template -class multi_int_wrap<0, Instance> { -public: - inline multi_int_wrap(int *indices) { - } - inline int current() const { - return 1; - } - -}; - -template -using multi_index_wrap = multi_int_wrap; -template -using multi_size_wrap = multi_int_wrap; - -template -struct multi_index_collector { - static constexpr int N = sizeof...(ConcreteArgs) + 1; - static inline void call(multi_index_wrap indices, ConcreteArg arg, ConcreteArgs... args) { - indices.current() = computeIndex(is_virtual_argument(), arg); - multi_index_collector::call(indices.subindex(), args...); - } - - static inline int computeIndex(std::integral_constant, ConcreteArg arg) { - return 0; - } - static inline int computeIndex(std::integral_constant, ConcreteArg arg) { - return arg->virtual_object_child_index(); - } - -}; - -template <> -struct multi_index_collector<> { - static inline void call(multi_index_wrap<0> indices) { - } -}; - -template -class override_key; - -template -class multi_int { -public: - inline multi_int_wrap data_wrap() { - return multi_int_wrap(_indices); - } - - template - static inline multi_int collect(ConcreteArgs... args) { - multi_int result; - multi_index_collector::call(result.data_wrap(), args...); - return result; - } - - inline void reset() { - memset(_indices, 0, sizeof(_indices)); - } - - inline int value(int index) const { - return _indices[index]; - } - - inline void copy(multi_int_wrap other) { - memcpy(_indices, &other.current(), sizeof(_indices)); - } - -private: - int _indices[N] = { 0 }; - friend class override_key; - -}; - -template -using multi_index = multi_int; -template -using multi_size = multi_int; - -template -class table_data_wrap { -public: - inline table_data_wrap(Call *data, multi_size_wrap size) : _data(data), _size(size) { - } - inline table_data_wrap operator[](int index) const { - return table_data_wrap(_data + index * _size.subindex().current(), _size.subindex()); - } - inline Call &operator[](multi_index_wrap index) const { - return (*this)[index.current()][index.subindex()]; - } - inline int size() const { - return count_size(std::integral_constant()); - } - -private: - template - inline int count_size(std::integral_constant) const { - return _size.current() / _size.subindex().current(); - } - inline int count_size(std::integral_constant) const { - return _size.current(); - } - - Call *_data; - multi_size_wrap _size; - -}; - -template -class table_data_wrap { -public: - inline table_data_wrap(Call *data, multi_size_wrap<0> size) : _data(data) { - } - inline Call &operator[](multi_index_wrap<0> index) const { - return *_data; - } - -private: - Call *_data; - -}; - -template -class table_data_wrap; - -template -struct table_count_size { - static constexpr int N = sizeof...(Args) + 1; - static inline void call(multi_size_wrap index) { - auto subindex = index.subindex(); - table_count_size::call(subindex); - index.current() = count(is_virtual_argument()) * subindex.current(); - } - - static inline int count(std::integral_constant) { - return 1; - } - static inline int count(std::integral_constant) { - return base::type_traits::pointed_type::virtual_object_get_child_entries().size(); - } - -}; - -template <> -struct table_count_size<> { - static inline void call(multi_size_wrap<0> index) { - } -}; - -template -class table_data { -public: - inline table_data_wrap data_wrap() { - return table_data_wrap(_data.data(), _size.data_wrap()); - } - - inline Call &operator[](multi_index index) { - int flat_index = 0; - for (int i = 0; i != N - 1; ++i) { - flat_index += _size.value(i + 1) * index.value(i); - } - flat_index += index.value(N - 1); - return _data[flat_index]; - } - - template - inline bool changed() { - if (!_data.empty()) { - return false; - } - - multi_size size; - table_count_size::call(size.data_wrap()); - _size = size; - _data.resize(_size.value(0), nullptr); - return true; - } - -private: - std::vector _data; - multi_size _size; - -}; - -template -class table_data { -public: - inline table_data_wrap data_wrap() { - return table_data_wrap(&_call, multi_size_wrap<0>(nullptr)); - } - - inline Call &operator[](multi_index<0> index) { - return _call; - } - - inline bool changed() const { - return false; - } - -private: - Call _call = nullptr; - -}; - -template -struct table_fill_entry_helper; - -template -struct table_fill_entry_helper { - static constexpr int N = sizeof...(Args) + 1; - - static inline bool call(table_data_wrap table, multi_index_wrap index, Call &fill) { - auto start = index.current(); - for (auto i = start, count = table.size(); i != count; ++i) { - auto foundGoodType = good(is_virtual_argument(), start, index.current()); - if (foundGoodType) { - index.current() = i; - if (table_fill_entry_helper::call(table[i], index.subindex(), fill)) { - return true; - } - } - } - index.current() = start; - return false; - } - - static inline bool good(std::integral_constant, int start, int current) { - return (start == current); - } - static inline bool good(std::integral_constant, int start, int current) { - using BaseObject = typename base::type_traits::pointed_type; - auto &entries = BaseObject::virtual_object_get_child_entries(); - return (start == current) || entries[start].check_is_parent(entries[current]); - } - -}; - -template -struct table_fill_entry_helper { - static inline bool call(table_data_wrap table, multi_index_wrap<0> index, Call &fill) { - if (auto overrideMethod = table[index]) { - fill = overrideMethod; - return true; - } - return false; - } -}; - -template -struct table_fill_entry; - -template -struct table_fill_entry { - using Call = ReturnType(*)(BaseMethod*, Args...); - static inline void call(table_data_wrap table, multi_index_wrap index, Call &fill) { - table_fill_entry_helper::call(table, index, fill); - } -}; - -template -inline void fill_entry(table_data_wrap table, multi_index_wrap index, Call &fill) { - return virtual_methods::table_fill_entry::call(table, index, fill); -} - -template -struct override_key_collector_helper; - -template -struct override_key_collector_helper { - static inline void call(int **indices) { - setValue(is_virtual_argument(), indices); - override_key_collector_helper::call(indices); - } - - static inline void setValue(std::integral_constant, int **indices) { - indices[M] = nullptr; - } - static inline void setValue(std::integral_constant, int **indices) { - using ConcreteObject = typename base::type_traits::pointed_type; - using IsParentCheckStruct = is_parent; - using IsParentCheckPointer = decltype(&IsParentCheckStruct::check); - using override_key_collector_dont_optimize_away = dont_optimize_away_struct; - override_key_collector_dont_optimize_away dont_optimize_away_object; - (void)dont_optimize_away_object; - - // Check that is_parent<> can be instantiated. - // So every ConcreteObject is a valid child of virtual_object<>. - dont_optimize_away(reinterpret_cast(&IsParentCheckStruct::check)); - indices[M] = &ConcreteObject::virtual_object_child_index_static(); - } - -}; - -template -struct override_key_collector_helper { - static inline void call(int **indices) { - } -}; - -template -struct override_key_collector; - -template -struct override_key_collector { - static inline void call(int **indices) { - override_key_collector_helper<0, ConcreteArgs...>::call(indices); - } -}; - -template -class override_key { -public: - inline multi_index value() const { - multi_index result; - for (int i = 0; i != N; ++i) { - auto pointer = _indices[i]; - result._indices[i] = (pointer ? *pointer : 0); - } - return result; - } - - friend inline bool operator<(const override_key &k1, const override_key &k2) { - for (int i = 0; i != N; ++i) { - auto pointer1 = k1._indices[i], pointer2 = k2._indices[i]; - if (pointer1 < pointer2) { - return true; - } else if (pointer1 > pointer2) { - return false; - } - } - return false; - } - - template - inline void collect() { - override_key_collector::call(_indices); - } - -private: - int *_indices[N]; - -}; - -template -struct static_cast_helper; - -template -struct static_cast_helper { - static inline ReturnType call(BaseMethod *context, Args ...args) { - return ConcreteMethod::call(context, static_cast(args)...); - } -}; - -} // namespace virtual_methods - -// This is a base class for all your virtual methods. -// It dispatches a call to one of the registered virtual_overrides -// or calls the fallback method of the BaseMethod class. -template -class virtual_method { - static constexpr int N = sizeof...(Args); - using virtual_method_call = ReturnType(*)(BaseMethod *context, Args... args); - -public: - inline ReturnType call(Args... args) { - auto context = static_cast(this); - auto index = virtual_methods::multi_index::collect(args...); - auto &table = virtual_method_prepare_table(); - auto &entry = table[index]; - if (!entry) { - virtual_methods::fill_entry(table.data_wrap(), index.data_wrap(), entry); - if (!entry) { - entry = &virtual_method::virtual_method_base_instance; - } - } - return (*entry)(context, args...); - } - -private: - // This map of methods contains only the original registered overrides. - using virtual_method_override_key = virtual_methods::override_key; - using virtual_method_override_map = std::map; - static inline virtual_method_override_map &virtual_method_get_override_map() { - static virtual_method_override_map override_map; - return override_map; - } - - // This method generates and returns a virtual table which holds a method - // for any child in the hierarchy or nullptr if none of the virtual_overrides fit. - using virtual_method_table_data = virtual_methods::table_data; - static inline virtual_method_table_data &virtual_method_get_table_data() { - static virtual_method_table_data virtual_table; - return virtual_table; - } - - static inline virtual_method_table_data &virtual_method_prepare_table() { - auto &virtual_table = virtual_method_get_table_data(); - if (virtual_table.template changed()) { - virtual_methods::first_dispatch_fired(true); - - // The class hierarchy has changed - we need to generate the virtual table once again. - // All other handlers will be placed if they're called. - for (auto &i : virtual_method_get_override_map()) { - virtual_table[i.first.value()] = i.second; - } - } - return virtual_table; - } - - static ReturnType virtual_method_base_instance(BaseMethod *context, Args... args) { - return BaseMethod::default_call(context, args...); - } - - template - static ReturnType virtual_method_override_instance(BaseMethod *context, Args... args) { - return virtual_methods::static_cast_helper::call(context, args...); - } - - template - static inline void virtual_method_register_override() { - auto call = &virtual_method_override_instance; - - virtual_methods::override_key key; - key.template collect(); - - virtual_method_get_override_map()[key] = call; - } - - template - friend class virtual_override; - -}; - -template -class virtual_override { -protected: - virtual ~virtual_override() { - virtual_methods::dont_optimize_away(&_virtual_override_registrator); - } - -private: - class virtual_override_registrator { - public: - inline virtual_override_registrator() { - Assert(!virtual_methods::first_dispatch_fired()); - BaseMethod::template virtual_method_register_override(); - } - - }; - static virtual_override_registrator _virtual_override_registrator; - using virtual_override_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; - -}; - -template -typename virtual_override::virtual_override_registrator virtual_override::_virtual_override_registrator = {}; - -} // namespace base diff --git a/Telegram/SourceFiles/base/weak_ptr.h b/Telegram/SourceFiles/base/weak_ptr.h deleted file mode 100644 index d396501c7..000000000 --- a/Telegram/SourceFiles/base/weak_ptr.h +++ /dev/null @@ -1,325 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace base { - -class has_weak_ptr; - -namespace details { - -struct alive_tracker { - explicit alive_tracker(const has_weak_ptr *value) : value(value) { - } - - std::atomic counter = 1; - std::atomic value; -}; - -inline alive_tracker *check_and_increment(alive_tracker *tracker) noexcept { - if (tracker) { - ++tracker->counter; - } - return tracker; -} - -inline void decrement(alive_tracker *tracker) noexcept { - if (tracker->counter.fetch_sub(1) == 0) { - delete tracker; - } -} - -} // namespace details - -template -class weak_ptr; - -class has_weak_ptr { -public: - has_weak_ptr() = default; - has_weak_ptr(const has_weak_ptr &other) noexcept { - } - has_weak_ptr(has_weak_ptr &&other) noexcept { - } - has_weak_ptr &operator=(const has_weak_ptr &other) noexcept { - return *this; - } - has_weak_ptr &operator=(has_weak_ptr &&other) noexcept { - return *this; - } - - ~has_weak_ptr() { - if (const auto alive = _alive.load()) { - alive->value.store(nullptr); - details::decrement(alive); - } - } - - friend inline void invalidate_weak_ptrs(has_weak_ptr *object) { - if (auto alive = object->_alive.load()) { - if (object->_alive.compare_exchange_strong(alive, nullptr)) { - alive->value.store(nullptr); - details::decrement(alive); - } - } - } - -private: - template - friend class weak_ptr; - - details::alive_tracker *incrementAliveTracker() const { - auto current = _alive.load(); - if (!current) { - auto alive = std::make_unique(this); - if (_alive.compare_exchange_strong(current, alive.get())) { - return alive.release(); - } - } - ++current->counter; - return current; - } - - mutable std::atomic _alive = nullptr; - -}; - -template -class weak_ptr { -public: - weak_ptr() = default; - weak_ptr(T *value) - : _alive(value ? value->incrementAliveTracker() : nullptr) { - } - weak_ptr(const std::unique_ptr &value) - : weak_ptr(value.get()) { - } - weak_ptr(const std::shared_ptr &value) - : weak_ptr(value.get()) { - } - weak_ptr(const std::weak_ptr &value) - : weak_ptr(value.lock().get()) { - } - weak_ptr(const weak_ptr &other) noexcept - : _alive(details::check_and_increment(other._alive)) { - } - weak_ptr(weak_ptr &&other) noexcept - : _alive(std::exchange(other._alive, nullptr)) { - } - template < - typename Other, - typename = std::enable_if_t< - std::is_base_of_v && !std::is_same_v>> - weak_ptr(const weak_ptr &other) noexcept - : _alive(details::check_and_increment(other._alive)) { - } - template < - typename Other, - typename = std::enable_if_t< - std::is_base_of_v && !std::is_same_v>> - weak_ptr(weak_ptr &&other) noexcept - : _alive(std::exchange(other._alive, nullptr)) { - } - - weak_ptr &operator=(T *value) { - reset(value); - return *this; - } - weak_ptr &operator=(const std::unique_ptr &value) { - reset(value.get()); - return *this; - } - weak_ptr &operator=(const std::shared_ptr &value) { - reset(value.get()); - return *this; - } - weak_ptr &operator=(const std::weak_ptr &value) { - reset(value.lock().get()); - return *this; - } - weak_ptr &operator=(const weak_ptr &other) noexcept { - if (_alive != other._alive) { - destroy(); - _alive = details::check_and_increment(other._alive); - } - return *this; - } - weak_ptr &operator=(weak_ptr &&other) noexcept { - if (_alive != other._alive) { - destroy(); - _alive = std::exchange(other._alive, nullptr); - } - return *this; - } - template < - typename Other, - typename = std::enable_if_t< - std::is_base_of_v && !std::is_same_v>> - weak_ptr &operator=(const weak_ptr &other) noexcept { - if (_alive != other._alive) { - destroy(); - _alive = details::check_and_increment(other._alive); - } - return *this; - } - template < - typename Other, - typename = std::enable_if_t< - std::is_base_of_v && !std::is_same_v>> - weak_ptr &operator=(weak_ptr &&other) noexcept { - if (_alive != other._alive) { - destroy(); - _alive = std::exchange(other._alive, nullptr); - } - return *this; - } - - ~weak_ptr() { - destroy(); - } - - T *get() const noexcept { - const auto strong = _alive ? _alive->value.load() : nullptr; - if constexpr (std::is_const_v) { - return static_cast(strong); - } else { - return const_cast(static_cast(strong)); - } - } - explicit operator bool() const noexcept { - return (_alive && _alive->value); - } - T &operator*() const noexcept { - return *get(); - } - T *operator->() const noexcept { - return get(); - } - - void reset(T *value = nullptr) { - if (get() != value) { - destroy(); - _alive = value ? value->incrementAliveTracker() : nullptr; - } - } - -private: - void destroy() noexcept { - if (_alive) { - details::decrement(_alive); - } - } - - details::alive_tracker *_alive = nullptr; - - template - friend class weak_ptr; - -}; - -template -inline bool operator==(const weak_ptr &pointer, std::nullptr_t) noexcept { - return (pointer.get() == nullptr); -} - -template -inline bool operator==(std::nullptr_t, const weak_ptr &pointer) noexcept { - return (pointer == nullptr); -} - -template -inline bool operator!=(const weak_ptr &pointer, std::nullptr_t) noexcept { - return !(pointer == nullptr); -} - -template -inline bool operator!=(std::nullptr_t, const weak_ptr &pointer) noexcept { - return !(pointer == nullptr); -} - -template < - typename T, - typename = std::enable_if_t>> -weak_ptr make_weak(T *value) { - return value; -} - -template < - typename T, - typename = std::enable_if_t>> -weak_ptr make_weak(const std::unique_ptr &value) { - return value; -} - -template < - typename T, - typename = std::enable_if_t>> -weak_ptr make_weak(const std::shared_ptr &value) { - return value; -} - -template < - typename T, - typename = std::enable_if_t>> -weak_ptr make_weak(const std::weak_ptr &value) { - return value; -} - -} // namespace base - -namespace crl { - -template -struct guard_traits; - -template -struct guard_traits, void> { - static base::weak_ptr create(const base::weak_ptr &value) { - return value; - } - static base::weak_ptr create(base::weak_ptr &&value) { - return std::move(value); - } - static bool check(const base::weak_ptr &guard) { - return guard.get() != nullptr; - } - -}; - -template -struct guard_traits< - T*, - std::enable_if_t< - std::is_base_of_v>>> { - static base::weak_ptr create(T *value) { - return value; - } - static bool check(const base::weak_ptr &guard) { - return guard.get() != nullptr; - } - -}; - -template -struct guard_traits< - gsl::not_null, - std::enable_if_t< - std::is_base_of_v>>> { - static base::weak_ptr create(gsl::not_null value) { - return value.get(); - } - static bool check(const base::weak_ptr &guard) { - return guard.get() != nullptr; - } - -}; - -} // namespace crl diff --git a/Telegram/SourceFiles/base/zlib_help.h b/Telegram/SourceFiles/base/zlib_help.h deleted file mode 100644 index 8c0147cdc..000000000 --- a/Telegram/SourceFiles/base/zlib_help.h +++ /dev/null @@ -1,410 +0,0 @@ -/* -WARNING! All changes made in this file will be lost! -Created from 'colors.palette' by 'codegen_style' - -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 -*/ -#pragma once - -#include "zip.h" -#include "unzip.h" -#include "logs.h" - -#ifdef small -#undef small -#endif // small - -namespace zlib { -namespace internal { - -class InMemoryFile { -public: - InMemoryFile(const QByteArray &data = QByteArray()) : _data(data) { - } - - zlib_filefunc_def funcs() { - zlib_filefunc_def result; - result.opaque = this; - result.zopen_file = &InMemoryFile::Open; - result.zerror_file = &InMemoryFile::Error; - result.zread_file = &InMemoryFile::Read; - result.zwrite_file = &InMemoryFile::Write; - result.zclose_file = &InMemoryFile::Close; - result.zseek_file = &InMemoryFile::Seek; - result.ztell_file = &InMemoryFile::Tell; - return result; - } - - int error() const { - return _error; - } - - QByteArray result() const { - return _data; - } - -private: - voidpf open(const char *filename, int mode) { - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - if (mode & ZLIB_FILEFUNC_MODE_CREATE) { - _data.clear(); - } - _position = _data.size(); - _data.reserve(2 * 1024 * 1024); - } else if (mode & ZLIB_FILEFUNC_MODE_READ) { - _position = 0; - } - _error = 0; - return this; - } - - uLong read(voidpf stream, void* buf, uLong size) { - uLong toRead = 0; - if (!_error) { - if (_data.size() > int(_position)) { - toRead = qMin(size, uLong(_data.size() - _position)); - memcpy(buf, _data.constData() + _position, toRead); - _position += toRead; - } - if (toRead < size) { - _error = -1; - } - } - return toRead; - } - - uLong write(voidpf stream, const void* buf, uLong size) { - if (_data.size() < int(_position + size)) { - _data.resize(_position + size); - } - memcpy(_data.data() + _position, buf, size); - _position += size; - return size; - } - - int close(voidpf stream) { - auto result = _error; - _position = 0; - _error = 0; - return result; - } - - int error(voidpf stream) const { - return _error; - } - - long tell(voidpf stream) const { - return _position; - } - - long seek(voidpf stream, uLong offset, int origin) { - if (!_error) { - switch (origin) { - case ZLIB_FILEFUNC_SEEK_SET: _position = offset; break; - case ZLIB_FILEFUNC_SEEK_CUR: _position += offset; break; - case ZLIB_FILEFUNC_SEEK_END: _position = _data.size() + offset; break; - } - if (int(_position) > _data.size()) { - _error = -1; - } - } - return _error; - } - - static voidpf Open(voidpf opaque, const char* filename, int mode) { - return static_cast(opaque)->open(filename, mode); - } - - static uLong Read(voidpf opaque, voidpf stream, void* buf, uLong size) { - return static_cast(opaque)->read(stream, buf, size); - } - - static uLong Write(voidpf opaque, voidpf stream, const void* buf, uLong size) { - return static_cast(opaque)->write(stream, buf, size); - } - - static int Close(voidpf opaque, voidpf stream) { - return static_cast(opaque)->close(stream); - } - - static int Error(voidpf opaque, voidpf stream) { - return static_cast(opaque)->error(stream); - } - - static long Tell(voidpf opaque, voidpf stream) { - return static_cast(opaque)->tell(stream); - } - - static long Seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - return static_cast(opaque)->seek(stream, offset, origin); - } - - uLong _position = 0; - int _error = 0; - QByteArray _data; - -}; - -} // namespace internal - -constexpr int kCaseSensitive = 1; -constexpr int kCaseInsensitive = 2; - -class FileToRead { -public: - FileToRead(const QByteArray &content) : _data(content) { - auto funcs = _data.funcs(); - if (!(_handle = unzOpen2(nullptr, &funcs))) { - _error = -1; - } - } - - int getGlobalInfo(unz_global_info *pglobal_info) { - if (error() == UNZ_OK) { - _error = _handle ? unzGetGlobalInfo(_handle, pglobal_info) : -1; - } - return _error; - } - - int locateFile(const char *szFileName, int iCaseSensitivity) { - if (error() == UNZ_OK) { - _error = _handle ? unzLocateFile(_handle, szFileName, iCaseSensitivity) : -1; - } - return error(); - } - - int goToFirstFile() { - if (error() == UNZ_OK) { - _error = _handle ? unzGoToFirstFile(_handle) : -1; - } - return error(); - } - - int goToNextFile() { - if (error() == UNZ_OK) { - _error = _handle ? unzGoToNextFile(_handle) : -1; - } - return error(); - } - - int getCurrentFileInfo( - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize) { - if (error() == UNZ_OK) { - _error = _handle ? unzGetCurrentFileInfo( - _handle, - pfile_info, - szFileName, - fileNameBufferSize, - extraField, - extraFieldBufferSize, - szComment, - commentBufferSize - ) : -1; - } - return error(); - } - - QString getCurrentFileName() { - unz_file_info info = { 0 }; - constexpr auto kMaxName = 128; - char name[kMaxName + 1] = { 0 }; - const auto result = getCurrentFileInfo( - &info, - name, - kMaxName, - nullptr, - 0, - nullptr, - 0); - return (result == UNZ_OK) ? QString::fromUtf8(name) : QString(); - } - - int openCurrentFile() { - if (error() == UNZ_OK) { - _error = _handle ? unzOpenCurrentFile(_handle) : -1; - } - return error(); - } - - int readCurrentFile(voidp buf, unsigned len) { - if (error() == UNZ_OK) { - auto result = _handle ? unzReadCurrentFile(_handle, buf, len) : -1; - if (result >= 0) { - return result; - } else { - _error = result; - } - } - return error(); - } - - int closeCurrentFile() { - if (error() == UNZ_OK) { - _error = _handle ? unzCloseCurrentFile(_handle) : -1; - } - return error(); - } - - QByteArray readCurrentFileContent(int fileSizeLimit) { - unz_file_info fileInfo = { 0 }; - if (getCurrentFileInfo(&fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK) { - LOG(("Error: could not get current file info in a zip file.")); - return QByteArray(); - } - - auto size = fileInfo.uncompressed_size; - if (size > static_cast(fileSizeLimit)) { - if (_error == UNZ_OK) _error = -1; - LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size)); - return QByteArray(); - } - if (openCurrentFile() != UNZ_OK) { - LOG(("Error: could not open current file in a zip file.")); - return QByteArray(); - } - - QByteArray result; - result.resize(size); - - auto couldRead = readCurrentFile(result.data(), size); - if (couldRead != static_cast(size)) { - LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead)); - return QByteArray(); - } - - if (closeCurrentFile() != UNZ_OK) { - LOG(("Error: could not close current file in a zip file.")); - return QByteArray(); - } - - return result; - } - - QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) { - if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) { - LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName)); - return QByteArray(); - } - return readCurrentFileContent(fileSizeLimit); - } - - void close() { - if (_handle && unzClose(_handle) != UNZ_OK && _error == UNZ_OK) { - _error = -1; - } - _handle = nullptr; - } - - int error() const { - if (auto dataError = _data.error()) { - return dataError; - } - return _error; - } - - void clearError() { - _error = UNZ_OK; - } - - ~FileToRead() { - close(); - } - -private: - internal::InMemoryFile _data; - unzFile _handle = nullptr; - int _error = 0; - -}; - -class FileToWrite { -public: - FileToWrite() { - auto funcs = _data.funcs(); - if (!(_handle = zipOpen2(nullptr, APPEND_STATUS_CREATE, nullptr, &funcs))) { - _error = -1; - } - } - - int openNewFile( - const char *filename, - const zip_fileinfo *zipfi, - const void *extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level - ) { - if (error() == ZIP_OK) { - _error = _handle ? zipOpenNewFileInZip( - _handle, - filename, - zipfi, - extrafield_local, - size_extrafield_local, - extrafield_global, - size_extrafield_global, - comment, - method, - level - ) : -1; - } - return error(); - } - - int writeInFile(const void* buf, unsigned len) { - if (error() == ZIP_OK) { - _error = _handle ? zipWriteInFileInZip(_handle, buf, len) : -1; - } - return error(); - } - - int closeFile() { - if (error() == ZIP_OK) { - _error = _handle ? zipCloseFileInZip(_handle) : -1; - } - return error(); - } - - void close() { - if (_handle && zipClose(_handle, nullptr) != ZIP_OK && _error == ZIP_OK) { - _error = -1; - } - _handle = nullptr; - } - - int error() const { - if (auto dataError = _data.error()) { - return dataError; - } - return _error; - } - - QByteArray result() const { - return _data.result(); - } - - ~FileToWrite() { - close(); - } - -private: - internal::InMemoryFile _data; - zipFile _handle = nullptr; - int _error = 0; - -}; - -} // namespace zlib diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index a2f474fd0..9bf5bbad0 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include -#include #include "base/basic_types.h" #include "base/match_method.h" #include "base/flags.h" diff --git a/Telegram/SourceFiles/rpl/after_next.h b/Telegram/SourceFiles/rpl/after_next.h deleted file mode 100644 index 12d20ed90..000000000 --- a/Telegram/SourceFiles/rpl/after_next.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -template -class after_next_helper { -public: - template - after_next_helper(OtherSideEffect &&method) - : _method(std::forward(method)) { - } - - template - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - method = std::move(_method) - ](const auto &consumer) mutable { - return std::move(initial).start( - [method = std::move(method), consumer](auto &&value) { - auto copy = method; - consumer.put_next_copy(value); - details::callable_invoke( - std::move(copy), - std::forward(value)); - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -private: - SideEffect _method; - -}; - -} // namespace details - -template -inline auto after_next(SideEffect &&method) --> details::after_next_helper> { - return details::after_next_helper>( - std::forward(method)); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/before_next.h b/Telegram/SourceFiles/rpl/before_next.h deleted file mode 100644 index 01261f1ee..000000000 --- a/Telegram/SourceFiles/rpl/before_next.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace rpl { - -template -inline auto before_next(SideEffect &&method) { - return filter([method = std::forward(method)]( - const auto &value) { - method(value); - return true; - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/combine.h b/Telegram/SourceFiles/rpl/combine.h deleted file mode 100644 index f7a1b018d..000000000 --- a/Telegram/SourceFiles/rpl/combine.h +++ /dev/null @@ -1,349 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/optional.h" -#include "base/variant.h" -#include -#include -#include -#include -#include -#include - -namespace rpl { -namespace details { - -template -struct combine_state { - combine_state() : accumulated(std::tuple...>()) { - } - std::optional...>> accumulated; - std::optional> latest; - int invalid = sizeof...(Values); - int working = sizeof...(Values); -}; - -template -inline std::tuple combine_make_first( - std::tuple...> &&accumulated, - std::index_sequence) { - return std::make_tuple(std::move(*std::get(accumulated))...); -} - -template -class combine_subscribe_one { -public: - combine_subscribe_one( - const consumer_type &consumer, - combine_state *state) - : _consumer(consumer) - , _state(state) { - } - - template - void subscribe(producer &&producer) { - _consumer.add_lifetime(std::move(producer).start( - [consumer = _consumer, state = _state](Value &&value) { - if (!state->accumulated) { - std::get(*state->latest) = std::move(value); - consumer.put_next_copy(*state->latest); - } else { - auto &accumulated = std::get( - *state->accumulated); - if (accumulated) { - accumulated = std::move(value); - } else { - accumulated = std::move(value); - if (!--state->invalid) { - constexpr auto kArity = sizeof...(Values); - state->latest = combine_make_first( - std::move(*state->accumulated), - std::make_index_sequence()); - state->accumulated = std::nullopt; - consumer.put_next_copy(*state->latest); - } - } - } - }, [consumer = _consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer = _consumer, state = _state] { - if (!--state->working) { - consumer.put_done(); - } - })); - } - -private: - const consumer_type &_consumer; - combine_state *_state = nullptr; - -}; - -template < - typename consumer_type, - typename ...Values, - typename ...Errors, - typename ...Generators, - std::size_t ...I> -inline void combine_subscribe( - const consumer_type &consumer, - combine_state *state, - std::index_sequence, - std::tuple...> &&saved) { - auto consume = { ( - combine_subscribe_one( - consumer, - state - ).subscribe(std::get(std::move(saved))), 0)... }; - (void)consume; -} - -template -class combine_implementation_helper; - -template -combine_implementation_helper...> -make_combine_implementation_helper(Producers &&...producers) { - return combine_implementation_helper...>( - std::forward(producers)...); -} - -template < - typename ...Values, - typename ...Errors, - typename ...Generators> -class combine_implementation_helper...> { -public: - using CombinedValue = std::tuple; - using CombinedError = base::normalized_variant_t; - - combine_implementation_helper( - producer &&...producers) - : _saved(std::make_tuple(std::move(producers)...)) { - } - - template - lifetime operator()(const consumer &consumer) { - auto state = consumer.template make_state< - combine_state>(); - constexpr auto kArity = sizeof...(Values); - combine_subscribe( - consumer, - state, - std::make_index_sequence(), - std::move(_saved)); - - return lifetime(); - } - -private: - std::tuple...> _saved; - -}; - -template < - typename ...Values, - typename ...Errors, - typename ...Generators> -inline auto combine_implementation( - producer &&...producers) { - using CombinedValue = std::tuple; - using CombinedError = base::normalized_variant_t; - - return make_producer( - make_combine_implementation_helper(std::move(producers)...)); -} - -template -struct combine_just_producers : std::false_type { -}; - -template -constexpr bool combine_just_producers_v - = combine_just_producers::value; - -template < - typename ...Values, - typename ...Errors, - typename ...Generators> -struct combine_just_producers< - producer...> - : std::true_type { -}; - -template -struct combine_just_producers_list - : type_list::extract_to_t { -}; - -template -struct combine_result_type; - -template -using combine_result_type_t - = typename combine_result_type::type; - -template < - typename ...Values, - typename ...Errors, - typename ...Generators> -struct combine_result_type...> { - using type = std::tuple; -}; - -template -struct combine_result_type_list - : type_list::extract_to_t { -}; - -template -using combine_result_type_list_t - = typename combine_result_type_list::type; - -template -using combine_producers_no_mapper_t - = type_list::chop_last_t; - -template -constexpr bool combine_is_good_mapper(std::true_type) { - return is_callable_v< - type_list::last_t, - combine_result_type_list_t< - combine_producers_no_mapper_t - >>; -} - -template -constexpr bool combine_is_good_mapper(std::false_type) { - return false; -} - -template -struct combine_producers_with_mapper_list : std::bool_constant< - combine_is_good_mapper( - combine_just_producers_list< - combine_producers_no_mapper_t - >())> { -}; - -template -struct combine_producers_with_mapper - : combine_producers_with_mapper_list> { -}; - -template -constexpr bool combine_producers_with_mapper_v - = combine_producers_with_mapper::value; - -template -inline decltype(auto) combine_call( - std::index_sequence, - Producers &&...producers) { - return combine_implementation( - argument_mapper::call(std::move(producers)...)...); -} - -} // namespace details - -template < - typename ...Args, - typename = std::enable_if_t< - details::combine_just_producers_v - || details::combine_producers_with_mapper_v>> -inline decltype(auto) combine(Args &&...args) { - if constexpr (details::combine_just_producers_v) { - return details::combine_implementation(std::move(args)...); - } else if constexpr (details::combine_producers_with_mapper_v) { - constexpr auto kProducersCount = sizeof...(Args) - 1; - return details::combine_call( - std::make_index_sequence(), - std::forward(args)...) - | map(details::argument_mapper::call( - std::forward(args)...)); - } else { - static_assert(false_(args...), "Bad combine() call."); - } -} - -namespace details { - -template -struct combine_vector_state { - std::vector> accumulated; - std::vector latest; - int invalid = 0; - int working = 0; -}; - -} // namespace details - -template -inline auto combine( - std::vector> &&producers) { - using state_type = details::combine_vector_state; - return make_producer, Error>([ - producers = std::move(producers) - ](const auto &consumer) mutable { - auto count = producers.size(); - auto state = consumer.template make_state(); - state->accumulated.resize(count); - state->invalid = count; - state->working = count; - for (auto index = 0; index != count; ++index) { - auto &producer = producers[index]; - consumer.add_lifetime(std::move(producer).start( - [consumer, state, index](Value &&value) { - if (state->accumulated.empty()) { - state->latest[index] = std::move(value); - consumer.put_next_copy(state->latest); - } else if (state->accumulated[index]) { - state->accumulated[index] = std::move(value); - } else { - state->accumulated[index] = std::move(value); - if (!--state->invalid) { - state->latest.reserve( - state->accumulated.size()); - for (auto &&value : state->accumulated) { - state->latest.push_back( - std::move(*value)); - } - details::take(state->accumulated); - consumer.put_next_copy(state->latest); - } - } - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer, state] { - if (!--state->working) { - consumer.put_done(); - } - })); - } - if (!count) { - consumer.put_done(); - } - return lifetime(); - }); -} - -template < - typename Value, - typename Error, - typename Generator, - typename Mapper> -inline auto combine( - std::vector> &&producers, - Mapper &&mapper) { - return combine(std::move(producers)) - | map(std::forward(mapper)); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/combine_previous.h b/Telegram/SourceFiles/rpl/combine_previous.h deleted file mode 100644 index d4c448a14..000000000 --- a/Telegram/SourceFiles/rpl/combine_previous.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include "base/optional.h" - -namespace rpl { -namespace details { - -class combine_previous_helper { -public: - template - auto operator()( - producer &&initial) const { - return make_producer, Error>([ - initial = std::move(initial) - ](const auto &consumer) mutable { - auto previous = consumer.template make_state< - std::optional - >(); - return std::move(initial).start( - [consumer, previous](auto &&value) { - if (auto &exists = *previous) { - auto &existing = *exists; - auto next = std::make_tuple( - std::move(existing), - value); - consumer.put_next(std::move(next)); - existing = std::forward( - value); - } else { - *previous = std::forward( - value); - } - }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -}; - -template -class combine_previous_with_default_helper { -public: - template - combine_previous_with_default_helper(OtherValue &&value) - : _value(std::forward(value)) { - } - - template - auto operator()(producer &&initial) { - return make_producer, Error>([ - initial = std::move(initial), - value = Value(std::move(_value)) - ](const auto &consumer) mutable { - auto previous = consumer.template make_state( - std::move(value)); - return std::move(initial).start( - [consumer, previous](auto &&value) { - auto &existing = *previous; - auto next = std::make_tuple( - std::move(existing), - value); - consumer.put_next(std::move(next)); - existing = std::forward(value); - }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -private: - DefaultValue _value; - -}; - -template -combine_previous_with_default_helper> -combine_previous_with_default(DefaultValue &&value) { - return { std::forward(value) }; -} - -} // namespace details - -inline auto combine_previous() --> details::combine_previous_helper { - return details::combine_previous_helper(); -} - -template -inline auto combine_previous(DefaultValue &&value) { - return details::combine_previous_with_default( - std::forward(value)); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/complete.h b/Telegram/SourceFiles/rpl/complete.h deleted file mode 100644 index 703b4a175..000000000 --- a/Telegram/SourceFiles/rpl/complete.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { - -template -inline auto complete() { - return make_producer([](const auto &consumer) { - consumer.put_done(); - return lifetime(); - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/conditional.h b/Telegram/SourceFiles/rpl/conditional.h deleted file mode 100644 index 15380a14f..000000000 --- a/Telegram/SourceFiles/rpl/conditional.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/optional.h" -#include "base/variant.h" -#include -#include - -namespace rpl { - -template < - typename Value, - typename Error, - typename GeneratorTest, - typename GeneratorA, - typename GeneratorB> -inline auto conditional( - rpl::producer &&test, - rpl::producer &&a, - rpl::producer &&b) { - return rpl::combine( - std::move(test), - std::move(a), - std::move(b) - ) | rpl::map([](bool test, Value &&a, Value &&b) { - return test ? std::move(a) : std::move(b); - }); - //struct conditional_state { - // std::optional a; - // std::optional b; - // char state = -1; - // int working = 3; - //}; - //return rpl::make_producer([ - // test = std::move(test), - // a = std::move(a), - // b = std::move(b) - //](const auto &consumer) mutable { - // auto result = lifetime(); - // const auto state = result.make_state(); - // result.add(std::move(test).start()) - // return result; - //}); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/consumer.h b/Telegram/SourceFiles/rpl/consumer.h deleted file mode 100644 index 09603116e..000000000 --- a/Telegram/SourceFiles/rpl/consumer.h +++ /dev/null @@ -1,636 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include - -// GCC 7.2 can't handle not type-erased consumers. -// It eats up 4GB RAM + 16GB swap on the unittest and dies. -// Clang and Visual C++ both handle it without such problems. -#if defined _DEBUG || defined COMPILER_GCC -#define RPL_CONSUMER_TYPE_ERASED_ALWAYS -#endif // _DEBUG || COMPILER_GCC - -namespace rpl { -namespace details { - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -class consumer_handlers; - -template -class type_erased_handlers { -public: - virtual bool put_next(Value &&value) = 0; - virtual bool put_next_copy(const Value &value) = 0; - virtual void put_error(Error &&error) = 0; - virtual void put_error_copy(const Error &error) = 0; - virtual void put_done() = 0; - - bool add_lifetime(lifetime &&lifetime); - - template - Type *make_state(Args&& ...args); - - void terminate(); - - virtual ~type_erased_handlers() = default; - -protected: - lifetime _lifetime; - bool _terminated = false; - -}; - -template -struct is_type_erased_handlers - : std::false_type { -}; - -template -struct is_type_erased_handlers> - : std::true_type { -}; - -template -constexpr bool is_type_erased_handlers_v - = is_type_erased_handlers::value; - -template -class consumer_handlers final - : public type_erased_handlers { -public: - template < - typename OnNextOther, - typename OnErrorOther, - typename OnDoneOther> - consumer_handlers( - OnNextOther &&next, - OnErrorOther &&error, - OnDoneOther &&done) - : _next(std::forward(next)) - , _error(std::forward(error)) - , _done(std::forward(done)) { - } - - bool put_next(Value &&value) final override; - bool put_next_copy(const Value &value) final override; - void put_error(Error &&error) final override; - void put_error_copy(const Error &error) final override; - void put_done() final override; - -private: - OnNext _next; - OnError _error; - OnDone _done; - -}; - -template -inline bool type_erased_handlers::add_lifetime( - lifetime &&lifetime) { - if (_terminated) { - lifetime.destroy(); - return false; - } - _lifetime.add(std::move(lifetime)); - return true; -} - -template -template -inline Type *type_erased_handlers::make_state( - Args&& ...args) { - if (_terminated) { - return nullptr; - } - return _lifetime.make_state(std::forward(args)...); -} - -template -inline void type_erased_handlers::terminate() { - if (!_terminated) { - _terminated = true; - _lifetime.destroy(); - } -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -bool consumer_handlers< - Value, - Error, - OnNext, - OnError, - OnDone ->::put_next(Value &&value) { - if (this->_terminated) { - return false; - } - auto handler = this->_next; - details::callable_invoke(std::move(handler), std::move(value)); - return true; -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -bool consumer_handlers< - Value, - Error, - OnNext, - OnError, - OnDone ->::put_next_copy(const Value &value) { - if (this->_terminated) { - return false; - } - auto handler = this->_next; - details::const_ref_call_invoke(std::move(handler), value); - return true; -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -void consumer_handlers< - Value, - Error, - OnNext, - OnError, - OnDone ->::put_error(Error &&error) { - if (!this->_terminated) { - details::callable_invoke( - std::move(this->_error), - std::move(error)); - this->terminate(); - } -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -void consumer_handlers< - Value, - Error, - OnNext, - OnError, - OnDone ->::put_error_copy(const Error &error) { - if (!this->_terminated) { - details::const_ref_call_invoke( - std::move(this->_error), - error); - this->terminate(); - } -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone> -void consumer_handlers< - Value, - Error, - OnNext, - OnError, - OnDone ->::put_done() { - if (!this->_terminated) { - std::move(this->_done)(); - this->terminate(); - } -} - -} // namespace details - -struct no_value { - no_value() = delete; -}; - -struct no_error { - no_error() = delete; -}; - -struct empty_value { -}; - -struct empty_error { -}; - -template < - typename Value = empty_value, - typename Error = no_error, - typename Handlers = details::type_erased_handlers> -class consumer; - -namespace details { - -template -class consumer_base { - static constexpr bool is_type_erased - = is_type_erased_handlers_v; - -public: - template < - typename OnNext, - typename OnError, - typename OnDone> - consumer_base( - OnNext &&next, - OnError &&error, - OnDone &&done); - - bool put_next(Value &&value) const; - bool put_next_copy(const Value &value) const; - bool put_next_forward(Value &&value) const { - return put_next(std::move(value)); - } - bool put_next_forward(const Value &value) const { - return put_next_copy(value); - } - void put_error(Error &&error) const; - void put_error_copy(const Error &error) const; - void put_error_forward(Error &&error) const { - return put_error(std::move(error)); - } - void put_error_forward(const Error &error) const { - return put_error_copy(error); - } - void put_done() const; - - bool add_lifetime(lifetime &&lifetime) const; - - template - Type *make_state(Args&& ...args) const; - - void terminate() const; - auto terminator() const { - return [self = *this] { - self.terminate(); - }; - } - - const details::type_erased_handlers *comparable() const { - return _handlers.get(); - } - -private: - template < - typename OtherHandlers, - typename = std::enable_if_t< - std::is_base_of_v>> - consumer_base(const std::shared_ptr &handlers) - : _handlers(handlers) { - } - - template < - typename OtherHandlers, - typename = std::enable_if_t< - std::is_base_of_v>> - consumer_base(std::shared_ptr &&handlers) - : _handlers(std::move(handlers)) { - } - - mutable std::shared_ptr _handlers; - - bool handlers_put_next(Value &&value) const { - if constexpr (is_type_erased) { - return _handlers->put_next(std::move(value)); - } else { - return _handlers->Handlers::put_next(std::move(value)); - } - } - bool handlers_put_next_copy(const Value &value) const { - if constexpr (is_type_erased) { - return _handlers->put_next_copy(value); - } else { - return _handlers->Handlers::put_next_copy(value); - } - } - std::shared_ptr take_handlers() const { - return std::exchange(_handlers, nullptr); - } - - template < - typename OtherValue, - typename OtherError, - typename OtherHandlers> - friend class ::rpl::consumer; - -}; - -template -template -inline consumer_base::consumer_base( - OnNext &&next, - OnError &&error, - OnDone &&done) -: _handlers(std::make_shared, - std::decay_t, - std::decay_t>>( - std::forward(next), - std::forward(error), - std::forward(done))) { -} - -template -inline bool consumer_base::put_next( - Value &&value) const { - if (_handlers) { - if (handlers_put_next(std::move(value))) { - return true; - } - _handlers = nullptr; - } - return false; -} - -template -inline bool consumer_base::put_next_copy( - const Value &value) const { - if (_handlers) { - if (handlers_put_next_copy(value)) { - return true; - } - _handlers = nullptr; - } - return false; -} - -template -inline void consumer_base::put_error( - Error &&error) const { - if (_handlers) { - if constexpr (is_type_erased) { - take_handlers()->put_error(std::move(error)); - } else { - take_handlers()->Handlers::put_error(std::move(error)); - } - } -} - -template -inline void consumer_base::put_error_copy( - const Error &error) const { - if (_handlers) { - if constexpr (is_type_erased) { - take_handlers()->put_error_copy(error); - } else { - take_handlers()->Handlers::put_error_copy(error); - } - } -} - -template -inline void consumer_base::put_done() const { - if (_handlers) { - if constexpr (is_type_erased) { - take_handlers()->put_done(); - } else { - take_handlers()->Handlers::put_done(); - } - } -} - -template -inline bool consumer_base::add_lifetime( - lifetime &&lifetime) const { - if (!_handlers) { - lifetime.destroy(); - return false; - } - if (_handlers->add_lifetime(std::move(lifetime))) { - return true; - } - _handlers = nullptr; - return false; -} - -template -template -inline Type *consumer_base::make_state( - Args&& ...args) const { - if (!_handlers) { - return nullptr; - } - if (auto result = _handlers->template make_state( - std::forward(args)...)) { - return result; - } - _handlers = nullptr; - return nullptr; -} - -template -inline void consumer_base::terminate() const { - if (_handlers) { - std::exchange(_handlers, nullptr)->terminate(); - } -} - -template -using consumer_base_type_erased = consumer_base< - Value, - Error, - details::type_erased_handlers>; - -template -constexpr bool is_specific_handlers_v = !std::is_same_v< - details::type_erased_handlers, - Handlers -> && std::is_base_of_v< - details::type_erased_handlers, - Handlers ->; - -} // namespace details - -template -class consumer final -: public details::consumer_base { - using parent_type = details::consumer_base< - Value, - Error, - Handlers>; - -public: - using parent_type::parent_type; - -}; - -template -class consumer> final -: public details::consumer_base_type_erased { - using parent_type = details::consumer_base_type_erased< - Value, - Error>; - -public: - using parent_type::parent_type; - - template < - typename Handlers, - typename = std::enable_if_t< - details::is_specific_handlers_v>> - consumer(const details::consumer_base &other) - : parent_type(other._handlers) { - } - - template < - typename Handlers, - typename = std::enable_if_t< - details::is_specific_handlers_v>> - consumer(details::consumer_base &&other) - : parent_type(std::move(other._handlers)) { - } - - template < - typename Handlers, - typename = std::enable_if_t< - details::is_specific_handlers_v>> - consumer &operator=( - const details::consumer_base &other) { - this->_handlers = other._handlers; - return *this; - } - - template < - typename Handlers, - typename = std::enable_if_t< - details::is_specific_handlers_v>> - consumer &operator=( - details::consumer_base &&other) { - this->_handlers = std::move(other._handlers); - return *this; - } - -}; - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator==( - const consumer &a, - const consumer &b) { - return a.comparable() == b.comparable(); -} - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator<( - const consumer &a, - const consumer &b) { - return a.comparable() < b.comparable(); -} - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator!=( - const consumer &a, - const consumer &b) { - return !(a == b); -} - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator>( - const consumer &a, - const consumer &b) { - return b < a; -} - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator<=( - const consumer &a, - const consumer &b) { - return !(b < a); -} - -template < - typename Value, - typename Error, - typename Handlers1, - typename Handlers2> -inline bool operator>=( - const consumer &a, - const consumer &b) { - return !(a < b); -} - -template < - typename Value, - typename Error, - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - details::is_callable_v && - details::is_callable_v && - details::is_callable_v>> -#ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS -inline consumer make_consumer( -#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS -inline auto make_consumer( -#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS - OnNext &&next, - OnError &&error, - OnDone &&done) { - return consumer, - std::decay_t, - std::decay_t>>( - std::forward(next), - std::forward(error), - std::forward(done)); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/deferred.h b/Telegram/SourceFiles/rpl/deferred.h deleted file mode 100644 index a8848e749..000000000 --- a/Telegram/SourceFiles/rpl/deferred.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { - -template < - typename Creator, - typename Value = typename decltype(std::declval()())::value_type, - typename Error = typename decltype(std::declval()())::error_type> -inline auto deferred(Creator &&creator) { - return make_producer([ - creator = std::forward(creator) - ](const auto &consumer) mutable { - return std::move(creator)().start_existing(consumer); - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/details/callable.h b/Telegram/SourceFiles/rpl/details/callable.h deleted file mode 100644 index 3598e0318..000000000 --- a/Telegram/SourceFiles/rpl/details/callable.h +++ /dev/null @@ -1,145 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/build_config.h" -#include - -namespace rpl { -namespace details { - -template -const Arg &const_ref_val() noexcept; - -template -Arg &lvalue_ref_val() noexcept; - -using false_t = char; -struct true_t { - false_t data[2]; -}; -static_assert(sizeof(false_t) != sizeof(true_t), "I can't work :("); - -template < - typename Method, - typename ...Args, - typename = decltype(std::declval()( - std::declval()...))> -true_t test_callable_plain(Method &&, Args &&...) noexcept; -false_t test_callable_plain(...) noexcept; - -template -struct is_callable_plain - : std::bool_constant<( - sizeof(test_callable_plain( - std::declval(), - std::declval()... - )) == sizeof(true_t))> { -}; - -template -constexpr bool is_callable_plain_v = is_callable_plain::value; - -template < - typename Method, - typename ...Types, - typename = decltype(std::declval()( - std::declval()...))> -true_t test_callable_tuple( - Method &&, - std::tuple &&) noexcept; -template < - typename Method, - typename ...Types, - typename = decltype(std::declval()( - const_ref_val()...))> -true_t test_callable_tuple( - Method &&, - const std::tuple &) noexcept; -false_t test_callable_tuple(...) noexcept; - -template -constexpr bool is_callable_tuple_v = (sizeof(test_callable_tuple( - std::declval(), - std::declval())) == sizeof(true_t)); - -template -struct is_callable_tuple - : std::bool_constant< - is_callable_tuple_v> { -}; - -template -struct is_callable; - -template -struct is_callable - : std::bool_constant< - is_callable_plain_v> { -}; - -template -struct is_callable - : std::bool_constant< - is_callable_plain_v || - is_callable_tuple_v || - is_callable_plain_v> { -}; - -template -constexpr bool is_callable_v = is_callable::value; - -template -inline decltype(auto) callable_invoke(Method &&method, Arg &&arg) { - if constexpr (is_callable_plain_v) { - return std::forward(method)(std::forward(arg)); - } else if constexpr (is_callable_tuple_v) { - return std::apply( - std::forward(method), - std::forward(arg)); - } else if constexpr (is_callable_v) { - return std::forward(method)(); - } else { - static_assert(false_(method, arg), "Bad callable_invoke() call."); - } -} - -template -using callable_result = decltype(callable_invoke( - std::declval(), - std::declval())); - -template < - typename Method, - typename Arg, - typename = decltype(std::declval()( - const_ref_val>()))> -true_t test_allows_const_ref(Method &&, Arg &&) noexcept; -false_t test_allows_const_ref(...) noexcept; - -template -constexpr bool allows_const_ref_v = (sizeof(test_allows_const_ref( - std::declval(), - std::declval())) == sizeof(true_t)); - -template -inline decltype(auto) const_ref_call_invoke( - Method &&method, - const Arg &arg) { - if constexpr (allows_const_ref_v) { - return callable_invoke(std::forward(method), arg); - } else { - auto copy = arg; - return callable_invoke( - std::forward(method), - std::move(copy)); - } -} - -} // namespace details -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/details/superset_type.h b/Telegram/SourceFiles/rpl/details/superset_type.h deleted file mode 100644 index dc70d78b9..000000000 --- a/Telegram/SourceFiles/rpl/details/superset_type.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace rpl { - -template -struct superset_type; - -template -using superset_type_t = typename superset_type::type; - -template -struct superset_type { - using type = Value; -}; - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/details/type_list.h b/Telegram/SourceFiles/rpl/details/type_list.h deleted file mode 100644 index 31a381de5..000000000 --- a/Telegram/SourceFiles/rpl/details/type_list.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { -namespace type_list { - -template -struct list { -}; - -template -struct list { - using head = Type; - using tail = list; -}; - -using empty_list = list<>; - -template -using head_t = typename TypeList::head; - -template -using tail_t = typename TypeList::tail; - -template -struct construct; - -template -using construct_t = typename construct::type; - -template -struct construct> { - using type = list; -}; - -template -struct size; - -template -constexpr std::size_t size_v = size::value; - -template -struct size> - : std::integral_constant< - std::size_t, - sizeof...(Types)> { -}; - -template -constexpr bool empty_v = (size_v == 0); - -template -struct empty : std::bool_constant> { -}; - -template -struct get; - -template -using get_t = typename get::type; - -template -struct get { - using type = get_t>; -}; - -template -struct get<0, TypeList> { - using type = head_t; -}; - -template -struct concat; - -template -using concat_t = typename concat::type; - -template -struct concat, list> { - using type = list; -}; - -template -struct remove_all; - -template -using remove_all_t = typename remove_all::type; - -template -struct remove_all { - using head = head_t; - using tail = tail_t; - using clean_tail = remove_all_t; - using type = std::conditional_t< - std::is_same_v, - clean_tail, - construct_t>; -}; - -template -struct remove_all { - using type = empty_list; -}; - -template -struct last; - -template -using last_t = typename last::type; - -template -struct last { - using type = last_t>; -}; - -template -struct last> { - using type = Type; -}; - -template -struct chop_last; - -template -using chop_last_t = typename chop_last::type; - -template -struct chop_last { - using type = construct_t< - head_t, - chop_last_t>>; -}; - -template -struct chop_last> { - using type = empty_list; -}; - -template -struct distinct; - -template -using distinct_t = typename distinct::type; - -template -struct distinct { - using type = construct_t< - head_t, - distinct_t< - remove_all_t, head_t>>>; -}; - -template <> -struct distinct { - using type = empty_list; -}; - -template typename To> -struct extract_to; - -template typename To> -using extract_to_t = typename extract_to::type; - -template typename To> -struct extract_to, To> { - using type = To; -}; - -} // namespace type_list -} // namespace details -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/distinct_until_changed.h b/Telegram/SourceFiles/rpl/distinct_until_changed.h deleted file mode 100644 index a81d00ce7..000000000 --- a/Telegram/SourceFiles/rpl/distinct_until_changed.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include "base/optional.h" - -namespace rpl { -namespace details { - -class distinct_until_changed_helper { -public: - template - auto operator()( - producer &&initial) const { - return make_producer([ - initial = std::move(initial) - ](const auto &consumer) mutable { - auto previous = consumer.template make_state< - std::optional - >(); - return std::move(initial).start( - [consumer, previous](auto &&value) { - if (!(*previous) || (**previous) != value) { - *previous = value; - consumer.put_next_forward(std::forward(value)); - } - }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -}; - -} // namespace details - -inline auto distinct_until_changed() --> details::distinct_until_changed_helper { - return details::distinct_until_changed_helper(); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/event_stream.h b/Telegram/SourceFiles/rpl/event_stream.h deleted file mode 100644 index f98be33d6..000000000 --- a/Telegram/SourceFiles/rpl/event_stream.h +++ /dev/null @@ -1,293 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "base/assertion.h" -#include "base/index_based_iterator.h" - -namespace rpl { - -// Currently not thread-safe :( - -template -class event_stream { -public: - event_stream(); - event_stream(event_stream &&other); - event_stream &operator=(event_stream &&other); - - template - void fire_forward(OtherValue &&value) const; - void fire(Value &&value) const { - return fire_forward(std::move(value)); - } - void fire_copy(const Value &value) const { - return fire_forward(value); - } - - template - void fire_error_forward(OtherError &&error) const; - void fire_error(Error &&error) const { - return fire_error_forward(std::move(error)); - } - void fire_error_copy(const Error &error) const { - return fire_error_forward(error); - } - - void fire_done() const; - -#if defined _MSC_VER && _MSC_VER >= 1914 && _MSC_VER < 1916 - producer events() const { -#else // _MSC_VER >= 1914 && _MSC_VER < 1916 - auto events() const { -#endif // _MSC_VER >= 1914 && _MSC_VER < 1916 - return make_producer([weak = make_weak()]( - const auto &consumer) { - if (const auto strong = weak.lock()) { - auto result = [weak, consumer] { - if (const auto strong = weak.lock()) { - const auto it = std::find( - strong->consumers.begin(), - strong->consumers.end(), - consumer); - if (it != strong->consumers.end()) { - it->terminate(); - } - } - }; - strong->consumers.push_back(std::move(consumer)); - return lifetime(std::move(result)); - } - return lifetime(); - }); - } - auto events_starting_with(Value &&value) const { - return single(std::move(value)) | then(events()); - } - auto events_starting_with_copy(const Value &value) const { - return single(value) | then(events()); - } - bool has_consumers() const { - return (_data != nullptr) && !_data->consumers.empty(); - } - - ~event_stream(); - -private: - struct Data { - std::vector> consumers; - int depth = 0; - }; - std::weak_ptr make_weak() const; - - mutable std::shared_ptr _data; - -}; - -template -inline event_stream::event_stream() { -} - -template -inline event_stream::event_stream(event_stream &&other) -: _data(details::take(other._data)) { -} - -template -inline event_stream &event_stream::operator=( - event_stream &&other) { - if (this != &other) { - std::swap(_data, other._data); - other.fire_done(); - } - return *this; -} - -template -template -inline void event_stream::fire_forward( - OtherValue &&value) const { - if (!_data) { - return; - } - const auto copy = _data; - auto &consumers = copy->consumers; - if (consumers.empty()) { - return; - } - - ++copy->depth; - const auto begin = base::index_based_begin(consumers); - const auto end = base::index_based_end(consumers); - - // Copy value for every consumer except the last. - const auto prev = end - 1; - auto staleFrom = std::remove_if(begin, prev, [&](const auto &consumer) { - return !consumer.put_next_copy(value); - }); - - // Perhaps move value for the last consumer. - if (prev->put_next_forward(std::forward(value))) { - if (staleFrom != prev) { - *staleFrom++ = std::move(*prev); - } else { - ++staleFrom; - } - } - - if (staleFrom != end) { - // Move new consumers. - const auto newEnd = base::index_based_end(consumers); - if (newEnd != end) { - Assert(newEnd > end); - for (auto i = end; i != newEnd;) { - *staleFrom++ = *i++; - } - } - - // Erase stale consumers. - if (copy->depth == 1) { - consumers.erase(staleFrom.base(), consumers.end()); - } - } - --copy->depth; -} - -template -template -inline void event_stream::fire_error_forward( - OtherError &&error) const { - if (!_data) { - return; - } - const auto data = std::move(_data); - const auto &consumers = data->consumers; - if (consumers.empty()) { - return; - } - const auto begin = base::index_based_begin(consumers); - const auto end = base::index_based_end(consumers); - - // Copy error for every consumer except the last. - const auto prev = end - 1; - std::for_each(begin, prev, [&](const auto &consumer) { - consumer.put_error_copy(error); - }); - - // Perhaps move error for the last consumer. - prev->put_error_forward(std::forward(error)); - - // Just drop any new consumers. -} - -template -void event_stream::fire_done() const { - if (const auto data = details::take(_data)) { - for (const auto &consumer : data->consumers) { - consumer.put_done(); - } - } -} - -template -inline auto event_stream::make_weak() const --> std::weak_ptr { - if (!_data) { - _data = std::make_shared(); - } - return _data; -} - -template -inline event_stream::~event_stream() { - fire_done(); -} - -template -inline auto start_to_stream( - event_stream &stream, - lifetime &alive_while) { - if constexpr (std::is_same_v) { - return start_with_next_done([&](auto &&value) { - stream.fire_forward(std::forward(value)); - }, [&] { - stream.fire_done(); - }, alive_while); - } else { - return start_with_next_error_done([&](auto &&value) { - stream.fire_forward(std::forward(value)); - }, [&](auto &&error) { - stream.fire_error_forward(std::forward(error)); - }, [&] { - stream.fire_done(); - }, alive_while); - } -} - -namespace details { - -class start_spawning_helper { -public: - start_spawning_helper(lifetime &alive_while) - : _lifetime(alive_while) { - } - - template - auto operator()(producer &&initial) { - auto stream = _lifetime.make_state>(); - auto values = std::vector(); - if constexpr (std::is_same_v) { - auto collecting = stream->events().start( - [&](Value &&value) { values.push_back(std::move(value)); }, - [](const Error &error) {}, - [] {}); - std::move(initial) | start_to_stream(*stream, _lifetime); - collecting.destroy(); - - return vector(std::move(values)) | then(stream->events()); - } else { - auto maybeError = std::optional(); - auto collecting = stream->events().start( - [&](Value && value) { values.push_back(std::move(value)); }, - [&](Error &&error) { maybeError = std::move(error); }, - [] {}); - std::move(initial) | start_to_stream(*stream, _lifetime); - collecting.destroy(); - - if (maybeError.has_value()) { - return rpl::producer([ - error = std::move(*maybeError) - ](const auto &consumer) mutable { - consumer.put_error(std::move(error)); - }); - } - return rpl::producer(vector( - std::move(values) - ) | then(stream->events())); - } - } - -private: - lifetime &_lifetime; - -}; - -} // namespace details - -inline auto start_spawning(lifetime &alive_while) --> details::start_spawning_helper { - return details::start_spawning_helper(alive_while); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/fail.h b/Telegram/SourceFiles/rpl/fail.h deleted file mode 100644 index 7db387cf2..000000000 --- a/Telegram/SourceFiles/rpl/fail.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { - -template -inline auto fail(Error &&error) { - return make_producer>([ - error = std::forward(error) - ](const auto &consumer) mutable { - consumer.put_error(std::move(error)); - return lifetime(); - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/filter.h b/Telegram/SourceFiles/rpl/filter.h deleted file mode 100644 index 08d11ba74..000000000 --- a/Telegram/SourceFiles/rpl/filter.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include "base/optional.h" - -namespace rpl { -namespace details { - -template -class filter_helper { -public: - template - filter_helper(OtherPredicate &&predicate) - : _predicate(std::forward(predicate)) { - } - - template < - typename Value, - typename Error, - typename Generator, - typename = std::enable_if_t< - details::is_callable_v>> - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - predicate = std::move(_predicate) - ](const auto &consumer) mutable { - return std::move(initial).start( - [ - consumer, - predicate = std::move(predicate) - ](auto &&value) { - const auto &immutable = value; - if (details::callable_invoke( - predicate, - immutable) - ) { - consumer.put_next_forward( - std::forward(value)); - } - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -private: - Predicate _predicate; - -}; - -} // namespace details - -template -inline auto filter(Predicate &&predicate) --> details::filter_helper> { - return details::filter_helper>( - std::forward(predicate)); -} - -namespace details { - -template -class filter_helper> { -public: - filter_helper( - producer &&filterer) - : _filterer(std::move(filterer)) { - } - - template - auto operator()(producer &&initial) { - using namespace mappers; - return combine(std::move(initial), std::move(_filterer)) - | filter(_2) - | map(_1_of_two); - } - -private: - producer _filterer; - -}; - -template -inline const Value &deref_optional_helper( - const std::optional &value) { - return *value; -} - -template -inline Value &&deref_optional_helper( - std::optional &&value) { - return std::move(*value); -} - -class filter_optional_helper { -public: - template - auto operator()(producer< - std::optional, - Error, - Generator> &&initial) const { - return make_producer([ - initial = std::move(initial) - ](const auto &consumer) mutable { - return std::move(initial).start( - [consumer](auto &&value) { - if (value) { - consumer.put_next_forward( - deref_optional_helper( - std::forward( - value))); - } - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -}; - -} // namespace details - -inline auto filter_optional() --> details::filter_optional_helper { - return details::filter_optional_helper(); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/flatten_latest.h b/Telegram/SourceFiles/rpl/flatten_latest.h deleted file mode 100644 index 33cdbd88a..000000000 --- a/Telegram/SourceFiles/rpl/flatten_latest.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -class flatten_latest_helper { -public: - template < - typename Value, - typename Error, - typename Generator, - typename MetaGenerator> - auto operator()(producer< - producer, - Error, - MetaGenerator> &&initial) const { - return make_producer([ - initial = std::move(initial) - ](const auto &consumer) mutable { - auto state = consumer.template make_state(); - return std::move(initial).start( - [consumer, state](producer &&inner) { - state->finished = false; - state->alive = lifetime(); - std::move(inner).start( - [consumer](auto &&value) { - consumer.put_next_forward(std::forward(value)); - }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); - }, [consumer, state] { - if (state->finished) { - consumer.put_done(); - } else { - state->finished = true; - } - }, state->alive); - }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); - }, [consumer, state] { - if (state->finished) { - consumer.put_done(); - } else { - state->finished = true; - } - }); - }); - } - -private: - struct State { - lifetime alive; - bool finished = false; - }; - -}; - -} // namespace details - -inline auto flatten_latest() --> details::flatten_latest_helper { - return details::flatten_latest_helper(); -} - -} // namespace rpl - diff --git a/Telegram/SourceFiles/rpl/lifetime.h b/Telegram/SourceFiles/rpl/lifetime.h deleted file mode 100644 index a8a2b77f7..000000000 --- a/Telegram/SourceFiles/rpl/lifetime.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/unique_function.h" -#include - -namespace rpl { -namespace details { - -template -inline Type take(Type &value) { - return std::exchange(value, Type{}); -} - -} // namespace details - -class lifetime { -public: - lifetime() = default; - lifetime(lifetime &&other); - lifetime &operator=(lifetime &&other); - ~lifetime() { destroy(); } - - template ()())> - lifetime(Destroy &&destroy); - - explicit operator bool() const { return !_callbacks.empty(); } - - template ()())> - void add(Destroy &&destroy); - void add(lifetime &&other); - void destroy(); - - template - Type *make_state(Args&& ...args) { - const auto result = new Type(std::forward(args)...); - add([=] { - static_assert(sizeof(Type) > 0, "Can't delete unknown type."); - delete result; - }); - return result; - } - -private: - std::vector> _callbacks; - -}; - -inline lifetime::lifetime(lifetime &&other) -: _callbacks(details::take(other._callbacks)) { -} - -inline lifetime &lifetime::operator=(lifetime &&other) { - std::swap(_callbacks, other._callbacks); - other.destroy(); - return *this; -} - -template -inline lifetime::lifetime(Destroy &&destroy) { - _callbacks.emplace_back(std::forward(destroy)); -} - -template -inline void lifetime::add(Destroy &&destroy) { - _callbacks.emplace_back(std::forward(destroy)); -} - -inline void lifetime::add(lifetime &&other) { - auto callbacks = details::take(other._callbacks); - _callbacks.insert( - _callbacks.end(), - std::make_move_iterator(callbacks.begin()), - std::make_move_iterator(callbacks.end())); -} - -inline void lifetime::destroy() { - auto callbacks = details::take(_callbacks); - for (auto i = callbacks.rbegin(), e = callbacks.rend(); i != e; ++i) { - (*i)(); - } -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/map.h b/Telegram/SourceFiles/rpl/map.h deleted file mode 100644 index 4003141a4..000000000 --- a/Telegram/SourceFiles/rpl/map.h +++ /dev/null @@ -1,211 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -template < - typename Transform, - typename NewValue, - typename Error, - typename Handlers> -class map_transform_helper { -public: - map_transform_helper( - Transform &&transform, - const consumer &consumer) - : _consumer(consumer) - , _transform(std::move(transform)) { - } - template < - typename OtherValue, - typename = std::enable_if_t< - std::is_rvalue_reference_v>> - void operator()(OtherValue &&value) const { - _consumer.put_next_forward( - details::callable_invoke(_transform, std::move(value))); - } - template < - typename OtherValue, - typename = decltype( - std::declval()(const_ref_val()))> - void operator()(const OtherValue &value) const { - _consumer.put_next_forward( - details::callable_invoke(_transform, value)); - } - -private: - consumer _consumer; - Transform _transform; - -}; - -template < - typename Transform, - typename NewValue, - typename Error, - typename Handlers, - typename = std::enable_if_t< - std::is_rvalue_reference_v>> -inline map_transform_helper -map_transform( - Transform &&transform, - const consumer &consumer) { - return { std::move(transform), consumer }; -} - -template -class map_helper { -public: - template - map_helper(OtherTransform &&transform) - : _transform(std::forward(transform)) { - } - - template < - typename Value, - typename Error, - typename Generator, - typename NewValue = details::callable_result< - Transform, - Value>> - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - transform = std::move(_transform) - ](const auto &consumer) mutable { - return std::move(initial).start( - map_transform( - std::move(transform), - consumer - ), [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - }); - } - -private: - Transform _transform; - -}; - -} // namespace details - -template -inline auto map(Transform &&transform) --> details::map_helper> { - return details::map_helper>( - std::forward(transform)); -} - -namespace details { - -template < - typename Transform, - typename Value, - typename NewError, - typename Handlers> -class map_error_transform_helper { -public: - map_error_transform_helper( - Transform &&transform, - const consumer &consumer) - : _transform(std::move(transform)) - , _consumer(consumer) { - } - template < - typename OtherError, - typename = std::enable_if_t< - std::is_rvalue_reference_v>> - void operator()(OtherError &&error) const { - _consumer.put_error_forward( - details::callable_invoke(_transform, std::move(error))); - } - template < - typename OtherError, - typename = decltype( - std::declval()(const_ref_val()))> - void operator()(const OtherError &error) const { - _consumer.put_error_forward( - details::callable_invoke(_transform, error)); - } - -private: - consumer _consumer; - Transform _transform; - -}; - -template < - typename Transform, - typename Value, - typename NewError, - typename Handlers, - typename = std::enable_if_t< - std::is_rvalue_reference_v>> -inline map_error_transform_helper -map_error_transform( - Transform &&transform, - const consumer &consumer) { - return { std::move(transform), consumer }; -} - -template -class map_error_helper { -public: - template - map_error_helper(OtherTransform &&transform) - : _transform(std::forward(transform)) { - } - - template < - typename Value, - typename Error, - typename Generator, - typename NewError = details::callable_result< - Transform, - Error>> - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - transform = std::move(_transform) - ](const auto &consumer) mutable { - return std::move(initial).start( - [consumer](auto &&value) { - consumer.put_next_forward( - std::forward(value)); - }, map_error_transform( - std::move(transform), - consumer - ), [consumer] { - consumer.put_done(); - }); - }); - } - -private: - Transform _transform; - -}; - -} // namespace details - -template -inline auto map_error(Transform &&transform) --> details::map_error_helper> { - return details::map_error_helper>( - std::forward(transform)); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/mappers.h b/Telegram/SourceFiles/rpl/mappers.h deleted file mode 100644 index 0e69da142..000000000 --- a/Telegram/SourceFiles/rpl/mappers.h +++ /dev/null @@ -1,474 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace rpl { -namespace details { - -struct base_mapper { -}; - -template -constexpr bool is_mapper_v = std::is_base_of_v< - base_mapper, - std::decay_t>; - -template -struct argument_mapper : base_mapper { - template < - typename Arg, - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) >= Index)>> - static constexpr decltype(auto) call(Arg &&arg, Args &&...args) { - return argument_mapper::call( - std::forward(args)...); - } - - template < - typename ...Args, - typename = std::enable_if_t<(sizeof...(Args) > Index)>> - constexpr auto operator()(Args &&...args) const { - return call(std::forward(args)...); - } - -}; - -template <> -struct argument_mapper<0> : base_mapper { - template < - typename Arg, - typename ...Args> - static constexpr decltype(auto) call(Arg &&arg, Args &&...args) { - return std::forward(arg); - } - - template < - typename Arg, - typename ...Args> - constexpr auto operator()(Arg &&arg, Args &&...args) const { - return std::forward(arg); - } - -}; - -template -class value_mapper : public base_mapper { -public: - template - constexpr value_mapper(OtherType &&value) - : _value(std::forward(value)) { - } - - template - constexpr auto operator()(Args &&...args) const { - return _value; - } - -private: - Type _value; - -}; - -template -struct wrap_mapper { - using type = std::conditional_t< - is_mapper_v, - Type, - value_mapper>; -}; - -template -using wrap_mapper_t = typename wrap_mapper::type; - -template -class unary_operator_mapper : public base_mapper { - using TypeWrapper = wrap_mapper_t>; - -public: - template - constexpr unary_operator_mapper(OtherType &&value) - : _value(std::forward(value)) { - } - - template < - typename ...Args, - typename Result = decltype((Operator{})( - std::declval()(std::declval()...)))> - constexpr std::decay_t operator()(Args &&...args) const { - return (Operator{})( - _value(std::forward(args)...)); - } - -private: - TypeWrapper _value; - -}; - -template -class binary_operator_mapper : public base_mapper { - using LeftWrapper = wrap_mapper_t>; - using RightWrapper = wrap_mapper_t>; - -public: - template - constexpr binary_operator_mapper(OtherLeft &&left, OtherRight &&right) - : _left(std::forward(left)) - , _right(std::forward(right)) { - } - - template < - typename ...Args, - typename Result = decltype((Operator{})( - std::declval()(std::declval()...), - std::declval()(std::declval()...)))> - constexpr std::decay_t operator()(Args &&...args) const { - return (Operator{})( - _left(std::forward(args)...), - _right(std::forward(args)...)); - } - -private: - LeftWrapper _left; - RightWrapper _right; - -}; - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator+(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::plus<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator-(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::minus<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator*(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::multiplies<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator/(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::divides<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator%(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::modulus<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Type, - typename = std::enable_if_t< - is_mapper_v - >> -inline auto operator-(Type &&value) { - return unary_operator_mapper< - Type, - std::negate<>>( - std::forward(value)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator<(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::less<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator<=(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::less_equal<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator>(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::greater<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator>=(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::greater_equal<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator==(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::equal_to<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator!=(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::not_equal_to<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator&&(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::logical_and<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator||(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::logical_or<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Type, - typename = std::enable_if_t< - is_mapper_v - >> -inline auto operator!(Type &&value) { - return unary_operator_mapper< - Type, - std::logical_not<>>( - std::forward(value)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator&(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::bit_and<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator|(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::bit_or<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Left, - typename Right, - typename = std::enable_if_t< - is_mapper_v || is_mapper_v - >> -inline auto operator^(Left &&left, Right &&right) { - return binary_operator_mapper< - Left, - Right, - std::bit_xor<>>( - std::forward(left), - std::forward(right)); -} - -template < - typename Type, - typename = std::enable_if_t< - is_mapper_v - >> -inline auto operator~(Type &&value) { - return unary_operator_mapper< - Type, - std::bit_not<>>( - std::forward(value)); -} - -template -class tuple_mapper { - template - using tuple_result = std::tuple>>()( - std::declval()...))...>; -public: - template - tuple_mapper(OtherMappers &&...mappers) : _mappers( - std::forward(mappers)...) { - } - - template - constexpr tuple_result operator()( - Args &&...args) const { - constexpr auto kArity = sizeof...(Mappers); - return call_helper( - std::make_index_sequence(), - std::forward(args)...); - } - -private: - template - inline tuple_result call_helper( - std::index_sequence, - Args &&...args) const { - return std::make_tuple( - std::get(_mappers)(std::forward(args)...)...); - } - - std::tuple>...> _mappers; - -}; - -template -tuple_mapper tuple(Args &&...args) { - return tuple_mapper(std::forward(args)...); -} - -} // namespace details - -namespace mappers { - -constexpr const details::argument_mapper<0> _1; -constexpr const details::argument_mapper<1> _2; -constexpr const details::argument_mapper<2> _3; -constexpr const details::argument_mapper<3> _4; -constexpr const details::argument_mapper<4> _5; -constexpr const details::argument_mapper<5> _6; -constexpr const details::argument_mapper<6> _7; -constexpr const details::argument_mapper<7> _8; -constexpr const details::argument_mapper<8> _9; -constexpr const details::argument_mapper<9> _10; - -constexpr const auto _1_of_two = ((void)_2, _1); - -} // namespace mappers -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/merge.h b/Telegram/SourceFiles/rpl/merge.h deleted file mode 100644 index 84287ef87..000000000 --- a/Telegram/SourceFiles/rpl/merge.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -struct merge_state { - merge_state(int working) : working(working) { - } - int working = 0; -}; - -template -class merge_subscribe_one { -public: - merge_subscribe_one( - const consumer_type &consumer, - merge_state *state) - : _consumer(consumer) - , _state(state) { - } - - template - void subscribe(producer &&producer) { - _consumer.add_lifetime(std::move(producer).start( - [consumer = _consumer](auto &&value) { - consumer.put_next_forward( - std::forward(value)); - }, [consumer = _consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer = _consumer, state = _state] { - if (!--state->working) { - consumer.put_done(); - } - })); - } - -private: - const consumer_type &_consumer; - merge_state *_state = nullptr; - -}; - -template < - typename consumer_type, - typename Value, - typename Error, - typename ...Generators, - std::size_t ...I> -inline void merge_subscribe( - const consumer_type &consumer, - merge_state *state, - std::index_sequence, - std::tuple...> &&saved) { - auto consume = { ( - details::merge_subscribe_one( - consumer, - state - ).subscribe(std::get(std::move(saved))), 0)... }; - (void)consume; -} - -template -class merge_implementation_helper; - -template -merge_implementation_helper...> -make_merge_implementation_helper(Producers &&...producers) { - return merge_implementation_helper...>( - std::forward(producers)...); -} - -template < - typename Value, - typename Error, - typename ...Generators> -class merge_implementation_helper...> { -public: - merge_implementation_helper( - producer &&...producers) - : _saved(std::make_tuple(std::move(producers)...)) { - } - - template - lifetime operator()(const consumer &consumer) { - auto state = consumer.template make_state< - details::merge_state>(sizeof...(Generators)); - constexpr auto kArity = sizeof...(Generators); - details::merge_subscribe( - consumer, - state, - std::make_index_sequence(), - std::move(_saved)); - - return lifetime(); - } - -private: - std::tuple...> _saved; - -}; - -template < - typename Value, - typename Error, - typename ...Generators> -inline auto merge_implementation( - producer &&...producers) { - return make_producer( - make_merge_implementation_helper(std::move(producers)...)); -} - -template -struct merge_producers : std::false_type { -}; - -template -constexpr bool merge_producers_v - = merge_producers::value; - -template < - typename Value, - typename Error, - typename ...Generators> -struct merge_producers< - producer...> - : std::true_type { -}; - -} // namespace details - -template < - typename ...Args, - typename = std::enable_if_t< - details::merge_producers_v>> -inline decltype(auto) merge(Args &&...args) { - return details::merge_implementation(std::move(args)...); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/never.h b/Telegram/SourceFiles/rpl/never.h deleted file mode 100644 index e6218b98e..000000000 --- a/Telegram/SourceFiles/rpl/never.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { - -template -inline auto never() { - return make_producer([](const auto &consumer) { - return lifetime(); - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/operators_tests.cpp b/Telegram/SourceFiles/rpl/operators_tests.cpp deleted file mode 100644 index 548057f44..000000000 --- a/Telegram/SourceFiles/rpl/operators_tests.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/* -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 "catch.hpp" - -#include -#include - -using namespace rpl; - -class OnDestructor { -public: - OnDestructor(std::function callback) - : _callback(std::move(callback)) { - } - ~OnDestructor() { - if (_callback) { - _callback(); - } - } - -private: - std::function _callback; - -}; - -class InvokeCounter { -public: - InvokeCounter( - const std::shared_ptr ©Counter, - const std::shared_ptr &moveCounter) - : _copyCounter(copyCounter) - , _moveCounter(moveCounter) { - } - InvokeCounter(const InvokeCounter &other) - : _copyCounter(other._copyCounter) - , _moveCounter(other._moveCounter) { - if (_copyCounter) { - ++*_copyCounter; - } - } - InvokeCounter(InvokeCounter &&other) - : _copyCounter(details::take(other._copyCounter)) - , _moveCounter(details::take(other._moveCounter)) { - if (_moveCounter) { - ++*_moveCounter; - } - } - InvokeCounter &operator=(const InvokeCounter &other) { - _copyCounter = other._copyCounter; - _moveCounter = other._moveCounter; - if (_copyCounter) { - ++*_copyCounter; - } - return *this; - } - InvokeCounter &operator=(InvokeCounter &&other) { - _copyCounter = details::take(other._copyCounter); - _moveCounter = details::take(other._moveCounter); - if (_moveCounter) { - ++*_moveCounter; - } - return *this; - } - -private: - std::shared_ptr _copyCounter; - std::shared_ptr _moveCounter; - -}; - -TEST_CASE("basic operators tests", "[rpl::operators]") { - SECTION("single test") { - auto sum = std::make_shared(0); - auto doneGenerated = std::make_shared(false); - auto destroyed = std::make_shared(false); - auto copyCount = std::make_shared(0); - auto moveCount = std::make_shared(0); - { - InvokeCounter counter(copyCount, moveCount); - auto destroyCalled = std::make_shared([=] { - *destroyed = true; - }); - rpl::lifetime lifetime; - single(std::move(counter)) - | start_with_next_error_done([=](InvokeCounter&&) { - (void)destroyCalled; - ++*sum; - }, [=](no_error) { - (void)destroyCalled; - }, [=] { - (void)destroyCalled; - *doneGenerated = true; - }, lifetime); - } - REQUIRE(*sum == 1); - REQUIRE(*doneGenerated); - REQUIRE(*destroyed); - REQUIRE(*copyCount == 0); - } - - SECTION("then test") { - auto sum = std::make_shared(0); - auto doneGenerated = std::make_shared(false); - auto destroyed = std::make_shared(false); - auto copyCount = std::make_shared(0); - auto moveCount = std::make_shared(0); - { - auto testing = complete() | type_erased(); - for (auto i = 0; i != 5; ++i) { - InvokeCounter counter(copyCount, moveCount); - testing = std::move(testing) - | then(single(std::move(counter))); - } - auto destroyCalled = std::make_shared([=] { - *destroyed = true; - }); - - rpl::lifetime lifetime; - std::move(testing) - | then(complete()) - | start_with_next_error_done([=](InvokeCounter&&) { - (void)destroyCalled; - ++*sum; - }, [=](no_error) { - (void)destroyCalled; - }, [=] { - (void)destroyCalled; - *doneGenerated = true; - }, lifetime); - } - REQUIRE(*sum == 5); - REQUIRE(*doneGenerated); - REQUIRE(*destroyed); - REQUIRE(*copyCount == 0); - } - - SECTION("map test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - single(1) - | then(single(2)) - | then(single(3)) - | then(single(4)) - | then(single(5)) - | map([](int value) { - return std::to_string(value); - }) - | start_with_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }, lifetime); - } - REQUIRE(*sum == "1 2 3 4 5 "); - } - - SECTION("deferred test") { - auto launched = std::make_shared(0); - auto checked = std::make_shared(0); - { - rpl::lifetime lifetime; - auto make_next = [=] { - return deferred([=] { - return single(++*launched); - }); - }; - make_next() - | then(make_next()) - | then(make_next()) - | then(make_next()) - | then(make_next()) - | start_with_next([=](int value) { - REQUIRE(++*checked == *launched); - REQUIRE(*checked == value); - }, lifetime); - REQUIRE(*launched == 5); - } - } - - SECTION("filter test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - single(1) - | then(single(1)) - | then(single(2)) - | then(single(2)) - | then(single(3)) - | filter([](int value) { return value != 2; }) - | map([](int value) { - return std::to_string(value); - }) - | start_with_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }, lifetime); - } - REQUIRE(*sum == "1 1 3 "); - } - - SECTION("filter tuple test") { - auto sum = std::make_shared(""); - { - auto lifetime = single(std::make_tuple(1, 2)) - | then(single(std::make_tuple(1, 2))) - | then(single(std::make_tuple(2, 3))) - | then(single(std::make_tuple(2, 3))) - | then(single(std::make_tuple(3, 4))) - | filter([](auto first, auto second) { return first != 2; }) - | map([](auto first, auto second) { - return std::to_string(second); - }) - | start_with_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }); - } - REQUIRE(*sum == "2 2 4 "); - } - - SECTION("distinct_until_changed test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - single(1) - | then(single(1)) - | then(single(2)) - | then(single(2)) - | then(single(3)) - | distinct_until_changed() - | map([](int value) { - return std::to_string(value); - }) - | start_with_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }, lifetime); - } - REQUIRE(*sum == "1 2 3 "); - } - - SECTION("flatten_latest test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - { - event_stream stream; - single(single(1) | then(single(2))) - | then(single(single(3) | then(single(4)))) - | then(single(single(5) | then(stream.events()))) - | flatten_latest() - | map([](int value) { - return std::to_string(value); - }) - | start_with_next_done([=](std::string &&value) { - *sum += std::move(value) + ' '; - }, [=] { - *sum += "done "; - }, lifetime); - stream.fire(6); - } - single(single(1)) - | then(single(single(2) | then(single(3)))) - | then(single(single(4) | then(single(5)) | then(single(6)))) - | flatten_latest() - | map([](int value) { - return std::to_string(value); - }) - | start_with_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }, lifetime); - } - REQUIRE(*sum == "1 2 3 4 5 6 done 1 2 3 4 5 6 "); - } - - SECTION("combine vector test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - event_stream a; - event_stream b; - event_stream c; - - std::vector> v; - v.push_back(a.events()); - v.push_back(b.events()); - v.push_back(c.events()); - - combine(std::move(v), [](const auto &values) { - return values[0] && values[1] && !values[2]; - }) - | start_with_next([=](bool value) { - *sum += std::to_string(value ? 1 : 0); - }, lifetime); - - a.fire(true); - b.fire(true); - c.fire(false); - a.fire(false); - b.fire(true); - a.fire(true); - c.fire(true); - } - REQUIRE(*sum == "10010"); - } - - SECTION("combine test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - event_stream a; - event_stream b; - event_stream c; - - combine( - a.events(), - b.events(), - c.events(), - [](long a, long b, long c) { - return a; - }) - | start_with_next([=](int value) { - *sum += std::to_string(value); - }, lifetime); - - combine( - a.events(), - b.events(), - c.events(), - [](auto &&value) { - return std::get<1>(value); - }) - | start_with_next([=](int value) { - *sum += std::to_string(value); - }, lifetime); - - combine(a.events(), b.events(), c.events()) - | map([](auto &&value) { - return std::make_tuple( - std::to_string(std::get<0>(value)), - std::to_string(std::get<1>(value)), - std::to_string(std::get<2>(value))); - }) - | start_with_next([=](auto &&value) { - *sum += std::get<0>(value) + ' ' - + std::get<1>(value) + ' ' - + std::get<2>(value) + ' '; - }, lifetime); - a.fire(1); - b.fire(2); - c.fire(3); - a.fire(4); - b.fire(5); - c.fire(6); - } - REQUIRE(*sum == "121 2 3 424 2 3 454 5 3 454 5 6 "); - } - - SECTION("mappers test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - event_stream a; - event_stream b; - event_stream c; - - using namespace mappers; - - // MSVC BUG + REGRESSION rpl::mappers::tuple :( - //combine( - // a.events(), - // b.events(), - // tuple(_1, _1 + _2) - //) | rpl::start_with_next([=](int a, int a_plus_b) { - // *sum += std::to_string(a * a_plus_b); - //}, lifetime); - - combine( - a.events(), - b.events(), - c.events(), - _1 + _2 + _3 + 10) - | start_with_next([=](int value) { - *sum += std::to_string(value); - }, lifetime); - - a.fire(1); - b.fire(2); - c.fire(3); - a.fire(4); - b.fire(5); - c.fire(6); - } - REQUIRE(*sum == "16192225"); - - // MSVC BUG + REGRESSION rpl::mappers::tuple :( - //REQUIRE(*sum == "3162419362225"); - } - - SECTION("after_next test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - ints(3) - | after_next([=](int value) { - *sum += std::to_string(-value-1); - }) - | start_with_next([=](int value) { - *sum += std::to_string(value); - }, lifetime); - } - REQUIRE(*sum == "0-11-22-3"); - } - - SECTION("combine_previous test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - event_stream a; - - a.events( - ) | combine_previous( - ) | start_with_next([=](int previous, int next) { - *sum += std::to_string(previous) + ' '; - *sum += std::to_string(next) + ' '; - }, lifetime); - - a.events( - ) | combine_previous( - 5 - ) | start_with_next([=](int previous, int next) { - *sum += std::to_string(10 + previous) + ' '; - *sum += std::to_string(next) + ' '; - }, lifetime); - - a.fire(1); - a.fire(2); - a.fire(3); - a.fire(4); - } - REQUIRE(*sum == "15 1 1 2 11 2 2 3 12 3 3 4 13 4 "); - } - - SECTION("take test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - ints(10) | take(3) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - { - rpl::lifetime lifetime; - ints(3) | take(3) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - { - rpl::lifetime lifetime; - ints(3) | take(10) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - REQUIRE(*sum == "012done012done012done"); - } - - SECTION("skip test") { - auto sum = std::make_shared(""); - { - rpl::lifetime lifetime; - ints(10) | skip(5) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - { - rpl::lifetime lifetime; - ints(3) | skip(3) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - { - rpl::lifetime lifetime; - ints(3) | skip(10) - | start_with_next_done([=](int value) { - *sum += std::to_string(value); - }, [=] { - *sum += "done"; - }, lifetime); - } - REQUIRE(*sum == "56789donedonedone"); - } -} diff --git a/Telegram/SourceFiles/rpl/producer.h b/Telegram/SourceFiles/rpl/producer.h deleted file mode 100644 index b00e677bf..000000000 --- a/Telegram/SourceFiles/rpl/producer.h +++ /dev/null @@ -1,1025 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include - -#if defined _DEBUG || defined COMPILER_MSVC -#define RPL_PRODUCER_TYPE_ERASED_ALWAYS -#endif // _DEBUG - -namespace rpl { -namespace details { - -template -const consumer &const_ref_consumer(); - -template -class mutable_lambda_wrap { -public: - mutable_lambda_wrap(Lambda &&lambda) - : _lambda(std::move(lambda)) { - } - mutable_lambda_wrap(const mutable_lambda_wrap &other) = default; - mutable_lambda_wrap(mutable_lambda_wrap &&other) = default; - mutable_lambda_wrap &operator=( - const mutable_lambda_wrap &other) = default; - mutable_lambda_wrap &operator=( - mutable_lambda_wrap &&other) = default; - - template - auto operator()(Args&&... args) const { - return (const_cast(this)->_lambda)( - std::forward(args)...); - } - -private: - Lambda _lambda; - -}; - -// Type-erased copyable mutable function using std::function. -template -class type_erased_generator final { -public: - template - using consumer_type = consumer; - using value_type = Value; - using error_type = Error; - - type_erased_generator( - const type_erased_generator &other) = default; - type_erased_generator( - type_erased_generator &&other) = default; - type_erased_generator &operator=( - const type_erased_generator &other) = default; - type_erased_generator &operator=( - type_erased_generator &&other) = default; - - type_erased_generator(std::nullptr_t = nullptr) { - } - type_erased_generator &operator=(std::nullptr_t) { - _implementation = nullptr; - return *this; - } - - template < - typename Generator, - typename = std::enable_if_t< - std::is_convertible_v< - decltype(std::declval()( - const_ref_consumer())), - lifetime> && - !std::is_same_v< - std::decay_t, - type_erased_generator>>> - type_erased_generator(Generator other) : _implementation( - mutable_lambda_wrap(std::move(other))) { - } - template < - typename Generator, - typename = std::enable_if_t< - std::is_convertible_v< - decltype(std::declval()( - const_ref_consumer())), - lifetime> && - !std::is_same_v< - std::decay_t, - type_erased_generator>>> - type_erased_generator &operator=(Generator other) { - _implementation = mutable_lambda_wrap( - std::move(other)); - return *this; - } - - template - lifetime operator()(const consumer_type &consumer) { - return _implementation ? _implementation(consumer) : lifetime(); - } - - bool empty() const { - return !_implementation; - } - -private: - std::function> &)> _implementation; - -}; - -} // namespace details - -template < - typename Value = empty_value, - typename Error = no_error, - typename Generator = details::type_erased_generator< - Value, - Error>> -class producer; - -template < - typename Value1, - typename Value2, - typename Error1, - typename Error2, - typename Generator> -struct superset_type< - producer, - producer> { - using type = producer< - superset_type_t, - superset_type_t, - Generator>; -}; - -template < - typename Value, - typename Error, - typename Generator1, - typename Generator2> -struct superset_type< - producer, - producer> { - using type = producer; -}; - -template < - typename Value, - typename Error, - typename Generator> -struct superset_type< - producer, - producer> { - using type = producer; -}; - -namespace details { - -template -class producer_base { -public: - template - using consumer_type = consumer; - using value_type = Value; - using error_type = Error; - - template < - typename OtherGenerator, - typename = std::enable_if_t< - std::is_constructible_v>> - producer_base(OtherGenerator &&generator); - - producer_base() = default; - producer_base(const producer_base &other) = default; - producer_base(producer_base &&other) = default; - producer_base &operator=(const producer_base &other) = default; - producer_base &operator=(producer_base &&other) = default; - - template < - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v - && is_callable_v - && is_callable_v>> - void start( - OnNext &&next, - OnError &&error, - OnDone &&done, - lifetime &alive_while) &&; - - template < - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v - && is_callable_v - && is_callable_v>> - [[nodiscard]] lifetime start( - OnNext &&next, - OnError &&error, - OnDone &&done) &&; - - template < - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v - && is_callable_v - && is_callable_v>> - void start_copy( - OnNext &&next, - OnError &&error, - OnDone &&done, - lifetime &alive_while) const &; - - template < - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v - && is_callable_v - && is_callable_v>> - [[nodiscard]] lifetime start_copy( - OnNext &&next, - OnError &&error, - OnDone &&done) const &; - - template - void start_existing( - const consumer_type &consumer, - lifetime &alive_while) &&; - - template - [[nodiscard]] lifetime start_existing( - const consumer_type &consumer) &&; - -private: - Generator _generator; - - template < - typename OtherValue, - typename OtherError, - typename OtherGenerator> - friend class ::rpl::producer; - -}; - -template -template -inline producer_base::producer_base( - OtherGenerator &&generator) -: _generator(std::forward(generator)) { -} - -template -template < - typename OnNext, - typename OnError, - typename OnDone, - typename> -inline void producer_base::start( - OnNext &&next, - OnError &&error, - OnDone &&done, - lifetime &alive_while) && { - return std::move(*this).start_existing( - make_consumer( - std::forward(next), - std::forward(error), - std::forward(done)), - alive_while); -} - -template -template < - typename OnNext, - typename OnError, - typename OnDone, - typename> -[[nodiscard]] inline lifetime producer_base::start( - OnNext &&next, - OnError &&error, - OnDone &&done) && { - auto result = lifetime(); - std::move(*this).start_existing( - make_consumer( - std::forward(next), - std::forward(error), - std::forward(done)), - result); - return result; -} - -template -template < - typename OnNext, - typename OnError, - typename OnDone, - typename> -inline void producer_base::start_copy( - OnNext &&next, - OnError &&error, - OnDone &&done, - lifetime &alive_while) const & { - auto copy = *this; - return std::move(copy).start_existing( - make_consumer( - std::forward(next), - std::forward(error), - std::forward(done)), - alive_while); -} - -template -template < - typename OnNext, - typename OnError, - typename OnDone, - typename> -[[nodiscard]] inline lifetime producer_base::start_copy( - OnNext &&next, - OnError &&error, - OnDone &&done) const & { - auto result = lifetime(); - auto copy = *this; - std::move(copy).start_existing( - make_consumer( - std::forward(next), - std::forward(error), - std::forward(done)), - result); - return result; -} - -template -template -inline void producer_base::start_existing( - const consumer_type &consumer, - lifetime &alive_while) && { - alive_while.add(consumer.terminator()); - consumer.add_lifetime(std::move(_generator)(consumer)); -} - -template -template -[[nodiscard]] inline lifetime producer_base::start_existing( - const consumer_type &consumer) && { - auto result = lifetime(); - std::move(*this).start_existing(consumer, result); - return result; -} - -template -using producer_base_type_erased = producer_base< - Value, - Error, - type_erased_generator>; - -} // namespace details - -template -class producer final -: public details::producer_base { - using parent_type = details::producer_base< - Value, - Error, - Generator>; - -public: - using parent_type::parent_type; - -}; - -template -class producer< - Value, - Error, - details::type_erased_generator> final -: public details::producer_base_type_erased { - using parent_type = details::producer_base_type_erased< - Value, - Error>; - -public: - using parent_type::parent_type; - - producer() = default; - producer(const producer &other) = default; - producer(producer &&other) = default; - producer &operator=(const producer &other) = default; - producer &operator=(producer &&other) = default; - - template < - typename Generic, - typename = std::enable_if_t>>> - producer(const details::producer_base &other) - : parent_type(other._generator) { - } - - template < - typename Generic, - typename = std::enable_if_t>>> - producer(details::producer_base &&other) - : parent_type(std::move(other._generator)) { - } - - template < - typename Generic, - typename = std::enable_if_t>>> - producer &operator=( - const details::producer_base &other) { - this->_generator = other._generator; - return *this; - } - - template < - typename Generic, - typename = std::enable_if_t>>> - producer &operator=( - details::producer_base &&other) { - this->_generator = std::move(other._generator); - return *this; - } - - explicit operator bool() const { - return !this->_generator.empty(); - } - -}; - -template < - typename Value = empty_value, - typename Error = no_error, - typename Generator, - typename = std::enable_if_t< - std::is_convertible_v< - decltype(std::declval()( - details::const_ref_consumer())), - lifetime>>> -inline auto make_producer(Generator &&generator) -#ifdef RPL_PRODUCER_TYPE_ERASED_ALWAYS --> producer { -#else // RPL_PRODUCER_TYPE_ERASED_ALWAYS --> producer> { -#endif // !RPL_PRODUCER_TYPE_ERASED_ALWAYS - return std::forward(generator); -} - -template -inline producer duplicate( - const producer &value) { - return value; -} - -template < - typename Value, - typename Error, - typename Generator, - typename Method, - typename = decltype(std::declval()( - std::declval>()))> -inline auto operator|( - producer &&value, - Method &&method) { - return std::forward(method)(std::move(value)); -} - -namespace details { - -struct with_none { -}; - -struct lifetime_with_none { - lifetime &alive_while; -}; - -template -struct with_next { - OnNext next; -}; - -template -struct lifetime_with_next { - lifetime &alive_while; - OnNext next; -}; - -template -struct with_error { - OnError error; -}; - -template -struct lifetime_with_error { - lifetime &alive_while; - OnError error; -}; - -template -struct with_done { - OnDone done; -}; - -template -struct lifetime_with_done { - lifetime &alive_while; - OnDone done; -}; - -template -struct with_next_error { - OnNext next; - OnError error; -}; - -template -struct lifetime_with_next_error { - lifetime &alive_while; - OnNext next; - OnError error; -}; - -template -struct with_error_done { - OnError error; - OnDone done; -}; - -template -struct lifetime_with_error_done { - lifetime &alive_while; - OnError error; - OnDone done; -}; - -template -struct with_next_done { - OnNext next; - OnDone done; -}; - -template -struct lifetime_with_next_done { - lifetime &alive_while; - OnNext next; - OnDone done; -}; - -template -struct with_next_error_done { - OnNext next; - OnError error; - OnDone done; -}; - -template -struct lifetime_with_next_error_done { - lifetime &alive_while; - OnNext next; - OnError error; - OnDone done; -}; - -} // namespace details - -inline auto start() --> details::with_none { - return {}; -} - -inline auto start(lifetime &alive_while) --> details::lifetime_with_none { - return { alive_while }; -} - -template -inline auto start_with_next(OnNext &&next) --> details::with_next> { - return { std::forward(next) }; -} - -template -inline auto start_with_next(OnNext &&next, lifetime &alive_while) --> details::lifetime_with_next> { - return { alive_while, std::forward(next) }; -} - -template -inline auto start_with_error(OnError &&error) --> details::with_error> { - return { std::forward(error) }; -} - -template -inline auto start_with_error(OnError &&error, lifetime &alive_while) --> details::lifetime_with_error> { - return { alive_while, std::forward(error) }; -} - -template -inline auto start_with_done(OnDone &&done) --> details::with_done> { - return { std::forward(done) }; -} - -template -inline auto start_with_done(OnDone &&done, lifetime &alive_while) --> details::lifetime_with_done> { - return { alive_while, std::forward(done) }; -} - -template -inline auto start_with_next_error( - OnNext &&next, - OnError &&error) --> details::with_next_error< - std::decay_t, - std::decay_t> { - return { - std::forward(next), - std::forward(error) - }; -} - -template -inline auto start_with_next_error( - OnNext &&next, - OnError &&error, - lifetime &alive_while) --> details::lifetime_with_next_error< - std::decay_t, - std::decay_t> { - return { - alive_while, - std::forward(next), - std::forward(error) - }; -} - -template -inline auto start_with_error_done( - OnError &&error, - OnDone &&done) --> details::with_error_done< - std::decay_t, - std::decay_t> { - return { - std::forward(error), - std::forward(done) - }; -} - -template -inline auto start_with_error_done( - OnError &&error, - OnDone &&done, - lifetime &alive_while) --> details::lifetime_with_error_done< - std::decay_t, - std::decay_t> { - return { - alive_while, - std::forward(error), - std::forward(done) - }; -} - -template -inline auto start_with_next_done( - OnNext &&next, - OnDone &&done) --> details::with_next_done< - std::decay_t, - std::decay_t> { - return { - std::forward(next), - std::forward(done) - }; -} - -template -inline auto start_with_next_done( - OnNext &&next, - OnDone &&done, - lifetime &alive_while) --> details::lifetime_with_next_done< - std::decay_t, - std::decay_t> { - return { - alive_while, - std::forward(next), - std::forward(done) - }; -} - -template -inline auto start_with_next_error_done( - OnNext &&next, - OnError &&error, - OnDone &&done) --> details::with_next_error_done< - std::decay_t, - std::decay_t, - std::decay_t> { - return { - std::forward(next), - std::forward(error), - std::forward(done) - }; -} - -template -inline auto start_with_next_error_done( - OnNext &&next, - OnError &&error, - OnDone &&done, - lifetime &alive_while) --> details::lifetime_with_next_error_done< - std::decay_t, - std::decay_t, - std::decay_t> { - return { - alive_while, - std::forward(next), - std::forward(error), - std::forward(done) - }; -} - -namespace details { - -template -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_none &&handlers) { - return std::move(value).start( - [] {}, - [] {}, - [] {}); -} - -template -inline void operator|( - producer &&value, - lifetime_with_none &&handlers) { - std::move(value).start( - [] {}, - [] {}, - [] {}, - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename = std::enable_if_t>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_next &&handlers) { - return std::move(value).start( - std::move(handlers.next), - [] {}, - [] {}); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename = std::enable_if_t>> -inline void operator|( - producer &&value, - lifetime_with_next &&handlers) { - std::move(value).start( - std::move(handlers.next), - [] {}, - [] {}, - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnError, - typename = std::enable_if_t>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_error &&handlers) { - return std::move(value).start( - [] {}, - std::move(handlers.error), - [] {}); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnError, - typename = std::enable_if_t>> -inline void operator|( - producer &&value, - lifetime_with_error &&handlers) { - std::move(value).start( - [] {}, - std::move(handlers.error), - [] {}, - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnDone, - typename = std::enable_if_t>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_done &&handlers) { - return std::move(value).start( - [] {}, - [] {}, - std::move(handlers.done)); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnDone, - typename = std::enable_if_t>> -inline void operator|( - producer &&value, - lifetime_with_done &&handlers) { - std::move(value).start( - [] {}, - [] {}, - std::move(handlers.done), - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnError, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_next_error &&handlers) { - return std::move(value).start( - std::move(handlers.next), - std::move(handlers.error), - [] {}); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnError, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -inline void operator|( - producer &&value, - lifetime_with_next_error &&handlers) { - std::move(value).start( - std::move(handlers.next), - std::move(handlers.error), - [] {}, - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_error_done &&handlers) { - return std::move(value).start( - [] {}, - std::move(handlers.error), - std::move(handlers.done)); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -inline void operator|( - producer &&value, - lifetime_with_error_done &&handlers) { - std::move(value).start( - [] {}, - std::move(handlers.error), - std::move(handlers.done), - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_next_done &&handlers) { - return std::move(value).start( - std::move(handlers.next), - [] {}, - std::move(handlers.done)); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v>> -inline void operator|( - producer &&value, - lifetime_with_next_done &&handlers) { - std::move(value).start( - std::move(handlers.next), - [] {}, - std::move(handlers.done), - handlers.alive_while); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v && - is_callable_v>> -[[nodiscard]] inline lifetime operator|( - producer &&value, - with_next_error_done< - OnNext, - OnError, - OnDone> &&handlers) { - return std::move(value).start( - std::move(handlers.next), - std::move(handlers.error), - std::move(handlers.done)); -} - -template < - typename Value, - typename Error, - typename Generator, - typename OnNext, - typename OnError, - typename OnDone, - typename = std::enable_if_t< - is_callable_v && - is_callable_v && - is_callable_v>> -inline void operator|( - producer &&value, - lifetime_with_next_error_done< - OnNext, - OnError, - OnDone> &&handlers) { - std::move(value).start( - std::move(handlers.next), - std::move(handlers.error), - std::move(handlers.done), - handlers.alive_while); -} - -} // namespace details -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/producer_tests.cpp b/Telegram/SourceFiles/rpl/producer_tests.cpp deleted file mode 100644 index 0267f92aa..000000000 --- a/Telegram/SourceFiles/rpl/producer_tests.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/* -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 "catch.hpp" - -#include -#include - -using namespace rpl; - -class OnDestructor { -public: - OnDestructor(std::function callback) - : _callback(std::move(callback)) { - } - ~OnDestructor() { - if (_callback) { - _callback(); - } - } - -private: - std::function _callback; - -}; - -TEST_CASE("basic producer tests", "[rpl::producer]") { - SECTION("producer next, done and lifetime end test") { - auto lifetimeEnded = std::make_shared(false); - auto sum = std::make_shared(0); - auto doneGenerated = std::make_shared(false); - auto destroyed = std::make_shared(false); - { - auto destroyCaller = std::make_shared([=] { - *destroyed = true; - }); - { - auto alive = make_producer([=](auto &&consumer) { - (void)destroyCaller; - consumer.put_next(1); - consumer.put_next(2); - consumer.put_next(3); - consumer.put_done(); - return [=] { - (void)destroyCaller; - *lifetimeEnded = true; - }; - }).start([=](int value) { - (void)destroyCaller; - *sum += value; - }, [=](no_error) { - (void)destroyCaller; - }, [=]() { - (void)destroyCaller; - *doneGenerated = true; - }); - } - } - REQUIRE(*sum == 1 + 2 + 3); - REQUIRE(*doneGenerated); - REQUIRE(*lifetimeEnded); - REQUIRE(*destroyed); - } - - SECTION("producer error test") { - auto errorGenerated = std::make_shared(false); - { - auto alive = make_producer([=](auto &&consumer) { - consumer.put_error(true); - return lifetime(); - }).start([=](no_value) { - }, [=](bool error) { - *errorGenerated = error; - }, [=]() { - }); - } - REQUIRE(*errorGenerated); - } - - SECTION("nested lifetimes test") { - auto lifetimeEndCount = std::make_shared(0); - { - auto lifetimes = lifetime(); - { - auto testProducer = make_producer([=](auto &&consumer) { - return [=] { - ++*lifetimeEndCount; - }; - }); - testProducer.start_copy([=](no_value) { - }, [=](no_error) { - }, [=] { - }, lifetimes); - std::move(testProducer).start([=](no_value) { - }, [=](no_error) { - }, [=] { - }, lifetimes); - } - REQUIRE(*lifetimeEndCount == 0); - } - REQUIRE(*lifetimeEndCount == 2); - } - - SECTION("nested producers test") { - auto sum = std::make_shared(0); - auto lifetimeEndCount = std::make_shared(0); - auto saved = lifetime(); - { - make_producer([=](auto &&consumer) { - auto inner = make_producer([=](auto &&consumer) { - consumer.put_next(1); - consumer.put_next(2); - consumer.put_next(3); - return [=] { - ++*lifetimeEndCount; - }; - }); - auto result = lifetime([=] { - ++*lifetimeEndCount; - }); - inner.start_copy([=](int value) { - consumer.put_next_copy(value); - }, [=](no_error) { - }, [=] { - }, result); - std::move(inner).start([=](int value) { - consumer.put_next_copy(value); - }, [=](no_error) { - }, [=] { - }, result); - return result; - }).start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, saved); - } - REQUIRE(*sum == 1 + 2 + 3 + 1 + 2 + 3); - REQUIRE(*lifetimeEndCount == 0); - saved.destroy(); - REQUIRE(*lifetimeEndCount == 3); - } - - SECTION("tuple producer test") { - auto result = std::make_shared(0); - { - auto alive = make_producer>([=]( - auto &&consumer) { - consumer.put_next(std::make_tuple(1, 2.)); - return lifetime(); - }).start([=](int a, double b) { - *result = a + int(b); - }, [=](no_error error) { - }, [=]() { - }); - } - REQUIRE(*result == 3); - } -} - -TEST_CASE("basic event_streams tests", "[rpl::event_stream]") { - SECTION("event_stream basic test") { - auto sum = std::make_shared(0); - event_stream stream; - stream.fire(1); - stream.fire(2); - stream.fire(3); - { - auto saved = lifetime(); - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, saved); - stream.fire(11); - stream.fire(12); - stream.fire(13); - } - stream.fire(21); - stream.fire(22); - stream.fire(23); - - REQUIRE(11 + 12 + 13); - } - - SECTION("event_stream add in handler test") { - auto sum = std::make_shared(0); - event_stream stream; - - { - auto composite = lifetime(); - stream.events().start([=, &stream, &composite](int value) { - *sum += value; - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, composite); - }, [=](no_error) { - }, [=] { - }, composite); - - { - auto inner = lifetime(); - stream.events().start([=, &stream, &inner](int value) { - *sum += value; - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, inner); - }, [=](no_error) { - }, [=] { - }, inner); - - stream.fire(1); - stream.fire(2); - stream.fire(3); - } - stream.fire(11); - stream.fire(12); - stream.fire(13); - } - stream.fire(21); - stream.fire(22); - stream.fire(23); - - REQUIRE(*sum == - (1 + 1) + - ((2 + 2) + (2 + 2)) + - ((3 + 3 + 3) + (3 + 3 + 3)) + - (11 + 11 + 11 + 11) + - (12 + 12 + 12 + 12 + 12) + - (13 + 13 + 13 + 13 + 13 + 13)); - } - - SECTION("event_stream add and remove in handler test") { - auto sum = std::make_shared(0); - event_stream stream; - - { - auto composite = lifetime(); - stream.events().start([=, &stream, &composite](int value) { - *sum += value; - composite.destroy(); - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, composite); - }, [=](no_error) { - }, [=] { - }, composite); - - { - auto inner = lifetime(); - stream.events().start([=, &stream, &inner](int value) { - *sum += value; - inner.destroy(); - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, inner); - }, [=](no_error) { - }, [=] { - }, inner); - - stream.fire(1); - stream.fire(2); - stream.fire(3); - } - stream.fire(11); - stream.fire(12); - stream.fire(13); - } - stream.fire(21); - stream.fire(22); - stream.fire(23); - - REQUIRE(*sum == - (1 + 1) + - (2 + 2) + - (3 + 3) + - (11) + - (12) + - (13)); - } - - SECTION("event_stream ends before handler lifetime") { - auto sum = std::make_shared(0); - lifetime extended; - { - event_stream stream; - stream.events().start([=](int value) { - *sum += value; - }, [=](no_error) { - }, [=] { - }, extended); - stream.fire(1); - stream.fire(2); - stream.fire(3); - } - REQUIRE(*sum == 1 + 2 + 3); - } - - SECTION("event_stream move test") { - auto sum = std::make_shared(0); - lifetime extended; - { - event_stream stream; - stream.events() - | start_with_next([=](int value) { - *sum += value; - }, extended); - - stream.fire(1); - stream.fire(2); - - auto movedStream = std::move(stream); - movedStream.fire(3); - movedStream.fire(4); - } - REQUIRE(*sum == 1 + 2 + 3 + 4); - } -} - -TEST_CASE("basic piping tests", "[rpl::producer]") { - - SECTION("start_with_*") { - auto sum = std::make_shared(0); - auto dones = std::make_shared(0); - { - auto alive = lifetime(); - make_producer([=](auto &&consumer) { - consumer.put_next(1); - consumer.put_done(); - return lifetime(); - }) | start_with_next([=](int value) { - *sum += value; - }, alive); - - make_producer([=](auto &&consumer) { - consumer.put_next(11); - consumer.put_error(111); - return lifetime(); - }) | start_with_error([=](int value) { - *sum += value; - }, alive); - - make_producer([=](auto &&consumer) { - consumer.put_next(1111); - consumer.put_done(); - return lifetime(); - }) | start_with_done([=]() { - *dones += 1; - }, alive); - - make_producer([=](auto &&consumer) { - consumer.put_next(11111); - consumer.put_next(11112); - consumer.put_next(11113); - consumer.put_error(11114); - return lifetime(); - }) | start_with_next_error([=](int value) { - *sum += value; - }, [=](int value) { - *sum += value; - }, alive); - } - - auto alive = lifetime(); - make_producer([=](auto &&consumer) { - consumer.put_next(111111); - consumer.put_next(111112); - consumer.put_next(111113); - consumer.put_done(); - return lifetime(); - }) | start_with_next_done([=](int value) { - *sum += value; - }, [=]() { - *dones += 11; - }, alive); - - make_producer([=](auto &&consumer) { - consumer.put_error(1111111); - return lifetime(); - }) | start_with_error_done([=](int value) { - *sum += value; - }, [=]() { - *dones = 0; - }, alive); - - make_producer([=](auto &&consumer) { - consumer.put_next(11111111); - consumer.put_next(11111112); - consumer.put_next(11111113); - consumer.put_error(11111114); - return lifetime(); - }) | start_with_next_error_done([=](int value) { - *sum += value; - }, [=](int value) { - *sum += value; - }, [=]() { - *dones = 0; - }, alive); - - REQUIRE(*sum == - 1 + - 111 + - 11111 + 11112 + 11113 + 11114 + - 111111 + 111112 + 111113 + - 1111111 + - 11111111 + 11111112 + 11111113 + 11111114); - REQUIRE(*dones == 1 + 11); - } - - SECTION("start_with_next should copy its callback") { - auto sum = std::make_shared(0); - { - auto next = [=](int value) { - REQUIRE(sum != nullptr); - *sum += value; - }; - - for (int i = 0; i != 3; ++i) { - auto alive = lifetime(); - make_producer([=](auto &&consumer) { - consumer.put_next(1); - consumer.put_done(); - return lifetime(); - }) | start_with_next(next, alive); - } - } - REQUIRE(*sum == 3); - } -} diff --git a/Telegram/SourceFiles/rpl/range.h b/Telegram/SourceFiles/rpl/range.h deleted file mode 100644 index 790e168b0..000000000 --- a/Telegram/SourceFiles/rpl/range.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace rpl { - -template -inline auto single(Value &&value) { - return make_producer, Error>([ - value = std::forward(value) - ](const auto &consumer) mutable { - consumer.put_next(std::move(value)); - consumer.put_done(); - return lifetime(); - }); -} - -template -inline auto single() { - return make_producer([](const auto &consumer) { - consumer.put_next({}); - consumer.put_done(); - return lifetime(); - }); -} - -template -inline auto vector(std::vector &&values) { - return make_producer([ - values = std::move(values) - ](const auto &consumer) mutable { - for (auto &value : values) { - consumer.put_next(std::move(value)); - } - consumer.put_done(); - return lifetime(); - }); -} - -template -inline auto vector(std::vector &&values) { - return make_producer([ - values = std::move(values) - ](const auto &consumer) { - for (auto value : values) { - consumer.put_next_copy(value); - } - consumer.put_done(); - return lifetime(); - }); -} - -template < - typename Range, - typename Value = std::decay_t< - decltype(*std::begin(std::declval()))>> -inline auto range(Range &&range) { - return vector(std::vector( - std::begin(range), - std::end(range))); -} - -inline auto ints(int from, int till) { - Expects(from <= till); - return make_producer([from, till](const auto &consumer) { - for (auto i = from; i != till; ++i) { - consumer.put_next_copy(i); - } - consumer.put_done(); - return lifetime(); - }); -} - -inline auto ints(int count) { - return ints(0, count); -} - -} // namespace rpl - diff --git a/Telegram/SourceFiles/rpl/rpl.h b/Telegram/SourceFiles/rpl/rpl.h deleted file mode 100644 index b2b0ebc53..000000000 --- a/Telegram/SourceFiles/rpl/rpl.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -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 -*/ -#pragma once - -// rpl - reactive programming library - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include diff --git a/Telegram/SourceFiles/rpl/skip.h b/Telegram/SourceFiles/rpl/skip.h deleted file mode 100644 index 8cfe5774b..000000000 --- a/Telegram/SourceFiles/rpl/skip.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace rpl { -namespace details { - -class skip_helper { -public: - skip_helper(int count) : _count(count) { - } - - template < - typename Value, - typename Error, - typename Generator> - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - skipping = _count - ](const auto &consumer) mutable { - auto count = consumer.template make_state(skipping); - auto initial_consumer = make_consumer( - [consumer, count](auto &&value) { - if (*count) { - --*count; - } else { - consumer.put_next_forward( - std::forward(value)); - } - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - consumer.add_lifetime(initial_consumer.terminator()); - return std::move(initial).start_existing(initial_consumer); - }); - } - -private: - int _count = 0; - -}; - -} // namespace details - -inline auto skip(int count) --> details::skip_helper { - Expects(count >= 0); - - return details::skip_helper(count); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/take.h b/Telegram/SourceFiles/rpl/take.h deleted file mode 100644 index 6164de4ad..000000000 --- a/Telegram/SourceFiles/rpl/take.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace rpl { -namespace details { - -class take_helper { -public: - take_helper(int count) : _count(count) { - } - - template < - typename Value, - typename Error, - typename Generator> - auto operator()(producer &&initial) { - return make_producer([ - initial = std::move(initial), - limit = _count - ](const auto &consumer) mutable { - auto count = consumer.template make_state(limit); - auto initial_consumer = make_consumer( - [consumer, count](auto &&value) { - auto left = (*count)--; - if (left) { - consumer.put_next_forward( - std::forward(value)); - --left; - } - if (!left) { - consumer.put_done(); - } - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - }); - consumer.add_lifetime(initial_consumer.terminator()); - return std::move(initial).start_existing(initial_consumer); - }); - } - -private: - int _count = 0; - -}; - -} // namespace details - -inline auto take(int count) --> details::take_helper { - Expects(count >= 0); - - return details::take_helper(count); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/then.h b/Telegram/SourceFiles/rpl/then.h deleted file mode 100644 index 0d24fa38d..000000000 --- a/Telegram/SourceFiles/rpl/then.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -template -class then_helper { -public: - then_helper(producer &&following) - : _following(std::move(following)) { - } - - template < - typename OtherValue, - typename OtherError, - typename OtherGenerator, - typename NewValue = superset_type_t, - typename NewError = superset_type_t> - auto operator()( - producer &&initial - ) { - return make_producer([ - initial = std::move(initial), - following = std::move(_following) - ](const auto &consumer) mutable { - return std::move(initial).start( - [consumer](auto &&value) { - consumer.put_next_forward( - std::forward(value)); - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [ - consumer, - following = std::move(following) - ]() mutable { - consumer.add_lifetime(std::move(following).start( - [consumer](auto &&value) { - consumer.put_next_forward( - std::forward(value)); - }, [consumer](auto &&error) { - consumer.put_error_forward( - std::forward(error)); - }, [consumer] { - consumer.put_done(); - })); - }); - }); - } - -private: - producer _following; - -}; - -} // namespace details -template -inline auto then(producer &&following) --> details::then_helper { - return { std::move(following) }; -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/type_erased.h b/Telegram/SourceFiles/rpl/type_erased.h deleted file mode 100644 index 7a871ff29..000000000 --- a/Telegram/SourceFiles/rpl/type_erased.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace rpl { -namespace details { - -class type_erased_helper { -public: - template - producer operator()( - producer &&initial) const { - return std::move(initial); - } - -}; - -} // namespace details - -inline auto type_erased() --> details::type_erased_helper { - return details::type_erased_helper(); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/variable.h b/Telegram/SourceFiles/rpl/variable.h deleted file mode 100644 index 06f9e0e74..000000000 --- a/Telegram/SourceFiles/rpl/variable.h +++ /dev/null @@ -1,181 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace mapbox { -namespace util { - -template -class variant; - -} // namespace util -} // namespace mapbox - -namespace rpl { -namespace details { - -template -struct supports_equality_compare { - template - static auto test(const U *u, const V *v) - -> decltype(*u == *v, true_t()); - static false_t test(...); - static constexpr bool value - = (sizeof(test((const A*)nullptr, (const B*)nullptr)) - == sizeof(true_t)); -}; - -// Fix for MSVC expression SFINAE. -// It still doesn't work! :( -// -//template -//struct supports_equality_compare< -// mapbox::util::variant, -// mapbox::util::variant> { -// static constexpr bool value -// = (supports_equality_compare::value -// && supports_equality_compare< -// mapbox::util::variant, -// mapbox::util::variant>::value); -// -//}; -//template -//struct supports_equality_compare< -// mapbox::util::variant, -// mapbox::util::variant> { -// static constexpr bool value = supports_equality_compare::value; -// -//}; - -template -constexpr bool supports_equality_compare_v - = supports_equality_compare, std::decay_t>::value; - -} // namespace details - -template -class variable final { -public: - variable() : _data{} { - } - variable(variable &&other) : _data(std::move(other._data)) { - } - variable &operator=(variable &&other) { - return (*this = std::move(other._data)); - } - - template < - typename OtherType, - typename = std::enable_if_t< - std::is_constructible_v>> - variable(OtherType &&data) : _data(std::forward(data)) { - } - - template < - typename OtherType, - typename = std::enable_if_t< - std::is_assignable_v>> - variable &operator=(OtherType &&data) { - _lifetime.destroy(); - return assign(std::forward(data)); - } - - template < - typename OtherType, - typename OtherError, - typename Generator, - typename = std::enable_if_t< - std::is_assignable_v>> - variable(producer &&stream) { - std::move(stream) - | start_with_next([=](auto &&data) { - assign(std::forward(data)); - }, _lifetime); - } - - template < - typename OtherType, - typename OtherError, - typename Generator, - typename = std::enable_if_t< - std::is_assignable_v>> - variable &operator=( - producer &&stream) { - _lifetime.destroy(); - std::move(stream) - | start_with_next([=](auto &&data) { - assign(std::forward(data)); - }, _lifetime); - return *this; - } - - Type current() const { - return _data; - } - auto value() const { - return _changes.events_starting_with_copy(_data); - } - auto changes() const { - return _changes.events(); - } - - // Send 'done' to all subscribers and unsubscribe them. - template < - typename OtherType, - typename = std::enable_if_t< - std::is_assignable_v>> - void reset(OtherType &&data) { - _data = std::forward(data); - _changes = event_stream(); - } - void reset() { - reset(Type()); - } - - template < - typename OtherError, - typename = std::enable_if_t< - std::is_constructible_v>> - void reset_with_error(OtherError &&error) { - _changes.fire_error(std::forward(error)); - } - void reset_with_error() { - reset_with_error(Error()); - } - -private: - template - variable &assign(OtherType &&data) { - if constexpr (details::supports_equality_compare_v) { - if (!(_data == data)) { - _data = std::forward(data); - _changes.fire_copy(_data); - } - } else if constexpr (details::supports_equality_compare_v) { - auto old = std::move(_data); - _data = std::forward(data); - if (!(_data == old)) { - _changes.fire_copy(_data); - } - } else { - _data = std::forward(data); - _changes.fire_copy(_data); - } - return *this; - } - - Type _data; - event_stream _changes; - lifetime _lifetime; - -}; - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/variable_tests.cpp b/Telegram/SourceFiles/rpl/variable_tests.cpp deleted file mode 100644 index 9c697fcd7..000000000 --- a/Telegram/SourceFiles/rpl/variable_tests.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* -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 "catch.hpp" - -#include -#include - -using namespace rpl; - -TEST_CASE("basic variable tests", "[rpl::variable]") { - SECTION("simple test") { - auto sum = std::make_shared(0); - { - auto var = variable(1); - auto lifeftime = var.value() - | start_with_next([=](int value) { - *sum += value; - }); - var = 1; - var = 11; - var = 111; - var = 111; - } - REQUIRE(*sum == 1 + 11 + 111); - } -} - diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 145fd678f..40723b255 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -6,7 +6,7 @@ { 'includes': [ - 'common/common.gypi', + '../ThirdParty/gyp_helpers/common/common.gypi', 'telegram/telegram.gypi', ], } diff --git a/Telegram/gyp/codegen.gyp b/Telegram/gyp/codegen.gyp index 79061051a..244ff15a5 100644 --- a/Telegram/gyp/codegen.gyp +++ b/Telegram/gyp/codegen.gyp @@ -6,7 +6,7 @@ { 'includes': [ - 'common/common.gypi', + '../ThirdParty/gyp_helpers/common/common.gypi', ], 'targets': [{ 'target_name': 'codegen_lang', @@ -15,8 +15,8 @@ 'mac_target': '10.10', }, 'includes': [ - 'common/executable.gypi', - 'modules/qt.gypi', + '../ThirdParty/gyp_helpers/common/executable.gypi', + '../ThirdParty/gyp_helpers/modules/qt.gypi', ], 'include_dirs': [ @@ -52,11 +52,11 @@ 'mac_target': '10.10', }, 'includes': [ - 'common/executable.gypi', - 'modules/qt.gypi', + '../ThirdParty/gyp_helpers/common/executable.gypi', + '../ThirdParty/gyp_helpers/modules/qt.gypi', ], 'dependencies': [ - 'lib_base.gyp:lib_base', + '../ThirdParty/lib_base/lib_base.gyp:lib_base', ], 'include_dirs': [ '<(src_loc)', @@ -95,8 +95,8 @@ 'mac_target': '10.10', }, 'includes': [ - 'common/executable.gypi', - 'modules/qt.gypi', + '../ThirdParty/gyp_helpers/common/executable.gypi', + '../ThirdParty/gyp_helpers/modules/qt.gypi', ], 'include_dirs': [ @@ -132,8 +132,8 @@ 'mac_target': '10.10', }, 'includes': [ - 'common/executable.gypi', - 'modules/qt.gypi', + '../ThirdParty/gyp_helpers/common/executable.gypi', + '../ThirdParty/gyp_helpers/modules/qt.gypi', ], 'include_dirs': [ diff --git a/Telegram/gyp/common/common.gypi b/Telegram/gyp/common/common.gypi deleted file mode 100644 index 6531a7a75..000000000 --- a/Telegram/gyp/common/common.gypi +++ /dev/null @@ -1,115 +0,0 @@ -# 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 - -{ - 'includes': [ - 'win.gypi', - 'mac.gypi', - 'linux.gypi', - ], - 'variables': { - 'variables': { - 'variables': { - 'variables': { - 'variables': { - 'build_os%': '<(OS)', - }, - 'build_os%': '<(build_os)', - 'conditions': [ - [ 'build_os == "win"', { - 'build_win': 1, - }, { - 'build_win': 0, - }], - [ 'build_os == "mac"', { - 'build_mac': 1, - }, { - 'build_mac': 0, - }], - [ 'build_os == "linux"', { - 'build_linux': 1, - }, { - 'build_linux': 0, - }], - ], - }, - 'build_os%': '<(build_os)', - 'build_win%': '<(build_win)', - 'build_mac%': '<(build_mac)', - 'build_linux%': '<(build_linux)', - }, - 'build_os%': '<(build_os)', - 'build_win%': '<(build_win)', - 'build_mac%': '<(build_mac)', - 'build_linux%': '<(build_linux)', - - 'official_build_target%': '', - 'build_standard_win%': 'c++17', - 'libs_loc%': '<(DEPTH)/../../../Libraries', - }, - 'build_os%': '<(build_os)', - 'build_win%': '<(build_win)', - 'build_mac%': '<(build_mac)', - 'build_linux%': '<(build_linux)', - 'official_build_target%': '<(official_build_target)', - 'build_standard_win%': '<(build_standard_win)', - 'libs_loc%': '<(libs_loc)', - - # GYP does not support per-configuration libraries :( - # So they will be emulated through additional link flags, - # which will contain <(ld_lib_prefix)LibraryName<(ld_lib_postfix) - 'conditions': [ - [ 'build_win', { - 'ld_lib_prefix': '', - 'ld_lib_postfix': '.lib', - 'exe_ext': '.exe', - }, { - 'ld_lib_prefix': '-l', - 'ld_lib_postfix': '', - 'exe_ext': '', - }], - [ '"<(official_build_target)" == "mac32"', { - 'mac_target%': '10.6', - 'build_macold': 1, - }, { - 'mac_target%': '10.8', - 'build_macold': 0, - }], - [ '"<(official_build_target)" == "macstore"', { - 'build_macstore': 1, - }, { - 'build_macstore': 0, - }], - [ '"<(official_build_target)" == "uwp"', { - 'build_uwp': 1, - }, { - 'build_uwp': 0, - }], - ], - 'ld_lib_prefix': '<(ld_lib_prefix)', - 'ld_lib_postfix': '<(ld_lib_postfix)', - 'exe_ext': '<(exe_ext)', - - 'library%': 'static_library', - - }, - - 'defines': [ - 'NOMINMAX' - ], - 'configurations': { - 'Debug': { - 'defines': [ - '_DEBUG', - ], - }, - 'Release': { - 'defines': [ - 'NDEBUG', - ], - }, - }, -} diff --git a/Telegram/gyp/common/executable.gypi b/Telegram/gyp/common/executable.gypi deleted file mode 100644 index df9671afc..000000000 --- a/Telegram/gyp/common/executable.gypi +++ /dev/null @@ -1,21 +0,0 @@ -# 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 - -{ - 'type': 'executable', - 'variables': { - 'win_subsystem': '2', # Windows application - }, - 'includes': [ - 'common.gypi', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '<(win_subsystem)', - 'ImportLibrary': '<(PRODUCT_DIR)/<(_target_name).lib', - }, - }, -} diff --git a/Telegram/gyp/common/library.gypi b/Telegram/gyp/common/library.gypi deleted file mode 100644 index bf06013ef..000000000 --- a/Telegram/gyp/common/library.gypi +++ /dev/null @@ -1,12 +0,0 @@ -# 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 - -{ - 'type': 'static_library', - 'includes': [ - 'common.gypi', - ], -} diff --git a/Telegram/gyp/common/linux.gypi b/Telegram/gyp/common/linux.gypi deleted file mode 100644 index cdcdef1dc..000000000 --- a/Telegram/gyp/common/linux.gypi +++ /dev/null @@ -1,112 +0,0 @@ -# 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 - -{ - 'conditions': [ - [ 'build_linux', { - 'variables': { - 'linux_common_flags': [ - '-pipe', - '-Wall', - '-Werror', - '-W', - '-fPIC', - '-Wno-unused-variable', - '-Wno-unused-parameter', - '-Wno-unused-function', - '-Wno-switch', - '-Wno-comment', - '-Wno-unused-but-set-variable', - '-Wno-missing-field-initializers', - '-Wno-sign-compare', - '-Wno-attributes', - '-Wno-error=class-memaccess', - '-Wno-error=parentheses', - ], - 'linux_path_ffmpeg%': '/usr/local', - 'linux_path_openal%': '/usr/local', - 'linux_path_va%': '/usr/local', - 'linux_path_vdpau%': '/usr/local', - 'linux_path_breakpad%': '/usr/local', - 'linux_path_opus_include%': '<(libs_loc)/opus/include', - 'linux_path_range%': '/usr/local', - }, - 'include_dirs': [ - '/usr/local/include', - '<(linux_path_ffmpeg)/include', - '<(linux_path_openal)/include', - '<(linux_path_breakpad)/include/breakpad', - '<(linux_path_opus_include)', - '<(linux_path_range)/include', - ], - 'library_dirs': [ - '/usr/local/lib', - '<(linux_path_ffmpeg)/lib', - '<(linux_path_openal)/lib', - '<(linux_path_va)/lib', - '<(linux_path_vdpau)/lib', - '<(linux_path_breakpad)/lib', - ], - 'conditions': [ - [ '" Date: Tue, 17 Sep 2019 19:13:12 +0300 Subject: [PATCH 04/59] Use lib_ui from submodule. --- .gitmodules | 6 + Telegram/Resources/basic.style | 303 - Telegram/Resources/colors.palette | 559 - Telegram/Resources/emoji_autocomplete.json | 15998 ---------------- Telegram/SourceFiles/boxes/about_box.cpp | 2 +- Telegram/SourceFiles/boxes/abstract_box.cpp | 10 + Telegram/SourceFiles/boxes/abstract_box.h | 17 + Telegram/SourceFiles/boxes/boxes.style | 2 +- .../SourceFiles/boxes/confirm_phone_box.cpp | 2 +- Telegram/SourceFiles/boxes/edit_color_box.cpp | 2 +- Telegram/SourceFiles/calls/calls.style | 2 +- Telegram/SourceFiles/calls/calls_call.cpp | 2 +- .../chat_helpers/chat_helpers.style | 2 +- .../chat_helpers/emoji_keywords.cpp | 4 +- .../chat_helpers/emoji_suggestions_helper.h | 31 - .../chat_helpers/emoji_suggestions_widget.cpp | 2 +- .../codegen/common/basic_tokenized_file.cpp | 300 - .../codegen/common/basic_tokenized_file.h | 156 - .../codegen/common/checked_utf8_string.cpp | 41 - .../codegen/common/checked_utf8_string.h | 44 - .../SourceFiles/codegen/common/clean_file.cpp | 173 - .../SourceFiles/codegen/common/clean_file.h | 52 - .../codegen/common/clean_file_reader.h | 71 - .../codegen/common/const_utf8_string.h | 62 - .../SourceFiles/codegen/common/cpp_file.cpp | 111 - .../SourceFiles/codegen/common/cpp_file.h | 57 - .../SourceFiles/codegen/common/logging.cpp | 40 - Telegram/SourceFiles/codegen/common/logging.h | 67 - Telegram/SourceFiles/codegen/emoji/data.cpp | 4030 ---- Telegram/SourceFiles/codegen/emoji/data.h | 45 - .../SourceFiles/codegen/emoji/generator.cpp | 1024 - .../SourceFiles/codegen/emoji/generator.h | 81 - Telegram/SourceFiles/codegen/emoji/main.cpp | 27 - .../SourceFiles/codegen/emoji/options.cpp | 64 - Telegram/SourceFiles/codegen/emoji/options.h | 32 - .../SourceFiles/codegen/emoji/replaces.cpp | 391 - Telegram/SourceFiles/codegen/emoji/replaces.h | 34 - .../SourceFiles/codegen/lang/generator.cpp | 593 - Telegram/SourceFiles/codegen/lang/generator.h | 53 - Telegram/SourceFiles/codegen/lang/main.cpp | 23 - Telegram/SourceFiles/codegen/lang/options.cpp | 74 - Telegram/SourceFiles/codegen/lang/options.h | 25 - .../SourceFiles/codegen/lang/parsed_file.cpp | 341 - .../SourceFiles/codegen/lang/parsed_file.h | 106 - .../SourceFiles/codegen/lang/processor.cpp | 71 - Telegram/SourceFiles/codegen/lang/processor.h | 40 - .../SourceFiles/codegen/numbers/generator.cpp | 97 - .../SourceFiles/codegen/numbers/generator.h | 37 - Telegram/SourceFiles/codegen/numbers/main.cpp | 23 - .../SourceFiles/codegen/numbers/options.cpp | 73 - .../SourceFiles/codegen/numbers/options.h | 25 - .../codegen/numbers/parsed_file.cpp | 132 - .../SourceFiles/codegen/numbers/parsed_file.h | 61 - .../SourceFiles/codegen/numbers/processor.cpp | 72 - .../SourceFiles/codegen/numbers/processor.h | 40 - .../SourceFiles/codegen/style/generator.cpp | 1343 -- .../SourceFiles/codegen/style/generator.h | 71 - Telegram/SourceFiles/codegen/style/main.cpp | 23 - Telegram/SourceFiles/codegen/style/module.cpp | 90 - Telegram/SourceFiles/codegen/style/module.h | 99 - .../SourceFiles/codegen/style/options.cpp | 87 - Telegram/SourceFiles/codegen/style/options.h | 27 - .../SourceFiles/codegen/style/parsed_file.cpp | 840 - .../SourceFiles/codegen/style/parsed_file.h | 132 - .../SourceFiles/codegen/style/processor.cpp | 76 - .../SourceFiles/codegen/style/processor.h | 43 - .../codegen/style/structure_types.cpp | 198 - .../codegen/style/structure_types.h | 230 - Telegram/SourceFiles/core/crash_reports.cpp | 2 +- Telegram/SourceFiles/core/launcher.cpp | 2 +- Telegram/SourceFiles/core/sandbox.cpp | 2 +- Telegram/SourceFiles/core/shortcuts.cpp | 2 +- Telegram/SourceFiles/core/update_checker.cpp | 2 +- Telegram/SourceFiles/data/data_session.cpp | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 2 +- Telegram/SourceFiles/export/view/export.style | 2 +- .../view/export_view_panel_controller.cpp | 2 +- Telegram/SourceFiles/facades.cpp | 2 +- .../admin_log/history_admin_log_inner.cpp | 2 +- Telegram/SourceFiles/history/history.style | 2 +- .../history/history_inner_widget.cpp | 2 +- .../view/history_view_context_menu.cpp | 2 +- Telegram/SourceFiles/info/info.style | 2 +- .../info/media/info_media_list_widget.cpp | 2 +- Telegram/SourceFiles/intro/intro.style | 2 +- Telegram/SourceFiles/intro/introwidget.cpp | 2 +- Telegram/SourceFiles/lang/lang_instance.cpp | 2 +- Telegram/SourceFiles/lang/lang_translator.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 2 +- .../media/player/media_player.style | 2 +- .../media/view/media_view_overlay_widget.cpp | 2 +- .../SourceFiles/media/view/mediaview.style | 2 +- Telegram/SourceFiles/overview/overview.style | 2 +- Telegram/SourceFiles/passport/passport.style | 2 +- .../passport/passport_panel_details_row.cpp | 2 +- .../SourceFiles/platform/linux/info_linux.cpp | 59 - .../SourceFiles/platform/linux/info_linux.h | 55 - .../platform/linux/launcher_linux.cpp | 2 +- .../platform/mac/file_utilities_mac.mm | 2 +- .../SourceFiles/platform/mac/info_mac.cpp | 9 - Telegram/SourceFiles/platform/mac/info_mac.h | 46 - Telegram/SourceFiles/platform/mac/info_mac.mm | 178 - .../SourceFiles/platform/mac/launcher_mac.mm | 4 +- .../SourceFiles/platform/mac/mac_touchbar.mm | 2 +- .../SourceFiles/platform/mac/mac_utilities.h | 37 - .../SourceFiles/platform/mac/mac_utilities.mm | 8 - .../platform/mac/main_window_mac.mm | 4 +- .../platform/mac/notifications_manager_mac.mm | 4 +- .../SourceFiles/platform/mac/specific_mac.mm | 2 +- .../platform/mac/specific_mac_p.mm | 4 +- Telegram/SourceFiles/platform/platform_info.h | 54 - .../SourceFiles/platform/win/info_win.cpp | 262 - Telegram/SourceFiles/platform/win/info_win.h | 42 - .../SourceFiles/platform/win/launcher_win.cpp | 4 +- .../platform/win/main_window_win.h | 2 +- .../SourceFiles/platform/win/specific_win.h | 2 +- .../platform/win/windows_app_user_model_id.h | 2 +- .../SourceFiles/platform/win/windows_dlls.h | 2 +- .../platform/win/windows_event_filter.h | 2 +- .../platform/win/wrapper_windows_h.h | 14 - Telegram/SourceFiles/profile/profile.style | 2 +- Telegram/SourceFiles/settings/settings.style | 2 +- .../settings/settings_advanced.cpp | 2 +- .../SourceFiles/settings/settings_chat.cpp | 2 +- .../SourceFiles/settings/settings_intro.cpp | 1 + .../settings/settings_notifications.cpp | 2 +- .../storage/storage_clear_legacy_win.cpp | 2 +- .../storage/storage_file_lock_win.cpp | 2 +- Telegram/SourceFiles/ui/abstract_button.cpp | 147 - Telegram/SourceFiles/ui/abstract_button.h | 104 - .../SourceFiles/ui/basic_click_handlers.cpp | 68 - .../SourceFiles/ui/basic_click_handlers.h | 79 - Telegram/SourceFiles/ui/click_handler.cpp | 172 - Telegram/SourceFiles/ui/click_handler.h | 129 - .../SourceFiles/ui/delayed_activation.cpp | 45 - Telegram/SourceFiles/ui/delayed_activation.h | 15 - .../ui/effects/animation_value.cpp | 104 - .../SourceFiles/ui/effects/animation_value.h | 356 - .../SourceFiles/ui/effects/animations.cpp | 202 - Telegram/SourceFiles/ui/effects/animations.h | 419 - .../ui/effects/cross_animation.cpp | 203 - .../SourceFiles/ui/effects/cross_animation.h | 38 - .../SourceFiles/ui/effects/fade_animation.cpp | 162 - .../SourceFiles/ui/effects/fade_animation.h | 66 - .../ui/effects/numbers_animation.cpp | 237 - .../ui/effects/numbers_animation.h | 110 - .../ui/effects/panel_animation.cpp | 512 - .../SourceFiles/ui/effects/panel_animation.h | 128 - .../ui/effects/ripple_animation.cpp | 250 - .../SourceFiles/ui/effects/ripple_animation.h | 54 - Telegram/SourceFiles/ui/emoji_config.cpp | 873 - Telegram/SourceFiles/ui/emoji_config.h | 192 - Telegram/SourceFiles/ui/focus_persister.h | 42 - .../SourceFiles/ui/image/image_prepare.cpp | 639 - Telegram/SourceFiles/ui/image/image_prepare.h | 77 - Telegram/SourceFiles/ui/inactive_press.cpp | 54 - Telegram/SourceFiles/ui/inactive_press.h | 15 - .../SourceFiles/ui/main_queue_processor.cpp | 128 - .../SourceFiles/ui/main_queue_processor.h | 28 - Telegram/SourceFiles/ui/painter.h | 118 - .../linux/ui_platform_utility_linux.cpp | 48 - .../linux/ui_platform_utility_linux.h | 42 - .../ui/platform/mac/ui_platform_utility_mac.h | 27 - .../platform/mac/ui_platform_utility_mac.mm | 96 - .../ui/platform/ui_platform_utility.h | 44 - .../platform/win/ui_platform_utility_win.cpp | 32 - .../ui/platform/win/ui_platform_utility_win.h | 45 - Telegram/SourceFiles/ui/rect_part.h | 59 - Telegram/SourceFiles/ui/round_rect.cpp | 83 - Telegram/SourceFiles/ui/round_rect.h | 39 - Telegram/SourceFiles/ui/rp_widget.cpp | 160 - Telegram/SourceFiles/ui/rp_widget.h | 379 - Telegram/SourceFiles/ui/style/style_core.cpp | 249 - Telegram/SourceFiles/ui/style/style_core.h | 80 - .../SourceFiles/ui/style/style_core_color.cpp | 30 - .../SourceFiles/ui/style/style_core_color.h | 112 - .../ui/style/style_core_direction.cpp | 25 - .../ui/style/style_core_direction.h | 64 - .../SourceFiles/ui/style/style_core_font.cpp | 250 - .../SourceFiles/ui/style/style_core_font.h | 126 - .../SourceFiles/ui/style/style_core_icon.cpp | 348 - .../SourceFiles/ui/style/style_core_icon.h | 284 - .../SourceFiles/ui/style/style_core_scale.cpp | 38 - .../SourceFiles/ui/style/style_core_scale.h | 50 - .../SourceFiles/ui/style/style_core_types.cpp | 11 - .../SourceFiles/ui/style/style_core_types.h | 54 - Telegram/SourceFiles/ui/text/text.cpp | 3329 ---- Telegram/SourceFiles/ui/text/text.h | 387 - Telegram/SourceFiles/ui/text/text_block.cpp | 362 - Telegram/SourceFiles/ui/text/text_block.h | 212 - Telegram/SourceFiles/ui/text/text_entity.cpp | 2113 -- Telegram/SourceFiles/ui/text/text_entity.h | 355 - .../SourceFiles/ui/text/text_isolated_emoji.h | 46 - .../SourceFiles/ui/text/text_utilities.cpp | 74 - Telegram/SourceFiles/ui/text/text_utilities.h | 65 - Telegram/SourceFiles/ui/ui_integration.cpp | 124 - Telegram/SourceFiles/ui/ui_integration.h | 73 - Telegram/SourceFiles/ui/ui_log.cpp | 20 - Telegram/SourceFiles/ui/ui_log.h | 16 - Telegram/SourceFiles/ui/ui_pch.cpp | 10 - Telegram/SourceFiles/ui/ui_pch.h | 34 - Telegram/SourceFiles/ui/ui_utility.cpp | 183 - Telegram/SourceFiles/ui/ui_utility.h | 196 - Telegram/SourceFiles/ui/widgets/buttons.cpp | 635 - Telegram/SourceFiles/ui/widgets/buttons.h | 231 - Telegram/SourceFiles/ui/widgets/checkbox.cpp | 777 - Telegram/SourceFiles/ui/widgets/checkbox.h | 362 - .../ui/widgets/continuous_sliders.cpp | 2 +- .../SourceFiles/ui/widgets/dropdown_menu.cpp | 284 - .../SourceFiles/ui/widgets/dropdown_menu.h | 101 - .../SourceFiles/ui/widgets/inner_dropdown.cpp | 405 - .../SourceFiles/ui/widgets/inner_dropdown.h | 149 - .../SourceFiles/ui/widgets/input_fields.cpp | 4023 ---- .../SourceFiles/ui/widgets/input_fields.h | 696 - Telegram/SourceFiles/ui/widgets/labels.cpp | 894 - Telegram/SourceFiles/ui/widgets/labels.h | 240 - Telegram/SourceFiles/ui/widgets/menu.cpp | 478 - Telegram/SourceFiles/ui/widgets/menu.h | 132 - .../SourceFiles/ui/widgets/popup_menu.cpp | 524 - Telegram/SourceFiles/ui/widgets/popup_menu.h | 128 - .../SourceFiles/ui/widgets/scroll_area.cpp | 725 - Telegram/SourceFiles/ui/widgets/scroll_area.h | 249 - Telegram/SourceFiles/ui/widgets/shadow.cpp | 117 - Telegram/SourceFiles/ui/widgets/shadow.h | 64 - Telegram/SourceFiles/ui/widgets/tooltip.cpp | 397 - Telegram/SourceFiles/ui/widgets/tooltip.h | 110 - Telegram/SourceFiles/ui/widgets/widgets.style | 1191 -- Telegram/SourceFiles/ui/wrap/fade_wrap.cpp | 118 - Telegram/SourceFiles/ui/wrap/fade_wrap.h | 123 - Telegram/SourceFiles/ui/wrap/padding_wrap.cpp | 101 - Telegram/SourceFiles/ui/wrap/padding_wrap.h | 110 - Telegram/SourceFiles/ui/wrap/slide_wrap.cpp | 156 - Telegram/SourceFiles/ui/wrap/slide_wrap.h | 142 - .../SourceFiles/ui/wrap/vertical_layout.cpp | 188 - .../SourceFiles/ui/wrap/vertical_layout.h | 79 - Telegram/SourceFiles/ui/wrap/wrap.h | 201 - Telegram/SourceFiles/window/main_window.cpp | 2 +- Telegram/SourceFiles/window/window.style | 3 +- .../window/window_outdated_bar.cpp | 2 +- Telegram/ThirdParty/codegen | 1 + .../emoji_suggestions/emoji_suggestions.cpp | 434 - .../emoji_suggestions/emoji_suggestions.h | 93 - Telegram/ThirdParty/gyp_helpers | 2 +- Telegram/ThirdParty/lib_base | 2 +- Telegram/ThirdParty/lib_ui | 1 + Telegram/gyp/codegen.gyp | 158 - Telegram/gyp/codegen/rules.gypi | 16 - Telegram/gyp/codegen/rules_ui.gypi | 55 - Telegram/gyp/codegen/styles_rule.gypi | 54 - Telegram/gyp/lib_ui.gyp | 68 - Telegram/gyp/lib_ui/sources.txt | 103 - Telegram/gyp/list_sources.py | 149 - Telegram/gyp/telegram/sources.txt | 13 - Telegram/gyp/telegram/telegram.gypi | 33 +- Telegram/gyp/update_dependent.py | 171 - 255 files changed, 122 insertions(+), 61372 deletions(-) delete mode 100644 Telegram/Resources/basic.style delete mode 100644 Telegram/Resources/colors.palette delete mode 100644 Telegram/Resources/emoji_autocomplete.json delete mode 100644 Telegram/SourceFiles/chat_helpers/emoji_suggestions_helper.h delete mode 100644 Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/common/basic_tokenized_file.h delete mode 100644 Telegram/SourceFiles/codegen/common/checked_utf8_string.cpp delete mode 100644 Telegram/SourceFiles/codegen/common/checked_utf8_string.h delete mode 100644 Telegram/SourceFiles/codegen/common/clean_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/common/clean_file.h delete mode 100644 Telegram/SourceFiles/codegen/common/clean_file_reader.h delete mode 100644 Telegram/SourceFiles/codegen/common/const_utf8_string.h delete mode 100644 Telegram/SourceFiles/codegen/common/cpp_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/common/cpp_file.h delete mode 100644 Telegram/SourceFiles/codegen/common/logging.cpp delete mode 100644 Telegram/SourceFiles/codegen/common/logging.h delete mode 100644 Telegram/SourceFiles/codegen/emoji/data.cpp delete mode 100644 Telegram/SourceFiles/codegen/emoji/data.h delete mode 100644 Telegram/SourceFiles/codegen/emoji/generator.cpp delete mode 100644 Telegram/SourceFiles/codegen/emoji/generator.h delete mode 100644 Telegram/SourceFiles/codegen/emoji/main.cpp delete mode 100644 Telegram/SourceFiles/codegen/emoji/options.cpp delete mode 100644 Telegram/SourceFiles/codegen/emoji/options.h delete mode 100644 Telegram/SourceFiles/codegen/emoji/replaces.cpp delete mode 100644 Telegram/SourceFiles/codegen/emoji/replaces.h delete mode 100644 Telegram/SourceFiles/codegen/lang/generator.cpp delete mode 100644 Telegram/SourceFiles/codegen/lang/generator.h delete mode 100644 Telegram/SourceFiles/codegen/lang/main.cpp delete mode 100644 Telegram/SourceFiles/codegen/lang/options.cpp delete mode 100644 Telegram/SourceFiles/codegen/lang/options.h delete mode 100644 Telegram/SourceFiles/codegen/lang/parsed_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/lang/parsed_file.h delete mode 100644 Telegram/SourceFiles/codegen/lang/processor.cpp delete mode 100644 Telegram/SourceFiles/codegen/lang/processor.h delete mode 100644 Telegram/SourceFiles/codegen/numbers/generator.cpp delete mode 100644 Telegram/SourceFiles/codegen/numbers/generator.h delete mode 100644 Telegram/SourceFiles/codegen/numbers/main.cpp delete mode 100644 Telegram/SourceFiles/codegen/numbers/options.cpp delete mode 100644 Telegram/SourceFiles/codegen/numbers/options.h delete mode 100644 Telegram/SourceFiles/codegen/numbers/parsed_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/numbers/parsed_file.h delete mode 100644 Telegram/SourceFiles/codegen/numbers/processor.cpp delete mode 100644 Telegram/SourceFiles/codegen/numbers/processor.h delete mode 100644 Telegram/SourceFiles/codegen/style/generator.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/generator.h delete mode 100644 Telegram/SourceFiles/codegen/style/main.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/module.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/module.h delete mode 100644 Telegram/SourceFiles/codegen/style/options.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/options.h delete mode 100644 Telegram/SourceFiles/codegen/style/parsed_file.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/parsed_file.h delete mode 100644 Telegram/SourceFiles/codegen/style/processor.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/processor.h delete mode 100644 Telegram/SourceFiles/codegen/style/structure_types.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/structure_types.h delete mode 100644 Telegram/SourceFiles/platform/linux/info_linux.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/info_linux.h delete mode 100644 Telegram/SourceFiles/platform/mac/info_mac.cpp delete mode 100644 Telegram/SourceFiles/platform/mac/info_mac.h delete mode 100644 Telegram/SourceFiles/platform/mac/info_mac.mm delete mode 100644 Telegram/SourceFiles/platform/mac/mac_utilities.h delete mode 100644 Telegram/SourceFiles/platform/mac/mac_utilities.mm delete mode 100644 Telegram/SourceFiles/platform/platform_info.h delete mode 100644 Telegram/SourceFiles/platform/win/info_win.cpp delete mode 100644 Telegram/SourceFiles/platform/win/info_win.h delete mode 100644 Telegram/SourceFiles/platform/win/wrapper_windows_h.h delete mode 100644 Telegram/SourceFiles/ui/abstract_button.cpp delete mode 100644 Telegram/SourceFiles/ui/abstract_button.h delete mode 100644 Telegram/SourceFiles/ui/basic_click_handlers.cpp delete mode 100644 Telegram/SourceFiles/ui/basic_click_handlers.h delete mode 100644 Telegram/SourceFiles/ui/click_handler.cpp delete mode 100644 Telegram/SourceFiles/ui/click_handler.h delete mode 100644 Telegram/SourceFiles/ui/delayed_activation.cpp delete mode 100644 Telegram/SourceFiles/ui/delayed_activation.h delete mode 100644 Telegram/SourceFiles/ui/effects/animation_value.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/animation_value.h delete mode 100644 Telegram/SourceFiles/ui/effects/animations.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/animations.h delete mode 100644 Telegram/SourceFiles/ui/effects/cross_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/cross_animation.h delete mode 100644 Telegram/SourceFiles/ui/effects/fade_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/fade_animation.h delete mode 100644 Telegram/SourceFiles/ui/effects/numbers_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/numbers_animation.h delete mode 100644 Telegram/SourceFiles/ui/effects/panel_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/panel_animation.h delete mode 100644 Telegram/SourceFiles/ui/effects/ripple_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/ripple_animation.h delete mode 100644 Telegram/SourceFiles/ui/emoji_config.cpp delete mode 100644 Telegram/SourceFiles/ui/emoji_config.h delete mode 100644 Telegram/SourceFiles/ui/focus_persister.h delete mode 100644 Telegram/SourceFiles/ui/image/image_prepare.cpp delete mode 100644 Telegram/SourceFiles/ui/image/image_prepare.h delete mode 100644 Telegram/SourceFiles/ui/inactive_press.cpp delete mode 100644 Telegram/SourceFiles/ui/inactive_press.h delete mode 100644 Telegram/SourceFiles/ui/main_queue_processor.cpp delete mode 100644 Telegram/SourceFiles/ui/main_queue_processor.h delete mode 100644 Telegram/SourceFiles/ui/painter.h delete mode 100644 Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.cpp delete mode 100644 Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.h delete mode 100644 Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.h delete mode 100644 Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.mm delete mode 100644 Telegram/SourceFiles/ui/platform/ui_platform_utility.h delete mode 100644 Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.cpp delete mode 100644 Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.h delete mode 100644 Telegram/SourceFiles/ui/rect_part.h delete mode 100644 Telegram/SourceFiles/ui/round_rect.cpp delete mode 100644 Telegram/SourceFiles/ui/round_rect.h delete mode 100644 Telegram/SourceFiles/ui/rp_widget.cpp delete mode 100644 Telegram/SourceFiles/ui/rp_widget.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_color.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_color.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_direction.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_direction.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_font.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_font.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_icon.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_icon.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_scale.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_scale.h delete mode 100644 Telegram/SourceFiles/ui/style/style_core_types.cpp delete mode 100644 Telegram/SourceFiles/ui/style/style_core_types.h delete mode 100644 Telegram/SourceFiles/ui/text/text.cpp delete mode 100644 Telegram/SourceFiles/ui/text/text.h delete mode 100644 Telegram/SourceFiles/ui/text/text_block.cpp delete mode 100644 Telegram/SourceFiles/ui/text/text_block.h delete mode 100644 Telegram/SourceFiles/ui/text/text_entity.cpp delete mode 100644 Telegram/SourceFiles/ui/text/text_entity.h delete mode 100644 Telegram/SourceFiles/ui/text/text_isolated_emoji.h delete mode 100644 Telegram/SourceFiles/ui/text/text_utilities.cpp delete mode 100644 Telegram/SourceFiles/ui/text/text_utilities.h delete mode 100644 Telegram/SourceFiles/ui/ui_integration.cpp delete mode 100644 Telegram/SourceFiles/ui/ui_integration.h delete mode 100644 Telegram/SourceFiles/ui/ui_log.cpp delete mode 100644 Telegram/SourceFiles/ui/ui_log.h delete mode 100644 Telegram/SourceFiles/ui/ui_pch.cpp delete mode 100644 Telegram/SourceFiles/ui/ui_pch.h delete mode 100644 Telegram/SourceFiles/ui/ui_utility.cpp delete mode 100644 Telegram/SourceFiles/ui/ui_utility.h delete mode 100644 Telegram/SourceFiles/ui/widgets/buttons.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/buttons.h delete mode 100644 Telegram/SourceFiles/ui/widgets/checkbox.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/checkbox.h delete mode 100644 Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/dropdown_menu.h delete mode 100644 Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/inner_dropdown.h delete mode 100644 Telegram/SourceFiles/ui/widgets/input_fields.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/input_fields.h delete mode 100644 Telegram/SourceFiles/ui/widgets/labels.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/labels.h delete mode 100644 Telegram/SourceFiles/ui/widgets/menu.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/menu.h delete mode 100644 Telegram/SourceFiles/ui/widgets/popup_menu.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/popup_menu.h delete mode 100644 Telegram/SourceFiles/ui/widgets/scroll_area.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/scroll_area.h delete mode 100644 Telegram/SourceFiles/ui/widgets/shadow.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/shadow.h delete mode 100644 Telegram/SourceFiles/ui/widgets/tooltip.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/tooltip.h delete mode 100644 Telegram/SourceFiles/ui/widgets/widgets.style delete mode 100644 Telegram/SourceFiles/ui/wrap/fade_wrap.cpp delete mode 100644 Telegram/SourceFiles/ui/wrap/fade_wrap.h delete mode 100644 Telegram/SourceFiles/ui/wrap/padding_wrap.cpp delete mode 100644 Telegram/SourceFiles/ui/wrap/padding_wrap.h delete mode 100644 Telegram/SourceFiles/ui/wrap/slide_wrap.cpp delete mode 100644 Telegram/SourceFiles/ui/wrap/slide_wrap.h delete mode 100644 Telegram/SourceFiles/ui/wrap/vertical_layout.cpp delete mode 100644 Telegram/SourceFiles/ui/wrap/vertical_layout.h delete mode 100644 Telegram/SourceFiles/ui/wrap/wrap.h create mode 160000 Telegram/ThirdParty/codegen delete mode 100644 Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp delete mode 100644 Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.h create mode 160000 Telegram/ThirdParty/lib_ui delete mode 100644 Telegram/gyp/codegen.gyp delete mode 100644 Telegram/gyp/codegen/rules_ui.gypi delete mode 100644 Telegram/gyp/codegen/styles_rule.gypi delete mode 100644 Telegram/gyp/lib_ui.gyp delete mode 100644 Telegram/gyp/lib_ui/sources.txt delete mode 100644 Telegram/gyp/list_sources.py delete mode 100644 Telegram/gyp/update_dependent.py diff --git a/.gitmodules b/.gitmodules index 2e9eb629e..65ea7c340 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,9 @@ [submodule "Telegram/ThirdParty/gyp_helpers"] path = Telegram/ThirdParty/gyp_helpers url = https://github.com/desktop-app/gyp_helpers.git +[submodule "Telegram/ThirdParty/codegen"] + path = Telegram/ThirdParty/codegen + url = https://github.com/desktop-app/codegen.git +[submodule "Telegram/ThirdParty/lib_ui"] + path = Telegram/ThirdParty/lib_ui + url = https://github.com/desktop-app/lib_ui.git diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style deleted file mode 100644 index a6cd8f0b2..000000000 --- a/Telegram/Resources/basic.style +++ /dev/null @@ -1,303 +0,0 @@ -/* -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 -*/ -using "colors.palette"; - -TextPalette { - linkFg: color; - monoFg: color; - selectBg: color; - selectFg: color; - selectLinkFg: color; - selectMonoFg: color; - selectOverlay: color; -} - -TextStyle { - font: font; - linkFont: font; - linkFontOver: font; - lineHeight: pixels; -} - -semibold: "Open Sans Semibold"; - -fsize: 13px; -normalFont: font(fsize); -semiboldFont: font(fsize semibold); -boxFontSize: 14px; -boxTextFont: font(boxFontSize); - -emojiImgSize: 18px; // exceptional value for retina -emojiSize: 18px; -emojiPadding: 1px; - -lineWidth: 1px; - -defaultTextPalette: TextPalette { - linkFg: windowActiveTextFg; - monoFg: msgInMonoFg; - selectBg: msgInBgSelected; - selectFg: transparent; // use painter current pen instead - selectLinkFg: historyLinkInFgSelected; - selectMonoFg: msgInMonoFgSelected; - selectOverlay: msgSelectOverlay; -} -defaultTextStyle: TextStyle { - font: normalFont; - linkFont: normalFont; - linkFontOver: font(fsize underline); - lineHeight: 0px; -} -semiboldTextStyle: TextStyle(defaultTextStyle) { - font: semiboldFont; - linkFont: semiboldFont; - linkFontOver: font(fsize semibold underline); -} - -shadowToggleDuration: 200; - -slideDuration: 240; -slideShift: 100px; -slideShadow: icon {{ "slide_shadow", slideFadeOutShadowFg }}; - -slideWrapDuration: 150; -fadeWrapDuration: 200; - -linkCropLimit: 360px; -linkFont: normalFont; -linkOverFont: font(fsize underline); - -roundRadiusLarge: 6px; -roundRadiusSmall: 3px; - -dateRadius: roundRadiusLarge; -buttonRadius: roundRadiusSmall; - -setLittleSkip: 9px; - -noContactsHeight: 100px; -noContactsFont: font(fsize); -noContactsColor: windowSubTextFg; - -activeFadeInDuration: 500; -activeFadeOutDuration: 3000; - -msgMaxWidth: 430px; -msgFont: font(fsize); -msgNameFont: semiboldFont; -msgNameStyle: semiboldTextStyle; -msgServiceFont: semiboldFont; -msgServiceNameFont: semiboldFont; -msgServicePhotoWidth: 100px; -msgDateFont: font(13px); -msgMinWidth: 160px; -msgPhotoSize: 33px; -msgPhotoSkip: 40px; -msgPadding: margins(13px, 7px, 13px, 8px); -msgMargin: margins(16px, 6px, 56px, 2px); -msgMarginTopAttached: 1px; -msgLnkPadding: 2px; // for media open / save links -msgShadow: 2px; - -msgReplyPadding: margins(6px, 6px, 11px, 6px); -msgReplyBarPos: point(1px, 0px); -msgReplyBarSize: size(2px, 36px); -msgReplyBarSkip: 10px; -msgServicePadding: margins(12px, 3px, 12px, 4px); -msgServiceMargin: margins(10px, 10px, 10px, 2px); - -msgDateSpace: 12px; -msgDateDelta: point(2px, 5px); - -msgDateImgDelta: 4px; -msgDateImgPadding: point(8px, 2px); -msgDateImgCheckSpace: 4px; - -messageTextStyle: defaultTextStyle; -msgDateTextStyle: defaultTextStyle; -serviceTextPalette: TextPalette(defaultTextPalette) { - linkFg: msgServiceFg; - monoFg: msgServiceFg; - selectBg: msgServiceBgSelected; - selectFg: msgServiceFg; - selectLinkFg: msgServiceFg; - selectMonoFg: msgServiceFg; - selectOverlay: msgServiceBgSelected; -} -serviceTextStyle: TextStyle(defaultTextStyle) { - font: msgServiceFont; - linkFont: msgServiceFont; - linkFontOver: font(fsize semibold underline); -} -inTextPalette: TextPalette(defaultTextPalette) { - linkFg: historyLinkInFg; - monoFg: msgInMonoFg; - selectBg: msgInBgSelected; - selectFg: historyTextInFgSelected; - selectLinkFg: historyLinkInFgSelected; - selectMonoFg: msgInMonoFgSelected; - selectOverlay: msgSelectOverlay; -} -inTextPaletteSelected: TextPalette(inTextPalette) { - linkFg: historyLinkInFgSelected; - monoFg: msgInMonoFgSelected; -} -outTextPalette: TextPalette(defaultTextPalette) { - linkFg: historyLinkOutFg; - monoFg: msgOutMonoFg; - selectBg: msgOutBgSelected; - selectFg: historyTextOutFgSelected; - selectLinkFg: historyLinkOutFgSelected; - selectMonoFg: msgOutMonoFgSelected; - selectOverlay: msgSelectOverlay; -} -outTextPaletteSelected: TextPalette(outTextPalette) { - linkFg: historyLinkOutFgSelected; - monoFg: msgOutMonoFgSelected; -} -fwdTextStyle: TextStyle(semiboldTextStyle) { - linkFontOver: semiboldFont; -} -inFwdTextPalette: TextPalette(defaultTextPalette) { - linkFg: msgInServiceFg; -} -outFwdTextPalette: TextPalette(defaultTextPalette) { - linkFg: msgOutServiceFg; -} -inFwdTextPaletteSelected: TextPalette(defaultTextPalette) { - linkFg: msgInServiceFgSelected; -} -outFwdTextPaletteSelected: TextPalette(defaultTextPalette) { - linkFg: msgOutServiceFgSelected; -} -inReplyTextPalette: TextPalette(inTextPalette) { - linkFg: msgInDateFg; -} -inReplyTextPaletteSelected: TextPalette(inTextPaletteSelected) { - linkFg: msgInDateFgSelected; -} -outReplyTextPalette: TextPalette(outTextPalette) { - linkFg: msgOutDateFg; -} -outReplyTextPaletteSelected: TextPalette(outTextPaletteSelected) { - linkFg: msgOutDateFgSelected; -} -imgReplyTextPalette: TextPalette(defaultTextPalette) { - linkFg: msgImgReplyBarColor; -} -inSemiboldPalette: TextPalette(inTextPalette) { - linkFg: msgInServiceFg; - selectFg: msgInServiceFgSelected; - selectLinkFg: msgInServiceFgSelected; -} -outSemiboldPalette: TextPalette(outTextPalette) { - linkFg: msgOutServiceFg; - selectFg: msgOutServiceFgSelected; - selectLinkFg: msgOutServiceFgSelected; -} -historyComposeAreaPalette: TextPalette(defaultTextPalette) { - linkFg: historyComposeAreaFgService; -} - -mediaCaptionSkip: 5px; -mediaInBubbleSkip: 5px; -mediaThumbSize: 48px; -mediaNameTop: 3px; -mediaDetailsShift: 3px; -mediaUnreadSize: 7px; -mediaUnreadSkip: 5px; -mediaUnreadTop: 6px; - -mediaInPalette: TextPalette(defaultTextPalette) { - linkFg: mediaInFg; -} -mediaInPaletteSelected: TextPalette(defaultTextPalette) { - linkFg: mediaInFgSelected; -} - -textRectMargins: margins(-2px, -1px, -2px, -1px); - -searchedBarHeight: 32px; -searchedBarFont: normalFont; -searchedBarPosition: point(17px, 7px); - -smallCloseIcon: icon {{ "simple_close", smallCloseIconFg }}; -smallCloseIconOver: icon {{ "simple_close", smallCloseIconFgOver }}; -dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }}; - -emojiTextFont: font(15px); -emojiReplaceWidth: 52px; -emojiReplaceHeight: 56px; -emojiReplaceInnerHeight: 42px; -emojiReplacePadding: 14px; - -dragFont: font(28px semibold); -dragSubfont: font(20px semibold); -dragColor: windowSubTextFg; -dragDropColor: windowActiveTextFg; - -dragMargin: margins(0px, 10px, 0px, 10px); -dragPadding: margins(20px, 10px, 20px, 10px); - -dragHeight: 72px; - -radialSize: size(50px, 50px); -radialLine: 3px; -radialDuration: 350; -radialPeriod: 3000; - -youtubeIcon: icon { - { "media_youtube_play_bg", youtubePlayIconBg }, - { "media_youtube_play", youtubePlayIconFg, point(24px, 12px) }, -}; -videoIcon: icon { - { "media_video_play_bg", videoPlayIconBg }, - { "media_video_play", videoPlayIconFg, point(12px, 12px) }, -}; -locationSize: size(320px, 240px); - -mediaPlayerSuppressDuration: 150; - -botDescSkip: 8px; - -inlineResultsLeft: 11px; -inlineResultsSkip: 3px; -inlineMediaHeight: 96px; -inlineThumbSize: 64px; -inlineThumbSkip: 10px; -inlineTitleFg: windowFg; -inlineDescriptionFg: windowSubTextFg; -inlineRowMargin: 6px; -inlineRowBorder: 1px; -inlineRowBorderFg: shadowFg; -inlineRowFileNameTop: 2px; -inlineRowFileDescriptionTop: 23px; -inlineResultsMinWidth: 48px; -inlineDurationMargin: 3px; - -toastTextStyle: defaultTextStyle; -toastMaxWidth: 480px; -toastMinMargin: 13px; -toastPadding: margins(19px, 13px, 19px, 12px); -toastFadeInDuration: 200; -toastFadeOutDuration: 1000; - -historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }}; -historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }}; -boxTitleCloseIcon: icon {{ "box_button_close", boxTitleCloseFg }}; -boxTitleCloseIconOver: icon {{ "box_button_close", boxTitleCloseFgOver }}; - -notifyFadeRight: icon {{ "fade_horizontal", notificationBg }}; - -stickerIconLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }}; -stickerIconRight: icon {{ "fade_horizontal", emojiPanCategories }}; - -emojiSuggestionsFadeLeft: icon {{ "fade_horizontal-flip_horizontal", boxBg }}; -emojiSuggestionsFadeRight: icon {{ "fade_horizontal", boxBg }}; - -transparentPlaceholderSize: 4px; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette deleted file mode 100644 index 82473313e..000000000 --- a/Telegram/Resources/colors.palette +++ /dev/null @@ -1,559 +0,0 @@ -/* -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 -*/ - -// basic -windowBg: #ffffff; // white: fallback for background -windowFg: #000000; // black: fallback for text -windowBgOver: #f1f1f1; // light gray: fallback for background with mouse over -windowBgRipple: #e5e5e5; // darker gray: fallback for ripple effect -windowFgOver: windowFg; // black: fallback for text with mouse over -windowSubTextFg: #999999; // gray: fallback for additional text -windowSubTextFgOver: #919191; // darker gray: fallback for additional text with mouse over -windowBoldFg: #222222; // dark gray: fallback for bold text -windowBoldFgOver: #222222; // dark gray: fallback for bold text with mouse over -windowBgActive: #40a7e3; // bright blue: fallback for blue filled active areas -windowFgActive: #ffffff; // white: fallback for text on active areas -windowActiveTextFg: #168acd; // online blue: fallback for active text like online status -windowShadowFg: #000000; // black: fallback for shadow -windowShadowFgFallback: #f1f1f1; // gray: fallback for shadow without opacity - -shadowFg: #00000018; // most shadows (including opacity) -slideFadeOutBg: #0000003c; // slide animation (chat to profile) fade out filling -slideFadeOutShadowFg: windowShadowFg; // slide animation (chat to profile) fade out right section shadow - -imageBg: #000000; // image background fallback (when photo size is less than minimum allowed) -imageBgTransparent: #ffffff; // image background when displaying an image with opacity where no opacity is needed - -// widgets -activeButtonBg: windowBgActive; // default active button background -activeButtonBgOver: #39a5db; // default active button background with mouse over -activeButtonBgRipple: #2095d0; // default active button ripple effect -activeButtonFg: windowFgActive; // default active button text -activeButtonFgOver: activeButtonFg; // default active button text with mouse over -activeButtonSecondaryFg: #cceeff; // default active button additional text (selected messages counter in forward / delete buttons) -activeButtonSecondaryFgOver: activeButtonSecondaryFg; // default active button additional text with mouse over -activeLineFg: #37a1de; // default active line (like code input field bottom border when you log in and field is focused) -activeLineFgError: #e48383; // default active line for error state (like code input field bottom border when you log in and you've entered incorrect code) - -lightButtonBg: windowBg; // default light button background (like buttons in boxes) -lightButtonBgOver: #e3f1fa; // default light button background with mouse over -lightButtonBgRipple: #c9e4f6; // default light button ripple effect -lightButtonFg: windowActiveTextFg; // default light button text -lightButtonFgOver: lightButtonFg; // default light button text with mouse over - -attentionButtonFg: #d14e4e; // default attention button text (like confirm button on log out) -attentionButtonFgOver: #d14e4e; // default attention button text with mouse over -attentionButtonBgOver: #fcdfde; // default attention button background with mouse over -attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect - -menuBg: windowBg; // default popup menu background -menuBgOver: windowBgOver; // default popup menu item background with mouse over -menuBgRipple: windowBgRipple; // default popup menu item ripple effect -menuIconFg: #a8a8a8; // default popup menu item icon (like main menu) -menuIconFgOver: #999999; // default popup menu item icon with mouse over -menuSubmenuArrowFg: #373737; // default popup menu submenu arrow icon (like in message field context menu in case of RTL system language) -menuFgDisabled: #cccccc; // default popup menu item disabled text (like unavailable items in message field context menu) -menuSeparatorFg: #f1f1f1; // default popup menu separator (like in message field context menu) - -scrollBarBg: #00000053; // default scroll bar current rectangle, the bar itself (like in chats list) -scrollBarBgOver: #0000007a; // default scroll bar current rectangle with mouse over it -scrollBg: #0000001a; // default scroll bar background -scrollBgOver: #0000002c; // default scroll bar background with mouse over the scroll bar - -smallCloseIconFg: #c7c7c7; // small X icon (like in Show all sessions box to the right for sessions termination) -smallCloseIconFgOver: #a3a3a3; // small X icon with mouse over - -radialFg: windowFgActive; // default radial loader line (like in Media Viewer when loading a photo) -radialBg: #00000056; // default radial loader background (like in Media Viewer when loading a photo) - -placeholderFg: windowSubTextFg; // default input field placeholder when field is not focused (like in phone input field when you log in) -placeholderFgActive: #aaaaaa; // default input field placeholder when field is focused -inputBorderFg: #e0e0e0; // default input field bottom border (like in code input field when you log in and field is not focused) -filterInputBorderFg: #54c3f3; // default rounded input field border (like in chats list search field when field is focused) -filterInputActiveBg: windowBg; // default rounded input field active background (like in chats list search field when field is focused) -filterInputInactiveBg: windowBgOver; // default rounded input field inactive background (like in chats list search field when field is inactive) -checkboxFg: #b3b3b3; // default unchecked checkbox rounded rectangle - -botKbBg: menuBgOver; // bot keyboard button background -botKbDownBg: menuBgRipple; // bot keyboard button ripple effect -botKbColor: windowBoldFgOver; // bot keyboard button text - -sliderBgInactive: #e1eaef; // default slider not active bar (like in Settings when you choose interface scale or custom notifications count) -sliderBgActive: windowBgActive; // default slider active bar (like in Settings when you choose interface scale or custom notifications count) - -tooltipBg: #eef2f5; // tooltip background (like when you put mouse over the message timestamp and wait) -tooltipFg: #5d6c80; // tooltip text -tooltipBorderFg: #c9d1db; // tooltip border - -// custom title bar for Windows and macOS -titleShadow: #00000003; // one pixel line shadow at the bottom of custom window title -titleBg: windowBgOver; // custom window title background when window is inactive -titleBgActive: titleBg; // custom window title background when window is active -titleButtonBg: titleBg; // custom window title minimize/maximize/restore button background when window is inactive (Windows only) -titleButtonFg: #ababab; // custom window title minimize/maximize/restore button icon when window is inactive (Windows only) -titleButtonBgOver: #e5e5e5; // custom window title minimize/maximize/restore button background with mouse over when window is inactive (Windows only) -titleButtonFgOver: #9a9a9a; // custom window title minimize/maximize/restore button icon with mouse over when window is inactive (Windows only) -titleButtonBgActive: titleButtonBg; // custom window title minimize/maximize/restore button background when window is active (Windows only) -titleButtonFgActive: titleButtonFg; // custom window title minimize/maximize/restore button icon when window is active (Windows only) -titleButtonBgActiveOver: titleButtonBgOver; // custom window title minimize/maximize/restore button background with mouse over when window is active (Windows only) -titleButtonFgActiveOver: titleButtonFgOver; // custom window title minimize/maximize/restore button icon with mouse over when window is active (Windows only) -titleButtonCloseBg: titleButtonBg; // custom window title close button background when window is inactive (Windows only) -titleButtonCloseFg: titleButtonFg; // custom window title close button icon when window is inactive (Windows only) -titleButtonCloseBgOver: #e81123; // custom window title close button background with mouse over when window is inactive (Windows only) -titleButtonCloseFgOver: windowFgActive; // custom window title close button icon with mouse over when window is inactive (Windows only) -titleButtonCloseBgActive: titleButtonCloseBg; // custom window title close button background when window is active (Windows only) -titleButtonCloseFgActive: titleButtonCloseFg; // custom window title close button icon when window is active (Windows only) -titleButtonCloseBgActiveOver: titleButtonCloseBgOver; // custom window title close button background with mouse over when window is active (Windows only) -titleButtonCloseFgActiveOver: titleButtonCloseFgOver; // custom window title close button icon with mouse over when window is active (Windows only) -titleFg: #acacac; // custom window title text when window is inactive (macOS only) -titleFgActive: #3e3c3e; // custom window title text when window is active (macOS only) - -// tray icon -trayCounterBg: #f23c34; // tray icon counter background -trayCounterBgMute: #888888; // tray icon counter background if all unread messages are muted -trayCounterFg: #ffffff; // tray icon counter text -trayCounterBgMacInvert: #ffffff; // tray icon counter background when tray icon is pressed or when dark theme of macOS is used (macOS only) -trayCounterFgMacInvert: #ffffff01; // tray icon counter text when tray icon is pressed or when dark theme of macOS is used (macOS only) - -// layers -layerBg: #0000007f; // box and main menu background layer fade - -cancelIconFg: menuIconFg; // default for settings close icon and box search cancel icon -cancelIconFgOver: menuIconFgOver; // default for settings close icon and box search cancel icon with mouse over - -// boxes -boxBg: windowBg; // box background -boxTextFg: windowFg; // box text -boxTextFgGood: #4ab44a; // accepted box text (like when choosing username that is not occupied) -boxTextFgError: #d84d4d; // rejecting box text (like when choosing username that is occupied) -boxTitleFg: #404040; // box title text -boxSearchBg: boxBg; // box search field background (like in contacts box) - -boxTitleAdditionalFg: #808080; // box title additional text (like in create group box when you see chosen members count) -boxTitleCloseFg: cancelIconFg; // settings close icon and box search cancel icon (like in contacts box) -boxTitleCloseFgOver: cancelIconFgOver; // settings close icon and box search cancel icon (like in contacts box) with mouse over - -//boxSearchCancelIconFg: cancelIconFg; // search cancel X button icon (like in contacts box) (not implemented yet) -//boxSearchCancelIconFgOver: cancelIconFgOver; // search cancel X button icon with mouse over (not implemented yet) - -membersAboutLimitFg: windowSubTextFgOver; // text in channel members box about the limit (max 200 last members are shown) - -contactsBg: windowBg; // contacts (and some other) box row background -contactsBgOver: windowBgOver; // contacts (and some other) box row background with mouse over -contactsNameFg: boxTextFg; // contacts (and some other) box row name text -contactsStatusFg: windowSubTextFg; // contacts (and some other) box row additional text (like last seen stamp) -contactsStatusFgOver: windowSubTextFgOver; // contacts (and some other) box row additional text (like last seen stamp) with mouse over -contactsStatusFgOnline: windowActiveTextFg; // contacts (and some other) box row active additional text (like online status) - -photoCropFadeBg: layerBg; // avatar crop box fade background (when choosing a new photo in Settings or for a group) -photoCropPointFg: #ffffff7f; // avatar crop box corner rectangles (when choosing a new photo in Settings or for a group) - -callArrowFg: #2ab32a | boxTextFgGood; // received phone call arrow (in calls list box) -callArrowMissedFg: #dd5b4a | boxTextFgError; // missed phone call arrow (in calls list box) - -// intro -introBg: windowBg; // login background -introTitleFg: windowBoldFg; // login title text -introDescriptionFg: windowSubTextFg; // login description text -introErrorFg: windowSubTextFg; // login error text (like when providing a wrong log in code) - -introCoverTopBg: #0f89d0; // intro gradient top (from) -introCoverBottomBg: #39b0f0; // intro gradient bottom (to) -introCoverIconsFg: #5ec6ff; // intro cloud graphics -introCoverPlaneTrace: #5ec6ff69; // intro plane traces -introCoverPlaneInner: #c6d8e8; // intro plane part -introCoverPlaneOuter: #a1bed4; // intro plane part -introCoverPlaneTop: #ffffff; // intro plane part - -// dialogs -dialogsMenuIconFg: menuIconFg; // main menu and lock telegram icon -dialogsMenuIconFgOver: menuIconFgOver; // main menu and lock telegram icon with mouse over - -dialogsBg: windowBg; // chat list background -dialogsNameFg: windowBoldFg; // chat list name text -dialogsChatIconFg: dialogsNameFg; // chat list group or channel icon -dialogsDateFg: windowSubTextFg; // chat list date text -dialogsTextFg: windowSubTextFg; // chat list message text -dialogsTextFgService: windowActiveTextFg; // chat list group sender name text (or media message type text) -dialogsDraftFg: #dd4b39; // chat list draft label -dialogsVerifiedIconBg: windowBgActive; // chat list verified icon background -dialogsVerifiedIconFg: windowFgActive; // chat list verified icon check -dialogsSendingIconFg: #c1c1c1; // chat list sending message icon (clock) -dialogsSentIconFg: #5dc452; // chat list sent message tick / double tick icon -dialogsUnreadBg: windowBgActive; // chat list unread badge background for not muted chat -dialogsUnreadBgMuted: #bbbbbb; // chat list unread badge background for muted chat -dialogsUnreadFg: windowFgActive; // chat list unread badge text -dialogsArchiveFg: #525252 | dialogsNameFg; // chat list archive name text -dialogsOnlineBadgeFg: #4dc920 | dialogsUnreadBg; // chat list online status -dialogsScamFg: dialogsDraftFg; // chat list scam label - -dialogsBgOver: windowBgOver; // chat list background with mouse over -dialogsNameFgOver: windowBoldFgOver; // chat list name text with mouse over -dialogsChatIconFgOver: dialogsNameFgOver; // chat list group or channel icon with mouse over -dialogsDateFgOver: windowSubTextFgOver; // chat list date text with mouse over -dialogsTextFgOver: windowSubTextFgOver; // chat list message text with mouse over -dialogsTextFgServiceOver: dialogsTextFgService; // chat list group sender name text with mouse over -dialogsDraftFgOver: dialogsDraftFg; // chat list draft label with mouse over -dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; // chat list verified icon background with mouse over -dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; // chat list verified icon check with mouse over -dialogsSendingIconFgOver: dialogsSendingIconFg; // chat list sending message icon (clock) with mouse over -dialogsSentIconFgOver: dialogsSentIconFg; // chat list sent message tick / double tick icon with mouse over -dialogsUnreadBgOver: dialogsUnreadBg; // chat list unread badge background for not muted chat with mouse over -dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; // chat list unread badge background for muted chat with mouse over -dialogsUnreadFgOver: dialogsUnreadFg; // chat list unread badge text with mouse over -dialogsArchiveFgOver: #525252 | dialogsNameFgOver; // chat list archive name text with mouse over -dialogsScamFgOver: dialogsDraftFgOver; // chat list scam label with mouse over - -dialogsBgActive: #419fd9; // chat list background for current (active) chat -dialogsNameFgActive: windowFgActive; // chat list name text for current (active) chat -dialogsChatIconFgActive: dialogsNameFgActive; // chat list group or channel icon for current (active) chat -dialogsDateFgActive: windowFgActive; // chat list date text for current (active) chat -dialogsTextFgActive: windowFgActive; // chat list message text for current (active) chat -dialogsTextFgServiceActive: dialogsTextFgActive; // chat list group sender name text for current (active) chat -dialogsDraftFgActive: #c6e1f7; // chat list draft label for current (active) chat -dialogsVerifiedIconBgActive: dialogsTextFgActive; // chat list verified icon background for current (active) chat -dialogsVerifiedIconFgActive: dialogsBgActive; // chat list verified icon check for current (active) chat -dialogsSendingIconFgActive: #ffffff99; // chat list sending message icon (clock) for current (active) chat -dialogsSentIconFgActive: dialogsTextFgActive; // chat list sent message tick / double tick icon for current (active) chat -dialogsUnreadBgActive: dialogsTextFgActive; // chat list unread badge background for not muted chat for current (active) chat -dialogsUnreadBgMutedActive: dialogsDraftFgActive; // chat list unread badge background for muted chat for current (active) chat -dialogsUnreadFgActive: dialogsBgActive; // chat list unread badge text for current (active) chat -dialogsOnlineBadgeFgActive: #ffffff; // chat list online status for current (active) chat -dialogsScamFgActive: dialogsDraftFgActive; // chat list scam label for current (active) chat - -dialogsRippleBg: windowBgRipple; // chat list background ripple effect -dialogsRippleBgActive: activeButtonBgRipple; // chat list background ripple effect for current (active) chat - -dialogsForwardBg: dialogsBgActive; // forwarding panel background (when forwarding messages in the smallest window size) -dialogsForwardFg: dialogsNameFgActive; // forwarding panel text (when forwarding messages in the smallest window size) - -searchedBarBg: windowBgOver; // search results bar background (in chats list, contacts box..) -searchedBarFg: windowSubTextFgOver; // search results bar text (in chats list, contacts box..) - -// history -topBarBg: windowBg; // top bar background (in chat view, media overview..) - -emojiPanBg: windowBg; // emoji panel background -emojiPanCategories: #f7f7f7 | windowBg; // emoji panel categories background -emojiPanHeaderFg: windowSubTextFg; // emoji panel section header text -emojiPanHeaderBg: #fffffff2 | emojiPanBg; // emoji panel section header background -emojiIconFg: checkboxFg; // emoji category icon -emojiIconFgActive: windowBgActive; // active emoji category icon -stickerPanDeleteBg: #000000ff; // delete X button background for custom sent stickers in stickers panel (legacy) -stickerPanDeleteFg: windowFgActive; // delete X button icon for custom sent stickers in stickers panel (legacy) -stickerPreviewBg: #ffffffb0; // sticker and GIF preview background (when you press and hold on a sticker) - -historyTextInFg: windowFg; // inbox message text -historyTextInFgSelected: historyTextInFg; // inbox message selected text or text in a selected message -historyTextOutFg: windowFg; // outbox message text -historyTextOutFgSelected: historyTextOutFg; // outbox message selected text or text in a selected message -historyLinkInFg: windowActiveTextFg; // inbox message link -historyLinkInFgSelected: historyLinkInFg; // inbox message link in a selected text or message -historyLinkOutFg: windowActiveTextFg; // outbox message link -historyLinkOutFgSelected: historyLinkOutFg; // outbox message link in a selected text or message -historyFileNameInFg: historyTextInFg; // inbox media filename text -historyFileNameInFgSelected: historyFileNameInFg; // inbox media filename text in a selected message -historyFileNameOutFg: historyTextOutFg; // outbox media filename text -historyFileNameOutFgSelected: historyFileNameOutFg; // outbox media filename text in a selected message -historyOutIconFg: dialogsSentIconFg; // outbox message tick / double tick icon -historyOutIconFgSelected: #4da79f; // outbox message tick / double tick icon in a selected message -historyIconFgInverted: windowFgActive; // media message tick / double tick icon (like in sent photo) -historySendingOutIconFg: #98d292; // outbox sending message icon (clock) -historySendingInIconFg: #a0adb5; // inbox sending message icon (clock) (like in sent messages to yourself or in sent messages to a channel) -historySendingInvertedIconFg: #ffffffc8; // media sending message icon (clock) (like in sent photo) -historyCallArrowInFg: callArrowFg; // received phone call arrow -historyCallArrowInFgSelected: callArrowFg; // received phone call arrow in a selected message -historyCallArrowMissedInFg: callArrowMissedFg; // missed phone call arrow -historyCallArrowMissedInFgSelected: callArrowMissedFg; // missed phone call arrow in a selected message -historyCallArrowOutFg: historyCallArrowInFg; // outgoing phone call arrow -historyCallArrowOutFgSelected: historyCallArrowInFgSelected; // outgoing phone call arrow - -historyUnreadBarBg: #fcfbfa; // new unread messages bar background -historyUnreadBarBorder: shadowFg; // new unread messages bar shadow -historyUnreadBarFg: #538bb4; // new unread messages bar text - -historyForwardChooseBg: #0000004c; // forwarding messages in a large window size "choose recipient" background -historyForwardChooseFg: windowFgActive; // forwarding messages in a large window size "choose recipient" text - -historyPeer1NameFg: #c03d33; // red group member name -historyPeer1NameFgSelected: historyPeer1NameFg; // red group member name in a selected message -historyPeer1UserpicBg: #e17076; // red userpic background -historyPeer2NameFg: #4fad2d; // green group member name -historyPeer2NameFgSelected: historyPeer2NameFg; // green group member name in a selected message -historyPeer2UserpicBg: #7bc862; // green userpic background -historyPeer3NameFg: #d09306; // yellow group member name -historyPeer3NameFgSelected: historyPeer3NameFg; // yellow group member name in a selected message -historyPeer3UserpicBg: #e5ca77; // yellow userpic background -historyPeer4NameFg: windowActiveTextFg; // blue group member name -historyPeer4NameFgSelected: historyPeer4NameFg; // blue group member name in a selected message -historyPeer4UserpicBg: #65aadd; // blue userpic background -historyPeer5NameFg: #8544d6; // purple group member name -historyPeer5NameFgSelected: historyPeer5NameFg; // purple group member name in a selected message -historyPeer5UserpicBg: #a695e7; // purple userpic background -historyPeer6NameFg: #cd4073; // pink group member name -historyPeer6NameFgSelected: historyPeer6NameFg; // pink group member name in a selected message -historyPeer6UserpicBg: #ee7aae; // pink userpic background -historyPeer7NameFg: #2996ad; // sea group member name -historyPeer7NameFgSelected: historyPeer7NameFg; // sea group member name in a selected message -historyPeer7UserpicBg: #6ec9cb; // sea userpic background -historyPeer8NameFg: #ce671b; // orange group member name -historyPeer8NameFgSelected: historyPeer8NameFg; // orange group member name in a selected message -historyPeer8UserpicBg: #faa774; // orange userpic background -historyPeerUserpicFg: windowFgActive; // default userpic initials -historyPeerSavedMessagesBg: historyPeer4UserpicBg; // saved messages userpic background -historyPeerArchiveUserpicBg: dialogsUnreadBgMuted; // archive folder userpic background - -// Some values are marked as (adjusted), it means they're adjusted by -// hue and saturation of the average background color if user chooses -// some other (not bundled to this color theme) background. If the -// bundled background is used those colors are not adjusted in any way. -historyScrollBarBg: #517c417a; // scroll bar current rectangle, the bar itself in the chat view (adjusted) -historyScrollBarBgOver: #517c41bc; // scroll bar current rectangle with mouse over it in the chat view (adjusted) -historyScrollBg: #517c414c; // scroll bar background (adjusted) -historyScrollBgOver: #517c416b; // scroll bar background with mouse over the scroll bar (adjusted) - -msgInBg: windowBg; // inbox message background -msgInBgSelected: #c2dcf2; // inbox selected message background (and background of selected text in those messages) -msgOutBg: #effdde; // outbox message background -msgOutBgSelected: #b7dbdb; // outbox selected message background (and background of selected text in those messages) -msgSelectOverlay: #358cd44c; // overlay which is filling the media parts of selected messages (like in selected photo message) -msgStickerOverlay: #358cd47f; // overlay which is filling the selected sticker message -msgInServiceFg: windowActiveTextFg; // inbox message information text (like information about a forwarded message original sender) -msgInServiceFgSelected: windowActiveTextFg; // inbox selected message information text (like information about a forwarded message original sender) -msgOutServiceFg: #3a8e26; // outbox message information text (like information about a forwarded message original sender) -msgOutServiceFgSelected: #367570; // outbox message information text (like information about a forwarded message original sender) -msgInShadow: #748ea229; // inbox message shadow (below the bubble) -msgInShadowSelected: #548dbb29; // inbox selected message shadow (below the bubble) -msgOutShadow: #3ac34740; // outbox message shadow (below the bubble) -msgOutShadowSelected: #37a78e40; // outbox selected message shadow (below the bubble) -msgInDateFg: #a0acb6; // inbox message time text -msgInDateFgSelected: #6a9cc5; // inbox selected message time text -msgOutDateFg: #6cc264; // outbox message time text -msgOutDateFgSelected: #50a79c; // outbox selected message time text -msgServiceFg: windowFgActive; // service message text (like date dividers or service message about the group title being changed) -msgServiceBg: #517c417f; // service message background (like in a service message about group title being changed) (adjusted) -msgServiceBgSelected: #96b38ba2; // service message selected text background (like in a service message about group title being changed) (adjusted) -msgInReplyBarColor: activeLineFg; // inbox message reply outline -msgInReplyBarSelColor: activeLineFg; // inbox selected message reply outline -msgOutReplyBarColor: historyOutIconFg; // outbox message reply outline -msgOutReplyBarSelColor: historyOutIconFgSelected; // outbox selected message reply outline -msgImgReplyBarColor: msgServiceFg; // sticker message reply outline -msgInMonoFg: #4e7391; // inbox message monospace text (like a message sent with `test` text) -msgOutMonoFg: #469165; // outbox message monospace text -msgInMonoFgSelected: msgInMonoFg; // inbox message monospace text in a selected text or message -msgOutMonoFgSelected: msgOutMonoFg; // outbox message monospace text in a selected text or message -msgDateImgFg: msgServiceFg; // media message time text (like time text in a sent photo) -msgDateImgBg: #00000054; // media message time bubble background (like time bubble in a sent photo) or file with thumbnail download icon circle background -msgDateImgBgOver: #00000074; // media message download icon circle background with mouse over (like file with thumbnail download icon) -msgDateImgBgSelected: #1c4a7187; // selected media message time bubble background - -msgFileThumbLinkInFg: lightButtonFg; // inbox media file message with thumbnail download / open with button text -msgFileThumbLinkInFgSelected: lightButtonFgOver; // inbox selected media file message with thumbnail download / open with button text -msgFileThumbLinkOutFg: #5eba5b; // outbox media file message with thumbnail download / open with button text -msgFileThumbLinkOutFgSelected: #31a298; // outbox selected media file message with thumbnail download / open with button text -msgFileInBg: windowBgActive; // inbox audio file download circle background -msgFileInBgOver: #4eade3; // inbox audio file download circle background with mouse over -msgFileInBgSelected: #51a3d3; // inbox selected audio file download circle background -msgFileOutBg: #78c67f; // outbox audio file download circle background -msgFileOutBgOver: #6bc272; // outbox audio file download circle background with mouse over -msgFileOutBgSelected: #5fb389; // outbox selected audio file download circle background - -msgFile1Bg: #72b1df; // blue shared links / files without image square thumbnail -msgFile1BgDark: #5c9ece; // blue shared files without image download circle background -msgFile1BgOver: #5294c4; // blue shared files without image download circle background with mouse over -msgFile1BgSelected: #5099d0; // blue shared files without image download circle background if file is selected -msgFile2Bg: #61b96e; // green shared links / shared files without image square thumbnail -msgFile2BgDark: #4da859; // green shared files without image download circle background -msgFile2BgOver: #44a050; // green shared files without image download circle background with mouse over -msgFile2BgSelected: #46a07e; // green shared files without image download circle background if file is selected -msgFile3Bg: #e47272; // red shared links / shared files without image square thumbnail -msgFile3BgDark: #cd5b5e; // red shared files without image download circle background -msgFile3BgOver: #c35154; // red shared files without image download circle background with mouse over -msgFile3BgSelected: #9f6a82; // red shared files without image download circle background if file is selected -msgFile4Bg: #efc274; // yellow shared links / shared files without image square thumbnail -msgFile4BgDark: #e6a561; // yellow shared files without image download circle background -msgFile4BgOver: #dc9c5a; // yellow shared files without image download circle background with mouse over -msgFile4BgSelected: #b19d84; // yellow shared files without image download circle background if file is selected - -historyFileInIconFg: msgInBg; // inbox file without thumbnail (like audio file) download arrow icon -historyFileInIconFgSelected: msgInBgSelected; // inbox selected file without thumbnail (like audio file) download arrow icon -historyFileInRadialFg: historyFileInIconFg; // inbox file without thumbnail (like audio file) radial download animation line -historyFileInRadialFgSelected: historyFileInIconFgSelected; // inbox selected file without thumbnail (like audio file) radial download animation line -historyFileOutIconFg: msgOutBg; // outbox file without thumbnail (like audio file) download arrow icon -historyFileOutIconFgSelected: msgOutBgSelected; // outbox selected file without thumbnail (like audio file) download arrow icon -historyFileOutRadialFg: historyFileOutIconFg; // outbox file without thumbnail (like audio file) radial download animation line -historyFileOutRadialFgSelected: historyFileOutIconFgSelected; // outbox selected file without thumbnail (like audio file) radial download animation line -historyFileThumbIconFg: msgInBg; // file with thumbnail (or photo / video) download arrow icon -historyFileThumbIconFgSelected: msgInBgSelected; // selected file with thumbnail (or photo / video) download arrow icon -historyFileThumbRadialFg: historyFileThumbIconFg; // file with thumbnail (or photo / video) radial download animation line -historyFileThumbRadialFgSelected: historyFileThumbIconFgSelected; // selected file with thumbnail (or photo / video) radial download animation line - -historyVideoMessageProgressFg: historyFileThumbIconFg; // radial playback progress in round video messages - -msgWaveformInActive: windowBgActive; // inbox voice message active waveform lines (like played part of currently playing voice message) -msgWaveformInActiveSelected: #51a3d3; // inbox selected voice message active waveform lines (like played part of currently playing voice message) -msgWaveformInInactive: #d4dee6; // inbox voice message inactive waveform lines (like upcoming part of currently playing voice message) -msgWaveformInInactiveSelected: #9cc1e1; // inbox selected voice message inactive waveform lines (like upcoming part of currently playing voice message) -msgWaveformOutActive: #78c67f; // outbox voice message active waveform lines (like played part of currently playing voice message) -msgWaveformOutActiveSelected: #6badad; // outbox selected voice message active waveform lines (like played part of currently playing voice message) -msgWaveformOutInactive: #b3e2b4; // outbox voice message inactive waveform lines (like upcoming part of currently playing voice message) -msgWaveformOutInactiveSelected: #91c3c3; // outbox selected voice message inactive waveform lines (like upcoming part of currently playing voice message) - -msgBotKbOverBgAdd: #ffffff20; // this is painted over a bot inline keyboard button (which has msgServiceBg background) when mouse is over that button -msgBotKbIconFg: msgServiceFg; // bot inline keyboard button icon in the top-right corner (like in @vote bot when a poll is ready to be shared) -msgBotKbRippleBg: #00000020; // bot inline keyboard button ripple effect - -mediaInFg: msgInDateFg; // inbox media message status text (like in file that is being downloaded) -mediaInFgSelected: msgInDateFgSelected; // inbox selected media message status text (like in file that is being downloaded) -mediaOutFg: msgOutDateFg; // outbox media message status text (like in file that is being downloaded) -mediaOutFgSelected: msgOutDateFgSelected; // outbox selected media message status text (like in file that is being downloaded) - -youtubePlayIconBg: #e83131c8; // youtube play icon background (when a link to a youtube video with a webpage preview is sent) -youtubePlayIconFg: windowFgActive; // youtube play icon arrow (when a link to a youtube video with a webpage preview is sent) -videoPlayIconBg: #0000007f; // other video play icon background (like when a link to a vimeo video with a webpage preview is sent) -videoPlayIconFg: #ffffff; // other video play icon arrow (like when a link to a vimeo video with a webpage preview is sent) -toastBg: #000000b2; // toast notification background (like when you click on your t.me link when editing your username) -toastFg: windowFgActive; // toast notification text (like when you click on your t.me link when editing your username) - -reportSpamBg: emojiPanHeaderBg; // report spam panel background (like a non contact user writes your for the first time) -reportSpamFg: windowFg; // report spam panel text (when you send a report from that panel) - -historyToDownBg: windowBg; // arrow button background (to scroll to the end of the viewed chat) -historyToDownBgOver: windowBgOver; // arrow button background with mouse over -historyToDownBgRipple: windowBgRipple; // arrow button ripple effect -historyToDownFg: menuIconFg; // arrow button icon -historyToDownFgOver: menuIconFgOver; // arrow button icon with mouse over -historyToDownShadow: #00000040; // arrow button shadow - -historyComposeAreaBg: msgInBg; // history compose area background (message write area / reply information / forwarding information) -historyComposeAreaFg: historyTextInFg; // history compose area text -historyComposeAreaFgService: msgInDateFg; // history compose area text when replying to a media message -historyComposeIconFg: menuIconFg; // history compose area icon (like emoji, attach, bot command..) -historyComposeIconFgOver: menuIconFgOver; // history compose area icon with mouse over -historySendIconFg: windowBgActive; // send message icon -historySendIconFgOver: windowBgActive; // send message icon with mouse over -historyPinnedBg: historyComposeAreaBg; // pinned message area background -historyReplyBg: historyComposeAreaBg; // reply / forward / edit message area background -historyReplyIconFg: windowBgActive; // reply / forward / edit message left icon -historyReplyCancelFg: cancelIconFg; // reply / forward / edit message cancel button -historyReplyCancelFgOver: cancelIconFgOver; // reply / forward / edit message cancel button with mouse over - -historyComposeButtonBg: historyComposeAreaBg; // unblock / join channel / mute channel button background -historyComposeButtonBgOver: windowBgOver; // unblock / join channel / mute channel button background with mouse over -historyComposeButtonBgRipple: windowBgRipple; // unblock / join channel / mute channel button ripple effect - -mapPointDrop: #fd4444; // geo location marker background -mapPointDot: #ffffff; // geo location marker point - -// overview -overviewCheckBg: #00000040; // shared media / files / links checkbox background for not selected rows when some rows are selected -overviewCheckBgActive: windowBgActive; // shared media / files / links checkbox background for selected rows -overviewCheckBorder: windowBg; // shared media round checkbox border -overviewCheckFg: windowBg; // shared files / links checkbox icon for not selected rows when some rows are selected -overviewCheckFgActive: windowBg; // shared files / links checkbox icon for selected rows -overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill for selected rows - -// profile -profileStatusFgOver: #7c99b2; // group members list in group profile user last seen text with mouse over -profileVerifiedCheckBg: windowBgActive; // profile verified check icon background -profileVerifiedCheckFg: windowFgActive; // profile verified check icon tick -profileAdminStartFg: windowBgActive; // group members list creator star icon -profileAdminStarFgOver: profileAdminStartFg; // group members list creator star icon with mouse over -profileOtherAdminStarFg: windowSubTextFg; // group members list admin star icon -profileOtherAdminStarFgOver: profileStatusFgOver; // group members list admin star icon with mouse over - -// settings -notificationsBoxMonitorFg: windowFg; // custom notifications settings box monitor color -notificationsBoxScreenBg: dialogsBgActive; // #6389a8; // custom notifications settings box monitor screen background - -notificationSampleUserpicFg: windowBgActive; // custom notifications settings box small sample userpic placeholder -notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample close button placeholder -notificationSampleTextFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample text placeholder -notificationSampleNameFg: #939393 | windowSubTextFg; // custom notifications settings box small sample name placeholder - -changePhoneSimcardFrom: notificationSampleTextFg; // change phone number box left simcard icon -changePhoneSimcardTo: notificationSampleNameFg; // change phone number box right simcard and plane icons - -mainMenuBg: windowBg; // main menu background -mainMenuCoverBg: dialogsBgActive; // main menu top cover background -mainMenuCoverFg: windowFgActive; // main menu top cover text -mainMenuCloudFg: activeButtonFg; -mainMenuCloudBg: #2785bf | activeButtonBgRipple; - -mediaPlayerBg: windowBg; // audio file player background -mediaPlayerActiveFg: windowBgActive; // audio file player playback progress already played part -mediaPlayerInactiveFg: sliderBgInactive; // audio file player playback progress upcoming (not played yet) part with mouse over -mediaPlayerDisabledFg: #9dd1ef; // audio file player loading progress (when you're playing an audio file and switch to the previous one which is not loaded yet) - -// mediaview -mediaviewFileBg: windowBg; // file rectangle background (when you view a png file in Media Viewer and go to a previous, not loaded yet, file) -mediaviewFileNameFg: windowFg; // file name in file rectangle -mediaviewFileSizeFg: windowSubTextFg; // file size text in file rectangle -mediaviewFileRedCornerFg: #d55959; // red file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .pdf) -mediaviewFileYellowCornerFg: #e8a659; // yellow file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .zip) -mediaviewFileGreenCornerFg: #49a957; // green file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .exe) -mediaviewFileBlueCornerFg: #599dcf; // blue file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .dmg) -mediaviewFileExtFg: activeButtonFg; // file extension text in file thumbnail placeholder in file rectangle - -mediaviewMenuBg: #383838; // context menu in Media Viewer background -mediaviewMenuBgOver: #505050; // context menu item background with mouse over -mediaviewMenuBgRipple: #676767; // context menu item ripple effect -mediaviewMenuFg: windowFgActive; // context menu item text - -mediaviewBg: #222222eb; // Media Viewer background -mediaviewVideoBg: imageBg; // Media Viewer background when viewing a video in full screen -mediaviewControlBg: #0000003c; // controls background (like next photo / previous photo) -mediaviewControlFg: windowFgActive; // controls icon (like next photo / previous photo) -mediaviewCaptionBg: #11111180; // caption text background (when viewing photo with caption) -mediaviewCaptionFg: mediaviewControlFg; // caption text -mediaviewTextLinkFg: #91d9ff; // caption text link -mediaviewSaveMsgBg: toastBg; // save to file toast message background in Media Viewer -mediaviewSaveMsgFg: toastFg; // save to file toast message text - -mediaviewPlaybackActive: #c7c7c7; // video playback progress already played part -mediaviewPlaybackInactive: #252525; // video playback progress upcoming (not played yet) part -mediaviewPlaybackActiveOver: #ffffff; // video playback progress already played part with mouse over -mediaviewPlaybackInactiveOver: #474747; // video playback progress upcoming (not played yet) part with mouse over -mediaviewPlaybackProgressFg: #ffffffc7; // video playback progress text -mediaviewPlaybackIconFg: mediaviewPlaybackActive; // video playback controls icon -mediaviewPlaybackIconFgOver: mediaviewPlaybackActiveOver; // video playback controls icon with mouse over -mediaviewTransparentBg: #ffffff; // transparent filling part (when viewing a transparent .png file in Media Viewer) -mediaviewTransparentFg: #cccccc; // another transparent filling part - -// notification -notificationBg: windowBg; // custom notification window background - -// calls -callBg: #26282cf2; // phone call popup background -callNameFg: #ffffff; // phone call popup name text -callFingerprintBg: #00000066; // phone call popup emoji fingerprint background -callStatusFg: #aaabac; // phone call popup status text -callIconFg: #ffffff; // phone call popup answer, hangup and mute mic icon -callAnswerBg: #64c15b; // phone call popup answer button background -callAnswerRipple: #52b149; // phone call popup answer button ripple effect -callAnswerBgOuter: #50eb4126; // phone call popup answer button outer ripple effect -callHangupBg: #d75a5a; // phone call popup hangup button background -callHangupRipple: #c04646; // phone call popup hangup button ripple effect -callCancelBg: #ffffff; // phone call popup line busy cancel button background -callCancelFg: #777777; // phone call popup line busy cancel button icon -callCancelRipple: #f1f1f1; // phone call popup line busy cancel button ripple effect -callMuteRipple: #ffffff12; // phone call popup mute mic ripple effect - -callBarBg: dialogsBgActive; // active phone call bar background -callBarMuteRipple: dialogsRippleBgActive; // active phone call bar mute and hangup button ripple effect -callBarBgMuted: #8f8f8f | dialogsUnreadBgMuted; // phone call bar with muted mic background -callBarUnmuteRipple: #7f7f7f | shadowFg; // phone call bar with muted mic mute and hangup button ripple effect -callBarFg: dialogsNameFgActive; // phone call bar text and icons - -importantTooltipBg: toastBg; -importantTooltipFg: toastFg; -importantTooltipFgLink: mediaviewTextLinkFg; - -outdatedFg: #ffffff; -outdateSoonBg: #e08543; -outdatedBg: #e05745; diff --git a/Telegram/Resources/emoji_autocomplete.json b/Telegram/Resources/emoji_autocomplete.json deleted file mode 100644 index 40efc5f71..000000000 --- a/Telegram/Resources/emoji_autocomplete.json +++ /dev/null @@ -1,15998 +0,0 @@ -{ - "0023-20e3": { - "output": "0023-fe0f-20e3", - "name": "keycap: #", - "alpha_code": ":hash:", - "aliases": "" - }, - "0030-20e3": { - "output": "0030-fe0f-20e3", - "name": "keycap: 0", - "alpha_code": ":zero:", - "aliases": "" - }, - "0031-20e3": { - "output": "0031-fe0f-20e3", - "name": "keycap: 1", - "alpha_code": ":one:", - "aliases": "" - }, - "0032-20e3": { - "output": "0032-fe0f-20e3", - "name": "keycap: 2", - "alpha_code": ":two:", - "aliases": "" - }, - "0033-20e3": { - "output": "0033-fe0f-20e3", - "name": "keycap: 3", - "alpha_code": ":three:", - "aliases": "" - }, - "0034-20e3": { - "output": "0034-fe0f-20e3", - "name": "keycap: 4", - "alpha_code": ":four:", - "aliases": "" - }, - "0035-20e3": { - "output": "0035-fe0f-20e3", - "name": "keycap: 5", - "alpha_code": ":five:", - "aliases": "" - }, - "0036-20e3": { - "output": "0036-fe0f-20e3", - "name": "keycap: 6", - "alpha_code": ":six:", - "aliases": "" - }, - "0037-20e3": { - "output": "0037-fe0f-20e3", - "name": "keycap: 7", - "alpha_code": ":seven:", - "aliases": "" - }, - "0038-20e3": { - "output": "0038-fe0f-20e3", - "name": "keycap: 8", - "alpha_code": ":eight:", - "aliases": "" - }, - "0039-20e3": { - "output": "0039-fe0f-20e3", - "name": "keycap: 9", - "alpha_code": ":nine:", - "aliases": "" - }, - "00a9": { - "output": "00a9-fe0f", - "name": "copyright", - "alpha_code": ":copyright:", - "aliases": "" - }, - "00ae": { - "output": "00ae-fe0f", - "name": "registered", - "alpha_code": ":registered:", - "aliases": "" - }, - "203c": { - "output": "203c-fe0f", - "name": "double exclamation mark", - "alpha_code": ":bangbang:", - "aliases": "" - }, - "2049": { - "output": "2049-fe0f", - "name": "exclamation question mark", - "alpha_code": ":interrobang:", - "aliases": "" - }, - "2122": { - "output": "2122-fe0f", - "name": "trade mark", - "alpha_code": ":tm:", - "aliases": "" - }, - "2139": { - "output": "2139-fe0f", - "name": "information", - "alpha_code": ":information_source:", - "aliases": "" - }, - "2194": { - "output": "2194-fe0f", - "name": "left-right arrow", - "alpha_code": ":left_right_arrow:", - "aliases": "" - }, - "2195": { - "output": "2195-fe0f", - "name": "up-down arrow", - "alpha_code": ":arrow_up_down:", - "aliases": "" - }, - "2196": { - "output": "2196-fe0f", - "name": "up-left arrow", - "alpha_code": ":arrow_upper_left:", - "aliases": "" - }, - "1f949": { - "output": "1f949", - "name": "3rd place medal", - "alpha_code": ":third_place:", - "aliases": ":third_place_medal:" - }, - "2197": { - "output": "2197-fe0f", - "name": "up-right arrow", - "alpha_code": ":arrow_upper_right:", - "aliases": "" - }, - "2198": { - "output": "2198-fe0f", - "name": "down-right arrow", - "alpha_code": ":arrow_lower_right:", - "aliases": "" - }, - "2199": { - "output": "2199-fe0f", - "name": "down-left arrow", - "alpha_code": ":arrow_lower_left:", - "aliases": "" - }, - "1f948": { - "output": "1f948", - "name": "2nd place medal", - "alpha_code": ":second_place:", - "aliases": ":second_place_medal:" - }, - "21a9": { - "output": "21a9-fe0f", - "name": "right arrow curving left", - "alpha_code": ":leftwards_arrow_with_hook:", - "aliases": "" - }, - "21aa": { - "output": "21aa-fe0f", - "name": "left arrow curving right", - "alpha_code": ":arrow_right_hook:", - "aliases": "" - }, - "231a": { - "output": "231a", - "name": "watch", - "alpha_code": ":watch:", - "aliases": "" - }, - "231b": { - "output": "231b", - "name": "hourglass", - "alpha_code": ":hourglass:", - "aliases": "" - }, - "23e9": { - "output": "23e9", - "name": "fast-forward button", - "alpha_code": ":fast_forward:", - "aliases": "" - }, - "23ea": { - "output": "23ea", - "name": "fast reverse button", - "alpha_code": ":rewind:", - "aliases": "" - }, - "23eb": { - "output": "23eb", - "name": "fast up button", - "alpha_code": ":arrow_double_up:", - "aliases": "" - }, - "23ec": { - "output": "23ec", - "name": "fast down button", - "alpha_code": ":arrow_double_down:", - "aliases": "" - }, - "23f0": { - "output": "23f0", - "name": "alarm clock", - "alpha_code": ":alarm_clock:", - "aliases": "" - }, - "23f3": { - "output": "23f3", - "name": "hourglass with flowing sand", - "alpha_code": ":hourglass_flowing_sand:", - "aliases": "" - }, - "24c2": { - "output": "24c2-fe0f", - "name": "circled M", - "alpha_code": ":m:", - "aliases": "" - }, - "25aa": { - "output": "25aa-fe0f", - "name": "black small square", - "alpha_code": ":black_small_square:", - "aliases": "" - }, - "25ab": { - "output": "25ab-fe0f", - "name": "white small square", - "alpha_code": ":white_small_square:", - "aliases": "" - }, - "25b6": { - "output": "25b6-fe0f", - "name": "play button", - "alpha_code": ":arrow_forward:", - "aliases": "" - }, - "25c0": { - "output": "25c0-fe0f", - "name": "reverse button", - "alpha_code": ":arrow_backward:", - "aliases": "" - }, - "25fb": { - "output": "25fb-fe0f", - "name": "white medium square", - "alpha_code": ":white_medium_square:", - "aliases": "" - }, - "25fc": { - "output": "25fc-fe0f", - "name": "black medium square", - "alpha_code": ":black_medium_square:", - "aliases": "" - }, - "25fd": { - "output": "25fd", - "name": "white medium-small square", - "alpha_code": ":white_medium_small_square:", - "aliases": "" - }, - "25fe": { - "output": "25fe", - "name": "black medium-small square", - "alpha_code": ":black_medium_small_square:", - "aliases": "" - }, - "2600": { - "output": "2600-fe0f", - "name": "sun", - "alpha_code": ":sunny:", - "aliases": "" - }, - "2601": { - "output": "2601-fe0f", - "name": "cloud", - "alpha_code": ":cloud:", - "aliases": "" - }, - "260e": { - "output": "260e-fe0f", - "name": "telephone", - "alpha_code": ":telephone:", - "aliases": "" - }, - "2611": { - "output": "2611-fe0f", - "name": "ballot box with check", - "alpha_code": ":ballot_box_with_check:", - "aliases": "" - }, - "2614": { - "output": "2614", - "name": "umbrella with rain drops", - "alpha_code": ":umbrella:", - "aliases": "" - }, - "2615": { - "output": "2615", - "name": "hot beverage", - "alpha_code": ":coffee:", - "aliases": "" - }, - "261d": { - "output": "261d-fe0f", - "name": "index pointing up", - "alpha_code": ":point_up:", - "aliases": "" - }, - "263a": { - "output": "263a-fe0f", - "name": "smiling face", - "alpha_code": ":relaxed:", - "aliases": "" - }, - "2648": { - "output": "2648", - "name": "Aries", - "alpha_code": ":aries:", - "aliases": "" - }, - "1f947": { - "output": "1f947", - "name": "1st place medal", - "alpha_code": ":first_place:", - "aliases": ":first_place_medal:" - }, - "2649": { - "output": "2649", - "name": "Taurus", - "alpha_code": ":taurus:", - "aliases": "" - }, - "1f93a": { - "output": "1f93a", - "name": "person fencing", - "alpha_code": ":person_fencing:", - "aliases": ":fencer:|:fencing:" - }, - "264a": { - "output": "264a", - "name": "Gemini", - "alpha_code": ":gemini:", - "aliases": "" - }, - "264b": { - "output": "264b", - "name": "Cancer", - "alpha_code": ":cancer:", - "aliases": "" - }, - "1f945": { - "output": "1f945", - "name": "goal net", - "alpha_code": ":goal:", - "aliases": ":goal_net:" - }, - "264c": { - "output": "264c", - "name": "Leo", - "alpha_code": ":leo:", - "aliases": "" - }, - "002a": { - "output": "002a-fe0f", - "name": "asterisk", - "alpha_code": ":asterisk_symbol:", - "aliases": "" - }, - "264d": { - "output": "264d", - "name": "Virgo", - "alpha_code": ":virgo:", - "aliases": "" - }, - "1f93e": { - "output": "1f93e", - "name": "person playing handball", - "alpha_code": ":person_playing_handball:", - "aliases": ":handball:" - }, - "264e": { - "output": "264e", - "name": "Libra", - "alpha_code": ":libra:", - "aliases": "" - }, - "1f1ff": { - "output": "1f1ff", - "name": "regional indicator symbol letter z", - "alpha_code": ":regional_indicator_z:", - "aliases": "" - }, - "1f93d": { - "output": "1f93d", - "name": "person playing water polo", - "alpha_code": ":person_playing_water_polo:", - "aliases": ":water_polo:" - }, - "264f": { - "output": "264f", - "name": "Scorpius", - "alpha_code": ":scorpius:", - "aliases": "" - }, - "2650": { - "output": "2650", - "name": "Sagittarius", - "alpha_code": ":sagittarius:", - "aliases": "" - }, - "1f94b": { - "output": "1f94b", - "name": "martial arts uniform", - "alpha_code": ":martial_arts_uniform:", - "aliases": ":karate_uniform:" - }, - "2651": { - "output": "2651", - "name": "Capricorn", - "alpha_code": ":capricorn:", - "aliases": "" - }, - "1f94a": { - "output": "1f94a", - "name": "boxing glove", - "alpha_code": ":boxing_glove:", - "aliases": ":boxing_gloves:" - }, - "2652": { - "output": "2652", - "name": "Aquarius", - "alpha_code": ":aquarius:", - "aliases": "" - }, - "1f93c": { - "output": "1f93c", - "name": "people wrestling", - "alpha_code": ":people_wrestling:", - "aliases": ":wrestlers:|:wrestling:" - }, - "2653": { - "output": "2653", - "name": "Pisces", - "alpha_code": ":pisces:", - "aliases": "" - }, - "2660": { - "output": "2660-fe0f", - "name": "spade suit", - "alpha_code": ":spades:", - "aliases": "" - }, - "2663": { - "output": "2663-fe0f", - "name": "club suit", - "alpha_code": ":clubs:", - "aliases": "" - }, - "2665": { - "output": "2665-fe0f", - "name": "heart suit", - "alpha_code": ":hearts:", - "aliases": "" - }, - "2666": { - "output": "2666-fe0f", - "name": "diamond suit", - "alpha_code": ":diamonds:", - "aliases": "" - }, - "2668": { - "output": "2668-fe0f", - "name": "hot springs", - "alpha_code": ":hotsprings:", - "aliases": "" - }, - "267b": { - "output": "267b-fe0f", - "name": "recycling symbol", - "alpha_code": ":recycle:", - "aliases": "" - }, - "1f939": { - "output": "1f939", - "name": "person juggling", - "alpha_code": ":person_juggling:", - "aliases": ":juggling:|:juggler:" - }, - "267f": { - "output": "267f", - "name": "wheelchair symbol", - "alpha_code": ":wheelchair:", - "aliases": "" - }, - "2693": { - "output": "2693", - "name": "anchor", - "alpha_code": ":anchor:", - "aliases": "" - }, - "26a0": { - "output": "26a0-fe0f", - "name": "warning", - "alpha_code": ":warning:", - "aliases": "" - }, - "26a1": { - "output": "26a1", - "name": "high voltage", - "alpha_code": ":zap:", - "aliases": "" - }, - "26aa": { - "output": "26aa", - "name": "white circle", - "alpha_code": ":white_circle:", - "aliases": "" - }, - "26ab": { - "output": "26ab", - "name": "black circle", - "alpha_code": ":black_circle:", - "aliases": "" - }, - "26bd": { - "output": "26bd", - "name": "soccer ball", - "alpha_code": ":soccer:", - "aliases": "" - }, - "26be": { - "output": "26be", - "name": "baseball", - "alpha_code": ":baseball:", - "aliases": "" - }, - "26c4": { - "output": "26c4", - "name": "snowman without snow", - "alpha_code": ":snowman:", - "aliases": "" - }, - "26c5": { - "output": "26c5", - "name": "sun behind cloud", - "alpha_code": ":partly_sunny:", - "aliases": "" - }, - "26ce": { - "output": "26ce", - "name": "Ophiuchus", - "alpha_code": ":ophiuchus:", - "aliases": "" - }, - "1f938": { - "output": "1f938", - "name": "person cartwheeling", - "alpha_code": ":person_doing_cartwheel:", - "aliases": ":cartwheel:" - }, - "26d4": { - "output": "26d4", - "name": "no entry", - "alpha_code": ":no_entry:", - "aliases": "" - }, - "26ea": { - "output": "26ea", - "name": "church", - "alpha_code": ":church:", - "aliases": "" - }, - "26f2": { - "output": "26f2", - "name": "fountain", - "alpha_code": ":fountain:", - "aliases": "" - }, - "1f6f6": { - "output": "1f6f6", - "name": "canoe", - "alpha_code": ":canoe:", - "aliases": ":kayak:" - }, - "26f3": { - "output": "26f3", - "name": "flag in hole", - "alpha_code": ":golf:", - "aliases": "" - }, - "26f5": { - "output": "26f5", - "name": "sailboat", - "alpha_code": ":sailboat:", - "aliases": "" - }, - "26fa": { - "output": "26fa", - "name": "tent", - "alpha_code": ":tent:", - "aliases": "" - }, - "26fd": { - "output": "26fd", - "name": "fuel pump", - "alpha_code": ":fuelpump:", - "aliases": "" - }, - "2702": { - "output": "2702-fe0f", - "name": "scissors", - "alpha_code": ":scissors:", - "aliases": "" - }, - "2705": { - "output": "2705", - "name": "white heavy check mark", - "alpha_code": ":white_check_mark:", - "aliases": "" - }, - "2708": { - "output": "2708-fe0f", - "name": "airplane", - "alpha_code": ":airplane:", - "aliases": "" - }, - "2709": { - "output": "2709-fe0f", - "name": "envelope", - "alpha_code": ":envelope:", - "aliases": "" - }, - "270a": { - "output": "270a", - "name": "raised fist", - "alpha_code": ":fist:", - "aliases": "" - }, - "270b": { - "output": "270b", - "name": "raised hand", - "alpha_code": ":raised_hand:", - "aliases": "" - }, - "270c": { - "output": "270c-fe0f", - "name": "victory hand", - "alpha_code": ":v:", - "aliases": "" - }, - "270f": { - "output": "270f-fe0f", - "name": "pencil", - "alpha_code": ":pencil2:", - "aliases": "" - }, - "2712": { - "output": "2712-fe0f", - "name": "black nib", - "alpha_code": ":black_nib:", - "aliases": "" - }, - "2714": { - "output": "2714-fe0f", - "name": "heavy check mark", - "alpha_code": ":heavy_check_mark:", - "aliases": "" - }, - "2716": { - "output": "2716-fe0f", - "name": "heavy multiplication x", - "alpha_code": ":heavy_multiplication_x:", - "aliases": "" - }, - "2728": { - "output": "2728", - "name": "sparkles", - "alpha_code": ":sparkles:", - "aliases": "" - }, - "2733": { - "output": "2733-fe0f", - "name": "eight-spoked asterisk", - "alpha_code": ":eight_spoked_asterisk:", - "aliases": "" - }, - "2734": { - "output": "2734-fe0f", - "name": "eight-pointed star", - "alpha_code": ":eight_pointed_black_star:", - "aliases": "" - }, - "2744": { - "output": "2744-fe0f", - "name": "snowflake", - "alpha_code": ":snowflake:", - "aliases": "" - }, - "2747": { - "output": "2747-fe0f", - "name": "sparkle", - "alpha_code": ":sparkle:", - "aliases": "" - }, - "274c": { - "output": "274c", - "name": "cross mark", - "alpha_code": ":x:", - "aliases": "" - }, - "274e": { - "output": "274e", - "name": "cross mark button", - "alpha_code": ":negative_squared_cross_mark:", - "aliases": "" - }, - "2753": { - "output": "2753", - "name": "question mark", - "alpha_code": ":question:", - "aliases": "" - }, - "2754": { - "output": "2754", - "name": "white question mark", - "alpha_code": ":grey_question:", - "aliases": "" - }, - "1f6f5": { - "output": "1f6f5", - "name": "motor scooter", - "alpha_code": ":motor_scooter:", - "aliases": ":motorbike:" - }, - "2755": { - "output": "2755", - "name": "white exclamation mark", - "alpha_code": ":grey_exclamation:", - "aliases": "" - }, - "2757": { - "output": "2757", - "name": "exclamation mark", - "alpha_code": ":exclamation:", - "aliases": "" - }, - "2764": { - "output": "2764-fe0f", - "name": "red heart", - "alpha_code": ":heart:", - "aliases": "" - }, - "2795": { - "output": "2795", - "name": "heavy plus sign", - "alpha_code": ":heavy_plus_sign:", - "aliases": "" - }, - "2796": { - "output": "2796", - "name": "heavy minus sign", - "alpha_code": ":heavy_minus_sign:", - "aliases": "" - }, - "2797": { - "output": "2797", - "name": "heavy division sign", - "alpha_code": ":heavy_division_sign:", - "aliases": "" - }, - "27a1": { - "output": "27a1-fe0f", - "name": "right arrow", - "alpha_code": ":arrow_right:", - "aliases": "" - }, - "27b0": { - "output": "27b0", - "name": "curly loop", - "alpha_code": ":curly_loop:", - "aliases": "" - }, - "2934": { - "output": "2934-fe0f", - "name": "right arrow curving up", - "alpha_code": ":arrow_heading_up:", - "aliases": "" - }, - "2935": { - "output": "2935-fe0f", - "name": "right arrow curving down", - "alpha_code": ":arrow_heading_down:", - "aliases": "" - }, - "2b05": { - "output": "2b05-fe0f", - "name": "left arrow", - "alpha_code": ":arrow_left:", - "aliases": "" - }, - "2b06": { - "output": "2b06-fe0f", - "name": "up arrow", - "alpha_code": ":arrow_up:", - "aliases": "" - }, - "1f6f4": { - "output": "1f6f4", - "name": "kick scooter", - "alpha_code": ":scooter:", - "aliases": "" - }, - "2b07": { - "output": "2b07-fe0f", - "name": "down arrow", - "alpha_code": ":arrow_down:", - "aliases": "" - }, - "2b1b": { - "output": "2b1b", - "name": "black large square", - "alpha_code": ":black_large_square:", - "aliases": "" - }, - "2b1c": { - "output": "2b1c", - "name": "white large square", - "alpha_code": ":white_large_square:", - "aliases": "" - }, - "2b50": { - "output": "2b50", - "name": "white medium star", - "alpha_code": ":star:", - "aliases": "" - }, - "2b55": { - "output": "2b55", - "name": "heavy large circle", - "alpha_code": ":o:", - "aliases": "" - }, - "3030": { - "output": "3030-fe0f", - "name": "wavy dash", - "alpha_code": ":wavy_dash:", - "aliases": "" - }, - "303d": { - "output": "303d-fe0f", - "name": "part alternation mark", - "alpha_code": ":part_alternation_mark:", - "aliases": "" - }, - "3297": { - "output": "3297-fe0f", - "name": "Japanese \u201ccongratulations\u201d button", - "alpha_code": ":congratulations:", - "aliases": "" - }, - "1f6d2": { - "output": "1f6d2", - "name": "shopping cart", - "alpha_code": ":shopping_cart:", - "aliases": ":shopping_trolley:" - }, - "3299": { - "output": "3299-fe0f", - "name": "Japanese \u201csecret\u201d button", - "alpha_code": ":secret:", - "aliases": "" - }, - "1f004": { - "output": "1f004", - "name": "mahjong red dragon", - "alpha_code": ":mahjong:", - "aliases": "" - }, - "1f0cf": { - "output": "1f0cf", - "name": "joker", - "alpha_code": ":black_joker:", - "aliases": "" - }, - "1f170": { - "output": "1f170-fe0f", - "name": "A button (blood type)", - "alpha_code": ":a:", - "aliases": "" - }, - "1f171": { - "output": "1f171-fe0f", - "name": "B button (blood type)", - "alpha_code": ":b:", - "aliases": "" - }, - "1f17e": { - "output": "1f17e-fe0f", - "name": "O button (blood type)", - "alpha_code": ":o2:", - "aliases": "" - }, - "1f17f": { - "output": "1f17f-fe0f", - "name": "P button", - "alpha_code": ":parking:", - "aliases": "" - }, - "1f6d1": { - "output": "1f6d1", - "name": "stop sign", - "alpha_code": ":octagonal_sign:", - "aliases": ":stop_sign:" - }, - "1f18e": { - "output": "1f18e", - "name": "AB button (blood type)", - "alpha_code": ":ab:", - "aliases": "" - }, - "1f191": { - "output": "1f191", - "name": "CL button", - "alpha_code": ":cl:", - "aliases": "" - }, - "1f1fe": { - "output": "1f1fe", - "name": "regional indicator symbol letter y", - "alpha_code": ":regional_indicator_y:", - "aliases": "" - }, - "1f192": { - "output": "1f192", - "name": "COOL button", - "alpha_code": ":cool:", - "aliases": "" - }, - "1f193": { - "output": "1f193", - "name": "FREE button", - "alpha_code": ":free:", - "aliases": "" - }, - "1f194": { - "output": "1f194", - "name": "ID button", - "alpha_code": ":id:", - "aliases": "" - }, - "1f195": { - "output": "1f195", - "name": "NEW button", - "alpha_code": ":new:", - "aliases": "" - }, - "1f196": { - "output": "1f196", - "name": "NG button", - "alpha_code": ":ng:", - "aliases": "" - }, - "1f197": { - "output": "1f197", - "name": "OK button", - "alpha_code": ":ok:", - "aliases": "" - }, - "1f198": { - "output": "1f198", - "name": "SOS button", - "alpha_code": ":sos:", - "aliases": "" - }, - "1f944": { - "output": "1f944", - "name": "spoon", - "alpha_code": ":spoon:", - "aliases": "" - }, - "1f199": { - "output": "1f199", - "name": "UP! button", - "alpha_code": ":up:", - "aliases": "" - }, - "1f19a": { - "output": "1f19a", - "name": "VS button", - "alpha_code": ":vs:", - "aliases": "" - }, - "1f1e8-1f1f3": { - "output": "1f1e8-1f1f3", - "name": "China", - "alpha_code": ":flag_cn:", - "aliases": ":cn:" - }, - "1f1e9-1f1ea": { - "output": "1f1e9-1f1ea", - "name": "Germany", - "alpha_code": ":flag_de:", - "aliases": ":de:" - }, - "1f1ea-1f1f8": { - "output": "1f1ea-1f1f8", - "name": "Spain", - "alpha_code": ":flag_es:", - "aliases": ":es:" - }, - "1f1eb-1f1f7": { - "output": "1f1eb-1f1f7", - "name": "France", - "alpha_code": ":flag_fr:", - "aliases": ":fr:" - }, - "1f1ec-1f1e7": { - "output": "1f1ec-1f1e7", - "name": "United Kingdom", - "alpha_code": ":flag_gb:", - "aliases": ":gb:" - }, - "1f942": { - "output": "1f942", - "name": "clinking glasses", - "alpha_code": ":champagne_glass:", - "aliases": ":clinking_glass:" - }, - "1f943": { - "output": "1f943", - "name": "tumbler glass", - "alpha_code": ":tumbler_glass:", - "aliases": ":whisky:" - }, - "1f1ee-1f1f9": { - "output": "1f1ee-1f1f9", - "name": "Italy", - "alpha_code": ":flag_it:", - "aliases": ":it:" - }, - "1f1ef-1f1f5": { - "output": "1f1ef-1f1f5", - "name": "Japan", - "alpha_code": ":flag_jp:", - "aliases": ":jp:" - }, - "1f1f0-1f1f7": { - "output": "1f1f0-1f1f7", - "name": "South Korea", - "alpha_code": ":flag_kr:", - "aliases": ":kr:" - }, - "1f1fa-1f1f8": { - "output": "1f1fa-1f1f8", - "name": "United States", - "alpha_code": ":flag_us:", - "aliases": ":us:" - }, - "1f1f7-1f1fa": { - "output": "1f1f7-1f1fa", - "name": "Russia", - "alpha_code": ":flag_ru:", - "aliases": ":ru:" - }, - "1f201": { - "output": "1f201", - "name": "Japanese \u201chere\u201d button", - "alpha_code": ":koko:", - "aliases": "" - }, - "1f202": { - "output": "1f202-fe0f", - "name": "Japanese \u201cservice charge\u201d button", - "alpha_code": ":sa:", - "aliases": "" - }, - "1f21a": { - "output": "1f21a", - "name": "Japanese \u201cfree of charge\u201d button", - "alpha_code": ":u7121:", - "aliases": "" - }, - "1f22f": { - "output": "1f22f", - "name": "Japanese \u201creserved\u201d button", - "alpha_code": ":u6307:", - "aliases": "" - }, - "1f959": { - "output": "1f959", - "name": "stuffed flatbread", - "alpha_code": ":stuffed_flatbread:", - "aliases": ":stuffed_pita:" - }, - "1f232": { - "output": "1f232", - "name": "Japanese \u201cprohibited\u201d button", - "alpha_code": ":u7981:", - "aliases": "" - }, - "1f233": { - "output": "1f233", - "name": "Japanese \u201cvacancy\u201d button", - "alpha_code": ":u7a7a:", - "aliases": "" - }, - "1f234": { - "output": "1f234", - "name": "Japanese \u201cpassing grade\u201d button", - "alpha_code": ":u5408:", - "aliases": "" - }, - "1f235": { - "output": "1f235", - "name": "Japanese \u201cno vacancy\u201d button", - "alpha_code": ":u6e80:", - "aliases": "" - }, - "1f236": { - "output": "1f236", - "name": "Japanese \u201cnot free of charge\u201d button", - "alpha_code": ":u6709:", - "aliases": "" - }, - "1f958": { - "output": "1f958", - "name": "shallow pan of food", - "alpha_code": ":shallow_pan_of_food:", - "aliases": ":paella:" - }, - "1f237": { - "output": "1f237-fe0f", - "name": "Japanese \u201cmonthly amount\u201d button", - "alpha_code": ":u6708:", - "aliases": "" - }, - "1f238": { - "output": "1f238", - "name": "Japanese \u201capplication\u201d button", - "alpha_code": ":u7533:", - "aliases": "" - }, - "1f239": { - "output": "1f239", - "name": "Japanese \u201cdiscount\u201d button", - "alpha_code": ":u5272:", - "aliases": "" - }, - "1f957": { - "output": "1f957", - "name": "green salad", - "alpha_code": ":salad:", - "aliases": ":green_salad:" - }, - "1f23a": { - "output": "1f23a", - "name": "Japanese \u201copen for business\u201d button", - "alpha_code": ":u55b6:", - "aliases": "" - }, - "1f250": { - "output": "1f250", - "name": "Japanese \u201cbargain\u201d button", - "alpha_code": ":ideograph_advantage:", - "aliases": "" - }, - "1f251": { - "output": "1f251", - "name": "Japanese \u201cacceptable\u201d button", - "alpha_code": ":accept:", - "aliases": "" - }, - "1f300": { - "output": "1f300", - "name": "cyclone", - "alpha_code": ":cyclone:", - "aliases": "" - }, - "1f956": { - "output": "1f956", - "name": "baguette bread", - "alpha_code": ":french_bread:", - "aliases": ":baguette_bread:" - }, - "1f301": { - "output": "1f301", - "name": "foggy", - "alpha_code": ":foggy:", - "aliases": "" - }, - "1f302": { - "output": "1f302", - "name": "closed umbrella", - "alpha_code": ":closed_umbrella:", - "aliases": "" - }, - "1f303": { - "output": "1f303", - "name": "night with stars", - "alpha_code": ":night_with_stars:", - "aliases": "" - }, - "1f304": { - "output": "1f304", - "name": "sunrise over mountains", - "alpha_code": ":sunrise_over_mountains:", - "aliases": "" - }, - "1f305": { - "output": "1f305", - "name": "sunrise", - "alpha_code": ":sunrise:", - "aliases": "" - }, - "1f306": { - "output": "1f306", - "name": "cityscape at dusk", - "alpha_code": ":city_dusk:", - "aliases": "" - }, - "1f955": { - "output": "1f955", - "name": "carrot", - "alpha_code": ":carrot:", - "aliases": "" - }, - "1f307": { - "output": "1f307", - "name": "sunset", - "alpha_code": ":city_sunset:", - "aliases": ":city_sunrise:" - }, - "1f308": { - "output": "1f308", - "name": "rainbow", - "alpha_code": ":rainbow:", - "aliases": "" - }, - "1f954": { - "output": "1f954", - "name": "potato", - "alpha_code": ":potato:", - "aliases": "" - }, - "1f309": { - "output": "1f309", - "name": "bridge at night", - "alpha_code": ":bridge_at_night:", - "aliases": "" - }, - "1f30a": { - "output": "1f30a", - "name": "water wave", - "alpha_code": ":ocean:", - "aliases": "" - }, - "1f30b": { - "output": "1f30b", - "name": "volcano", - "alpha_code": ":volcano:", - "aliases": "" - }, - "1f30c": { - "output": "1f30c", - "name": "milky way", - "alpha_code": ":milky_way:", - "aliases": "" - }, - "1f30f": { - "output": "1f30f", - "name": "globe showing Asia-Australia", - "alpha_code": ":earth_asia:", - "aliases": "" - }, - "1f311": { - "output": "1f311", - "name": "new moon", - "alpha_code": ":new_moon:", - "aliases": "" - }, - "1f953": { - "output": "1f953", - "name": "bacon", - "alpha_code": ":bacon:", - "aliases": "" - }, - "1f313": { - "output": "1f313", - "name": "first quarter moon", - "alpha_code": ":first_quarter_moon:", - "aliases": "" - }, - "1f314": { - "output": "1f314", - "name": "waxing gibbous moon", - "alpha_code": ":waxing_gibbous_moon:", - "aliases": "" - }, - "1f315": { - "output": "1f315", - "name": "full moon", - "alpha_code": ":full_moon:", - "aliases": "" - }, - "1f319": { - "output": "1f319", - "name": "crescent moon", - "alpha_code": ":crescent_moon:", - "aliases": "" - }, - "1f31b": { - "output": "1f31b", - "name": "first quarter moon with face", - "alpha_code": ":first_quarter_moon_with_face:", - "aliases": "" - }, - "1f31f": { - "output": "1f31f", - "name": "glowing star", - "alpha_code": ":star2:", - "aliases": "" - }, - "1f952": { - "output": "1f952", - "name": "cucumber", - "alpha_code": ":cucumber:", - "aliases": "" - }, - "1f320": { - "output": "1f320", - "name": "shooting star", - "alpha_code": ":stars:", - "aliases": "" - }, - "1f330": { - "output": "1f330", - "name": "chestnut", - "alpha_code": ":chestnut:", - "aliases": "" - }, - "1f951": { - "output": "1f951", - "name": "avocado", - "alpha_code": ":avocado:", - "aliases": "" - }, - "1f331": { - "output": "1f331", - "name": "seedling", - "alpha_code": ":seedling:", - "aliases": "" - }, - "1f334": { - "output": "1f334", - "name": "palm tree", - "alpha_code": ":palm_tree:", - "aliases": "" - }, - "1f335": { - "output": "1f335", - "name": "cactus", - "alpha_code": ":cactus:", - "aliases": "" - }, - "1f337": { - "output": "1f337", - "name": "tulip", - "alpha_code": ":tulip:", - "aliases": "" - }, - "1f338": { - "output": "1f338", - "name": "cherry blossom", - "alpha_code": ":cherry_blossom:", - "aliases": "" - }, - "1f339": { - "output": "1f339", - "name": "rose", - "alpha_code": ":rose:", - "aliases": "" - }, - "1f33a": { - "output": "1f33a", - "name": "hibiscus", - "alpha_code": ":hibiscus:", - "aliases": "" - }, - "1f33b": { - "output": "1f33b", - "name": "sunflower", - "alpha_code": ":sunflower:", - "aliases": "" - }, - "1f33c": { - "output": "1f33c", - "name": "blossom", - "alpha_code": ":blossom:", - "aliases": "" - }, - "1f33d": { - "output": "1f33d", - "name": "ear of corn", - "alpha_code": ":corn:", - "aliases": "" - }, - "1f950": { - "output": "1f950", - "name": "croissant", - "alpha_code": ":croissant:", - "aliases": "" - }, - "1f33e": { - "output": "1f33e", - "name": "sheaf of rice", - "alpha_code": ":ear_of_rice:", - "aliases": "" - }, - "1f33f": { - "output": "1f33f", - "name": "herb", - "alpha_code": ":herb:", - "aliases": "" - }, - "1f340": { - "output": "1f340", - "name": "four leaf clover", - "alpha_code": ":four_leaf_clover:", - "aliases": "" - }, - "1f341": { - "output": "1f341", - "name": "maple leaf", - "alpha_code": ":maple_leaf:", - "aliases": "" - }, - "1f342": { - "output": "1f342", - "name": "fallen leaf", - "alpha_code": ":fallen_leaf:", - "aliases": "" - }, - "1f343": { - "output": "1f343", - "name": "leaf fluttering in wind", - "alpha_code": ":leaves:", - "aliases": "" - }, - "1f344": { - "output": "1f344", - "name": "mushroom", - "alpha_code": ":mushroom:", - "aliases": "" - }, - "1f345": { - "output": "1f345", - "name": "tomato", - "alpha_code": ":tomato:", - "aliases": "" - }, - "1f346": { - "output": "1f346", - "name": "eggplant", - "alpha_code": ":eggplant:", - "aliases": "" - }, - "1f347": { - "output": "1f347", - "name": "grapes", - "alpha_code": ":grapes:", - "aliases": "" - }, - "1f348": { - "output": "1f348", - "name": "melon", - "alpha_code": ":melon:", - "aliases": "" - }, - "1f349": { - "output": "1f349", - "name": "watermelon", - "alpha_code": ":watermelon:", - "aliases": "" - }, - "1f34a": { - "output": "1f34a", - "name": "tangerine", - "alpha_code": ":tangerine:", - "aliases": "" - }, - "1f940": { - "output": "1f940", - "name": "wilted flower", - "alpha_code": ":wilted_rose:", - "aliases": ":wilted_flower:" - }, - "1f34c": { - "output": "1f34c", - "name": "banana", - "alpha_code": ":banana:", - "aliases": "" - }, - "1f34d": { - "output": "1f34d", - "name": "pineapple", - "alpha_code": ":pineapple:", - "aliases": "" - }, - "1f34e": { - "output": "1f34e", - "name": "red apple", - "alpha_code": ":apple:", - "aliases": "" - }, - "1f34f": { - "output": "1f34f", - "name": "green apple", - "alpha_code": ":green_apple:", - "aliases": "" - }, - "1f351": { - "output": "1f351", - "name": "peach", - "alpha_code": ":peach:", - "aliases": "" - }, - "1f352": { - "output": "1f352", - "name": "cherries", - "alpha_code": ":cherries:", - "aliases": "" - }, - "1f353": { - "output": "1f353", - "name": "strawberry", - "alpha_code": ":strawberry:", - "aliases": "" - }, - "1f98f": { - "output": "1f98f", - "name": "rhinoceros", - "alpha_code": ":rhino:", - "aliases": ":rhinoceros:" - }, - "1f354": { - "output": "1f354", - "name": "hamburger", - "alpha_code": ":hamburger:", - "aliases": "" - }, - "1f355": { - "output": "1f355", - "name": "pizza", - "alpha_code": ":pizza:", - "aliases": "" - }, - "1f356": { - "output": "1f356", - "name": "meat on bone", - "alpha_code": ":meat_on_bone:", - "aliases": "" - }, - "1f98e": { - "output": "1f98e", - "name": "lizard", - "alpha_code": ":lizard:", - "aliases": "" - }, - "1f357": { - "output": "1f357", - "name": "poultry leg", - "alpha_code": ":poultry_leg:", - "aliases": "" - }, - "1f358": { - "output": "1f358", - "name": "rice cracker", - "alpha_code": ":rice_cracker:", - "aliases": "" - }, - "1f359": { - "output": "1f359", - "name": "rice ball", - "alpha_code": ":rice_ball:", - "aliases": "" - }, - "1f98d": { - "output": "1f98d", - "name": "gorilla", - "alpha_code": ":gorilla:", - "aliases": "" - }, - "1f35a": { - "output": "1f35a", - "name": "cooked rice", - "alpha_code": ":rice:", - "aliases": "" - }, - "1f35b": { - "output": "1f35b", - "name": "curry rice", - "alpha_code": ":curry:", - "aliases": "" - }, - "1f98c": { - "output": "1f98c", - "name": "deer", - "alpha_code": ":deer:", - "aliases": "" - }, - "1f35c": { - "output": "1f35c", - "name": "steaming bowl", - "alpha_code": ":ramen:", - "aliases": "" - }, - "1f35d": { - "output": "1f35d", - "name": "spaghetti", - "alpha_code": ":spaghetti:", - "aliases": "" - }, - "1f35e": { - "output": "1f35e", - "name": "bread", - "alpha_code": ":bread:", - "aliases": "" - }, - "1f35f": { - "output": "1f35f", - "name": "french fries", - "alpha_code": ":fries:", - "aliases": "" - }, - "1f98b": { - "output": "1f98b", - "name": "butterfly", - "alpha_code": ":butterfly:", - "aliases": "" - }, - "1f360": { - "output": "1f360", - "name": "roasted sweet potato", - "alpha_code": ":sweet_potato:", - "aliases": "" - }, - "1f361": { - "output": "1f361", - "name": "dango", - "alpha_code": ":dango:", - "aliases": "" - }, - "1f98a": { - "output": "1f98a", - "name": "fox face", - "alpha_code": ":fox:", - "aliases": ":fox_face:" - }, - "1f362": { - "output": "1f362", - "name": "oden", - "alpha_code": ":oden:", - "aliases": "" - }, - "1f363": { - "output": "1f363", - "name": "sushi", - "alpha_code": ":sushi:", - "aliases": "" - }, - "1f989": { - "output": "1f989", - "name": "owl", - "alpha_code": ":owl:", - "aliases": "" - }, - "1f364": { - "output": "1f364", - "name": "fried shrimp", - "alpha_code": ":fried_shrimp:", - "aliases": "" - }, - "1f365": { - "output": "1f365", - "name": "fish cake with swirl", - "alpha_code": ":fish_cake:", - "aliases": "" - }, - "1f988": { - "output": "1f988", - "name": "shark", - "alpha_code": ":shark:", - "aliases": "" - }, - "1f366": { - "output": "1f366", - "name": "soft ice cream", - "alpha_code": ":icecream:", - "aliases": "" - }, - "1f987": { - "output": "1f987", - "name": "bat", - "alpha_code": ":bat:", - "aliases": "" - }, - "1f367": { - "output": "1f367", - "name": "shaved ice", - "alpha_code": ":shaved_ice:", - "aliases": "" - }, - "1f1fd": { - "output": "1f1fd", - "name": "regional indicator symbol letter x", - "alpha_code": ":regional_indicator_x:", - "aliases": "" - }, - "1f368": { - "output": "1f368", - "name": "ice cream", - "alpha_code": ":ice_cream:", - "aliases": "" - }, - "1f986": { - "output": "1f986", - "name": "duck", - "alpha_code": ":duck:", - "aliases": "" - }, - "1f369": { - "output": "1f369", - "name": "doughnut", - "alpha_code": ":doughnut:", - "aliases": "" - }, - "1f985": { - "output": "1f985", - "name": "eagle", - "alpha_code": ":eagle:", - "aliases": "" - }, - "1f36a": { - "output": "1f36a", - "name": "cookie", - "alpha_code": ":cookie:", - "aliases": "" - }, - "1f5a4": { - "output": "1f5a4", - "name": "black heart", - "alpha_code": ":black_heart:", - "aliases": "" - }, - "1f36b": { - "output": "1f36b", - "name": "chocolate bar", - "alpha_code": ":chocolate_bar:", - "aliases": "" - }, - "1f36c": { - "output": "1f36c", - "name": "candy", - "alpha_code": ":candy:", - "aliases": "" - }, - "1f36d": { - "output": "1f36d", - "name": "lollipop", - "alpha_code": ":lollipop:", - "aliases": "" - }, - "1f36e": { - "output": "1f36e", - "name": "custard", - "alpha_code": ":custard:", - "aliases": ":pudding:|:flan:" - }, - "1f36f": { - "output": "1f36f", - "name": "honey pot", - "alpha_code": ":honey_pot:", - "aliases": "" - }, - "1f91e": { - "output": "1f91e", - "name": "crossed fingers", - "alpha_code": ":fingers_crossed:", - "aliases": ":hand_with_index_and_middle_finger_crossed:" - }, - "1f370": { - "output": "1f370", - "name": "shortcake", - "alpha_code": ":cake:", - "aliases": "" - }, - "1f371": { - "output": "1f371", - "name": "bento box", - "alpha_code": ":bento:", - "aliases": "" - }, - "1f372": { - "output": "1f372", - "name": "pot of food", - "alpha_code": ":stew:", - "aliases": "" - }, - "1f91d": { - "output": "1f91d", - "name": "handshake", - "alpha_code": ":handshake:", - "aliases": ":shaking_hands:" - }, - "1f373": { - "output": "1f373", - "name": "cooking", - "alpha_code": ":cooking:", - "aliases": "" - }, - "1f374": { - "output": "1f374", - "name": "fork and knife", - "alpha_code": ":fork_and_knife:", - "aliases": "" - }, - "1f375": { - "output": "1f375", - "name": "teacup without handle", - "alpha_code": ":tea:", - "aliases": "" - }, - "1f376": { - "output": "1f376", - "name": "sake", - "alpha_code": ":sake:", - "aliases": "" - }, - "1f377": { - "output": "1f377", - "name": "wine glass", - "alpha_code": ":wine_glass:", - "aliases": "" - }, - "1f378": { - "output": "1f378", - "name": "cocktail glass", - "alpha_code": ":cocktail:", - "aliases": "" - }, - "1f379": { - "output": "1f379", - "name": "tropical drink", - "alpha_code": ":tropical_drink:", - "aliases": "" - }, - "1f37a": { - "output": "1f37a", - "name": "beer mug", - "alpha_code": ":beer:", - "aliases": "" - }, - "1f37b": { - "output": "1f37b", - "name": "clinking beer mugs", - "alpha_code": ":beers:", - "aliases": "" - }, - "1f380": { - "output": "1f380", - "name": "ribbon", - "alpha_code": ":ribbon:", - "aliases": "" - }, - "1f381": { - "output": "1f381", - "name": "wrapped gift", - "alpha_code": ":gift:", - "aliases": "" - }, - "1f382": { - "output": "1f382", - "name": "birthday cake", - "alpha_code": ":birthday:", - "aliases": "" - }, - "1f383": { - "output": "1f383", - "name": "jack-o-lantern", - "alpha_code": ":jack_o_lantern:", - "aliases": "" - }, - "1f91b": { - "output": "1f91b", - "name": "left-facing fist", - "alpha_code": ":left_facing_fist:", - "aliases": ":left_fist:" - }, - "1f91c": { - "output": "1f91c", - "name": "right-facing fist", - "alpha_code": ":right_facing_fist:", - "aliases": ":right_fist:" - }, - "1f384": { - "output": "1f384", - "name": "Christmas tree", - "alpha_code": ":christmas_tree:", - "aliases": "" - }, - "1f385": { - "output": "1f385", - "name": "Santa Claus", - "alpha_code": ":santa:", - "aliases": "" - }, - "1f386": { - "output": "1f386", - "name": "fireworks", - "alpha_code": ":fireworks:", - "aliases": "" - }, - "1f91a": { - "output": "1f91a", - "name": "raised back of hand", - "alpha_code": ":raised_back_of_hand:", - "aliases": ":back_of_hand:" - }, - "1f387": { - "output": "1f387", - "name": "sparkler", - "alpha_code": ":sparkler:", - "aliases": "" - }, - "1f388": { - "output": "1f388", - "name": "balloon", - "alpha_code": ":balloon:", - "aliases": "" - }, - "1f389": { - "output": "1f389", - "name": "party popper", - "alpha_code": ":tada:", - "aliases": "" - }, - "1f38a": { - "output": "1f38a", - "name": "confetti ball", - "alpha_code": ":confetti_ball:", - "aliases": "" - }, - "1f38b": { - "output": "1f38b", - "name": "tanabata tree", - "alpha_code": ":tanabata_tree:", - "aliases": "" - }, - "1f38c": { - "output": "1f38c", - "name": "crossed flags", - "alpha_code": ":crossed_flags:", - "aliases": "" - }, - "1f919": { - "output": "1f919", - "name": "call me hand", - "alpha_code": ":call_me:", - "aliases": ":call_me_hand:" - }, - "1f38d": { - "output": "1f38d", - "name": "pine decoration", - "alpha_code": ":bamboo:", - "aliases": "" - }, - "1f57a": { - "output": "1f57a", - "name": "man dancing", - "alpha_code": ":man_dancing:", - "aliases": ":male_dancer:" - }, - "1f38e": { - "output": "1f38e", - "name": "Japanese dolls", - "alpha_code": ":dolls:", - "aliases": "" - }, - "1f933": { - "output": "1f933", - "name": "selfie", - "alpha_code": ":selfie:", - "aliases": "" - }, - "1f38f": { - "output": "1f38f", - "name": "carp streamer", - "alpha_code": ":flags:", - "aliases": "" - }, - "1f930": { - "output": "1f930", - "name": "pregnant woman", - "alpha_code": ":pregnant_woman:", - "aliases": ":expecting_woman:" - }, - "1f390": { - "output": "1f390", - "name": "wind chime", - "alpha_code": ":wind_chime:", - "aliases": "" - }, - "1f926": { - "output": "1f926", - "name": "person facepalming", - "alpha_code": ":person_facepalming:", - "aliases": ":face_palm:|:facepalm:" - }, - "1f937": { - "output": "1f937", - "name": "person shrugging", - "alpha_code": ":person_shrugging:", - "aliases": ":shrug:" - }, - "1f391": { - "output": "1f391", - "name": "moon viewing ceremony", - "alpha_code": ":rice_scene:", - "aliases": "" - }, - "1f392": { - "output": "1f392", - "name": "school backpack", - "alpha_code": ":school_satchel:", - "aliases": "" - }, - "1f393": { - "output": "1f393", - "name": "graduation cap", - "alpha_code": ":mortar_board:", - "aliases": "" - }, - "1f3a0": { - "output": "1f3a0", - "name": "carousel horse", - "alpha_code": ":carousel_horse:", - "aliases": "" - }, - "1f3a1": { - "output": "1f3a1", - "name": "ferris wheel", - "alpha_code": ":ferris_wheel:", - "aliases": "" - }, - "1f3a2": { - "output": "1f3a2", - "name": "roller coaster", - "alpha_code": ":roller_coaster:", - "aliases": "" - }, - "1f3a3": { - "output": "1f3a3", - "name": "fishing pole", - "alpha_code": ":fishing_pole_and_fish:", - "aliases": "" - }, - "1f3a4": { - "output": "1f3a4", - "name": "microphone", - "alpha_code": ":microphone:", - "aliases": "" - }, - "0023": { - "output": "0023-fe0f", - "name": "pound symbol", - "alpha_code": ":pound_symbol:", - "aliases": "" - }, - "1f3a5": { - "output": "1f3a5", - "name": "movie camera", - "alpha_code": ":movie_camera:", - "aliases": "" - }, - "1f3a6": { - "output": "1f3a6", - "name": "cinema", - "alpha_code": ":cinema:", - "aliases": "" - }, - "1f3a7": { - "output": "1f3a7", - "name": "headphone", - "alpha_code": ":headphones:", - "aliases": "" - }, - "1f936": { - "output": "1f936", - "name": "Mrs. Claus", - "alpha_code": ":mrs_claus:", - "aliases": ":mother_christmas:" - }, - "1f3a8": { - "output": "1f3a8", - "name": "artist palette", - "alpha_code": ":art:", - "aliases": "" - }, - "1f935": { - "output": "1f935", - "name": "man in tuxedo", - "alpha_code": ":man_in_tuxedo:", - "aliases": "" - }, - "1f3a9": { - "output": "1f3a9", - "name": "top hat", - "alpha_code": ":tophat:", - "aliases": "" - }, - "1f3aa": { - "output": "1f3aa", - "name": "circus tent", - "alpha_code": ":circus_tent:", - "aliases": "" - }, - "1f934": { - "output": "1f934", - "name": "prince", - "alpha_code": ":prince:", - "aliases": "" - }, - "1f3ab": { - "output": "1f3ab", - "name": "ticket", - "alpha_code": ":ticket:", - "aliases": "" - }, - "1f3ac": { - "output": "1f3ac", - "name": "clapper board", - "alpha_code": ":clapper:", - "aliases": "" - }, - "1f3ad": { - "output": "1f3ad", - "name": "performing arts", - "alpha_code": ":performing_arts:", - "aliases": "" - }, - "1f927": { - "output": "1f927", - "name": "sneezing face", - "alpha_code": ":sneezing_face:", - "aliases": ":sneeze:" - }, - "1f3ae": { - "output": "1f3ae", - "name": "video game", - "alpha_code": ":video_game:", - "aliases": "" - }, - "1f3af": { - "output": "1f3af", - "name": "direct hit", - "alpha_code": ":dart:", - "aliases": "" - }, - "1f3b0": { - "output": "1f3b0", - "name": "slot machine", - "alpha_code": ":slot_machine:", - "aliases": "" - }, - "1f3b1": { - "output": "1f3b1", - "name": "pool 8 ball", - "alpha_code": ":8ball:", - "aliases": "" - }, - "1f3b2": { - "output": "1f3b2", - "name": "game die", - "alpha_code": ":game_die:", - "aliases": "" - }, - "1f3b3": { - "output": "1f3b3", - "name": "bowling", - "alpha_code": ":bowling:", - "aliases": "" - }, - "1f3b4": { - "output": "1f3b4", - "name": "flower playing cards", - "alpha_code": ":flower_playing_cards:", - "aliases": "" - }, - "1f925": { - "output": "1f925", - "name": "lying face", - "alpha_code": ":lying_face:", - "aliases": ":liar:" - }, - "1f3b5": { - "output": "1f3b5", - "name": "musical note", - "alpha_code": ":musical_note:", - "aliases": "" - }, - "1f3b6": { - "output": "1f3b6", - "name": "musical notes", - "alpha_code": ":notes:", - "aliases": "" - }, - "1f3b7": { - "output": "1f3b7", - "name": "saxophone", - "alpha_code": ":saxophone:", - "aliases": "" - }, - "1f924": { - "output": "1f924", - "name": "drooling face", - "alpha_code": ":drooling_face:", - "aliases": ":drool:" - }, - "1f3b8": { - "output": "1f3b8", - "name": "guitar", - "alpha_code": ":guitar:", - "aliases": "" - }, - "1f3b9": { - "output": "1f3b9", - "name": "musical keyboard", - "alpha_code": ":musical_keyboard:", - "aliases": "" - }, - "1f3ba": { - "output": "1f3ba", - "name": "trumpet", - "alpha_code": ":trumpet:", - "aliases": "" - }, - "1f923": { - "output": "1f923", - "name": "rolling on the floor laughing", - "alpha_code": ":rofl:", - "aliases": ":rolling_on_the_floor_laughing:" - }, - "1f3bb": { - "output": "1f3bb", - "name": "violin", - "alpha_code": ":violin:", - "aliases": "" - }, - "1f3bc": { - "output": "1f3bc", - "name": "musical score", - "alpha_code": ":musical_score:", - "aliases": "" - }, - "1f3bd": { - "output": "1f3bd", - "name": "running shirt", - "alpha_code": ":running_shirt_with_sash:", - "aliases": "" - }, - "1f922": { - "output": "1f922", - "name": "nauseated face", - "alpha_code": ":nauseated_face:", - "aliases": ":sick:" - }, - "1f3be": { - "output": "1f3be", - "name": "tennis", - "alpha_code": ":tennis:", - "aliases": "" - }, - "1f3bf": { - "output": "1f3bf", - "name": "skis", - "alpha_code": ":ski:", - "aliases": "" - }, - "1f3c0": { - "output": "1f3c0", - "name": "basketball", - "alpha_code": ":basketball:", - "aliases": "" - }, - "1f3c1": { - "output": "1f3c1", - "name": "chequered flag", - "alpha_code": ":checkered_flag:", - "aliases": "" - }, - "1f921": { - "output": "1f921", - "name": "clown face", - "alpha_code": ":clown:", - "aliases": ":clown_face:" - }, - "1f3c2": { - "output": "1f3c2", - "name": "snowboarder", - "alpha_code": ":snowboarder:", - "aliases": "" - }, - "1f3c3": { - "output": "1f3c3", - "name": "person running", - "alpha_code": ":person_running:", - "aliases": ":runner:" - }, - "1f3c4": { - "output": "1f3c4", - "name": "person surfing", - "alpha_code": ":person_surfing:", - "aliases": ":surfer:" - }, - "1f3c6": { - "output": "1f3c6", - "name": "trophy", - "alpha_code": ":trophy:", - "aliases": "" - }, - "1f3c8": { - "output": "1f3c8", - "name": "american football", - "alpha_code": ":football:", - "aliases": "" - }, - "1f3ca": { - "output": "1f3ca", - "name": "person swimming", - "alpha_code": ":person_swimming:", - "aliases": ":swimmer:" - }, - "1f3e0": { - "output": "1f3e0", - "name": "house", - "alpha_code": ":house:", - "aliases": "" - }, - "1f3e1": { - "output": "1f3e1", - "name": "house with garden", - "alpha_code": ":house_with_garden:", - "aliases": "" - }, - "1f3e2": { - "output": "1f3e2", - "name": "office building", - "alpha_code": ":office:", - "aliases": "" - }, - "1f3e3": { - "output": "1f3e3", - "name": "Japanese post office", - "alpha_code": ":post_office:", - "aliases": "" - }, - "1f3e5": { - "output": "1f3e5", - "name": "hospital", - "alpha_code": ":hospital:", - "aliases": "" - }, - "1f3e6": { - "output": "1f3e6", - "name": "bank", - "alpha_code": ":bank:", - "aliases": "" - }, - "1f3e7": { - "output": "1f3e7", - "name": "ATM sign", - "alpha_code": ":atm:", - "aliases": "" - }, - "1f3e8": { - "output": "1f3e8", - "name": "hotel", - "alpha_code": ":hotel:", - "aliases": "" - }, - "1f3e9": { - "output": "1f3e9", - "name": "love hotel", - "alpha_code": ":love_hotel:", - "aliases": "" - }, - "1f3ea": { - "output": "1f3ea", - "name": "convenience store", - "alpha_code": ":convenience_store:", - "aliases": "" - }, - "1f3eb": { - "output": "1f3eb", - "name": "school", - "alpha_code": ":school:", - "aliases": "" - }, - "1f3ec": { - "output": "1f3ec", - "name": "department store", - "alpha_code": ":department_store:", - "aliases": "" - }, - "1f920": { - "output": "1f920", - "name": "cowboy hat face", - "alpha_code": ":cowboy:", - "aliases": ":face_with_cowboy_hat:" - }, - "1f3ed": { - "output": "1f3ed", - "name": "factory", - "alpha_code": ":factory:", - "aliases": "" - }, - "1f3ee": { - "output": "1f3ee", - "name": "red paper lantern", - "alpha_code": ":izakaya_lantern:", - "aliases": "" - }, - "1f3ef": { - "output": "1f3ef", - "name": "Japanese castle", - "alpha_code": ":japanese_castle:", - "aliases": "" - }, - "1f3f0": { - "output": "1f3f0", - "name": "castle", - "alpha_code": ":european_castle:", - "aliases": "" - }, - "1f40c": { - "output": "1f40c", - "name": "snail", - "alpha_code": ":snail:", - "aliases": "" - }, - "1f40d": { - "output": "1f40d", - "name": "snake", - "alpha_code": ":snake:", - "aliases": "" - }, - "1f40e": { - "output": "1f40e", - "name": "horse", - "alpha_code": ":racehorse:", - "aliases": "" - }, - "1f411": { - "output": "1f411", - "name": "ewe", - "alpha_code": ":sheep:", - "aliases": "" - }, - "1f412": { - "output": "1f412", - "name": "monkey", - "alpha_code": ":monkey:", - "aliases": "" - }, - "1f414": { - "output": "1f414", - "name": "chicken", - "alpha_code": ":chicken:", - "aliases": "" - }, - "1f417": { - "output": "1f417", - "name": "boar", - "alpha_code": ":boar:", - "aliases": "" - }, - "1f418": { - "output": "1f418", - "name": "elephant", - "alpha_code": ":elephant:", - "aliases": "" - }, - "1f419": { - "output": "1f419", - "name": "octopus", - "alpha_code": ":octopus:", - "aliases": "" - }, - "1f41a": { - "output": "1f41a", - "name": "spiral shell", - "alpha_code": ":shell:", - "aliases": "" - }, - "1f934-1f3fb": { - "output": "1f934-1f3fb", - "name": "prince: light skin tone", - "alpha_code": ":prince_tone1:", - "aliases": "" - }, - "1f41b": { - "output": "1f41b", - "name": "bug", - "alpha_code": ":bug:", - "aliases": "" - }, - "1f41c": { - "output": "1f41c", - "name": "ant", - "alpha_code": ":ant:", - "aliases": "" - }, - "1f41d": { - "output": "1f41d", - "name": "honeybee", - "alpha_code": ":bee:", - "aliases": "" - }, - "1f41e": { - "output": "1f41e", - "name": "lady beetle", - "alpha_code": ":beetle:", - "aliases": "" - }, - "1f41f": { - "output": "1f41f", - "name": "fish", - "alpha_code": ":fish:", - "aliases": "" - }, - "1f420": { - "output": "1f420", - "name": "tropical fish", - "alpha_code": ":tropical_fish:", - "aliases": "" - }, - "1f421": { - "output": "1f421", - "name": "blowfish", - "alpha_code": ":blowfish:", - "aliases": "" - }, - "1f422": { - "output": "1f422", - "name": "turtle", - "alpha_code": ":turtle:", - "aliases": "" - }, - "1f423": { - "output": "1f423", - "name": "hatching chick", - "alpha_code": ":hatching_chick:", - "aliases": "" - }, - "1f424": { - "output": "1f424", - "name": "baby chick", - "alpha_code": ":baby_chick:", - "aliases": "" - }, - "1f425": { - "output": "1f425", - "name": "front-facing baby chick", - "alpha_code": ":hatched_chick:", - "aliases": "" - }, - "1f426": { - "output": "1f426", - "name": "bird", - "alpha_code": ":bird:", - "aliases": "" - }, - "1f427": { - "output": "1f427", - "name": "penguin", - "alpha_code": ":penguin:", - "aliases": "" - }, - "1f428": { - "output": "1f428", - "name": "koala", - "alpha_code": ":koala:", - "aliases": "" - }, - "1f429": { - "output": "1f429", - "name": "poodle", - "alpha_code": ":poodle:", - "aliases": "" - }, - "1f42b": { - "output": "1f42b", - "name": "two-hump camel", - "alpha_code": ":camel:", - "aliases": "" - }, - "1f42c": { - "output": "1f42c", - "name": "dolphin", - "alpha_code": ":dolphin:", - "aliases": "" - }, - "1f42d": { - "output": "1f42d", - "name": "mouse face", - "alpha_code": ":mouse:", - "aliases": "" - }, - "1f42e": { - "output": "1f42e", - "name": "cow face", - "alpha_code": ":cow:", - "aliases": "" - }, - "1f42f": { - "output": "1f42f", - "name": "tiger face", - "alpha_code": ":tiger:", - "aliases": "" - }, - "1f430": { - "output": "1f430", - "name": "rabbit face", - "alpha_code": ":rabbit:", - "aliases": "" - }, - "1f431": { - "output": "1f431", - "name": "cat face", - "alpha_code": ":cat:", - "aliases": "" - }, - "1f432": { - "output": "1f432", - "name": "dragon face", - "alpha_code": ":dragon_face:", - "aliases": "" - }, - "1f433": { - "output": "1f433", - "name": "spouting whale", - "alpha_code": ":whale:", - "aliases": "" - }, - "1f434": { - "output": "1f434", - "name": "horse face", - "alpha_code": ":horse:", - "aliases": "" - }, - "1f435": { - "output": "1f435", - "name": "monkey face", - "alpha_code": ":monkey_face:", - "aliases": "" - }, - "1f436": { - "output": "1f436", - "name": "dog face", - "alpha_code": ":dog:", - "aliases": "" - }, - "1f437": { - "output": "1f437", - "name": "pig face", - "alpha_code": ":pig:", - "aliases": "" - }, - "1f438": { - "output": "1f438", - "name": "frog face", - "alpha_code": ":frog:", - "aliases": "" - }, - "1f439": { - "output": "1f439", - "name": "hamster face", - "alpha_code": ":hamster:", - "aliases": "" - }, - "1f43a": { - "output": "1f43a", - "name": "wolf face", - "alpha_code": ":wolf:", - "aliases": "" - }, - "1f43b": { - "output": "1f43b", - "name": "bear face", - "alpha_code": ":bear:", - "aliases": "" - }, - "1f43c": { - "output": "1f43c", - "name": "panda face", - "alpha_code": ":panda_face:", - "aliases": "" - }, - "1f43d": { - "output": "1f43d", - "name": "pig nose", - "alpha_code": ":pig_nose:", - "aliases": "" - }, - "1f43e": { - "output": "1f43e", - "name": "paw prints", - "alpha_code": ":feet:", - "aliases": ":paw_prints:" - }, - "1f934-1f3fc": { - "output": "1f934-1f3fc", - "name": "prince: medium-light skin tone", - "alpha_code": ":prince_tone2:", - "aliases": "" - }, - "1f440": { - "output": "1f440", - "name": "eyes", - "alpha_code": ":eyes:", - "aliases": "" - }, - "1f442": { - "output": "1f442", - "name": "ear", - "alpha_code": ":ear:", - "aliases": "" - }, - "1f443": { - "output": "1f443", - "name": "nose", - "alpha_code": ":nose:", - "aliases": "" - }, - "1f444": { - "output": "1f444", - "name": "mouth", - "alpha_code": ":lips:", - "aliases": "" - }, - "1f445": { - "output": "1f445", - "name": "tongue", - "alpha_code": ":tongue:", - "aliases": "" - }, - "1f934-1f3fd": { - "output": "1f934-1f3fd", - "name": "prince: medium skin tone", - "alpha_code": ":prince_tone3:", - "aliases": "" - }, - "1f446": { - "output": "1f446", - "name": "backhand index pointing up", - "alpha_code": ":point_up_2:", - "aliases": "" - }, - "1f447": { - "output": "1f447", - "name": "backhand index pointing down", - "alpha_code": ":point_down:", - "aliases": "" - }, - "1f448": { - "output": "1f448", - "name": "backhand index pointing left", - "alpha_code": ":point_left:", - "aliases": "" - }, - "1f449": { - "output": "1f449", - "name": "backhand index pointing right", - "alpha_code": ":point_right:", - "aliases": "" - }, - "1f44a": { - "output": "1f44a", - "name": "oncoming fist", - "alpha_code": ":punch:", - "aliases": "" - }, - "1f44b": { - "output": "1f44b", - "name": "waving hand", - "alpha_code": ":wave:", - "aliases": "" - }, - "1f44c": { - "output": "1f44c", - "name": "OK hand", - "alpha_code": ":ok_hand:", - "aliases": "" - }, - "1f44d": { - "output": "1f44d", - "name": "thumbs up", - "alpha_code": ":thumbsup:", - "aliases": ":+1:|:thumbup:" - }, - "1f44e": { - "output": "1f44e", - "name": "thumbs down", - "alpha_code": ":thumbsdown:", - "aliases": ":-1:|:thumbdown:" - }, - "1f44f": { - "output": "1f44f", - "name": "clapping hands", - "alpha_code": ":clap:", - "aliases": "" - }, - "1f450": { - "output": "1f450", - "name": "open hands", - "alpha_code": ":open_hands:", - "aliases": "" - }, - "1f451": { - "output": "1f451", - "name": "crown", - "alpha_code": ":crown:", - "aliases": "" - }, - "1f452": { - "output": "1f452", - "name": "woman\u2019s hat", - "alpha_code": ":womans_hat:", - "aliases": "" - }, - "1f453": { - "output": "1f453", - "name": "glasses", - "alpha_code": ":eyeglasses:", - "aliases": "" - }, - "1f454": { - "output": "1f454", - "name": "necktie", - "alpha_code": ":necktie:", - "aliases": "" - }, - "1f455": { - "output": "1f455", - "name": "t-shirt", - "alpha_code": ":shirt:", - "aliases": "" - }, - "1f934-1f3fe": { - "output": "1f934-1f3fe", - "name": "prince: medium-dark skin tone", - "alpha_code": ":prince_tone4:", - "aliases": "" - }, - "1f456": { - "output": "1f456", - "name": "jeans", - "alpha_code": ":jeans:", - "aliases": "" - }, - "1f457": { - "output": "1f457", - "name": "dress", - "alpha_code": ":dress:", - "aliases": "" - }, - "1f458": { - "output": "1f458", - "name": "kimono", - "alpha_code": ":kimono:", - "aliases": "" - }, - "1f934-1f3ff": { - "output": "1f934-1f3ff", - "name": "prince: dark skin tone", - "alpha_code": ":prince_tone5:", - "aliases": "" - }, - "1f459": { - "output": "1f459", - "name": "bikini", - "alpha_code": ":bikini:", - "aliases": "" - }, - "1f45a": { - "output": "1f45a", - "name": "woman\u2019s clothes", - "alpha_code": ":womans_clothes:", - "aliases": "" - }, - "1f45b": { - "output": "1f45b", - "name": "purse", - "alpha_code": ":purse:", - "aliases": "" - }, - "1f45c": { - "output": "1f45c", - "name": "handbag", - "alpha_code": ":handbag:", - "aliases": "" - }, - "1f45d": { - "output": "1f45d", - "name": "clutch bag", - "alpha_code": ":pouch:", - "aliases": "" - }, - "1f45e": { - "output": "1f45e", - "name": "man\u2019s shoe", - "alpha_code": ":mans_shoe:", - "aliases": "" - }, - "1f45f": { - "output": "1f45f", - "name": "running shoe", - "alpha_code": ":athletic_shoe:", - "aliases": "" - }, - "1f460": { - "output": "1f460", - "name": "high-heeled shoe", - "alpha_code": ":high_heel:", - "aliases": "" - }, - "1f461": { - "output": "1f461", - "name": "woman\u2019s sandal", - "alpha_code": ":sandal:", - "aliases": "" - }, - "1f462": { - "output": "1f462", - "name": "woman\u2019s boot", - "alpha_code": ":boot:", - "aliases": "" - }, - "1f463": { - "output": "1f463", - "name": "footprints", - "alpha_code": ":footprints:", - "aliases": "" - }, - "1f464": { - "output": "1f464", - "name": "bust in silhouette", - "alpha_code": ":bust_in_silhouette:", - "aliases": "" - }, - "1f936-1f3fb": { - "output": "1f936-1f3fb", - "name": "Mrs. Claus: light skin tone", - "alpha_code": ":mrs_claus_tone1:", - "aliases": ":mother_christmas_tone1:" - }, - "1f466": { - "output": "1f466", - "name": "boy", - "alpha_code": ":boy:", - "aliases": "" - }, - "1f467": { - "output": "1f467", - "name": "girl", - "alpha_code": ":girl:", - "aliases": "" - }, - "1f468": { - "output": "1f468", - "name": "man", - "alpha_code": ":man:", - "aliases": "" - }, - "1f469": { - "output": "1f469", - "name": "woman", - "alpha_code": ":woman:", - "aliases": "" - }, - "1f46a": { - "output": "1f46a", - "name": "family", - "alpha_code": ":family:", - "aliases": "" - }, - "1f46b": { - "output": "1f46b", - "name": "man and woman holding hands", - "alpha_code": ":couple:", - "aliases": "" - }, - "1f46e": { - "output": "1f46e", - "name": "police officer", - "alpha_code": ":police_officer:", - "aliases": ":cop:" - }, - "1f46f": { - "output": "1f46f", - "name": "people with bunny ears partying", - "alpha_code": ":people_with_bunny_ears_partying:", - "aliases": ":dancers:" - }, - "1f470": { - "output": "1f470", - "name": "bride with veil", - "alpha_code": ":bride_with_veil:", - "aliases": "" - }, - "1f471": { - "output": "1f471", - "name": "blond-haired person", - "alpha_code": ":blond_haired_person:", - "aliases": ":person_with_blond_hair:" - }, - "1f472": { - "output": "1f472", - "name": "man with Chinese cap", - "alpha_code": ":man_with_chinese_cap:", - "aliases": ":man_with_gua_pi_mao:" - }, - "1f473": { - "output": "1f473", - "name": "person wearing turban", - "alpha_code": ":person_wearing_turban:", - "aliases": ":man_with_turban:" - }, - "1f474": { - "output": "1f474", - "name": "old man", - "alpha_code": ":older_man:", - "aliases": "" - }, - "1f475": { - "output": "1f475", - "name": "old woman", - "alpha_code": ":older_woman:", - "aliases": ":grandma:" - }, - "1f476": { - "output": "1f476", - "name": "baby", - "alpha_code": ":baby:", - "aliases": "" - }, - "1f477": { - "output": "1f477", - "name": "construction worker", - "alpha_code": ":construction_worker:", - "aliases": "" - }, - "1f478": { - "output": "1f478", - "name": "princess", - "alpha_code": ":princess:", - "aliases": "" - }, - "1f479": { - "output": "1f479", - "name": "ogre", - "alpha_code": ":japanese_ogre:", - "aliases": "" - }, - "1f936-1f3fc": { - "output": "1f936-1f3fc", - "name": "Mrs. Claus: medium-light skin tone", - "alpha_code": ":mrs_claus_tone2:", - "aliases": ":mother_christmas_tone2:" - }, - "1f47a": { - "output": "1f47a", - "name": "goblin", - "alpha_code": ":japanese_goblin:", - "aliases": "" - }, - "1f47b": { - "output": "1f47b", - "name": "ghost", - "alpha_code": ":ghost:", - "aliases": "" - }, - "1f47c": { - "output": "1f47c", - "name": "baby angel", - "alpha_code": ":angel:", - "aliases": "" - }, - "1f47d": { - "output": "1f47d", - "name": "alien", - "alpha_code": ":alien:", - "aliases": "" - }, - "1f47e": { - "output": "1f47e", - "name": "alien monster", - "alpha_code": ":space_invader:", - "aliases": "" - }, - "1f936-1f3fd": { - "output": "1f936-1f3fd", - "name": "Mrs. Claus: medium skin tone", - "alpha_code": ":mrs_claus_tone3:", - "aliases": ":mother_christmas_tone3:" - }, - "1f47f": { - "output": "1f47f", - "name": "angry face with horns", - "alpha_code": ":imp:", - "aliases": "" - }, - "1f480": { - "output": "1f480", - "name": "skull", - "alpha_code": ":skull:", - "aliases": ":skeleton:" - }, - "1f4c7": { - "output": "1f4c7", - "name": "card index", - "alpha_code": ":card_index:", - "aliases": "" - }, - "1f481": { - "output": "1f481", - "name": "person tipping hand", - "alpha_code": ":person_tipping_hand:", - "aliases": ":information_desk_person:" - }, - "1f482": { - "output": "1f482", - "name": "guard", - "alpha_code": ":guard:", - "aliases": ":guardsman:" - }, - "1f483": { - "output": "1f483", - "name": "woman dancing", - "alpha_code": ":dancer:", - "aliases": "" - }, - "1f484": { - "output": "1f484", - "name": "lipstick", - "alpha_code": ":lipstick:", - "aliases": "" - }, - "1f485": { - "output": "1f485", - "name": "nail polish", - "alpha_code": ":nail_care:", - "aliases": "" - }, - "1f4d2": { - "output": "1f4d2", - "name": "ledger", - "alpha_code": ":ledger:", - "aliases": "" - }, - "1f486": { - "output": "1f486", - "name": "person getting massage", - "alpha_code": ":person_getting_massage:", - "aliases": ":massage:" - }, - "1f4d3": { - "output": "1f4d3", - "name": "notebook", - "alpha_code": ":notebook:", - "aliases": "" - }, - "1f487": { - "output": "1f487", - "name": "person getting haircut", - "alpha_code": ":person_getting_haircut:", - "aliases": ":haircut:" - }, - "1f4d4": { - "output": "1f4d4", - "name": "notebook with decorative cover", - "alpha_code": ":notebook_with_decorative_cover:", - "aliases": "" - }, - "1f488": { - "output": "1f488", - "name": "barber pole", - "alpha_code": ":barber:", - "aliases": "" - }, - "1f4d5": { - "output": "1f4d5", - "name": "closed book", - "alpha_code": ":closed_book:", - "aliases": "" - }, - "1f489": { - "output": "1f489", - "name": "syringe", - "alpha_code": ":syringe:", - "aliases": "" - }, - "1f4d6": { - "output": "1f4d6", - "name": "open book", - "alpha_code": ":book:", - "aliases": "" - }, - "1f48a": { - "output": "1f48a", - "name": "pill", - "alpha_code": ":pill:", - "aliases": "" - }, - "1f4d7": { - "output": "1f4d7", - "name": "green book", - "alpha_code": ":green_book:", - "aliases": "" - }, - "1f48b": { - "output": "1f48b", - "name": "kiss mark", - "alpha_code": ":kiss:", - "aliases": "" - }, - "1f4d8": { - "output": "1f4d8", - "name": "blue book", - "alpha_code": ":blue_book:", - "aliases": "" - }, - "1f48c": { - "output": "1f48c", - "name": "love letter", - "alpha_code": ":love_letter:", - "aliases": "" - }, - "1f4d9": { - "output": "1f4d9", - "name": "orange book", - "alpha_code": ":orange_book:", - "aliases": "" - }, - "1f48d": { - "output": "1f48d", - "name": "ring", - "alpha_code": ":ring:", - "aliases": "" - }, - "1f4da": { - "output": "1f4da", - "name": "books", - "alpha_code": ":books:", - "aliases": "" - }, - "1f48e": { - "output": "1f48e", - "name": "gem stone", - "alpha_code": ":gem:", - "aliases": "" - }, - "1f936-1f3fe": { - "output": "1f936-1f3fe", - "name": "Mrs. Claus: medium-dark skin tone", - "alpha_code": ":mrs_claus_tone4:", - "aliases": ":mother_christmas_tone4:" - }, - "1f4db": { - "output": "1f4db", - "name": "name badge", - "alpha_code": ":name_badge:", - "aliases": "" - }, - "1f48f": { - "output": "1f48f", - "name": "kiss", - "alpha_code": ":couplekiss:", - "aliases": "" - }, - "1f4dc": { - "output": "1f4dc", - "name": "scroll", - "alpha_code": ":scroll:", - "aliases": "" - }, - "1f490": { - "output": "1f490", - "name": "bouquet", - "alpha_code": ":bouquet:", - "aliases": "" - }, - "1f4dd": { - "output": "1f4dd", - "name": "memo", - "alpha_code": ":pencil:", - "aliases": ":memo:" - }, - "1f936-1f3ff": { - "output": "1f936-1f3ff", - "name": "Mrs. Claus: dark skin tone", - "alpha_code": ":mrs_claus_tone5:", - "aliases": ":mother_christmas_tone5:" - }, - "1f491": { - "output": "1f491", - "name": "couple with heart", - "alpha_code": ":couple_with_heart:", - "aliases": "" - }, - "1f4de": { - "output": "1f4de", - "name": "telephone receiver", - "alpha_code": ":telephone_receiver:", - "aliases": "" - }, - "1f492": { - "output": "1f492", - "name": "wedding", - "alpha_code": ":wedding:", - "aliases": "" - }, - "1f4df": { - "output": "1f4df", - "name": "pager", - "alpha_code": ":pager:", - "aliases": "" - }, - "1f4e0": { - "output": "1f4e0", - "name": "fax machine", - "alpha_code": ":fax:", - "aliases": "" - }, - "1f493": { - "output": "1f493", - "name": "beating heart", - "alpha_code": ":heartbeat:", - "aliases": "" - }, - "1f4e1": { - "output": "1f4e1", - "name": "satellite antenna", - "alpha_code": ":satellite:", - "aliases": "" - }, - "1f4e2": { - "output": "1f4e2", - "name": "loudspeaker", - "alpha_code": ":loudspeaker:", - "aliases": "" - }, - "1f935-1f3fb": { - "output": "1f935-1f3fb", - "name": "man in tuxedo: light skin tone", - "alpha_code": ":man_in_tuxedo_tone1:", - "aliases": ":tuxedo_tone1:" - }, - "1f494": { - "output": "1f494", - "name": "broken heart", - "alpha_code": ":broken_heart:", - "aliases": "" - }, - "1f4e3": { - "output": "1f4e3", - "name": "megaphone", - "alpha_code": ":mega:", - "aliases": "" - }, - "1f4e4": { - "output": "1f4e4", - "name": "outbox tray", - "alpha_code": ":outbox_tray:", - "aliases": "" - }, - "1f495": { - "output": "1f495", - "name": "two hearts", - "alpha_code": ":two_hearts:", - "aliases": "" - }, - "1f4e5": { - "output": "1f4e5", - "name": "inbox tray", - "alpha_code": ":inbox_tray:", - "aliases": "" - }, - "1f4e6": { - "output": "1f4e6", - "name": "package", - "alpha_code": ":package:", - "aliases": "" - }, - "1f935-1f3fc": { - "output": "1f935-1f3fc", - "name": "man in tuxedo: medium-light skin tone", - "alpha_code": ":man_in_tuxedo_tone2:", - "aliases": ":tuxedo_tone2:" - }, - "1f496": { - "output": "1f496", - "name": "sparkling heart", - "alpha_code": ":sparkling_heart:", - "aliases": "" - }, - "1f4e7": { - "output": "1f4e7", - "name": "e-mail", - "alpha_code": ":e-mail:", - "aliases": ":email:" - }, - "1f4e8": { - "output": "1f4e8", - "name": "incoming envelope", - "alpha_code": ":incoming_envelope:", - "aliases": "" - }, - "1f497": { - "output": "1f497", - "name": "growing heart", - "alpha_code": ":heartpulse:", - "aliases": "" - }, - "1f935-1f3fd": { - "output": "1f935-1f3fd", - "name": "man in tuxedo: medium skin tone", - "alpha_code": ":man_in_tuxedo_tone3:", - "aliases": ":tuxedo_tone3:" - }, - "1f4e9": { - "output": "1f4e9", - "name": "envelope with arrow", - "alpha_code": ":envelope_with_arrow:", - "aliases": "" - }, - "1f4ea": { - "output": "1f4ea", - "name": "closed mailbox with lowered flag", - "alpha_code": ":mailbox_closed:", - "aliases": "" - }, - "1f498": { - "output": "1f498", - "name": "heart with arrow", - "alpha_code": ":cupid:", - "aliases": "" - }, - "1f4eb": { - "output": "1f4eb", - "name": "closed mailbox with raised flag", - "alpha_code": ":mailbox:", - "aliases": "" - }, - "1f935-1f3fe": { - "output": "1f935-1f3fe", - "name": "man in tuxedo: medium-dark skin tone", - "alpha_code": ":man_in_tuxedo_tone4:", - "aliases": ":tuxedo_tone4:" - }, - "1f4ee": { - "output": "1f4ee", - "name": "postbox", - "alpha_code": ":postbox:", - "aliases": "" - }, - "1f499": { - "output": "1f499", - "name": "blue heart", - "alpha_code": ":blue_heart:", - "aliases": "" - }, - "1f4f0": { - "output": "1f4f0", - "name": "newspaper", - "alpha_code": ":newspaper:", - "aliases": "" - }, - "1f935-1f3ff": { - "output": "1f935-1f3ff", - "name": "man in tuxedo: dark skin tone", - "alpha_code": ":man_in_tuxedo_tone5:", - "aliases": ":tuxedo_tone5:" - }, - "1f4f1": { - "output": "1f4f1", - "name": "mobile phone", - "alpha_code": ":iphone:", - "aliases": "" - }, - "1f49a": { - "output": "1f49a", - "name": "green heart", - "alpha_code": ":green_heart:", - "aliases": "" - }, - "1f937-1f3fb": { - "output": "1f937-1f3fb", - "name": "person shrugging: light skin tone", - "alpha_code": ":person_shrugging_tone1:", - "aliases": ":shrug_tone1:" - }, - "1f4f2": { - "output": "1f4f2", - "name": "mobile phone with arrow", - "alpha_code": ":calling:", - "aliases": "" - }, - "1f4f3": { - "output": "1f4f3", - "name": "vibration mode", - "alpha_code": ":vibration_mode:", - "aliases": "" - }, - "1f49b": { - "output": "1f49b", - "name": "yellow heart", - "alpha_code": ":yellow_heart:", - "aliases": "" - }, - "1f937-1f3fc": { - "output": "1f937-1f3fc", - "name": "person shrugging: medium-light skin tone", - "alpha_code": ":person_shrugging_tone2:", - "aliases": ":shrug_tone2:" - }, - "1f4f4": { - "output": "1f4f4", - "name": "mobile phone off", - "alpha_code": ":mobile_phone_off:", - "aliases": "" - }, - "1f4f6": { - "output": "1f4f6", - "name": "antenna bars", - "alpha_code": ":signal_strength:", - "aliases": "" - }, - "1f937-1f3fd": { - "output": "1f937-1f3fd", - "name": "person shrugging: medium skin tone", - "alpha_code": ":person_shrugging_tone3:", - "aliases": ":shrug_tone3:" - }, - "1f49c": { - "output": "1f49c", - "name": "purple heart", - "alpha_code": ":purple_heart:", - "aliases": "" - }, - "1f937-1f3fe": { - "output": "1f937-1f3fe", - "name": "person shrugging: medium-dark skin tone", - "alpha_code": ":person_shrugging_tone4:", - "aliases": ":shrug_tone4:" - }, - "1f4f7": { - "output": "1f4f7", - "name": "camera", - "alpha_code": ":camera:", - "aliases": "" - }, - "1f4f9": { - "output": "1f4f9", - "name": "video camera", - "alpha_code": ":video_camera:", - "aliases": "" - }, - "1f49d": { - "output": "1f49d", - "name": "heart with ribbon", - "alpha_code": ":gift_heart:", - "aliases": "" - }, - "1f4fa": { - "output": "1f4fa", - "name": "television", - "alpha_code": ":tv:", - "aliases": "" - }, - "1f937-1f3ff": { - "output": "1f937-1f3ff", - "name": "person shrugging: dark skin tone", - "alpha_code": ":person_shrugging_tone5:", - "aliases": ":shrug_tone5:" - }, - "1f4fb": { - "output": "1f4fb", - "name": "radio", - "alpha_code": ":radio:", - "aliases": "" - }, - "1f49e": { - "output": "1f49e", - "name": "revolving hearts", - "alpha_code": ":revolving_hearts:", - "aliases": "" - }, - "1f4fc": { - "output": "1f4fc", - "name": "videocassette", - "alpha_code": ":vhs:", - "aliases": "" - }, - "1f926-1f3fb": { - "output": "1f926-1f3fb", - "name": "person facepalming: light skin tone", - "alpha_code": ":person_facepalming_tone1:", - "aliases": ":face_palm_tone1:|:facepalm_tone1:" - }, - "1f503": { - "output": "1f503", - "name": "clockwise vertical arrows", - "alpha_code": ":arrows_clockwise:", - "aliases": "" - }, - "1f49f": { - "output": "1f49f", - "name": "heart decoration", - "alpha_code": ":heart_decoration:", - "aliases": "" - }, - "1f50a": { - "output": "1f50a", - "name": "speaker high volume", - "alpha_code": ":loud_sound:", - "aliases": "" - }, - "1f50b": { - "output": "1f50b", - "name": "battery", - "alpha_code": ":battery:", - "aliases": "" - }, - "1f4a0": { - "output": "1f4a0", - "name": "diamond with a dot", - "alpha_code": ":diamond_shape_with_a_dot_inside:", - "aliases": "" - }, - "1f50c": { - "output": "1f50c", - "name": "electric plug", - "alpha_code": ":electric_plug:", - "aliases": "" - }, - "1f50d": { - "output": "1f50d", - "name": "left-pointing magnifying glass", - "alpha_code": ":mag:", - "aliases": "" - }, - "1f926-1f3fc": { - "output": "1f926-1f3fc", - "name": "person facepalming: medium-light skin tone", - "alpha_code": ":person_facepalming_tone2:", - "aliases": ":face_palm_tone2:|:facepalm_tone2:" - }, - "1f4a1": { - "output": "1f4a1", - "name": "light bulb", - "alpha_code": ":bulb:", - "aliases": "" - }, - "1f50e": { - "output": "1f50e", - "name": "right-pointing magnifying glass", - "alpha_code": ":mag_right:", - "aliases": "" - }, - "1f50f": { - "output": "1f50f", - "name": "locked with pen", - "alpha_code": ":lock_with_ink_pen:", - "aliases": "" - }, - "1f4a2": { - "output": "1f4a2", - "name": "anger symbol", - "alpha_code": ":anger:", - "aliases": "" - }, - "1f510": { - "output": "1f510", - "name": "locked with key", - "alpha_code": ":closed_lock_with_key:", - "aliases": "" - }, - "1f511": { - "output": "1f511", - "name": "key", - "alpha_code": ":key:", - "aliases": "" - }, - "1f4a3": { - "output": "1f4a3", - "name": "bomb", - "alpha_code": ":bomb:", - "aliases": "" - }, - "1f512": { - "output": "1f512", - "name": "locked", - "alpha_code": ":lock:", - "aliases": "" - }, - "1f513": { - "output": "1f513", - "name": "unlocked", - "alpha_code": ":unlock:", - "aliases": "" - }, - "1f4a4": { - "output": "1f4a4", - "name": "zzz", - "alpha_code": ":zzz:", - "aliases": "" - }, - "1f514": { - "output": "1f514", - "name": "bell", - "alpha_code": ":bell:", - "aliases": "" - }, - "1f516": { - "output": "1f516", - "name": "bookmark", - "alpha_code": ":bookmark:", - "aliases": "" - }, - "1f4a5": { - "output": "1f4a5", - "name": "collision", - "alpha_code": ":boom:", - "aliases": "" - }, - "1f926-1f3fd": { - "output": "1f926-1f3fd", - "name": "person facepalming: medium skin tone", - "alpha_code": ":person_facepalming_tone3:", - "aliases": ":face_palm_tone3:|:facepalm_tone3:" - }, - "1f517": { - "output": "1f517", - "name": "link", - "alpha_code": ":link:", - "aliases": "" - }, - "1f518": { - "output": "1f518", - "name": "radio button", - "alpha_code": ":radio_button:", - "aliases": "" - }, - "1f4a6": { - "output": "1f4a6", - "name": "sweat droplets", - "alpha_code": ":sweat_drops:", - "aliases": "" - }, - "1f519": { - "output": "1f519", - "name": "BACK arrow", - "alpha_code": ":back:", - "aliases": "" - }, - "1f51a": { - "output": "1f51a", - "name": "END arrow", - "alpha_code": ":end:", - "aliases": "" - }, - "1f4a7": { - "output": "1f4a7", - "name": "droplet", - "alpha_code": ":droplet:", - "aliases": "" - }, - "1f926-1f3fe": { - "output": "1f926-1f3fe", - "name": "person facepalming: medium-dark skin tone", - "alpha_code": ":person_facepalming_tone4:", - "aliases": ":face_palm_tone4:|:facepalm_tone4:" - }, - "1f51b": { - "output": "1f51b", - "name": "ON! arrow", - "alpha_code": ":on:", - "aliases": "" - }, - "1f51c": { - "output": "1f51c", - "name": "SOON arrow", - "alpha_code": ":soon:", - "aliases": "" - }, - "1f4a8": { - "output": "1f4a8", - "name": "dashing away", - "alpha_code": ":dash:", - "aliases": "" - }, - "1f51d": { - "output": "1f51d", - "name": "TOP arrow", - "alpha_code": ":top:", - "aliases": "" - }, - "1f51e": { - "output": "1f51e", - "name": "no one under eighteen", - "alpha_code": ":underage:", - "aliases": "" - }, - "1f4a9": { - "output": "1f4a9", - "name": "pile of poo", - "alpha_code": ":poop:", - "aliases": ":shit:|:hankey:|:poo:" - }, - "1f51f": { - "output": "1f51f", - "name": "keycap 10", - "alpha_code": ":keycap_ten:", - "aliases": "" - }, - "1f4aa": { - "output": "1f4aa", - "name": "flexed biceps", - "alpha_code": ":muscle:", - "aliases": "" - }, - "1f520": { - "output": "1f520", - "name": "input latin uppercase", - "alpha_code": ":capital_abcd:", - "aliases": "" - }, - "1f521": { - "output": "1f521", - "name": "input latin lowercase", - "alpha_code": ":abcd:", - "aliases": "" - }, - "1f4ab": { - "output": "1f4ab", - "name": "dizzy", - "alpha_code": ":dizzy:", - "aliases": "" - }, - "1f926-1f3ff": { - "output": "1f926-1f3ff", - "name": "person facepalming: dark skin tone", - "alpha_code": ":person_facepalming_tone5:", - "aliases": ":face_palm_tone5:|:facepalm_tone5:" - }, - "1f522": { - "output": "1f522", - "name": "input numbers", - "alpha_code": ":1234:", - "aliases": "" - }, - "1f523": { - "output": "1f523", - "name": "input symbols", - "alpha_code": ":symbols:", - "aliases": "" - }, - "1f4ac": { - "output": "1f4ac", - "name": "speech balloon", - "alpha_code": ":speech_balloon:", - "aliases": "" - }, - "1f524": { - "output": "1f524", - "name": "input latin letters", - "alpha_code": ":abc:", - "aliases": "" - }, - "1f525": { - "output": "1f525", - "name": "fire", - "alpha_code": ":fire:", - "aliases": ":flame:" - }, - "1f4ae": { - "output": "1f4ae", - "name": "white flower", - "alpha_code": ":white_flower:", - "aliases": "" - }, - "1f526": { - "output": "1f526", - "name": "flashlight", - "alpha_code": ":flashlight:", - "aliases": "" - }, - "1f527": { - "output": "1f527", - "name": "wrench", - "alpha_code": ":wrench:", - "aliases": "" - }, - "1f4af": { - "output": "1f4af", - "name": "hundred points", - "alpha_code": ":100:", - "aliases": "" - }, - "1f528": { - "output": "1f528", - "name": "hammer", - "alpha_code": ":hammer:", - "aliases": "" - }, - "1f529": { - "output": "1f529", - "name": "nut and bolt", - "alpha_code": ":nut_and_bolt:", - "aliases": "" - }, - "1f4b0": { - "output": "1f4b0", - "name": "money bag", - "alpha_code": ":moneybag:", - "aliases": "" - }, - "1f52a": { - "output": "1f52a", - "name": "kitchen knife", - "alpha_code": ":knife:", - "aliases": "" - }, - "1f52b": { - "output": "1f52b", - "name": "pistol", - "alpha_code": ":gun:", - "aliases": "" - }, - "1f4b1": { - "output": "1f4b1", - "name": "currency exchange", - "alpha_code": ":currency_exchange:", - "aliases": "" - }, - "1f930-1f3fb": { - "output": "1f930-1f3fb", - "name": "pregnant woman: light skin tone", - "alpha_code": ":pregnant_woman_tone1:", - "aliases": ":expecting_woman_tone1:" - }, - "1f52e": { - "output": "1f52e", - "name": "crystal ball", - "alpha_code": ":crystal_ball:", - "aliases": "" - }, - "1f4b2": { - "output": "1f4b2", - "name": "heavy dollar sign", - "alpha_code": ":heavy_dollar_sign:", - "aliases": "" - }, - "1f52f": { - "output": "1f52f", - "name": "dotted six-pointed star", - "alpha_code": ":six_pointed_star:", - "aliases": "" - }, - "1f4b3": { - "output": "1f4b3", - "name": "credit card", - "alpha_code": ":credit_card:", - "aliases": "" - }, - "1f530": { - "output": "1f530", - "name": "Japanese symbol for beginner", - "alpha_code": ":beginner:", - "aliases": "" - }, - "1f531": { - "output": "1f531", - "name": "trident emblem", - "alpha_code": ":trident:", - "aliases": "" - }, - "1f4b4": { - "output": "1f4b4", - "name": "yen banknote", - "alpha_code": ":yen:", - "aliases": "" - }, - "1f930-1f3fc": { - "output": "1f930-1f3fc", - "name": "pregnant woman: medium-light skin tone", - "alpha_code": ":pregnant_woman_tone2:", - "aliases": ":expecting_woman_tone2:" - }, - "1f532": { - "output": "1f532", - "name": "black square button", - "alpha_code": ":black_square_button:", - "aliases": "" - }, - "1f533": { - "output": "1f533", - "name": "white square button", - "alpha_code": ":white_square_button:", - "aliases": "" - }, - "1f4b5": { - "output": "1f4b5", - "name": "dollar banknote", - "alpha_code": ":dollar:", - "aliases": "" - }, - "1f534": { - "output": "1f534", - "name": "red circle", - "alpha_code": ":red_circle:", - "aliases": "" - }, - "1f535": { - "output": "1f535", - "name": "blue circle", - "alpha_code": ":blue_circle:", - "aliases": "" - }, - "1f4b8": { - "output": "1f4b8", - "name": "money with wings", - "alpha_code": ":money_with_wings:", - "aliases": "" - }, - "1f536": { - "output": "1f536", - "name": "large orange diamond", - "alpha_code": ":large_orange_diamond:", - "aliases": "" - }, - "1f537": { - "output": "1f537", - "name": "large blue diamond", - "alpha_code": ":large_blue_diamond:", - "aliases": "" - }, - "1f4b9": { - "output": "1f4b9", - "name": "chart increasing with yen", - "alpha_code": ":chart:", - "aliases": "" - }, - "1f930-1f3fd": { - "output": "1f930-1f3fd", - "name": "pregnant woman: medium skin tone", - "alpha_code": ":pregnant_woman_tone3:", - "aliases": ":expecting_woman_tone3:" - }, - "1f538": { - "output": "1f538", - "name": "small orange diamond", - "alpha_code": ":small_orange_diamond:", - "aliases": "" - }, - "1f539": { - "output": "1f539", - "name": "small blue diamond", - "alpha_code": ":small_blue_diamond:", - "aliases": "" - }, - "1f4ba": { - "output": "1f4ba", - "name": "seat", - "alpha_code": ":seat:", - "aliases": "" - }, - "1f53a": { - "output": "1f53a", - "name": "red triangle pointed up", - "alpha_code": ":small_red_triangle:", - "aliases": "" - }, - "1f53b": { - "output": "1f53b", - "name": "red triangle pointed down", - "alpha_code": ":small_red_triangle_down:", - "aliases": "" - }, - "1f4bb": { - "output": "1f4bb", - "name": "laptop computer", - "alpha_code": ":computer:", - "aliases": "" - }, - "1f53c": { - "output": "1f53c", - "name": "up button", - "alpha_code": ":arrow_up_small:", - "aliases": "" - }, - "1f4bc": { - "output": "1f4bc", - "name": "briefcase", - "alpha_code": ":briefcase:", - "aliases": "" - }, - "1f53d": { - "output": "1f53d", - "name": "down button", - "alpha_code": ":arrow_down_small:", - "aliases": "" - }, - "1f550": { - "output": "1f550", - "name": "one o\u2019clock", - "alpha_code": ":clock1:", - "aliases": "" - }, - "1f4bd": { - "output": "1f4bd", - "name": "computer disk", - "alpha_code": ":minidisc:", - "aliases": "" - }, - "1f551": { - "output": "1f551", - "name": "two o\u2019clock", - "alpha_code": ":clock2:", - "aliases": "" - }, - "1f4be": { - "output": "1f4be", - "name": "floppy disk", - "alpha_code": ":floppy_disk:", - "aliases": "" - }, - "1f930-1f3fe": { - "output": "1f930-1f3fe", - "name": "pregnant woman: medium-dark skin tone", - "alpha_code": ":pregnant_woman_tone4:", - "aliases": ":expecting_woman_tone4:" - }, - "1f552": { - "output": "1f552", - "name": "three o\u2019clock", - "alpha_code": ":clock3:", - "aliases": "" - }, - "1f4bf": { - "output": "1f4bf", - "name": "optical disk", - "alpha_code": ":cd:", - "aliases": "" - }, - "1f553": { - "output": "1f553", - "name": "four o\u2019clock", - "alpha_code": ":clock4:", - "aliases": "" - }, - "1f4c0": { - "output": "1f4c0", - "name": "dvd", - "alpha_code": ":dvd:", - "aliases": "" - }, - "1f554": { - "output": "1f554", - "name": "five o\u2019clock", - "alpha_code": ":clock5:", - "aliases": "" - }, - "1f555": { - "output": "1f555", - "name": "six o\u2019clock", - "alpha_code": ":clock6:", - "aliases": "" - }, - "1f4c1": { - "output": "1f4c1", - "name": "file folder", - "alpha_code": ":file_folder:", - "aliases": "" - }, - "1f556": { - "output": "1f556", - "name": "seven o\u2019clock", - "alpha_code": ":clock7:", - "aliases": "" - }, - "1f557": { - "output": "1f557", - "name": "eight o\u2019clock", - "alpha_code": ":clock8:", - "aliases": "" - }, - "1f4c2": { - "output": "1f4c2", - "name": "open file folder", - "alpha_code": ":open_file_folder:", - "aliases": "" - }, - "1f558": { - "output": "1f558", - "name": "nine o\u2019clock", - "alpha_code": ":clock9:", - "aliases": "" - }, - "1f559": { - "output": "1f559", - "name": "ten o\u2019clock", - "alpha_code": ":clock10:", - "aliases": "" - }, - "1f4c3": { - "output": "1f4c3", - "name": "page with curl", - "alpha_code": ":page_with_curl:", - "aliases": "" - }, - "1f55a": { - "output": "1f55a", - "name": "eleven o\u2019clock", - "alpha_code": ":clock11:", - "aliases": "" - }, - "1f55b": { - "output": "1f55b", - "name": "twelve o\u2019clock", - "alpha_code": ":clock12:", - "aliases": "" - }, - "1f4c4": { - "output": "1f4c4", - "name": "page facing up", - "alpha_code": ":page_facing_up:", - "aliases": "" - }, - "1f5fb": { - "output": "1f5fb", - "name": "mount fuji", - "alpha_code": ":mount_fuji:", - "aliases": "" - }, - "1f5fc": { - "output": "1f5fc", - "name": "Tokyo tower", - "alpha_code": ":tokyo_tower:", - "aliases": "" - }, - "1f4c5": { - "output": "1f4c5", - "name": "calendar", - "alpha_code": ":date:", - "aliases": "" - }, - "1f5fd": { - "output": "1f5fd", - "name": "Statue of Liberty", - "alpha_code": ":statue_of_liberty:", - "aliases": "" - }, - "1f5fe": { - "output": "1f5fe", - "name": "map of Japan", - "alpha_code": ":japan:", - "aliases": "" - }, - "1f4c6": { - "output": "1f4c6", - "name": "tear-off calendar", - "alpha_code": ":calendar:", - "aliases": "" - }, - "1f5ff": { - "output": "1f5ff", - "name": "moai", - "alpha_code": ":moyai:", - "aliases": "" - }, - "1f601": { - "output": "1f601", - "name": "grinning face with smiling eyes", - "alpha_code": ":grin:", - "aliases": "" - }, - "1f602": { - "output": "1f602", - "name": "face with tears of joy", - "alpha_code": ":joy:", - "aliases": "" - }, - "1f603": { - "output": "1f603", - "name": "smiling face with open mouth", - "alpha_code": ":smiley:", - "aliases": "" - }, - "1f4c8": { - "output": "1f4c8", - "name": "chart increasing", - "alpha_code": ":chart_with_upwards_trend:", - "aliases": "" - }, - "1f604": { - "output": "1f604", - "name": "smiling face with open mouth & smiling eyes", - "alpha_code": ":smile:", - "aliases": "" - }, - "1f605": { - "output": "1f605", - "name": "smiling face with open mouth & cold sweat", - "alpha_code": ":sweat_smile:", - "aliases": "" - }, - "1f4c9": { - "output": "1f4c9", - "name": "chart decreasing", - "alpha_code": ":chart_with_downwards_trend:", - "aliases": "" - }, - "1f606": { - "output": "1f606", - "name": "smiling face with open mouth & closed eyes", - "alpha_code": ":laughing:", - "aliases": ":satisfied:" - }, - "1f609": { - "output": "1f609", - "name": "winking face", - "alpha_code": ":wink:", - "aliases": "" - }, - "1f4ca": { - "output": "1f4ca", - "name": "bar chart", - "alpha_code": ":bar_chart:", - "aliases": "" - }, - "1f60a": { - "output": "1f60a", - "name": "smiling face with smiling eyes", - "alpha_code": ":blush:", - "aliases": "" - }, - "1f60b": { - "output": "1f60b", - "name": "face savouring delicious food", - "alpha_code": ":yum:", - "aliases": "" - }, - "1f4cb": { - "output": "1f4cb", - "name": "clipboard", - "alpha_code": ":clipboard:", - "aliases": "" - }, - "1f60c": { - "output": "1f60c", - "name": "relieved face", - "alpha_code": ":relieved:", - "aliases": "" - }, - "1f60d": { - "output": "1f60d", - "name": "smiling face with heart-eyes", - "alpha_code": ":heart_eyes:", - "aliases": "" - }, - "1f4cc": { - "output": "1f4cc", - "name": "pushpin", - "alpha_code": ":pushpin:", - "aliases": "" - }, - "1f60f": { - "output": "1f60f", - "name": "smirking face", - "alpha_code": ":smirk:", - "aliases": "" - }, - "1f612": { - "output": "1f612", - "name": "unamused face", - "alpha_code": ":unamused:", - "aliases": "" - }, - "1f4cd": { - "output": "1f4cd", - "name": "round pushpin", - "alpha_code": ":round_pushpin:", - "aliases": "" - }, - "1f613": { - "output": "1f613", - "name": "face with cold sweat", - "alpha_code": ":sweat:", - "aliases": "" - }, - "1f614": { - "output": "1f614", - "name": "pensive face", - "alpha_code": ":pensive:", - "aliases": "" - }, - "1f4ce": { - "output": "1f4ce", - "name": "paperclip", - "alpha_code": ":paperclip:", - "aliases": "" - }, - "1f616": { - "output": "1f616", - "name": "confounded face", - "alpha_code": ":confounded:", - "aliases": "" - }, - "1f618": { - "output": "1f618", - "name": "face blowing a kiss", - "alpha_code": ":kissing_heart:", - "aliases": "" - }, - "1f930-1f3ff": { - "output": "1f930-1f3ff", - "name": "pregnant woman: dark skin tone", - "alpha_code": ":pregnant_woman_tone5:", - "aliases": ":expecting_woman_tone5:" - }, - "1f4cf": { - "output": "1f4cf", - "name": "straight ruler", - "alpha_code": ":straight_ruler:", - "aliases": "" - }, - "1f61a": { - "output": "1f61a", - "name": "kissing face with closed eyes", - "alpha_code": ":kissing_closed_eyes:", - "aliases": "" - }, - "1f61c": { - "output": "1f61c", - "name": "face with stuck-out tongue & winking eye", - "alpha_code": ":stuck_out_tongue_winking_eye:", - "aliases": "" - }, - "1f4d0": { - "output": "1f4d0", - "name": "triangular ruler", - "alpha_code": ":triangular_ruler:", - "aliases": "" - }, - "1f61d": { - "output": "1f61d", - "name": "face with stuck-out tongue & closed eyes", - "alpha_code": ":stuck_out_tongue_closed_eyes:", - "aliases": "" - }, - "1f61e": { - "output": "1f61e", - "name": "disappointed face", - "alpha_code": ":disappointed:", - "aliases": "" - }, - "1f4d1": { - "output": "1f4d1", - "name": "bookmark tabs", - "alpha_code": ":bookmark_tabs:", - "aliases": "" - }, - "1f620": { - "output": "1f620", - "name": "angry face", - "alpha_code": ":angry:", - "aliases": "" - }, - "1f621": { - "output": "1f621", - "name": "pouting face", - "alpha_code": ":rage:", - "aliases": "" - }, - "1f622": { - "output": "1f622", - "name": "crying face", - "alpha_code": ":cry:", - "aliases": "" - }, - "1f623": { - "output": "1f623", - "name": "persevering face", - "alpha_code": ":persevere:", - "aliases": "" - }, - "1f624": { - "output": "1f624", - "name": "face with steam from nose", - "alpha_code": ":triumph:", - "aliases": "" - }, - "1f625": { - "output": "1f625", - "name": "disappointed but relieved face", - "alpha_code": ":disappointed_relieved:", - "aliases": "" - }, - "1f628": { - "output": "1f628", - "name": "fearful face", - "alpha_code": ":fearful:", - "aliases": "" - }, - "1f629": { - "output": "1f629", - "name": "weary face", - "alpha_code": ":weary:", - "aliases": "" - }, - "1f62a": { - "output": "1f62a", - "name": "sleepy face", - "alpha_code": ":sleepy:", - "aliases": "" - }, - "1f62b": { - "output": "1f62b", - "name": "tired face", - "alpha_code": ":tired_face:", - "aliases": "" - }, - "1f62d": { - "output": "1f62d", - "name": "loudly crying face", - "alpha_code": ":sob:", - "aliases": "" - }, - "1f630": { - "output": "1f630", - "name": "face with open mouth & cold sweat", - "alpha_code": ":cold_sweat:", - "aliases": "" - }, - "1f631": { - "output": "1f631", - "name": "face screaming in fear", - "alpha_code": ":scream:", - "aliases": "" - }, - "1f632": { - "output": "1f632", - "name": "astonished face", - "alpha_code": ":astonished:", - "aliases": "" - }, - "1f633": { - "output": "1f633", - "name": "flushed face", - "alpha_code": ":flushed:", - "aliases": "" - }, - "1f635": { - "output": "1f635", - "name": "dizzy face", - "alpha_code": ":dizzy_face:", - "aliases": "" - }, - "1f637": { - "output": "1f637", - "name": "face with medical mask", - "alpha_code": ":mask:", - "aliases": "" - }, - "1f638": { - "output": "1f638", - "name": "grinning cat face with smiling eyes", - "alpha_code": ":smile_cat:", - "aliases": "" - }, - "1f639": { - "output": "1f639", - "name": "cat face with tears of joy", - "alpha_code": ":joy_cat:", - "aliases": "" - }, - "1f63a": { - "output": "1f63a", - "name": "smiling cat face with open mouth", - "alpha_code": ":smiley_cat:", - "aliases": "" - }, - "1f63b": { - "output": "1f63b", - "name": "smiling cat face with heart-eyes", - "alpha_code": ":heart_eyes_cat:", - "aliases": "" - }, - "1f63c": { - "output": "1f63c", - "name": "cat face with wry smile", - "alpha_code": ":smirk_cat:", - "aliases": "" - }, - "1f63d": { - "output": "1f63d", - "name": "kissing cat face with closed eyes", - "alpha_code": ":kissing_cat:", - "aliases": "" - }, - "1f63e": { - "output": "1f63e", - "name": "pouting cat face", - "alpha_code": ":pouting_cat:", - "aliases": "" - }, - "1f63f": { - "output": "1f63f", - "name": "crying cat face", - "alpha_code": ":crying_cat_face:", - "aliases": "" - }, - "1f640": { - "output": "1f640", - "name": "weary cat face", - "alpha_code": ":scream_cat:", - "aliases": "" - }, - "1f645": { - "output": "1f645", - "name": "person gesturing NO", - "alpha_code": ":person_gesturing_no:", - "aliases": ":no_good:" - }, - "1f646": { - "output": "1f646", - "name": "person gesturing OK", - "alpha_code": ":person_gesturing_ok:", - "aliases": ":ok_woman:" - }, - "1f647": { - "output": "1f647", - "name": "person bowing", - "alpha_code": ":person_bowing:", - "aliases": ":bow:" - }, - "1f648": { - "output": "1f648", - "name": "see-no-evil monkey", - "alpha_code": ":see_no_evil:", - "aliases": "" - }, - "1f57a-1f3fb": { - "output": "1f57a-1f3fb", - "name": "man dancing: light skin tone", - "alpha_code": ":man_dancing_tone1:", - "aliases": ":male_dancer_tone1:" - }, - "1f649": { - "output": "1f649", - "name": "hear-no-evil monkey", - "alpha_code": ":hear_no_evil:", - "aliases": "" - }, - "1f64a": { - "output": "1f64a", - "name": "speak-no-evil monkey", - "alpha_code": ":speak_no_evil:", - "aliases": "" - }, - "1f57a-1f3fc": { - "output": "1f57a-1f3fc", - "name": "man dancing: medium-light skin tone", - "alpha_code": ":man_dancing_tone2:", - "aliases": ":male_dancer_tone2:" - }, - "1f64b": { - "output": "1f64b", - "name": "person raising hand", - "alpha_code": ":person_raising_hand:", - "aliases": ":raising_hand:" - }, - "1f64c": { - "output": "1f64c", - "name": "raising hands", - "alpha_code": ":raised_hands:", - "aliases": "" - }, - "1f64d": { - "output": "1f64d", - "name": "person frowning", - "alpha_code": ":person_frowning:", - "aliases": "" - }, - "1f64e": { - "output": "1f64e", - "name": "person pouting", - "alpha_code": ":person_pouting:", - "aliases": ":person_with_pouting_face:" - }, - "1f64f": { - "output": "1f64f", - "name": "folded hands", - "alpha_code": ":pray:", - "aliases": "" - }, - "1f680": { - "output": "1f680", - "name": "rocket", - "alpha_code": ":rocket:", - "aliases": "" - }, - "1f683": { - "output": "1f683", - "name": "railway car", - "alpha_code": ":railway_car:", - "aliases": "" - }, - "1f684": { - "output": "1f684", - "name": "high-speed train", - "alpha_code": ":bullettrain_side:", - "aliases": "" - }, - "1f685": { - "output": "1f685", - "name": "high-speed train with bullet nose", - "alpha_code": ":bullettrain_front:", - "aliases": "" - }, - "1f687": { - "output": "1f687", - "name": "metro", - "alpha_code": ":metro:", - "aliases": "" - }, - "1f689": { - "output": "1f689", - "name": "station", - "alpha_code": ":station:", - "aliases": "" - }, - "1f68c": { - "output": "1f68c", - "name": "bus", - "alpha_code": ":bus:", - "aliases": "" - }, - "1f68f": { - "output": "1f68f", - "name": "bus stop", - "alpha_code": ":busstop:", - "aliases": "" - }, - "1f691": { - "output": "1f691", - "name": "ambulance", - "alpha_code": ":ambulance:", - "aliases": "" - }, - "1f692": { - "output": "1f692", - "name": "fire engine", - "alpha_code": ":fire_engine:", - "aliases": "" - }, - "1f693": { - "output": "1f693", - "name": "police car", - "alpha_code": ":police_car:", - "aliases": "" - }, - "1f695": { - "output": "1f695", - "name": "taxi", - "alpha_code": ":taxi:", - "aliases": "" - }, - "1f697": { - "output": "1f697", - "name": "automobile", - "alpha_code": ":red_car:", - "aliases": "" - }, - "1f699": { - "output": "1f699", - "name": "sport utility vehicle", - "alpha_code": ":blue_car:", - "aliases": "" - }, - "1f69a": { - "output": "1f69a", - "name": "delivery truck", - "alpha_code": ":truck:", - "aliases": "" - }, - "1f6a2": { - "output": "1f6a2", - "name": "ship", - "alpha_code": ":ship:", - "aliases": "" - }, - "1f6a4": { - "output": "1f6a4", - "name": "speedboat", - "alpha_code": ":speedboat:", - "aliases": "" - }, - "1f6a5": { - "output": "1f6a5", - "name": "horizontal traffic light", - "alpha_code": ":traffic_light:", - "aliases": "" - }, - "1f6a7": { - "output": "1f6a7", - "name": "construction", - "alpha_code": ":construction:", - "aliases": "" - }, - "1f6a8": { - "output": "1f6a8", - "name": "police car light", - "alpha_code": ":rotating_light:", - "aliases": "" - }, - "1f6a9": { - "output": "1f6a9", - "name": "triangular flag", - "alpha_code": ":triangular_flag_on_post:", - "aliases": "" - }, - "1f6aa": { - "output": "1f6aa", - "name": "door", - "alpha_code": ":door:", - "aliases": "" - }, - "1f57a-1f3fd": { - "output": "1f57a-1f3fd", - "name": "man dancing: medium skin tone", - "alpha_code": ":man_dancing_tone3:", - "aliases": ":male_dancer_tone3:" - }, - "1f6ab": { - "output": "1f6ab", - "name": "prohibited", - "alpha_code": ":no_entry_sign:", - "aliases": "" - }, - "1f6ac": { - "output": "1f6ac", - "name": "cigarette", - "alpha_code": ":smoking:", - "aliases": "" - }, - "1f6ad": { - "output": "1f6ad", - "name": "no smoking", - "alpha_code": ":no_smoking:", - "aliases": "" - }, - "1f6b2": { - "output": "1f6b2", - "name": "bicycle", - "alpha_code": ":bike:", - "aliases": "" - }, - "1f6b6": { - "output": "1f6b6", - "name": "person walking", - "alpha_code": ":person_walking:", - "aliases": ":walking:" - }, - "1f6b9": { - "output": "1f6b9", - "name": "men\u2019s room", - "alpha_code": ":mens:", - "aliases": "" - }, - "1f6ba": { - "output": "1f6ba", - "name": "women\u2019s room", - "alpha_code": ":womens:", - "aliases": "" - }, - "1f57a-1f3fe": { - "output": "1f57a-1f3fe", - "name": "man dancing: medium-dark skin tone", - "alpha_code": ":man_dancing_tone4:", - "aliases": ":male_dancer_tone4:" - }, - "1f6bb": { - "output": "1f6bb", - "name": "restroom", - "alpha_code": ":restroom:", - "aliases": "" - }, - "1f6bc": { - "output": "1f6bc", - "name": "baby symbol", - "alpha_code": ":baby_symbol:", - "aliases": "" - }, - "1f57a-1f3ff": { - "output": "1f57a-1f3ff", - "name": "man dancing: dark skin tone", - "alpha_code": ":man_dancing_tone5:", - "aliases": ":male_dancer_tone5:" - }, - "1f6bd": { - "output": "1f6bd", - "name": "toilet", - "alpha_code": ":toilet:", - "aliases": "" - }, - "1f6be": { - "output": "1f6be", - "name": "water closet", - "alpha_code": ":wc:", - "aliases": "" - }, - "1f933-1f3fb": { - "output": "1f933-1f3fb", - "name": "selfie: light skin tone", - "alpha_code": ":selfie_tone1:", - "aliases": "" - }, - "1f6c0": { - "output": "1f6c0", - "name": "person taking bath", - "alpha_code": ":bath:", - "aliases": "" - }, - "1f918": { - "output": "1f918", - "name": "sign of the horns", - "alpha_code": ":metal:", - "aliases": ":sign_of_the_horns:" - }, - "1f600": { - "output": "1f600", - "name": "grinning face", - "alpha_code": ":grinning:", - "aliases": "" - }, - "1f607": { - "output": "1f607", - "name": "smiling face with halo", - "alpha_code": ":innocent:", - "aliases": "" - }, - "1f608": { - "output": "1f608", - "name": "smiling face with horns", - "alpha_code": ":smiling_imp:", - "aliases": "" - }, - "1f60e": { - "output": "1f60e", - "name": "smiling face with sunglasses", - "alpha_code": ":sunglasses:", - "aliases": "" - }, - "1f610": { - "output": "1f610", - "name": "neutral face", - "alpha_code": ":neutral_face:", - "aliases": "" - }, - "1f611": { - "output": "1f611", - "name": "expressionless face", - "alpha_code": ":expressionless:", - "aliases": "" - }, - "1f615": { - "output": "1f615", - "name": "confused face", - "alpha_code": ":confused:", - "aliases": "" - }, - "1f617": { - "output": "1f617", - "name": "kissing face", - "alpha_code": ":kissing:", - "aliases": "" - }, - "1f933-1f3fc": { - "output": "1f933-1f3fc", - "name": "selfie: medium-light skin tone", - "alpha_code": ":selfie_tone2:", - "aliases": "" - }, - "1f619": { - "output": "1f619", - "name": "kissing face with smiling eyes", - "alpha_code": ":kissing_smiling_eyes:", - "aliases": "" - }, - "1f61b": { - "output": "1f61b", - "name": "face with stuck-out tongue", - "alpha_code": ":stuck_out_tongue:", - "aliases": "" - }, - "1f61f": { - "output": "1f61f", - "name": "worried face", - "alpha_code": ":worried:", - "aliases": "" - }, - "1f626": { - "output": "1f626", - "name": "frowning face with open mouth", - "alpha_code": ":frowning:", - "aliases": "" - }, - "1f627": { - "output": "1f627", - "name": "anguished face", - "alpha_code": ":anguished:", - "aliases": "" - }, - "1f62c": { - "output": "1f62c", - "name": "grimacing face", - "alpha_code": ":grimacing:", - "aliases": "" - }, - "1f62e": { - "output": "1f62e", - "name": "face with open mouth", - "alpha_code": ":open_mouth:", - "aliases": "" - }, - "1f62f": { - "output": "1f62f", - "name": "hushed face", - "alpha_code": ":hushed:", - "aliases": "" - }, - "1f634": { - "output": "1f634", - "name": "sleeping face", - "alpha_code": ":sleeping:", - "aliases": "" - }, - "1f636": { - "output": "1f636", - "name": "face without mouth", - "alpha_code": ":no_mouth:", - "aliases": "" - }, - "1f681": { - "output": "1f681", - "name": "helicopter", - "alpha_code": ":helicopter:", - "aliases": "" - }, - "1f682": { - "output": "1f682", - "name": "locomotive", - "alpha_code": ":steam_locomotive:", - "aliases": "" - }, - "1f686": { - "output": "1f686", - "name": "train", - "alpha_code": ":train2:", - "aliases": "" - }, - "1f688": { - "output": "1f688", - "name": "light rail", - "alpha_code": ":light_rail:", - "aliases": "" - }, - "1f68a": { - "output": "1f68a", - "name": "tram", - "alpha_code": ":tram:", - "aliases": "" - }, - "1f68d": { - "output": "1f68d", - "name": "oncoming bus", - "alpha_code": ":oncoming_bus:", - "aliases": "" - }, - "1f68e": { - "output": "1f68e", - "name": "trolleybus", - "alpha_code": ":trolleybus:", - "aliases": "" - }, - "1f690": { - "output": "1f690", - "name": "minibus", - "alpha_code": ":minibus:", - "aliases": "" - }, - "1f694": { - "output": "1f694", - "name": "oncoming police car", - "alpha_code": ":oncoming_police_car:", - "aliases": "" - }, - "1f696": { - "output": "1f696", - "name": "oncoming taxi", - "alpha_code": ":oncoming_taxi:", - "aliases": "" - }, - "1f698": { - "output": "1f698", - "name": "oncoming automobile", - "alpha_code": ":oncoming_automobile:", - "aliases": "" - }, - "1f69b": { - "output": "1f69b", - "name": "articulated lorry", - "alpha_code": ":articulated_lorry:", - "aliases": "" - }, - "1f933-1f3fd": { - "output": "1f933-1f3fd", - "name": "selfie: medium skin tone", - "alpha_code": ":selfie_tone3:", - "aliases": "" - }, - "1f69c": { - "output": "1f69c", - "name": "tractor", - "alpha_code": ":tractor:", - "aliases": "" - }, - "1f69d": { - "output": "1f69d", - "name": "monorail", - "alpha_code": ":monorail:", - "aliases": "" - }, - "1f69e": { - "output": "1f69e", - "name": "mountain railway", - "alpha_code": ":mountain_railway:", - "aliases": "" - }, - "1f69f": { - "output": "1f69f", - "name": "suspension railway", - "alpha_code": ":suspension_railway:", - "aliases": "" - }, - "1f6a0": { - "output": "1f6a0", - "name": "mountain cableway", - "alpha_code": ":mountain_cableway:", - "aliases": "" - }, - "1f6a1": { - "output": "1f6a1", - "name": "aerial tramway", - "alpha_code": ":aerial_tramway:", - "aliases": "" - }, - "1f6a3": { - "output": "1f6a3", - "name": "person rowing boat", - "alpha_code": ":person_rowing_boat:", - "aliases": ":rowboat:" - }, - "1f6a6": { - "output": "1f6a6", - "name": "vertical traffic light", - "alpha_code": ":vertical_traffic_light:", - "aliases": "" - }, - "1f933-1f3fe": { - "output": "1f933-1f3fe", - "name": "selfie: medium-dark skin tone", - "alpha_code": ":selfie_tone4:", - "aliases": "" - }, - "1f6ae": { - "output": "1f6ae", - "name": "litter in bin sign", - "alpha_code": ":put_litter_in_its_place:", - "aliases": "" - }, - "1f6af": { - "output": "1f6af", - "name": "no littering", - "alpha_code": ":do_not_litter:", - "aliases": "" - }, - "1f933-1f3ff": { - "output": "1f933-1f3ff", - "name": "selfie: dark skin tone", - "alpha_code": ":selfie_tone5:", - "aliases": "" - }, - "1f6b0": { - "output": "1f6b0", - "name": "potable water", - "alpha_code": ":potable_water:", - "aliases": "" - }, - "1f6b1": { - "output": "1f6b1", - "name": "non-potable water", - "alpha_code": ":non-potable_water:", - "aliases": "" - }, - "1f6b3": { - "output": "1f6b3", - "name": "no bicycles", - "alpha_code": ":no_bicycles:", - "aliases": "" - }, - "1f91e-1f3fb": { - "output": "1f91e-1f3fb", - "name": "crossed fingers: light skin tone", - "alpha_code": ":fingers_crossed_tone1:", - "aliases": ":hand_with_index_and_middle_fingers_crossed_tone1:" - }, - "1f6b4": { - "output": "1f6b4", - "name": "person biking", - "alpha_code": ":person_biking:", - "aliases": ":bicyclist:" - }, - "1f6b5": { - "output": "1f6b5", - "name": "person mountain biking", - "alpha_code": ":person_mountain_biking:", - "aliases": ":mountain_bicyclist:" - }, - "1f6b7": { - "output": "1f6b7", - "name": "no pedestrians", - "alpha_code": ":no_pedestrians:", - "aliases": "" - }, - "1f6b8": { - "output": "1f6b8", - "name": "children crossing", - "alpha_code": ":children_crossing:", - "aliases": "" - }, - "1f6bf": { - "output": "1f6bf", - "name": "shower", - "alpha_code": ":shower:", - "aliases": "" - }, - "1f6c1": { - "output": "1f6c1", - "name": "bathtub", - "alpha_code": ":bathtub:", - "aliases": "" - }, - "1f6c2": { - "output": "1f6c2", - "name": "passport control", - "alpha_code": ":passport_control:", - "aliases": "" - }, - "1f91e-1f3fc": { - "output": "1f91e-1f3fc", - "name": "crossed fingers: medium-light skin tone", - "alpha_code": ":fingers_crossed_tone2:", - "aliases": ":hand_with_index_and_middle_fingers_crossed_tone2:" - }, - "1f6c3": { - "output": "1f6c3", - "name": "customs", - "alpha_code": ":customs:", - "aliases": "" - }, - "1f6c4": { - "output": "1f6c4", - "name": "baggage claim", - "alpha_code": ":baggage_claim:", - "aliases": "" - }, - "1f91e-1f3fd": { - "output": "1f91e-1f3fd", - "name": "crossed fingers: medium skin tone", - "alpha_code": ":fingers_crossed_tone3:", - "aliases": ":hand_with_index_and_middle_fingers_crossed_tone3:" - }, - "1f6c5": { - "output": "1f6c5", - "name": "left luggage", - "alpha_code": ":left_luggage:", - "aliases": "" - }, - "1f30d": { - "output": "1f30d", - "name": "globe showing Europe-Africa", - "alpha_code": ":earth_africa:", - "aliases": "" - }, - "1f30e": { - "output": "1f30e", - "name": "globe showing Americas", - "alpha_code": ":earth_americas:", - "aliases": "" - }, - "1f310": { - "output": "1f310", - "name": "globe with meridians", - "alpha_code": ":globe_with_meridians:", - "aliases": "" - }, - "1f312": { - "output": "1f312", - "name": "waxing crescent moon", - "alpha_code": ":waxing_crescent_moon:", - "aliases": "" - }, - "1f316": { - "output": "1f316", - "name": "waning gibbous moon", - "alpha_code": ":waning_gibbous_moon:", - "aliases": "" - }, - "1f317": { - "output": "1f317", - "name": "last quarter moon", - "alpha_code": ":last_quarter_moon:", - "aliases": "" - }, - "1f318": { - "output": "1f318", - "name": "waning crescent moon", - "alpha_code": ":waning_crescent_moon:", - "aliases": "" - }, - "1f31a": { - "output": "1f31a", - "name": "new moon face", - "alpha_code": ":new_moon_with_face:", - "aliases": "" - }, - "1f31c": { - "output": "1f31c", - "name": "last quarter moon with face", - "alpha_code": ":last_quarter_moon_with_face:", - "aliases": "" - }, - "1f31d": { - "output": "1f31d", - "name": "full moon with face", - "alpha_code": ":full_moon_with_face:", - "aliases": "" - }, - "1f31e": { - "output": "1f31e", - "name": "sun with face", - "alpha_code": ":sun_with_face:", - "aliases": "" - }, - "1f332": { - "output": "1f332", - "name": "evergreen tree", - "alpha_code": ":evergreen_tree:", - "aliases": "" - }, - "1f333": { - "output": "1f333", - "name": "deciduous tree", - "alpha_code": ":deciduous_tree:", - "aliases": "" - }, - "1f34b": { - "output": "1f34b", - "name": "lemon", - "alpha_code": ":lemon:", - "aliases": "" - }, - "1f91e-1f3fe": { - "output": "1f91e-1f3fe", - "name": "crossed fingers: medium-dark skin tone", - "alpha_code": ":fingers_crossed_tone4:", - "aliases": ":hand_with_index_and_middle_fingers_crossed_tone4:" - }, - "1f350": { - "output": "1f350", - "name": "pear", - "alpha_code": ":pear:", - "aliases": "" - }, - "1f37c": { - "output": "1f37c", - "name": "baby bottle", - "alpha_code": ":baby_bottle:", - "aliases": "" - }, - "1f3c7": { - "output": "1f3c7", - "name": "horse racing", - "alpha_code": ":horse_racing:", - "aliases": "" - }, - "1f3c9": { - "output": "1f3c9", - "name": "rugby football", - "alpha_code": ":rugby_football:", - "aliases": "" - }, - "1f3e4": { - "output": "1f3e4", - "name": "post office", - "alpha_code": ":european_post_office:", - "aliases": "" - }, - "1f400": { - "output": "1f400", - "name": "rat", - "alpha_code": ":rat:", - "aliases": "" - }, - "1f401": { - "output": "1f401", - "name": "mouse", - "alpha_code": ":mouse2:", - "aliases": "" - }, - "1f402": { - "output": "1f402", - "name": "ox", - "alpha_code": ":ox:", - "aliases": "" - }, - "1f403": { - "output": "1f403", - "name": "water buffalo", - "alpha_code": ":water_buffalo:", - "aliases": "" - }, - "1f404": { - "output": "1f404", - "name": "cow", - "alpha_code": ":cow2:", - "aliases": "" - }, - "1f405": { - "output": "1f405", - "name": "tiger", - "alpha_code": ":tiger2:", - "aliases": "" - }, - "1f406": { - "output": "1f406", - "name": "leopard", - "alpha_code": ":leopard:", - "aliases": "" - }, - "1f407": { - "output": "1f407", - "name": "rabbit", - "alpha_code": ":rabbit2:", - "aliases": "" - }, - "1f408": { - "output": "1f408", - "name": "cat", - "alpha_code": ":cat2:", - "aliases": "" - }, - "1f409": { - "output": "1f409", - "name": "dragon", - "alpha_code": ":dragon:", - "aliases": "" - }, - "1f40a": { - "output": "1f40a", - "name": "crocodile", - "alpha_code": ":crocodile:", - "aliases": "" - }, - "1f40b": { - "output": "1f40b", - "name": "whale", - "alpha_code": ":whale2:", - "aliases": "" - }, - "1f40f": { - "output": "1f40f", - "name": "ram", - "alpha_code": ":ram:", - "aliases": "" - }, - "1f410": { - "output": "1f410", - "name": "goat", - "alpha_code": ":goat:", - "aliases": "" - }, - "1f413": { - "output": "1f413", - "name": "rooster", - "alpha_code": ":rooster:", - "aliases": "" - }, - "1f415": { - "output": "1f415", - "name": "dog", - "alpha_code": ":dog2:", - "aliases": "" - }, - "1f416": { - "output": "1f416", - "name": "pig", - "alpha_code": ":pig2:", - "aliases": "" - }, - "1f91e-1f3ff": { - "output": "1f91e-1f3ff", - "name": "crossed fingers: dark skin tone", - "alpha_code": ":fingers_crossed_tone5:", - "aliases": ":hand_with_index_and_middle_fingers_crossed_tone5:" - }, - "1f42a": { - "output": "1f42a", - "name": "camel", - "alpha_code": ":dromedary_camel:", - "aliases": "" - }, - "1f465": { - "output": "1f465", - "name": "busts in silhouette", - "alpha_code": ":busts_in_silhouette:", - "aliases": "" - }, - "1f46c": { - "output": "1f46c", - "name": "two men holding hands", - "alpha_code": ":two_men_holding_hands:", - "aliases": "" - }, - "1f46d": { - "output": "1f46d", - "name": "two women holding hands", - "alpha_code": ":two_women_holding_hands:", - "aliases": "" - }, - "1f4ad": { - "output": "1f4ad", - "name": "thought balloon", - "alpha_code": ":thought_balloon:", - "aliases": "" - }, - "1f4b6": { - "output": "1f4b6", - "name": "euro banknote", - "alpha_code": ":euro:", - "aliases": "" - }, - "1f919-1f3fb": { - "output": "1f919-1f3fb", - "name": "call me hand: light skin tone", - "alpha_code": ":call_me_tone1:", - "aliases": ":call_me_hand_tone1:" - }, - "1f4b7": { - "output": "1f4b7", - "name": "pound banknote", - "alpha_code": ":pound:", - "aliases": "" - }, - "1f4ec": { - "output": "1f4ec", - "name": "open mailbox with raised flag", - "alpha_code": ":mailbox_with_mail:", - "aliases": "" - }, - "1f4ed": { - "output": "1f4ed", - "name": "open mailbox with lowered flag", - "alpha_code": ":mailbox_with_no_mail:", - "aliases": "" - }, - "1f919-1f3fc": { - "output": "1f919-1f3fc", - "name": "call me hand: medium-light skin tone", - "alpha_code": ":call_me_tone2:", - "aliases": ":call_me_hand_tone2:" - }, - "1f4ef": { - "output": "1f4ef", - "name": "postal horn", - "alpha_code": ":postal_horn:", - "aliases": "" - }, - "1f4f5": { - "output": "1f4f5", - "name": "no mobile phones", - "alpha_code": ":no_mobile_phones:", - "aliases": "" - }, - "1f500": { - "output": "1f500", - "name": "shuffle tracks button", - "alpha_code": ":twisted_rightwards_arrows:", - "aliases": "" - }, - "1f501": { - "output": "1f501", - "name": "repeat button", - "alpha_code": ":repeat:", - "aliases": "" - }, - "1f502": { - "output": "1f502", - "name": "repeat single button", - "alpha_code": ":repeat_one:", - "aliases": "" - }, - "1f504": { - "output": "1f504", - "name": "anticlockwise arrows button", - "alpha_code": ":arrows_counterclockwise:", - "aliases": "" - }, - "1f919-1f3fd": { - "output": "1f919-1f3fd", - "name": "call me hand: medium skin tone", - "alpha_code": ":call_me_tone3:", - "aliases": ":call_me_hand_tone3:" - }, - "1f505": { - "output": "1f505", - "name": "dim button", - "alpha_code": ":low_brightness:", - "aliases": "" - }, - "1f506": { - "output": "1f506", - "name": "bright button", - "alpha_code": ":high_brightness:", - "aliases": "" - }, - "1f507": { - "output": "1f507", - "name": "muted speaker", - "alpha_code": ":mute:", - "aliases": "" - }, - "1f509": { - "output": "1f509", - "name": "speaker medium volume", - "alpha_code": ":sound:", - "aliases": "" - }, - "1f515": { - "output": "1f515", - "name": "bell with slash", - "alpha_code": ":no_bell:", - "aliases": "" - }, - "1f52c": { - "output": "1f52c", - "name": "microscope", - "alpha_code": ":microscope:", - "aliases": "" - }, - "1f52d": { - "output": "1f52d", - "name": "telescope", - "alpha_code": ":telescope:", - "aliases": "" - }, - "1f55c": { - "output": "1f55c", - "name": "one-thirty", - "alpha_code": ":clock130:", - "aliases": "" - }, - "1f55d": { - "output": "1f55d", - "name": "two-thirty", - "alpha_code": ":clock230:", - "aliases": "" - }, - "1f55e": { - "output": "1f55e", - "name": "three-thirty", - "alpha_code": ":clock330:", - "aliases": "" - }, - "1f55f": { - "output": "1f55f", - "name": "four-thirty", - "alpha_code": ":clock430:", - "aliases": "" - }, - "1f560": { - "output": "1f560", - "name": "five-thirty", - "alpha_code": ":clock530:", - "aliases": "" - }, - "1f561": { - "output": "1f561", - "name": "six-thirty", - "alpha_code": ":clock630:", - "aliases": "" - }, - "1f562": { - "output": "1f562", - "name": "seven-thirty", - "alpha_code": ":clock730:", - "aliases": "" - }, - "1f563": { - "output": "1f563", - "name": "eight-thirty", - "alpha_code": ":clock830:", - "aliases": "" - }, - "1f564": { - "output": "1f564", - "name": "nine-thirty", - "alpha_code": ":clock930:", - "aliases": "" - }, - "1f565": { - "output": "1f565", - "name": "ten-thirty", - "alpha_code": ":clock1030:", - "aliases": "" - }, - "1f566": { - "output": "1f566", - "name": "eleven-thirty", - "alpha_code": ":clock1130:", - "aliases": "" - }, - "1f567": { - "output": "1f567", - "name": "twelve-thirty", - "alpha_code": ":clock1230:", - "aliases": "" - }, - "1f508": { - "output": "1f508", - "name": "speaker low volume", - "alpha_code": ":speaker:", - "aliases": "" - }, - "1f68b": { - "output": "1f68b", - "name": "tram car", - "alpha_code": ":train:", - "aliases": "" - }, - "27bf": { - "output": "27bf", - "name": "double curly loop", - "alpha_code": ":loop:", - "aliases": "" - }, - "1f1e6-1f1eb": { - "output": "1f1e6-1f1eb", - "name": "Afghanistan", - "alpha_code": ":flag_af:", - "aliases": ":af:" - }, - "1f1e6-1f1f1": { - "output": "1f1e6-1f1f1", - "name": "Albania", - "alpha_code": ":flag_al:", - "aliases": ":al:" - }, - "1f1e9-1f1ff": { - "output": "1f1e9-1f1ff", - "name": "Algeria", - "alpha_code": ":flag_dz:", - "aliases": ":dz:" - }, - "1f1e6-1f1e9": { - "output": "1f1e6-1f1e9", - "name": "Andorra", - "alpha_code": ":flag_ad:", - "aliases": ":ad:" - }, - "1f1e6-1f1f4": { - "output": "1f1e6-1f1f4", - "name": "Angola", - "alpha_code": ":flag_ao:", - "aliases": ":ao:" - }, - "1f1e6-1f1ec": { - "output": "1f1e6-1f1ec", - "name": "Antigua & Barbuda", - "alpha_code": ":flag_ag:", - "aliases": ":ag:" - }, - "1f1e6-1f1f7": { - "output": "1f1e6-1f1f7", - "name": "Argentina", - "alpha_code": ":flag_ar:", - "aliases": ":ar:" - }, - "1f1e6-1f1f2": { - "output": "1f1e6-1f1f2", - "name": "Armenia", - "alpha_code": ":flag_am:", - "aliases": ":am:" - }, - "1f1e6-1f1fa": { - "output": "1f1e6-1f1fa", - "name": "Australia", - "alpha_code": ":flag_au:", - "aliases": ":au:" - }, - "1f1e6-1f1f9": { - "output": "1f1e6-1f1f9", - "name": "Austria", - "alpha_code": ":flag_at:", - "aliases": ":at:" - }, - "1f1e6-1f1ff": { - "output": "1f1e6-1f1ff", - "name": "Azerbaijan", - "alpha_code": ":flag_az:", - "aliases": ":az:" - }, - "1f1e7-1f1f8": { - "output": "1f1e7-1f1f8", - "name": "Bahamas", - "alpha_code": ":flag_bs:", - "aliases": ":bs:" - }, - "1f1e7-1f1ed": { - "output": "1f1e7-1f1ed", - "name": "Bahrain", - "alpha_code": ":flag_bh:", - "aliases": ":bh:" - }, - "1f1e7-1f1e9": { - "output": "1f1e7-1f1e9", - "name": "Bangladesh", - "alpha_code": ":flag_bd:", - "aliases": ":bd:" - }, - "1f1e7-1f1e7": { - "output": "1f1e7-1f1e7", - "name": "Barbados", - "alpha_code": ":flag_bb:", - "aliases": ":bb:" - }, - "1f1e7-1f1fe": { - "output": "1f1e7-1f1fe", - "name": "Belarus", - "alpha_code": ":flag_by:", - "aliases": ":by:" - }, - "1f1e7-1f1ea": { - "output": "1f1e7-1f1ea", - "name": "Belgium", - "alpha_code": ":flag_be:", - "aliases": ":be:" - }, - "1f1e7-1f1ff": { - "output": "1f1e7-1f1ff", - "name": "Belize", - "alpha_code": ":flag_bz:", - "aliases": ":bz:" - }, - "1f1e7-1f1ef": { - "output": "1f1e7-1f1ef", - "name": "Benin", - "alpha_code": ":flag_bj:", - "aliases": ":bj:" - }, - "1f1e7-1f1f9": { - "output": "1f1e7-1f1f9", - "name": "Bhutan", - "alpha_code": ":flag_bt:", - "aliases": ":bt:" - }, - "1f1e7-1f1f4": { - "output": "1f1e7-1f1f4", - "name": "Bolivia", - "alpha_code": ":flag_bo:", - "aliases": ":bo:" - }, - "1f1e7-1f1e6": { - "output": "1f1e7-1f1e6", - "name": "Bosnia & Herzegovina", - "alpha_code": ":flag_ba:", - "aliases": ":ba:" - }, - "1f1e7-1f1fc": { - "output": "1f1e7-1f1fc", - "name": "Botswana", - "alpha_code": ":flag_bw:", - "aliases": ":bw:" - }, - "1f1e7-1f1f7": { - "output": "1f1e7-1f1f7", - "name": "Brazil", - "alpha_code": ":flag_br:", - "aliases": ":br:" - }, - "1f1e7-1f1f3": { - "output": "1f1e7-1f1f3", - "name": "Brunei", - "alpha_code": ":flag_bn:", - "aliases": ":bn:" - }, - "1f1e7-1f1ec": { - "output": "1f1e7-1f1ec", - "name": "Bulgaria", - "alpha_code": ":flag_bg:", - "aliases": ":bg:" - }, - "1f1e7-1f1eb": { - "output": "1f1e7-1f1eb", - "name": "Burkina Faso", - "alpha_code": ":flag_bf:", - "aliases": ":bf:" - }, - "1f1e7-1f1ee": { - "output": "1f1e7-1f1ee", - "name": "Burundi", - "alpha_code": ":flag_bi:", - "aliases": ":bi:" - }, - "1f1f0-1f1ed": { - "output": "1f1f0-1f1ed", - "name": "Cambodia", - "alpha_code": ":flag_kh:", - "aliases": ":kh:" - }, - "1f1e8-1f1f2": { - "output": "1f1e8-1f1f2", - "name": "Cameroon", - "alpha_code": ":flag_cm:", - "aliases": ":cm:" - }, - "1f1e8-1f1e6": { - "output": "1f1e8-1f1e6", - "name": "Canada", - "alpha_code": ":flag_ca:", - "aliases": ":ca:" - }, - "1f1e8-1f1fb": { - "output": "1f1e8-1f1fb", - "name": "Cape Verde", - "alpha_code": ":flag_cv:", - "aliases": ":cv:" - }, - "1f919-1f3fe": { - "output": "1f919-1f3fe", - "name": "call me hand: medium-dark skin tone", - "alpha_code": ":call_me_tone4:", - "aliases": ":call_me_hand_tone4:" - }, - "1f1e8-1f1eb": { - "output": "1f1e8-1f1eb", - "name": "Central African Republic", - "alpha_code": ":flag_cf:", - "aliases": ":cf:" - }, - "1f1f9-1f1e9": { - "output": "1f1f9-1f1e9", - "name": "Chad", - "alpha_code": ":flag_td:", - "aliases": ":td:" - }, - "1f1e8-1f1f1": { - "output": "1f1e8-1f1f1", - "name": "Chile", - "alpha_code": ":flag_cl:", - "aliases": ":chile:" - }, - "1f1e8-1f1f4": { - "output": "1f1e8-1f1f4", - "name": "Colombia", - "alpha_code": ":flag_co:", - "aliases": ":co:" - }, - "1f1f0-1f1f2": { - "output": "1f1f0-1f1f2", - "name": "Comoros", - "alpha_code": ":flag_km:", - "aliases": ":km:" - }, - "1f1e8-1f1f7": { - "output": "1f1e8-1f1f7", - "name": "Costa Rica", - "alpha_code": ":flag_cr:", - "aliases": ":cr:" - }, - "1f1e8-1f1ee": { - "output": "1f1e8-1f1ee", - "name": "C\u00f4te d\u2019Ivoire", - "alpha_code": ":flag_ci:", - "aliases": ":ci:" - }, - "1f1ed-1f1f7": { - "output": "1f1ed-1f1f7", - "name": "Croatia", - "alpha_code": ":flag_hr:", - "aliases": ":hr:" - }, - "1f1e8-1f1fa": { - "output": "1f1e8-1f1fa", - "name": "Cuba", - "alpha_code": ":flag_cu:", - "aliases": ":cu:" - }, - "1f1e8-1f1fe": { - "output": "1f1e8-1f1fe", - "name": "Cyprus", - "alpha_code": ":flag_cy:", - "aliases": ":cy:" - }, - "1f1e8-1f1ff": { - "output": "1f1e8-1f1ff", - "name": "Czechia", - "alpha_code": ":flag_cz:", - "aliases": ":cz:" - }, - "1f919-1f3ff": { - "output": "1f919-1f3ff", - "name": "call me hand: dark skin tone", - "alpha_code": ":call_me_tone5:", - "aliases": ":call_me_hand_tone5:" - }, - "1f1e8-1f1e9": { - "output": "1f1e8-1f1e9", - "name": "Congo - Kinshasa", - "alpha_code": ":flag_cd:", - "aliases": ":congo:" - }, - "1f91b-1f3fb": { - "output": "1f91b-1f3fb", - "name": "left-facing fist: light skin tone", - "alpha_code": ":left_facing_fist_tone1:", - "aliases": ":left_fist_tone1:" - }, - "1f1e9-1f1f0": { - "output": "1f1e9-1f1f0", - "name": "Denmark", - "alpha_code": ":flag_dk:", - "aliases": ":dk:" - }, - "1f1e9-1f1ef": { - "output": "1f1e9-1f1ef", - "name": "Djibouti", - "alpha_code": ":flag_dj:", - "aliases": ":dj:" - }, - "1f1e9-1f1f2": { - "output": "1f1e9-1f1f2", - "name": "Dominica", - "alpha_code": ":flag_dm:", - "aliases": ":dm:" - }, - "1f1e9-1f1f4": { - "output": "1f1e9-1f1f4", - "name": "Dominican Republic", - "alpha_code": ":flag_do:", - "aliases": ":do:" - }, - "1f1f9-1f1f1": { - "output": "1f1f9-1f1f1", - "name": "Timor-Leste", - "alpha_code": ":flag_tl:", - "aliases": ":tl:" - }, - "1f1ea-1f1e8": { - "output": "1f1ea-1f1e8", - "name": "Ecuador", - "alpha_code": ":flag_ec:", - "aliases": ":ec:" - }, - "1f1ea-1f1ec": { - "output": "1f1ea-1f1ec", - "name": "Egypt", - "alpha_code": ":flag_eg:", - "aliases": ":eg:" - }, - "1f1f8-1f1fb": { - "output": "1f1f8-1f1fb", - "name": "El Salvador", - "alpha_code": ":flag_sv:", - "aliases": ":sv:" - }, - "1f1ec-1f1f6": { - "output": "1f1ec-1f1f6", - "name": "Equatorial Guinea", - "alpha_code": ":flag_gq:", - "aliases": ":gq:" - }, - "1f1ea-1f1f7": { - "output": "1f1ea-1f1f7", - "name": "Eritrea", - "alpha_code": ":flag_er:", - "aliases": ":er:" - }, - "1f1ea-1f1ea": { - "output": "1f1ea-1f1ea", - "name": "Estonia", - "alpha_code": ":flag_ee:", - "aliases": ":ee:" - }, - "1f1ea-1f1f9": { - "output": "1f1ea-1f1f9", - "name": "Ethiopia", - "alpha_code": ":flag_et:", - "aliases": ":et:" - }, - "1f91b-1f3fc": { - "output": "1f91b-1f3fc", - "name": "left-facing fist: medium-light skin tone", - "alpha_code": ":left_facing_fist_tone2:", - "aliases": ":left_fist_tone2:" - }, - "1f1eb-1f1ef": { - "output": "1f1eb-1f1ef", - "name": "Fiji", - "alpha_code": ":flag_fj:", - "aliases": ":fj:" - }, - "1f1eb-1f1ee": { - "output": "1f1eb-1f1ee", - "name": "Finland", - "alpha_code": ":flag_fi:", - "aliases": ":fi:" - }, - "1f1ec-1f1e6": { - "output": "1f1ec-1f1e6", - "name": "Gabon", - "alpha_code": ":flag_ga:", - "aliases": ":ga:" - }, - "1f1ec-1f1f2": { - "output": "1f1ec-1f1f2", - "name": "Gambia", - "alpha_code": ":flag_gm:", - "aliases": ":gm:" - }, - "1f1ec-1f1ea": { - "output": "1f1ec-1f1ea", - "name": "Georgia", - "alpha_code": ":flag_ge:", - "aliases": ":ge:" - }, - "1f1ec-1f1ed": { - "output": "1f1ec-1f1ed", - "name": "Ghana", - "alpha_code": ":flag_gh:", - "aliases": ":gh:" - }, - "1f1ec-1f1f7": { - "output": "1f1ec-1f1f7", - "name": "Greece", - "alpha_code": ":flag_gr:", - "aliases": ":gr:" - }, - "1f1ec-1f1e9": { - "output": "1f1ec-1f1e9", - "name": "Grenada", - "alpha_code": ":flag_gd:", - "aliases": ":gd:" - }, - "1f1ec-1f1f9": { - "output": "1f1ec-1f1f9", - "name": "Guatemala", - "alpha_code": ":flag_gt:", - "aliases": ":gt:" - }, - "1f1ec-1f1f3": { - "output": "1f1ec-1f1f3", - "name": "Guinea", - "alpha_code": ":flag_gn:", - "aliases": ":gn:" - }, - "1f1ec-1f1fc": { - "output": "1f1ec-1f1fc", - "name": "Guinea-Bissau", - "alpha_code": ":flag_gw:", - "aliases": ":gw:" - }, - "1f1ec-1f1fe": { - "output": "1f1ec-1f1fe", - "name": "Guyana", - "alpha_code": ":flag_gy:", - "aliases": ":gy:" - }, - "1f1ed-1f1f9": { - "output": "1f1ed-1f1f9", - "name": "Haiti", - "alpha_code": ":flag_ht:", - "aliases": ":ht:" - }, - "1f1ed-1f1f3": { - "output": "1f1ed-1f1f3", - "name": "Honduras", - "alpha_code": ":flag_hn:", - "aliases": ":hn:" - }, - "1f1ed-1f1fa": { - "output": "1f1ed-1f1fa", - "name": "Hungary", - "alpha_code": ":flag_hu:", - "aliases": ":hu:" - }, - "1f1ee-1f1f8": { - "output": "1f1ee-1f1f8", - "name": "Iceland", - "alpha_code": ":flag_is:", - "aliases": ":is:" - }, - "1f1ee-1f1f3": { - "output": "1f1ee-1f1f3", - "name": "India", - "alpha_code": ":flag_in:", - "aliases": ":in:" - }, - "1f1ee-1f1e9": { - "output": "1f1ee-1f1e9", - "name": "Indonesia", - "alpha_code": ":flag_id:", - "aliases": ":indonesia:" - }, - "1f1ee-1f1f7": { - "output": "1f1ee-1f1f7", - "name": "Iran", - "alpha_code": ":flag_ir:", - "aliases": ":ir:" - }, - "1f1ee-1f1f6": { - "output": "1f1ee-1f1f6", - "name": "Iraq", - "alpha_code": ":flag_iq:", - "aliases": ":iq:" - }, - "1f1ee-1f1ea": { - "output": "1f1ee-1f1ea", - "name": "Ireland", - "alpha_code": ":flag_ie:", - "aliases": ":ie:" - }, - "1f1ee-1f1f1": { - "output": "1f1ee-1f1f1", - "name": "Israel", - "alpha_code": ":flag_il:", - "aliases": ":il:" - }, - "1f1ef-1f1f2": { - "output": "1f1ef-1f1f2", - "name": "Jamaica", - "alpha_code": ":flag_jm:", - "aliases": ":jm:" - }, - "1f1ef-1f1f4": { - "output": "1f1ef-1f1f4", - "name": "Jordan", - "alpha_code": ":flag_jo:", - "aliases": ":jo:" - }, - "1f1f0-1f1ff": { - "output": "1f1f0-1f1ff", - "name": "Kazakhstan", - "alpha_code": ":flag_kz:", - "aliases": ":kz:" - }, - "1f1f0-1f1ea": { - "output": "1f1f0-1f1ea", - "name": "Kenya", - "alpha_code": ":flag_ke:", - "aliases": ":ke:" - }, - "1f1f0-1f1ee": { - "output": "1f1f0-1f1ee", - "name": "Kiribati", - "alpha_code": ":flag_ki:", - "aliases": ":ki:" - }, - "1f1fd-1f1f0": { - "output": "1f1fd-1f1f0", - "name": "Kosovo", - "alpha_code": ":flag_xk:", - "aliases": ":xk:" - }, - "1f1f0-1f1fc": { - "output": "1f1f0-1f1fc", - "name": "Kuwait", - "alpha_code": ":flag_kw:", - "aliases": ":kw:" - }, - "1f1f0-1f1ec": { - "output": "1f1f0-1f1ec", - "name": "Kyrgyzstan", - "alpha_code": ":flag_kg:", - "aliases": ":kg:" - }, - "1f91b-1f3fd": { - "output": "1f91b-1f3fd", - "name": "left-facing fist: medium skin tone", - "alpha_code": ":left_facing_fist_tone3:", - "aliases": ":left_fist_tone3:" - }, - "1f1f1-1f1e6": { - "output": "1f1f1-1f1e6", - "name": "Laos", - "alpha_code": ":flag_la:", - "aliases": ":la:" - }, - "1f1f1-1f1fb": { - "output": "1f1f1-1f1fb", - "name": "Latvia", - "alpha_code": ":flag_lv:", - "aliases": ":lv:" - }, - "1f1f1-1f1e7": { - "output": "1f1f1-1f1e7", - "name": "Lebanon", - "alpha_code": ":flag_lb:", - "aliases": ":lb:" - }, - "1f1f1-1f1f8": { - "output": "1f1f1-1f1f8", - "name": "Lesotho", - "alpha_code": ":flag_ls:", - "aliases": ":ls:" - }, - "1f1f1-1f1f7": { - "output": "1f1f1-1f1f7", - "name": "Liberia", - "alpha_code": ":flag_lr:", - "aliases": ":lr:" - }, - "1f1f1-1f1fe": { - "output": "1f1f1-1f1fe", - "name": "Libya", - "alpha_code": ":flag_ly:", - "aliases": ":ly:" - }, - "1f1f1-1f1ee": { - "output": "1f1f1-1f1ee", - "name": "Liechtenstein", - "alpha_code": ":flag_li:", - "aliases": ":li:" - }, - "1f1f1-1f1f9": { - "output": "1f1f1-1f1f9", - "name": "Lithuania", - "alpha_code": ":flag_lt:", - "aliases": ":lt:" - }, - "1f1f1-1f1fa": { - "output": "1f1f1-1f1fa", - "name": "Luxembourg", - "alpha_code": ":flag_lu:", - "aliases": ":lu:" - }, - "1f1f2-1f1f0": { - "output": "1f1f2-1f1f0", - "name": "Macedonia", - "alpha_code": ":flag_mk:", - "aliases": ":mk:" - }, - "1f1f2-1f1ec": { - "output": "1f1f2-1f1ec", - "name": "Madagascar", - "alpha_code": ":flag_mg:", - "aliases": ":mg:" - }, - "1f1f2-1f1fc": { - "output": "1f1f2-1f1fc", - "name": "Malawi", - "alpha_code": ":flag_mw:", - "aliases": ":mw:" - }, - "1f1f2-1f1fe": { - "output": "1f1f2-1f1fe", - "name": "Malaysia", - "alpha_code": ":flag_my:", - "aliases": ":my:" - }, - "1f1f2-1f1fb": { - "output": "1f1f2-1f1fb", - "name": "Maldives", - "alpha_code": ":flag_mv:", - "aliases": ":mv:" - }, - "1f1f2-1f1f1": { - "output": "1f1f2-1f1f1", - "name": "Mali", - "alpha_code": ":flag_ml:", - "aliases": ":ml:" - }, - "1f1f2-1f1f9": { - "output": "1f1f2-1f1f9", - "name": "Malta", - "alpha_code": ":flag_mt:", - "aliases": ":mt:" - }, - "1f1f2-1f1ed": { - "output": "1f1f2-1f1ed", - "name": "Marshall Islands", - "alpha_code": ":flag_mh:", - "aliases": ":mh:" - }, - "1f1f2-1f1f7": { - "output": "1f1f2-1f1f7", - "name": "Mauritania", - "alpha_code": ":flag_mr:", - "aliases": ":mr:" - }, - "1f1f2-1f1fa": { - "output": "1f1f2-1f1fa", - "name": "Mauritius", - "alpha_code": ":flag_mu:", - "aliases": ":mu:" - }, - "1f1f2-1f1fd": { - "output": "1f1f2-1f1fd", - "name": "Mexico", - "alpha_code": ":flag_mx:", - "aliases": ":mx:" - }, - "1f1eb-1f1f2": { - "output": "1f1eb-1f1f2", - "name": "Micronesia", - "alpha_code": ":flag_fm:", - "aliases": ":fm:" - }, - "1f1f2-1f1e9": { - "output": "1f1f2-1f1e9", - "name": "Moldova", - "alpha_code": ":flag_md:", - "aliases": ":md:" - }, - "1f1f2-1f1e8": { - "output": "1f1f2-1f1e8", - "name": "Monaco", - "alpha_code": ":flag_mc:", - "aliases": ":mc:" - }, - "1f1f2-1f1f3": { - "output": "1f1f2-1f1f3", - "name": "Mongolia", - "alpha_code": ":flag_mn:", - "aliases": ":mn:" - }, - "1f1f2-1f1ea": { - "output": "1f1f2-1f1ea", - "name": "Montenegro", - "alpha_code": ":flag_me:", - "aliases": ":me:" - }, - "1f1f2-1f1e6": { - "output": "1f1f2-1f1e6", - "name": "Morocco", - "alpha_code": ":flag_ma:", - "aliases": ":ma:" - }, - "1f1f2-1f1ff": { - "output": "1f1f2-1f1ff", - "name": "Mozambique", - "alpha_code": ":flag_mz:", - "aliases": ":mz:" - }, - "1f1f2-1f1f2": { - "output": "1f1f2-1f1f2", - "name": "Myanmar (Burma)", - "alpha_code": ":flag_mm:", - "aliases": ":mm:" - }, - "1f1f3-1f1e6": { - "output": "1f1f3-1f1e6", - "name": "Namibia", - "alpha_code": ":flag_na:", - "aliases": ":na:" - }, - "1f1f3-1f1f7": { - "output": "1f1f3-1f1f7", - "name": "Nauru", - "alpha_code": ":flag_nr:", - "aliases": ":nr:" - }, - "1f1f3-1f1f5": { - "output": "1f1f3-1f1f5", - "name": "Nepal", - "alpha_code": ":flag_np:", - "aliases": ":np:" - }, - "1f1f3-1f1f1": { - "output": "1f1f3-1f1f1", - "name": "Netherlands", - "alpha_code": ":flag_nl:", - "aliases": ":nl:" - }, - "1f1f3-1f1ff": { - "output": "1f1f3-1f1ff", - "name": "New Zealand", - "alpha_code": ":flag_nz:", - "aliases": ":nz:" - }, - "1f1f3-1f1ee": { - "output": "1f1f3-1f1ee", - "name": "Nicaragua", - "alpha_code": ":flag_ni:", - "aliases": ":ni:" - }, - "1f1f3-1f1ea": { - "output": "1f1f3-1f1ea", - "name": "Niger", - "alpha_code": ":flag_ne:", - "aliases": ":ne:" - }, - "1f1f3-1f1ec": { - "output": "1f1f3-1f1ec", - "name": "Nigeria", - "alpha_code": ":flag_ng:", - "aliases": ":nigeria:" - }, - "1f1f0-1f1f5": { - "output": "1f1f0-1f1f5", - "name": "North Korea", - "alpha_code": ":flag_kp:", - "aliases": ":kp:" - }, - "1f1f3-1f1f4": { - "output": "1f1f3-1f1f4", - "name": "Norway", - "alpha_code": ":flag_no:", - "aliases": ":no:" - }, - "1f1f4-1f1f2": { - "output": "1f1f4-1f1f2", - "name": "Oman", - "alpha_code": ":flag_om:", - "aliases": ":om:" - }, - "1f1f5-1f1f0": { - "output": "1f1f5-1f1f0", - "name": "Pakistan", - "alpha_code": ":flag_pk:", - "aliases": ":pk:" - }, - "1f1f5-1f1fc": { - "output": "1f1f5-1f1fc", - "name": "Palau", - "alpha_code": ":flag_pw:", - "aliases": ":pw:" - }, - "1f1f5-1f1e6": { - "output": "1f1f5-1f1e6", - "name": "Panama", - "alpha_code": ":flag_pa:", - "aliases": ":pa:" - }, - "1f1f5-1f1ec": { - "output": "1f1f5-1f1ec", - "name": "Papua New Guinea", - "alpha_code": ":flag_pg:", - "aliases": ":pg:" - }, - "1f91b-1f3fe": { - "output": "1f91b-1f3fe", - "name": "left-facing fist: medium-dark skin tone", - "alpha_code": ":left_facing_fist_tone4:", - "aliases": ":left_fist_tone4:" - }, - "1f1f5-1f1fe": { - "output": "1f1f5-1f1fe", - "name": "Paraguay", - "alpha_code": ":flag_py:", - "aliases": ":py:" - }, - "1f1f5-1f1ea": { - "output": "1f1f5-1f1ea", - "name": "Peru", - "alpha_code": ":flag_pe:", - "aliases": ":pe:" - }, - "1f1f5-1f1ed": { - "output": "1f1f5-1f1ed", - "name": "Philippines", - "alpha_code": ":flag_ph:", - "aliases": ":ph:" - }, - "1f1f5-1f1f1": { - "output": "1f1f5-1f1f1", - "name": "Poland", - "alpha_code": ":flag_pl:", - "aliases": ":pl:" - }, - "1f1f5-1f1f9": { - "output": "1f1f5-1f1f9", - "name": "Portugal", - "alpha_code": ":flag_pt:", - "aliases": ":pt:" - }, - "1f1f6-1f1e6": { - "output": "1f1f6-1f1e6", - "name": "Qatar", - "alpha_code": ":flag_qa:", - "aliases": ":qa:" - }, - "1f1f9-1f1fc": { - "output": "1f1f9-1f1fc", - "name": "Taiwan", - "alpha_code": ":flag_tw:", - "aliases": ":tw:" - }, - "1f1e8-1f1ec": { - "output": "1f1e8-1f1ec", - "name": "Congo - Brazzaville", - "alpha_code": ":flag_cg:", - "aliases": ":cg:" - }, - "1f1f7-1f1f4": { - "output": "1f1f7-1f1f4", - "name": "Romania", - "alpha_code": ":flag_ro:", - "aliases": ":ro:" - }, - "1f1f7-1f1fc": { - "output": "1f1f7-1f1fc", - "name": "Rwanda", - "alpha_code": ":flag_rw:", - "aliases": ":rw:" - }, - "1f1f0-1f1f3": { - "output": "1f1f0-1f1f3", - "name": "St. Kitts & Nevis", - "alpha_code": ":flag_kn:", - "aliases": ":kn:" - }, - "1f1f1-1f1e8": { - "output": "1f1f1-1f1e8", - "name": "St. Lucia", - "alpha_code": ":flag_lc:", - "aliases": ":lc:" - }, - "1f1fb-1f1e8": { - "output": "1f1fb-1f1e8", - "name": "St. Vincent & Grenadines", - "alpha_code": ":flag_vc:", - "aliases": ":vc:" - }, - "1f1fc-1f1f8": { - "output": "1f1fc-1f1f8", - "name": "Samoa", - "alpha_code": ":flag_ws:", - "aliases": ":ws:" - }, - "1f1f8-1f1f2": { - "output": "1f1f8-1f1f2", - "name": "San Marino", - "alpha_code": ":flag_sm:", - "aliases": ":sm:" - }, - "1f1f8-1f1f9": { - "output": "1f1f8-1f1f9", - "name": "S\u00e3o Tom\u00e9 & Pr\u00edncipe", - "alpha_code": ":flag_st:", - "aliases": ":st:" - }, - "1f1f8-1f1e6": { - "output": "1f1f8-1f1e6", - "name": "Saudi Arabia", - "alpha_code": ":flag_sa:", - "aliases": ":saudiarabia:|:saudi:" - }, - "1f91b-1f3ff": { - "output": "1f91b-1f3ff", - "name": "left-facing fist: dark skin tone", - "alpha_code": ":left_facing_fist_tone5:", - "aliases": ":left_fist_tone5:" - }, - "1f1f8-1f1f3": { - "output": "1f1f8-1f1f3", - "name": "Senegal", - "alpha_code": ":flag_sn:", - "aliases": ":sn:" - }, - "1f1f7-1f1f8": { - "output": "1f1f7-1f1f8", - "name": "Serbia", - "alpha_code": ":flag_rs:", - "aliases": ":rs:" - }, - "1f1f8-1f1e8": { - "output": "1f1f8-1f1e8", - "name": "Seychelles", - "alpha_code": ":flag_sc:", - "aliases": ":sc:" - }, - "1f1f8-1f1f1": { - "output": "1f1f8-1f1f1", - "name": "Sierra Leone", - "alpha_code": ":flag_sl:", - "aliases": ":sl:" - }, - "1f1f8-1f1ec": { - "output": "1f1f8-1f1ec", - "name": "Singapore", - "alpha_code": ":flag_sg:", - "aliases": ":sg:" - }, - "1f1f8-1f1f0": { - "output": "1f1f8-1f1f0", - "name": "Slovakia", - "alpha_code": ":flag_sk:", - "aliases": ":sk:" - }, - "1f1f8-1f1ee": { - "output": "1f1f8-1f1ee", - "name": "Slovenia", - "alpha_code": ":flag_si:", - "aliases": ":si:" - }, - "1f1f8-1f1e7": { - "output": "1f1f8-1f1e7", - "name": "Solomon Islands", - "alpha_code": ":flag_sb:", - "aliases": ":sb:" - }, - "1f1f8-1f1f4": { - "output": "1f1f8-1f1f4", - "name": "Somalia", - "alpha_code": ":flag_so:", - "aliases": ":so:" - }, - "1f1ff-1f1e6": { - "output": "1f1ff-1f1e6", - "name": "South Africa", - "alpha_code": ":flag_za:", - "aliases": ":za:" - }, - "1f1f1-1f1f0": { - "output": "1f1f1-1f1f0", - "name": "Sri Lanka", - "alpha_code": ":flag_lk:", - "aliases": ":lk:" - }, - "1f1f8-1f1e9": { - "output": "1f1f8-1f1e9", - "name": "Sudan", - "alpha_code": ":flag_sd:", - "aliases": ":sd:" - }, - "1f1f8-1f1f7": { - "output": "1f1f8-1f1f7", - "name": "Suriname", - "alpha_code": ":flag_sr:", - "aliases": ":sr:" - }, - "1f1f8-1f1ff": { - "output": "1f1f8-1f1ff", - "name": "Swaziland", - "alpha_code": ":flag_sz:", - "aliases": ":sz:" - }, - "1f1f8-1f1ea": { - "output": "1f1f8-1f1ea", - "name": "Sweden", - "alpha_code": ":flag_se:", - "aliases": ":se:" - }, - "1f1e8-1f1ed": { - "output": "1f1e8-1f1ed", - "name": "Switzerland", - "alpha_code": ":flag_ch:", - "aliases": ":ch:" - }, - "1f1f8-1f1fe": { - "output": "1f1f8-1f1fe", - "name": "Syria", - "alpha_code": ":flag_sy:", - "aliases": ":sy:" - }, - "1f1f9-1f1ef": { - "output": "1f1f9-1f1ef", - "name": "Tajikistan", - "alpha_code": ":flag_tj:", - "aliases": ":tj:" - }, - "1f1f9-1f1ff": { - "output": "1f1f9-1f1ff", - "name": "Tanzania", - "alpha_code": ":flag_tz:", - "aliases": ":tz:" - }, - "1f1f9-1f1ed": { - "output": "1f1f9-1f1ed", - "name": "Thailand", - "alpha_code": ":flag_th:", - "aliases": ":th:" - }, - "1f1f9-1f1ec": { - "output": "1f1f9-1f1ec", - "name": "Togo", - "alpha_code": ":flag_tg:", - "aliases": ":tg:" - }, - "1f1f9-1f1f4": { - "output": "1f1f9-1f1f4", - "name": "Tonga", - "alpha_code": ":flag_to:", - "aliases": ":to:" - }, - "1f1f9-1f1f9": { - "output": "1f1f9-1f1f9", - "name": "Trinidad & Tobago", - "alpha_code": ":flag_tt:", - "aliases": ":tt:" - }, - "1f1f9-1f1f3": { - "output": "1f1f9-1f1f3", - "name": "Tunisia", - "alpha_code": ":flag_tn:", - "aliases": ":tn:" - }, - "1f1f9-1f1f7": { - "output": "1f1f9-1f1f7", - "name": "Turkey", - "alpha_code": ":flag_tr:", - "aliases": ":tr:" - }, - "1f1f9-1f1f2": { - "output": "1f1f9-1f1f2", - "name": "Turkmenistan", - "alpha_code": ":flag_tm:", - "aliases": ":turkmenistan:" - }, - "1f1f9-1f1fb": { - "output": "1f1f9-1f1fb", - "name": "Tuvalu", - "alpha_code": ":flag_tv:", - "aliases": ":tuvalu:" - }, - "1f1fa-1f1ec": { - "output": "1f1fa-1f1ec", - "name": "Uganda", - "alpha_code": ":flag_ug:", - "aliases": ":ug:" - }, - "1f1fa-1f1e6": { - "output": "1f1fa-1f1e6", - "name": "Ukraine", - "alpha_code": ":flag_ua:", - "aliases": ":ua:" - }, - "1f1e6-1f1ea": { - "output": "1f1e6-1f1ea", - "name": "United Arab Emirates", - "alpha_code": ":flag_ae:", - "aliases": ":ae:" - }, - "1f1fa-1f1fe": { - "output": "1f1fa-1f1fe", - "name": "Uruguay", - "alpha_code": ":flag_uy:", - "aliases": ":uy:" - }, - "1f1fa-1f1ff": { - "output": "1f1fa-1f1ff", - "name": "Uzbekistan", - "alpha_code": ":flag_uz:", - "aliases": ":uz:" - }, - "1f1fb-1f1fa": { - "output": "1f1fb-1f1fa", - "name": "Vanuatu", - "alpha_code": ":flag_vu:", - "aliases": ":vu:" - }, - "1f1fb-1f1e6": { - "output": "1f1fb-1f1e6", - "name": "Vatican City", - "alpha_code": ":flag_va:", - "aliases": ":va:" - }, - "1f1fb-1f1ea": { - "output": "1f1fb-1f1ea", - "name": "Venezuela", - "alpha_code": ":flag_ve:", - "aliases": ":ve:" - }, - "1f1fb-1f1f3": { - "output": "1f1fb-1f1f3", - "name": "Vietnam", - "alpha_code": ":flag_vn:", - "aliases": ":vn:" - }, - "1f1ea-1f1ed": { - "output": "1f1ea-1f1ed", - "name": "Western Sahara", - "alpha_code": ":flag_eh:", - "aliases": ":eh:" - }, - "1f91c-1f3fb": { - "output": "1f91c-1f3fb", - "name": "right-facing fist: light skin tone", - "alpha_code": ":right_facing_fist_tone1:", - "aliases": ":right_fist_tone1:" - }, - "1f1fe-1f1ea": { - "output": "1f1fe-1f1ea", - "name": "Yemen", - "alpha_code": ":flag_ye:", - "aliases": ":ye:" - }, - "1f1ff-1f1f2": { - "output": "1f1ff-1f1f2", - "name": "Zambia", - "alpha_code": ":flag_zm:", - "aliases": ":zm:" - }, - "1f1ff-1f1fc": { - "output": "1f1ff-1f1fc", - "name": "Zimbabwe", - "alpha_code": ":flag_zw:", - "aliases": ":zw:" - }, - "1f1f5-1f1f7": { - "output": "1f1f5-1f1f7", - "name": "Puerto Rico", - "alpha_code": ":flag_pr:", - "aliases": ":pr:" - }, - "1f1f0-1f1fe": { - "output": "1f1f0-1f1fe", - "name": "Cayman Islands", - "alpha_code": ":flag_ky:", - "aliases": ":ky:" - }, - "1f1e7-1f1f2": { - "output": "1f1e7-1f1f2", - "name": "Bermuda", - "alpha_code": ":flag_bm:", - "aliases": ":bm:" - }, - "1f1f5-1f1eb": { - "output": "1f1f5-1f1eb", - "name": "French Polynesia", - "alpha_code": ":flag_pf:", - "aliases": ":pf:" - }, - "1f1f5-1f1f8": { - "output": "1f1f5-1f1f8", - "name": "Palestinian Territories", - "alpha_code": ":flag_ps:", - "aliases": ":ps:" - }, - "1f1f3-1f1e8": { - "output": "1f1f3-1f1e8", - "name": "New Caledonia", - "alpha_code": ":flag_nc:", - "aliases": ":nc:" - }, - "1f91c-1f3fc": { - "output": "1f91c-1f3fc", - "name": "right-facing fist: medium-light skin tone", - "alpha_code": ":right_facing_fist_tone2:", - "aliases": ":right_fist_tone2:" - }, - "1f1f8-1f1ed": { - "output": "1f1f8-1f1ed", - "name": "St. Helena", - "alpha_code": ":flag_sh:", - "aliases": ":sh:" - }, - "1f1e6-1f1fc": { - "output": "1f1e6-1f1fc", - "name": "Aruba", - "alpha_code": ":flag_aw:", - "aliases": ":aw:" - }, - "1f1fb-1f1ee": { - "output": "1f1fb-1f1ee", - "name": "U.S. Virgin Islands", - "alpha_code": ":flag_vi:", - "aliases": ":vi:" - }, - "1f1ed-1f1f0": { - "output": "1f1ed-1f1f0", - "name": "Hong Kong SAR China", - "alpha_code": ":flag_hk:", - "aliases": ":hk:" - }, - "1f1e6-1f1e8": { - "output": "1f1e6-1f1e8", - "name": "Ascension Island", - "alpha_code": ":flag_ac:", - "aliases": ":ac:" - }, - "1f1f2-1f1f8": { - "output": "1f1f2-1f1f8", - "name": "Montserrat", - "alpha_code": ":flag_ms:", - "aliases": ":ms:" - }, - "1f1ec-1f1fa": { - "output": "1f1ec-1f1fa", - "name": "Guam", - "alpha_code": ":flag_gu:", - "aliases": ":gu:" - }, - "1f1ec-1f1f1": { - "output": "1f1ec-1f1f1", - "name": "Greenland", - "alpha_code": ":flag_gl:", - "aliases": ":gl:" - }, - "1f1f3-1f1fa": { - "output": "1f1f3-1f1fa", - "name": "Niue", - "alpha_code": ":flag_nu:", - "aliases": ":nu:" - }, - "1f1fc-1f1eb": { - "output": "1f1fc-1f1eb", - "name": "Wallis & Futuna", - "alpha_code": ":flag_wf:", - "aliases": ":wf:" - }, - "1f1f2-1f1f4": { - "output": "1f1f2-1f1f4", - "name": "Macau SAR China", - "alpha_code": ":flag_mo:", - "aliases": ":mo:" - }, - "1f91c-1f3fd": { - "output": "1f91c-1f3fd", - "name": "right-facing fist: medium skin tone", - "alpha_code": ":right_facing_fist_tone3:", - "aliases": ":right_fist_tone3:" - }, - "1f1eb-1f1f4": { - "output": "1f1eb-1f1f4", - "name": "Faroe Islands", - "alpha_code": ":flag_fo:", - "aliases": ":fo:" - }, - "1f1eb-1f1f0": { - "output": "1f1eb-1f1f0", - "name": "Falkland Islands", - "alpha_code": ":flag_fk:", - "aliases": ":fk:" - }, - "1f1ef-1f1ea": { - "output": "1f1ef-1f1ea", - "name": "Jersey", - "alpha_code": ":flag_je:", - "aliases": ":je:" - }, - "1f1e6-1f1ee": { - "output": "1f1e6-1f1ee", - "name": "Anguilla", - "alpha_code": ":flag_ai:", - "aliases": ":ai:" - }, - "1f1ec-1f1ee": { - "output": "1f1ec-1f1ee", - "name": "Gibraltar", - "alpha_code": ":flag_gi:", - "aliases": ":gi:" - }, - "1f39e": { - "output": "1f39e-fe0f", - "name": "film frames", - "alpha_code": ":film_frames:", - "aliases": "" - }, - "1f39f": { - "output": "1f39f-fe0f", - "name": "admission tickets", - "alpha_code": ":tickets:", - "aliases": ":admission_tickets:" - }, - "1f3c5": { - "output": "1f3c5", - "name": "sports medal", - "alpha_code": ":medal:", - "aliases": ":sports_medal:" - }, - "1f3cb": { - "output": "1f3cb-fe0f", - "name": "person lifting weights", - "alpha_code": ":person_lifting_weights:", - "aliases": ":lifter:|:weight_lifter:" - }, - "1f3cc": { - "output": "1f3cc-fe0f", - "name": "person golfing", - "alpha_code": ":person_golfing:", - "aliases": ":golfer:" - }, - "1f3cd": { - "output": "1f3cd-fe0f", - "name": "motorcycle", - "alpha_code": ":motorcycle:", - "aliases": ":racing_motorcycle:" - }, - "1f3ce": { - "output": "1f3ce-fe0f", - "name": "racing car", - "alpha_code": ":race_car:", - "aliases": ":racing_car:" - }, - "1f396": { - "output": "1f396-fe0f", - "name": "military medal", - "alpha_code": ":military_medal:", - "aliases": "" - }, - "1f397": { - "output": "1f397-fe0f", - "name": "reminder ribbon", - "alpha_code": ":reminder_ribbon:", - "aliases": "" - }, - "1f336": { - "output": "1f336-fe0f", - "name": "hot pepper", - "alpha_code": ":hot_pepper:", - "aliases": "" - }, - "1f91c-1f3fe": { - "output": "1f91c-1f3fe", - "name": "right-facing fist: medium-dark skin tone", - "alpha_code": ":right_facing_fist_tone4:", - "aliases": ":right_fist_tone4:" - }, - "1f327": { - "output": "1f327-fe0f", - "name": "cloud with rain", - "alpha_code": ":cloud_rain:", - "aliases": ":cloud_with_rain:" - }, - "1f328": { - "output": "1f328-fe0f", - "name": "cloud with snow", - "alpha_code": ":cloud_snow:", - "aliases": ":cloud_with_snow:" - }, - "1f329": { - "output": "1f329-fe0f", - "name": "cloud with lightning", - "alpha_code": ":cloud_lightning:", - "aliases": ":cloud_with_lightning:" - }, - "1f32a": { - "output": "1f32a-fe0f", - "name": "tornado", - "alpha_code": ":cloud_tornado:", - "aliases": ":cloud_with_tornado:" - }, - "1f32b": { - "output": "1f32b-fe0f", - "name": "fog", - "alpha_code": ":fog:", - "aliases": "" - }, - "1f32c": { - "output": "1f32c-fe0f", - "name": "wind face", - "alpha_code": ":wind_blowing_face:", - "aliases": "" - }, - "1f43f": { - "output": "1f43f-fe0f", - "name": "chipmunk", - "alpha_code": ":chipmunk:", - "aliases": "" - }, - "1f577": { - "output": "1f577-fe0f", - "name": "spider", - "alpha_code": ":spider:", - "aliases": "" - }, - "1f578": { - "output": "1f578-fe0f", - "name": "spider web", - "alpha_code": ":spider_web:", - "aliases": "" - }, - "1f321": { - "output": "1f321-fe0f", - "name": "thermometer", - "alpha_code": ":thermometer:", - "aliases": "" - }, - "1f399": { - "output": "1f399-fe0f", - "name": "studio microphone", - "alpha_code": ":microphone2:", - "aliases": ":studio_microphone:" - }, - "1f39a": { - "output": "1f39a-fe0f", - "name": "level slider", - "alpha_code": ":level_slider:", - "aliases": "" - }, - "1f39b": { - "output": "1f39b-fe0f", - "name": "control knobs", - "alpha_code": ":control_knobs:", - "aliases": "" - }, - "1f3f3": { - "output": "1f3f3-fe0f", - "name": "white flag", - "alpha_code": ":flag_white:", - "aliases": ":waving_white_flag:" - }, - "1f3f4": { - "output": "1f3f4", - "name": "black flag", - "alpha_code": ":flag_black:", - "aliases": ":waving_black_flag:" - }, - "1f3f5": { - "output": "1f3f5-fe0f", - "name": "rosette", - "alpha_code": ":rosette:", - "aliases": "" - }, - "1f3f7": { - "output": "1f3f7-fe0f", - "name": "label", - "alpha_code": ":label:", - "aliases": "" - }, - "1f4f8": { - "output": "1f4f8", - "name": "camera with flash", - "alpha_code": ":camera_with_flash:", - "aliases": "" - }, - "1f4fd": { - "output": "1f4fd-fe0f", - "name": "film projector", - "alpha_code": ":projector:", - "aliases": ":film_projector:" - }, - "271d": { - "output": "271d-fe0f", - "name": "latin cross", - "alpha_code": ":cross:", - "aliases": ":latin_cross:" - }, - "1f549": { - "output": "1f549-fe0f", - "name": "om", - "alpha_code": ":om_symbol:", - "aliases": "" - }, - "1f54a": { - "output": "1f54a-fe0f", - "name": "dove", - "alpha_code": ":dove:", - "aliases": ":dove_of_peace:" - }, - "1f56f": { - "output": "1f56f-fe0f", - "name": "candle", - "alpha_code": ":candle:", - "aliases": "" - }, - "1f570": { - "output": "1f570-fe0f", - "name": "mantelpiece clock", - "alpha_code": ":clock:", - "aliases": ":mantlepiece_clock:" - }, - "1f573": { - "output": "1f573-fe0f", - "name": "hole", - "alpha_code": ":hole:", - "aliases": "" - }, - "1f576": { - "output": "1f576-fe0f", - "name": "sunglasses", - "alpha_code": ":dark_sunglasses:", - "aliases": "" - }, - "1f579": { - "output": "1f579-fe0f", - "name": "joystick", - "alpha_code": ":joystick:", - "aliases": "" - }, - "1f587": { - "output": "1f587-fe0f", - "name": "linked paperclips", - "alpha_code": ":paperclips:", - "aliases": ":linked_paperclips:" - }, - "1f58a": { - "output": "1f58a-fe0f", - "name": "pen", - "alpha_code": ":pen_ballpoint:", - "aliases": ":lower_left_ballpoint_pen:" - }, - "1f58b": { - "output": "1f58b-fe0f", - "name": "fountain pen", - "alpha_code": ":pen_fountain:", - "aliases": ":lower_left_fountain_pen:" - }, - "1f58c": { - "output": "1f58c-fe0f", - "name": "paintbrush", - "alpha_code": ":paintbrush:", - "aliases": ":lower_left_paintbrush:" - }, - "1f58d": { - "output": "1f58d-fe0f", - "name": "crayon", - "alpha_code": ":crayon:", - "aliases": ":lower_left_crayon:" - }, - "1f5a5": { - "output": "1f5a5-fe0f", - "name": "desktop computer", - "alpha_code": ":desktop:", - "aliases": ":desktop_computer:" - }, - "1f5a8": { - "output": "1f5a8-fe0f", - "name": "printer", - "alpha_code": ":printer:", - "aliases": "" - }, - "1f91c-1f3ff": { - "output": "1f91c-1f3ff", - "name": "right-facing fist: dark skin tone", - "alpha_code": ":right_facing_fist_tone5:", - "aliases": ":right_fist_tone5:" - }, - "2328": { - "output": "2328-fe0f", - "name": "keyboard", - "alpha_code": ":keyboard:", - "aliases": "" - }, - "1f5b2": { - "output": "1f5b2-fe0f", - "name": "trackball", - "alpha_code": ":trackball:", - "aliases": "" - }, - "1f91a-1f3fb": { - "output": "1f91a-1f3fb", - "name": "raised back of hand: light skin tone", - "alpha_code": ":raised_back_of_hand_tone1:", - "aliases": ":back_of_hand_tone1:" - }, - "1f5bc": { - "output": "1f5bc-fe0f", - "name": "framed picture", - "alpha_code": ":frame_photo:", - "aliases": ":frame_with_picture:" - }, - "1f5c2": { - "output": "1f5c2-fe0f", - "name": "card index dividers", - "alpha_code": ":dividers:", - "aliases": ":card_index_dividers:" - }, - "1f5c3": { - "output": "1f5c3-fe0f", - "name": "card file box", - "alpha_code": ":card_box:", - "aliases": ":card_file_box:" - }, - "1f5c4": { - "output": "1f5c4-fe0f", - "name": "file cabinet", - "alpha_code": ":file_cabinet:", - "aliases": "" - }, - "1f5d1": { - "output": "1f5d1-fe0f", - "name": "wastebasket", - "alpha_code": ":wastebasket:", - "aliases": "" - }, - "1f5d2": { - "output": "1f5d2-fe0f", - "name": "spiral notepad", - "alpha_code": ":notepad_spiral:", - "aliases": ":spiral_note_pad:" - }, - "1f5d3": { - "output": "1f5d3-fe0f", - "name": "spiral calendar", - "alpha_code": ":calendar_spiral:", - "aliases": ":spiral_calendar_pad:" - }, - "1f5dc": { - "output": "1f5dc-fe0f", - "name": "clamp", - "alpha_code": ":compression:", - "aliases": "" - }, - "1f5dd": { - "output": "1f5dd-fe0f", - "name": "old key", - "alpha_code": ":key2:", - "aliases": ":old_key:" - }, - "1f5de": { - "output": "1f5de-fe0f", - "name": "rolled-up newspaper", - "alpha_code": ":newspaper2:", - "aliases": ":rolled_up_newspaper:" - }, - "1f5e1": { - "output": "1f5e1-fe0f", - "name": "dagger", - "alpha_code": ":dagger:", - "aliases": ":dagger_knife:" - }, - "1f5e3": { - "output": "1f5e3-fe0f", - "name": "speaking head", - "alpha_code": ":speaking_head:", - "aliases": ":speaking_head_in_silhouette:" - }, - "1f5e8": { - "output": "1f5e8-fe0f", - "name": "left speech bubble", - "alpha_code": ":speech_left:", - "aliases": ":left_speech_bubble:" - }, - "1f91a-1f3fc": { - "output": "1f91a-1f3fc", - "name": "raised back of hand: medium-light skin tone", - "alpha_code": ":raised_back_of_hand_tone2:", - "aliases": ":back_of_hand_tone2:" - }, - "1f5ef": { - "output": "1f5ef-fe0f", - "name": "right anger bubble", - "alpha_code": ":anger_right:", - "aliases": ":right_anger_bubble:" - }, - "1f91a-1f3fd": { - "output": "1f91a-1f3fd", - "name": "raised back of hand: medium skin tone", - "alpha_code": ":raised_back_of_hand_tone3:", - "aliases": ":back_of_hand_tone3:" - }, - "1f5f3": { - "output": "1f5f3-fe0f", - "name": "ballot box with ballot", - "alpha_code": ":ballot_box:", - "aliases": ":ballot_box_with_ballot:" - }, - "1f5fa": { - "output": "1f5fa-fe0f", - "name": "world map", - "alpha_code": ":map:", - "aliases": ":world_map:" - }, - "1f6cc": { - "output": "1f6cc", - "name": "person in bed", - "alpha_code": ":sleeping_accommodation:", - "aliases": "" - }, - "1f6e0": { - "output": "1f6e0-fe0f", - "name": "hammer and wrench", - "alpha_code": ":tools:", - "aliases": ":hammer_and_wrench:" - }, - "1f6e1": { - "output": "1f6e1-fe0f", - "name": "shield", - "alpha_code": ":shield:", - "aliases": "" - }, - "1f6e2": { - "output": "1f6e2-fe0f", - "name": "oil drum", - "alpha_code": ":oil:", - "aliases": ":oil_drum:" - }, - "1f6f0": { - "output": "1f6f0-fe0f", - "name": "satellite", - "alpha_code": ":satellite_orbital:", - "aliases": "" - }, - "1f37d": { - "output": "1f37d-fe0f", - "name": "fork and knife with plate", - "alpha_code": ":fork_knife_plate:", - "aliases": ":fork_and_knife_with_plate:" - }, - "1f441": { - "output": "1f441-fe0f", - "name": "eye", - "alpha_code": ":eye:", - "aliases": "" - }, - "1f574": { - "output": "1f574-fe0f", - "name": "man in business suit levitating", - "alpha_code": ":man_in_business_suit_levitating:", - "aliases": "" - }, - "1f575": { - "output": "1f575-fe0f", - "name": "detective", - "alpha_code": ":detective:", - "aliases": ":spy:|:sleuth_or_spy:" - }, - "270d": { - "output": "270d-fe0f", - "name": "writing hand", - "alpha_code": ":writing_hand:", - "aliases": "" - }, - "1f590": { - "output": "1f590-fe0f", - "name": "raised hand with fingers splayed", - "alpha_code": ":hand_splayed:", - "aliases": ":raised_hand_with_fingers_splayed:" - }, - "1f595": { - "output": "1f595", - "name": "middle finger", - "alpha_code": ":middle_finger:", - "aliases": ":reversed_hand_with_middle_finger_extended:" - }, - "1f596": { - "output": "1f596", - "name": "vulcan salute", - "alpha_code": ":vulcan:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers:" - }, - "1f641": { - "output": "1f641", - "name": "slightly frowning face", - "alpha_code": ":slight_frown:", - "aliases": ":slightly_frowning_face:" - }, - "1f642": { - "output": "1f642", - "name": "slightly smiling face", - "alpha_code": ":slight_smile:", - "aliases": ":slightly_smiling_face:" - }, - "1f3d4": { - "output": "1f3d4-fe0f", - "name": "snow-capped mountain", - "alpha_code": ":mountain_snow:", - "aliases": ":snow_capped_mountain:" - }, - "1f3d5": { - "output": "1f3d5-fe0f", - "name": "camping", - "alpha_code": ":camping:", - "aliases": "" - }, - "1f3d6": { - "output": "1f3d6-fe0f", - "name": "beach with umbrella", - "alpha_code": ":beach:", - "aliases": ":beach_with_umbrella:" - }, - "1f3d7": { - "output": "1f3d7-fe0f", - "name": "building construction", - "alpha_code": ":construction_site:", - "aliases": ":building_construction:" - }, - "1f3d8": { - "output": "1f3d8-fe0f", - "name": "houses", - "alpha_code": ":homes:", - "aliases": ":house_buildings:" - }, - "1f3d9": { - "output": "1f3d9-fe0f", - "name": "cityscape", - "alpha_code": ":cityscape:", - "aliases": "" - }, - "1f3da": { - "output": "1f3da-fe0f", - "name": "derelict house", - "alpha_code": ":house_abandoned:", - "aliases": ":derelict_house_building:" - }, - "1f3db": { - "output": "1f3db-fe0f", - "name": "classical building", - "alpha_code": ":classical_building:", - "aliases": "" - }, - "1f3dc": { - "output": "1f3dc-fe0f", - "name": "desert", - "alpha_code": ":desert:", - "aliases": "" - }, - "1f3dd": { - "output": "1f3dd-fe0f", - "name": "desert island", - "alpha_code": ":island:", - "aliases": ":desert_island:" - }, - "1f3de": { - "output": "1f3de-fe0f", - "name": "national park", - "alpha_code": ":park:", - "aliases": ":national_park:" - }, - "1f3df": { - "output": "1f3df-fe0f", - "name": "stadium", - "alpha_code": ":stadium:", - "aliases": "" - }, - "1f6cb": { - "output": "1f6cb-fe0f", - "name": "couch and lamp", - "alpha_code": ":couch:", - "aliases": ":couch_and_lamp:" - }, - "1f91a-1f3fe": { - "output": "1f91a-1f3fe", - "name": "raised back of hand: medium-dark skin tone", - "alpha_code": ":raised_back_of_hand_tone4:", - "aliases": ":back_of_hand_tone4:" - }, - "1f6cd": { - "output": "1f6cd-fe0f", - "name": "shopping bags", - "alpha_code": ":shopping_bags:", - "aliases": "" - }, - "1f6ce": { - "output": "1f6ce-fe0f", - "name": "bellhop bell", - "alpha_code": ":bellhop:", - "aliases": ":bellhop_bell:" - }, - "1f6cf": { - "output": "1f6cf-fe0f", - "name": "bed", - "alpha_code": ":bed:", - "aliases": "" - }, - "1f6e3": { - "output": "1f6e3-fe0f", - "name": "motorway", - "alpha_code": ":motorway:", - "aliases": "" - }, - "1f6e4": { - "output": "1f6e4-fe0f", - "name": "railway track", - "alpha_code": ":railway_track:", - "aliases": ":railroad_track:" - }, - "1f6e5": { - "output": "1f6e5-fe0f", - "name": "motor boat", - "alpha_code": ":motorboat:", - "aliases": "" - }, - "1f6e9": { - "output": "1f6e9-fe0f", - "name": "small airplane", - "alpha_code": ":airplane_small:", - "aliases": ":small_airplane:" - }, - "1f6eb": { - "output": "1f6eb", - "name": "airplane departure", - "alpha_code": ":airplane_departure:", - "aliases": "" - }, - "1f6ec": { - "output": "1f6ec", - "name": "airplane arrival", - "alpha_code": ":airplane_arriving:", - "aliases": "" - }, - "1f6f3": { - "output": "1f6f3-fe0f", - "name": "passenger ship", - "alpha_code": ":cruise_ship:", - "aliases": ":passenger_ship:" - }, - "1f476-1f3fb": { - "output": "1f476-1f3fb", - "name": "baby: light skin tone", - "alpha_code": ":baby_tone1:", - "aliases": "" - }, - "1f476-1f3fc": { - "output": "1f476-1f3fc", - "name": "baby: medium-light skin tone", - "alpha_code": ":baby_tone2:", - "aliases": "" - }, - "1f476-1f3fd": { - "output": "1f476-1f3fd", - "name": "baby: medium skin tone", - "alpha_code": ":baby_tone3:", - "aliases": "" - }, - "1f476-1f3fe": { - "output": "1f476-1f3fe", - "name": "baby: medium-dark skin tone", - "alpha_code": ":baby_tone4:", - "aliases": "" - }, - "1f476-1f3ff": { - "output": "1f476-1f3ff", - "name": "baby: dark skin tone", - "alpha_code": ":baby_tone5:", - "aliases": "" - }, - "1f466-1f3fb": { - "output": "1f466-1f3fb", - "name": "boy: light skin tone", - "alpha_code": ":boy_tone1:", - "aliases": "" - }, - "1f466-1f3fc": { - "output": "1f466-1f3fc", - "name": "boy: medium-light skin tone", - "alpha_code": ":boy_tone2:", - "aliases": "" - }, - "1f466-1f3fd": { - "output": "1f466-1f3fd", - "name": "boy: medium skin tone", - "alpha_code": ":boy_tone3:", - "aliases": "" - }, - "1f466-1f3fe": { - "output": "1f466-1f3fe", - "name": "boy: medium-dark skin tone", - "alpha_code": ":boy_tone4:", - "aliases": "" - }, - "1f466-1f3ff": { - "output": "1f466-1f3ff", - "name": "boy: dark skin tone", - "alpha_code": ":boy_tone5:", - "aliases": "" - }, - "1f467-1f3fb": { - "output": "1f467-1f3fb", - "name": "girl: light skin tone", - "alpha_code": ":girl_tone1:", - "aliases": "" - }, - "1f467-1f3fc": { - "output": "1f467-1f3fc", - "name": "girl: medium-light skin tone", - "alpha_code": ":girl_tone2:", - "aliases": "" - }, - "1f467-1f3fd": { - "output": "1f467-1f3fd", - "name": "girl: medium skin tone", - "alpha_code": ":girl_tone3:", - "aliases": "" - }, - "1f467-1f3fe": { - "output": "1f467-1f3fe", - "name": "girl: medium-dark skin tone", - "alpha_code": ":girl_tone4:", - "aliases": "" - }, - "1f467-1f3ff": { - "output": "1f467-1f3ff", - "name": "girl: dark skin tone", - "alpha_code": ":girl_tone5:", - "aliases": "" - }, - "1f468-1f3fb": { - "output": "1f468-1f3fb", - "name": "man: light skin tone", - "alpha_code": ":man_tone1:", - "aliases": "" - }, - "1f468-1f3fc": { - "output": "1f468-1f3fc", - "name": "man: medium-light skin tone", - "alpha_code": ":man_tone2:", - "aliases": "" - }, - "1f468-1f3fd": { - "output": "1f468-1f3fd", - "name": "man: medium skin tone", - "alpha_code": ":man_tone3:", - "aliases": "" - }, - "1f468-1f3fe": { - "output": "1f468-1f3fe", - "name": "man: medium-dark skin tone", - "alpha_code": ":man_tone4:", - "aliases": "" - }, - "1f468-1f3ff": { - "output": "1f468-1f3ff", - "name": "man: dark skin tone", - "alpha_code": ":man_tone5:", - "aliases": "" - }, - "1f469-1f3fb": { - "output": "1f469-1f3fb", - "name": "woman: light skin tone", - "alpha_code": ":woman_tone1:", - "aliases": "" - }, - "1f469-1f3fc": { - "output": "1f469-1f3fc", - "name": "woman: medium-light skin tone", - "alpha_code": ":woman_tone2:", - "aliases": "" - }, - "1f469-1f3fd": { - "output": "1f469-1f3fd", - "name": "woman: medium skin tone", - "alpha_code": ":woman_tone3:", - "aliases": "" - }, - "1f469-1f3fe": { - "output": "1f469-1f3fe", - "name": "woman: medium-dark skin tone", - "alpha_code": ":woman_tone4:", - "aliases": "" - }, - "1f469-1f3ff": { - "output": "1f469-1f3ff", - "name": "woman: dark skin tone", - "alpha_code": ":woman_tone5:", - "aliases": "" - }, - "1f470-1f3fb": { - "output": "1f470-1f3fb", - "name": "bride with veil: light skin tone", - "alpha_code": ":bride_with_veil_tone1:", - "aliases": "" - }, - "1f470-1f3fc": { - "output": "1f470-1f3fc", - "name": "bride with veil: medium-light skin tone", - "alpha_code": ":bride_with_veil_tone2:", - "aliases": "" - }, - "1f91a-1f3ff": { - "output": "1f91a-1f3ff", - "name": "raised back of hand: dark skin tone", - "alpha_code": ":raised_back_of_hand_tone5:", - "aliases": ":back_of_hand_tone5:" - }, - "1f470-1f3fd": { - "output": "1f470-1f3fd", - "name": "bride with veil: medium skin tone", - "alpha_code": ":bride_with_veil_tone3:", - "aliases": "" - }, - "1f470-1f3fe": { - "output": "1f470-1f3fe", - "name": "bride with veil: medium-dark skin tone", - "alpha_code": ":bride_with_veil_tone4:", - "aliases": "" - }, - "1f470-1f3ff": { - "output": "1f470-1f3ff", - "name": "bride with veil: dark skin tone", - "alpha_code": ":bride_with_veil_tone5:", - "aliases": "" - }, - "1f471-1f3fb": { - "output": "1f471-1f3fb", - "name": "blond-haired person: light skin tone", - "alpha_code": ":blond_haired_person_tone1:", - "aliases": ":person_with_blond_hair_tone1:" - }, - "1f471-1f3fc": { - "output": "1f471-1f3fc", - "name": "blond-haired person: medium-light skin tone", - "alpha_code": ":blond_haired_person_tone2:", - "aliases": ":person_with_blond_hair_tone2:" - }, - "1f471-1f3fd": { - "output": "1f471-1f3fd", - "name": "blond-haired person: medium skin tone", - "alpha_code": ":blond_haired_person_tone3:", - "aliases": ":person_with_blond_hair_tone3:" - }, - "1f471-1f3fe": { - "output": "1f471-1f3fe", - "name": "blond-haired person: medium-dark skin tone", - "alpha_code": ":blond_haired_person_tone4:", - "aliases": ":person_with_blond_hair_tone4:" - }, - "1f471-1f3ff": { - "output": "1f471-1f3ff", - "name": "blond-haired person: dark skin tone", - "alpha_code": ":blond_haired_person_tone5:", - "aliases": ":person_with_blond_hair_tone5:" - }, - "1f472-1f3fb": { - "output": "1f472-1f3fb", - "name": "man with Chinese cap: light skin tone", - "alpha_code": ":man_with_chinese_cap_tone1:", - "aliases": ":man_with_gua_pi_mao_tone1:" - }, - "1f472-1f3fc": { - "output": "1f472-1f3fc", - "name": "man with Chinese cap: medium-light skin tone", - "alpha_code": ":man_with_chinese_cap_tone2:", - "aliases": ":man_with_gua_pi_mao_tone2:" - }, - "1f472-1f3fd": { - "output": "1f472-1f3fd", - "name": "man with Chinese cap: medium skin tone", - "alpha_code": ":man_with_chinese_cap_tone3:", - "aliases": ":man_with_gua_pi_mao_tone3:" - }, - "1f472-1f3fe": { - "output": "1f472-1f3fe", - "name": "man with Chinese cap: medium-dark skin tone", - "alpha_code": ":man_with_chinese_cap_tone4:", - "aliases": ":man_with_gua_pi_mao_tone4:" - }, - "1f472-1f3ff": { - "output": "1f472-1f3ff", - "name": "man with Chinese cap: dark skin tone", - "alpha_code": ":man_with_chinese_cap_tone5:", - "aliases": ":man_with_gua_pi_mao_tone5:" - }, - "1f473-1f3fb": { - "output": "1f473-1f3fb", - "name": "person wearing turban: light skin tone", - "alpha_code": ":person_wearing_turban_tone1:", - "aliases": ":man_with_turban_tone1:" - }, - "1f473-1f3fc": { - "output": "1f473-1f3fc", - "name": "person wearing turban: medium-light skin tone", - "alpha_code": ":person_wearing_turban_tone2:", - "aliases": ":man_with_turban_tone2:" - }, - "1f473-1f3fd": { - "output": "1f473-1f3fd", - "name": "person wearing turban: medium skin tone", - "alpha_code": ":person_wearing_turban_tone3:", - "aliases": ":man_with_turban_tone3:" - }, - "1f473-1f3fe": { - "output": "1f473-1f3fe", - "name": "person wearing turban: medium-dark skin tone", - "alpha_code": ":person_wearing_turban_tone4:", - "aliases": ":man_with_turban_tone4:" - }, - "1f473-1f3ff": { - "output": "1f473-1f3ff", - "name": "person wearing turban: dark skin tone", - "alpha_code": ":person_wearing_turban_tone5:", - "aliases": ":man_with_turban_tone5:" - }, - "1f474-1f3fb": { - "output": "1f474-1f3fb", - "name": "old man: light skin tone", - "alpha_code": ":older_man_tone1:", - "aliases": "" - }, - "1f474-1f3fc": { - "output": "1f474-1f3fc", - "name": "old man: medium-light skin tone", - "alpha_code": ":older_man_tone2:", - "aliases": "" - }, - "1f474-1f3fd": { - "output": "1f474-1f3fd", - "name": "old man: medium skin tone", - "alpha_code": ":older_man_tone3:", - "aliases": "" - }, - "1f474-1f3fe": { - "output": "1f474-1f3fe", - "name": "old man: medium-dark skin tone", - "alpha_code": ":older_man_tone4:", - "aliases": "" - }, - "1f474-1f3ff": { - "output": "1f474-1f3ff", - "name": "old man: dark skin tone", - "alpha_code": ":older_man_tone5:", - "aliases": "" - }, - "1f475-1f3fb": { - "output": "1f475-1f3fb", - "name": "old woman: light skin tone", - "alpha_code": ":older_woman_tone1:", - "aliases": ":grandma_tone1:" - }, - "1f475-1f3fc": { - "output": "1f475-1f3fc", - "name": "old woman: medium-light skin tone", - "alpha_code": ":older_woman_tone2:", - "aliases": ":grandma_tone2:" - }, - "1f475-1f3fd": { - "output": "1f475-1f3fd", - "name": "old woman: medium skin tone", - "alpha_code": ":older_woman_tone3:", - "aliases": ":grandma_tone3:" - }, - "1f475-1f3fe": { - "output": "1f475-1f3fe", - "name": "old woman: medium-dark skin tone", - "alpha_code": ":older_woman_tone4:", - "aliases": ":grandma_tone4:" - }, - "1f475-1f3ff": { - "output": "1f475-1f3ff", - "name": "old woman: dark skin tone", - "alpha_code": ":older_woman_tone5:", - "aliases": ":grandma_tone5:" - }, - "1f46e-1f3fb": { - "output": "1f46e-1f3fb", - "name": "police officer: light skin tone", - "alpha_code": ":police_officer_tone1:", - "aliases": ":cop_tone1:" - }, - "1f46e-1f3fc": { - "output": "1f46e-1f3fc", - "name": "police officer: medium-light skin tone", - "alpha_code": ":police_officer_tone2:", - "aliases": ":cop_tone2:" - }, - "1f46e-1f3fd": { - "output": "1f46e-1f3fd", - "name": "police officer: medium skin tone", - "alpha_code": ":police_officer_tone3:", - "aliases": ":cop_tone3:" - }, - "1f46e-1f3fe": { - "output": "1f46e-1f3fe", - "name": "police officer: medium-dark skin tone", - "alpha_code": ":police_officer_tone4:", - "aliases": ":cop_tone4:" - }, - "1f46e-1f3ff": { - "output": "1f46e-1f3ff", - "name": "police officer: dark skin tone", - "alpha_code": ":police_officer_tone5:", - "aliases": ":cop_tone5:" - }, - "1f477-1f3fb": { - "output": "1f477-1f3fb", - "name": "construction worker: light skin tone", - "alpha_code": ":construction_worker_tone1:", - "aliases": "" - }, - "1f477-1f3fc": { - "output": "1f477-1f3fc", - "name": "construction worker: medium-light skin tone", - "alpha_code": ":construction_worker_tone2:", - "aliases": "" - }, - "1f477-1f3fd": { - "output": "1f477-1f3fd", - "name": "construction worker: medium skin tone", - "alpha_code": ":construction_worker_tone3:", - "aliases": "" - }, - "1f477-1f3fe": { - "output": "1f477-1f3fe", - "name": "construction worker: medium-dark skin tone", - "alpha_code": ":construction_worker_tone4:", - "aliases": "" - }, - "1f477-1f3ff": { - "output": "1f477-1f3ff", - "name": "construction worker: dark skin tone", - "alpha_code": ":construction_worker_tone5:", - "aliases": "" - }, - "1f478-1f3fb": { - "output": "1f478-1f3fb", - "name": "princess: light skin tone", - "alpha_code": ":princess_tone1:", - "aliases": "" - }, - "1f478-1f3fc": { - "output": "1f478-1f3fc", - "name": "princess: medium-light skin tone", - "alpha_code": ":princess_tone2:", - "aliases": "" - }, - "1f478-1f3fd": { - "output": "1f478-1f3fd", - "name": "princess: medium skin tone", - "alpha_code": ":princess_tone3:", - "aliases": "" - }, - "1f478-1f3fe": { - "output": "1f478-1f3fe", - "name": "princess: medium-dark skin tone", - "alpha_code": ":princess_tone4:", - "aliases": "" - }, - "1f938-1f3fb": { - "output": "1f938-1f3fb", - "name": "person cartwheeling: light skin tone", - "alpha_code": ":person_doing_cartwheel_tone1:", - "aliases": ":cartwheel_tone1:" - }, - "1f478-1f3ff": { - "output": "1f478-1f3ff", - "name": "princess: dark skin tone", - "alpha_code": ":princess_tone5:", - "aliases": "" - }, - "1f482-1f3fb": { - "output": "1f482-1f3fb", - "name": "guard: light skin tone", - "alpha_code": ":guard_tone1:", - "aliases": ":guardsman_tone1:" - }, - "1f482-1f3fc": { - "output": "1f482-1f3fc", - "name": "guard: medium-light skin tone", - "alpha_code": ":guard_tone2:", - "aliases": ":guardsman_tone2:" - }, - "1f938-1f3fc": { - "output": "1f938-1f3fc", - "name": "person cartwheeling: medium-light skin tone", - "alpha_code": ":person_doing_cartwheel_tone2:", - "aliases": ":cartwheel_tone2:" - }, - "1f482-1f3fd": { - "output": "1f482-1f3fd", - "name": "guard: medium skin tone", - "alpha_code": ":guard_tone3:", - "aliases": ":guardsman_tone3:" - }, - "1f482-1f3fe": { - "output": "1f482-1f3fe", - "name": "guard: medium-dark skin tone", - "alpha_code": ":guard_tone4:", - "aliases": ":guardsman_tone4:" - }, - "1f482-1f3ff": { - "output": "1f482-1f3ff", - "name": "guard: dark skin tone", - "alpha_code": ":guard_tone5:", - "aliases": ":guardsman_tone5:" - }, - "1f938-1f3fd": { - "output": "1f938-1f3fd", - "name": "person cartwheeling: medium skin tone", - "alpha_code": ":person_doing_cartwheel_tone3:", - "aliases": ":cartwheel_tone3:" - }, - "1f47c-1f3fb": { - "output": "1f47c-1f3fb", - "name": "baby angel: light skin tone", - "alpha_code": ":angel_tone1:", - "aliases": "" - }, - "1f47c-1f3fc": { - "output": "1f47c-1f3fc", - "name": "baby angel: medium-light skin tone", - "alpha_code": ":angel_tone2:", - "aliases": "" - }, - "1f47c-1f3fd": { - "output": "1f47c-1f3fd", - "name": "baby angel: medium skin tone", - "alpha_code": ":angel_tone3:", - "aliases": "" - }, - "1f47c-1f3fe": { - "output": "1f47c-1f3fe", - "name": "baby angel: medium-dark skin tone", - "alpha_code": ":angel_tone4:", - "aliases": "" - }, - "1f47c-1f3ff": { - "output": "1f47c-1f3ff", - "name": "baby angel: dark skin tone", - "alpha_code": ":angel_tone5:", - "aliases": "" - }, - "1f647-1f3fb": { - "output": "1f647-1f3fb", - "name": "person bowing: light skin tone", - "alpha_code": ":person_bowing_tone1:", - "aliases": ":bow_tone1:" - }, - "1f647-1f3fc": { - "output": "1f647-1f3fc", - "name": "person bowing: medium-light skin tone", - "alpha_code": ":person_bowing_tone2:", - "aliases": ":bow_tone2:" - }, - "1f647-1f3fd": { - "output": "1f647-1f3fd", - "name": "person bowing: medium skin tone", - "alpha_code": ":person_bowing_tone3:", - "aliases": ":bow_tone3:" - }, - "1f647-1f3fe": { - "output": "1f647-1f3fe", - "name": "person bowing: medium-dark skin tone", - "alpha_code": ":person_bowing_tone4:", - "aliases": ":bow_tone4:" - }, - "1f647-1f3ff": { - "output": "1f647-1f3ff", - "name": "person bowing: dark skin tone", - "alpha_code": ":person_bowing_tone5:", - "aliases": ":bow_tone5:" - }, - "1f481-1f3fb": { - "output": "1f481-1f3fb", - "name": "person tipping hand: light skin tone", - "alpha_code": ":person_tipping_hand_tone1:", - "aliases": ":information_desk_person_tone1:" - }, - "1f481-1f3fc": { - "output": "1f481-1f3fc", - "name": "person tipping hand: medium-light skin tone", - "alpha_code": ":person_tipping_hand_tone2:", - "aliases": ":information_desk_person_tone2:" - }, - "1f481-1f3fd": { - "output": "1f481-1f3fd", - "name": "person tipping hand: medium skin tone", - "alpha_code": ":person_tipping_hand_tone3:", - "aliases": ":information_desk_person_tone3:" - }, - "1f938-1f3fe": { - "output": "1f938-1f3fe", - "name": "person cartwheeling: medium-dark skin tone", - "alpha_code": ":person_doing_cartwheel_tone4:", - "aliases": ":cartwheel_tone4:" - }, - "1f481-1f3fe": { - "output": "1f481-1f3fe", - "name": "person tipping hand: medium-dark skin tone", - "alpha_code": ":person_tipping_hand_tone4:", - "aliases": ":information_desk_person_tone4:" - }, - "1f481-1f3ff": { - "output": "1f481-1f3ff", - "name": "person tipping hand: dark skin tone", - "alpha_code": ":person_tipping_hand_tone5:", - "aliases": ":information_desk_person_tone5:" - }, - "1f645-1f3fb": { - "output": "1f645-1f3fb", - "name": "person gesturing NO: light skin tone", - "alpha_code": ":person_gesturing_no_tone1:", - "aliases": ":no_good_tone1:" - }, - "1f938-1f3ff": { - "output": "1f938-1f3ff", - "name": "person cartwheeling: dark skin tone", - "alpha_code": ":person_doing_cartwheel_tone5:", - "aliases": ":cartwheel_tone5:" - }, - "1f645-1f3fc": { - "output": "1f645-1f3fc", - "name": "person gesturing NO: medium-light skin tone", - "alpha_code": ":person_gesturing_no_tone2:", - "aliases": ":no_good_tone2:" - }, - "1f645-1f3fd": { - "output": "1f645-1f3fd", - "name": "person gesturing NO: medium skin tone", - "alpha_code": ":person_gesturing_no_tone3:", - "aliases": ":no_good_tone3:" - }, - "1f645-1f3fe": { - "output": "1f645-1f3fe", - "name": "person gesturing NO: medium-dark skin tone", - "alpha_code": ":person_gesturing_no_tone4:", - "aliases": ":no_good_tone4:" - }, - "1f645-1f3ff": { - "output": "1f645-1f3ff", - "name": "person gesturing NO: dark skin tone", - "alpha_code": ":person_gesturing_no_tone5:", - "aliases": ":no_good_tone5:" - }, - "1f646-1f3fb": { - "output": "1f646-1f3fb", - "name": "person gesturing OK: light skin tone", - "alpha_code": ":person_gesturing_ok_tone1:", - "aliases": ":ok_woman_tone1:" - }, - "1f646-1f3fc": { - "output": "1f646-1f3fc", - "name": "person gesturing OK: medium-light skin tone", - "alpha_code": ":person_gesturing_ok_tone2:", - "aliases": ":ok_woman_tone2:" - }, - "1f646-1f3fd": { - "output": "1f646-1f3fd", - "name": "person gesturing OK: medium skin tone", - "alpha_code": ":person_gesturing_ok_tone3:", - "aliases": ":ok_woman_tone3:" - }, - "1f646-1f3fe": { - "output": "1f646-1f3fe", - "name": "person gesturing OK: medium-dark skin tone", - "alpha_code": ":person_gesturing_ok_tone4:", - "aliases": ":ok_woman_tone4:" - }, - "1f646-1f3ff": { - "output": "1f646-1f3ff", - "name": "person gesturing OK: dark skin tone", - "alpha_code": ":person_gesturing_ok_tone5:", - "aliases": ":ok_woman_tone5:" - }, - "1f64b-1f3fb": { - "output": "1f64b-1f3fb", - "name": "person raising hand: light skin tone", - "alpha_code": ":person_raising_hand_tone1:", - "aliases": ":raising_hand_tone1:" - }, - "1f64b-1f3fc": { - "output": "1f64b-1f3fc", - "name": "person raising hand: medium-light skin tone", - "alpha_code": ":person_raising_hand_tone2:", - "aliases": ":raising_hand_tone2:" - }, - "1f64b-1f3fd": { - "output": "1f64b-1f3fd", - "name": "person raising hand: medium skin tone", - "alpha_code": ":person_raising_hand_tone3:", - "aliases": ":raising_hand_tone3:" - }, - "1f64b-1f3fe": { - "output": "1f64b-1f3fe", - "name": "person raising hand: medium-dark skin tone", - "alpha_code": ":person_raising_hand_tone4:", - "aliases": ":raising_hand_tone4:" - }, - "1f64b-1f3ff": { - "output": "1f64b-1f3ff", - "name": "person raising hand: dark skin tone", - "alpha_code": ":person_raising_hand_tone5:", - "aliases": ":raising_hand_tone5:" - }, - "1f64e-1f3fb": { - "output": "1f64e-1f3fb", - "name": "person pouting: light skin tone", - "alpha_code": ":person_pouting_tone1:", - "aliases": ":person_with_pouting_face_tone1:" - }, - "1f64e-1f3fc": { - "output": "1f64e-1f3fc", - "name": "person pouting: medium-light skin tone", - "alpha_code": ":person_pouting_tone2:", - "aliases": ":person_with_pouting_face_tone2:" - }, - "1f64e-1f3fd": { - "output": "1f64e-1f3fd", - "name": "person pouting: medium skin tone", - "alpha_code": ":person_pouting_tone3:", - "aliases": ":person_with_pouting_face_tone3:" - }, - "1f64e-1f3fe": { - "output": "1f64e-1f3fe", - "name": "person pouting: medium-dark skin tone", - "alpha_code": ":person_pouting_tone4:", - "aliases": ":person_with_pouting_face_tone4:" - }, - "1f64e-1f3ff": { - "output": "1f64e-1f3ff", - "name": "person pouting: dark skin tone", - "alpha_code": ":person_pouting_tone5:", - "aliases": ":person_with_pouting_face_tone5:" - }, - "1f64d-1f3fb": { - "output": "1f64d-1f3fb", - "name": "person frowning: light skin tone", - "alpha_code": ":person_frowning_tone1:", - "aliases": "" - }, - "1f64d-1f3fc": { - "output": "1f64d-1f3fc", - "name": "person frowning: medium-light skin tone", - "alpha_code": ":person_frowning_tone2:", - "aliases": "" - }, - "1f64d-1f3fd": { - "output": "1f64d-1f3fd", - "name": "person frowning: medium skin tone", - "alpha_code": ":person_frowning_tone3:", - "aliases": "" - }, - "1f64d-1f3fe": { - "output": "1f64d-1f3fe", - "name": "person frowning: medium-dark skin tone", - "alpha_code": ":person_frowning_tone4:", - "aliases": "" - }, - "1f64d-1f3ff": { - "output": "1f64d-1f3ff", - "name": "person frowning: dark skin tone", - "alpha_code": ":person_frowning_tone5:", - "aliases": "" - }, - "1f486-1f3fb": { - "output": "1f486-1f3fb", - "name": "person getting massage: light skin tone", - "alpha_code": ":person_getting_massage_tone1:", - "aliases": ":massage_tone1:" - }, - "1f486-1f3fc": { - "output": "1f486-1f3fc", - "name": "person getting massage: medium-light skin tone", - "alpha_code": ":person_getting_massage_tone2:", - "aliases": ":massage_tone2:" - }, - "1f486-1f3fd": { - "output": "1f486-1f3fd", - "name": "person getting massage: medium skin tone", - "alpha_code": ":person_getting_massage_tone3:", - "aliases": ":massage_tone3:" - }, - "1f486-1f3fe": { - "output": "1f486-1f3fe", - "name": "person getting massage: medium-dark skin tone", - "alpha_code": ":person_getting_massage_tone4:", - "aliases": ":massage_tone4:" - }, - "1f486-1f3ff": { - "output": "1f486-1f3ff", - "name": "person getting massage: dark skin tone", - "alpha_code": ":person_getting_massage_tone5:", - "aliases": ":massage_tone5:" - }, - "1f487-1f3fb": { - "output": "1f487-1f3fb", - "name": "person getting haircut: light skin tone", - "alpha_code": ":person_getting_haircut_tone1:", - "aliases": ":haircut_tone1:" - }, - "1f487-1f3fc": { - "output": "1f487-1f3fc", - "name": "person getting haircut: medium-light skin tone", - "alpha_code": ":person_getting_haircut_tone2:", - "aliases": ":haircut_tone2:" - }, - "1f487-1f3fd": { - "output": "1f487-1f3fd", - "name": "person getting haircut: medium skin tone", - "alpha_code": ":person_getting_haircut_tone3:", - "aliases": ":haircut_tone3:" - }, - "1f487-1f3fe": { - "output": "1f487-1f3fe", - "name": "person getting haircut: medium-dark skin tone", - "alpha_code": ":person_getting_haircut_tone4:", - "aliases": ":haircut_tone4:" - }, - "1f487-1f3ff": { - "output": "1f487-1f3ff", - "name": "person getting haircut: dark skin tone", - "alpha_code": ":person_getting_haircut_tone5:", - "aliases": ":haircut_tone5:" - }, - "1f64c-1f3fb": { - "output": "1f64c-1f3fb", - "name": "raising hands: light skin tone", - "alpha_code": ":raised_hands_tone1:", - "aliases": "" - }, - "1f64c-1f3fc": { - "output": "1f64c-1f3fc", - "name": "raising hands: medium-light skin tone", - "alpha_code": ":raised_hands_tone2:", - "aliases": "" - }, - "1f64c-1f3fd": { - "output": "1f64c-1f3fd", - "name": "raising hands: medium skin tone", - "alpha_code": ":raised_hands_tone3:", - "aliases": "" - }, - "1f64c-1f3fe": { - "output": "1f64c-1f3fe", - "name": "raising hands: medium-dark skin tone", - "alpha_code": ":raised_hands_tone4:", - "aliases": "" - }, - "1f64c-1f3ff": { - "output": "1f64c-1f3ff", - "name": "raising hands: dark skin tone", - "alpha_code": ":raised_hands_tone5:", - "aliases": "" - }, - "1f44f-1f3fb": { - "output": "1f44f-1f3fb", - "name": "clapping hands: light skin tone", - "alpha_code": ":clap_tone1:", - "aliases": "" - }, - "1f44f-1f3fc": { - "output": "1f44f-1f3fc", - "name": "clapping hands: medium-light skin tone", - "alpha_code": ":clap_tone2:", - "aliases": "" - }, - "1f44f-1f3fd": { - "output": "1f44f-1f3fd", - "name": "clapping hands: medium skin tone", - "alpha_code": ":clap_tone3:", - "aliases": "" - }, - "1f93d-1f3fb": { - "output": "1f93d-1f3fb", - "name": "person playing water polo: light skin tone", - "alpha_code": ":person_playing_water_polo_tone1:", - "aliases": ":water_polo_tone1:" - }, - "1f44f-1f3fe": { - "output": "1f44f-1f3fe", - "name": "clapping hands: medium-dark skin tone", - "alpha_code": ":clap_tone4:", - "aliases": "" - }, - "1f44f-1f3ff": { - "output": "1f44f-1f3ff", - "name": "clapping hands: dark skin tone", - "alpha_code": ":clap_tone5:", - "aliases": "" - }, - "1f93d-1f3fc": { - "output": "1f93d-1f3fc", - "name": "person playing water polo: medium-light skin tone", - "alpha_code": ":person_playing_water_polo_tone2:", - "aliases": ":water_polo_tone2:" - }, - "1f442-1f3fb": { - "output": "1f442-1f3fb", - "name": "ear: light skin tone", - "alpha_code": ":ear_tone1:", - "aliases": "" - }, - "1f442-1f3fc": { - "output": "1f442-1f3fc", - "name": "ear: medium-light skin tone", - "alpha_code": ":ear_tone2:", - "aliases": "" - }, - "1f442-1f3fd": { - "output": "1f442-1f3fd", - "name": "ear: medium skin tone", - "alpha_code": ":ear_tone3:", - "aliases": "" - }, - "1f442-1f3fe": { - "output": "1f442-1f3fe", - "name": "ear: medium-dark skin tone", - "alpha_code": ":ear_tone4:", - "aliases": "" - }, - "1f442-1f3ff": { - "output": "1f442-1f3ff", - "name": "ear: dark skin tone", - "alpha_code": ":ear_tone5:", - "aliases": "" - }, - "1f443-1f3fb": { - "output": "1f443-1f3fb", - "name": "nose: light skin tone", - "alpha_code": ":nose_tone1:", - "aliases": "" - }, - "1f443-1f3fc": { - "output": "1f443-1f3fc", - "name": "nose: medium-light skin tone", - "alpha_code": ":nose_tone2:", - "aliases": "" - }, - "1f443-1f3fd": { - "output": "1f443-1f3fd", - "name": "nose: medium skin tone", - "alpha_code": ":nose_tone3:", - "aliases": "" - }, - "1f443-1f3fe": { - "output": "1f443-1f3fe", - "name": "nose: medium-dark skin tone", - "alpha_code": ":nose_tone4:", - "aliases": "" - }, - "1f443-1f3ff": { - "output": "1f443-1f3ff", - "name": "nose: dark skin tone", - "alpha_code": ":nose_tone5:", - "aliases": "" - }, - "1f485-1f3fb": { - "output": "1f485-1f3fb", - "name": "nail polish: light skin tone", - "alpha_code": ":nail_care_tone1:", - "aliases": "" - }, - "1f485-1f3fc": { - "output": "1f485-1f3fc", - "name": "nail polish: medium-light skin tone", - "alpha_code": ":nail_care_tone2:", - "aliases": "" - }, - "1f485-1f3fd": { - "output": "1f485-1f3fd", - "name": "nail polish: medium skin tone", - "alpha_code": ":nail_care_tone3:", - "aliases": "" - }, - "1f485-1f3fe": { - "output": "1f485-1f3fe", - "name": "nail polish: medium-dark skin tone", - "alpha_code": ":nail_care_tone4:", - "aliases": "" - }, - "1f485-1f3ff": { - "output": "1f485-1f3ff", - "name": "nail polish: dark skin tone", - "alpha_code": ":nail_care_tone5:", - "aliases": "" - }, - "1f44b-1f3fb": { - "output": "1f44b-1f3fb", - "name": "waving hand: light skin tone", - "alpha_code": ":wave_tone1:", - "aliases": "" - }, - "1f44b-1f3fc": { - "output": "1f44b-1f3fc", - "name": "waving hand: medium-light skin tone", - "alpha_code": ":wave_tone2:", - "aliases": "" - }, - "1f44b-1f3fd": { - "output": "1f44b-1f3fd", - "name": "waving hand: medium skin tone", - "alpha_code": ":wave_tone3:", - "aliases": "" - }, - "1f44b-1f3fe": { - "output": "1f44b-1f3fe", - "name": "waving hand: medium-dark skin tone", - "alpha_code": ":wave_tone4:", - "aliases": "" - }, - "1f44b-1f3ff": { - "output": "1f44b-1f3ff", - "name": "waving hand: dark skin tone", - "alpha_code": ":wave_tone5:", - "aliases": "" - }, - "1f44d-1f3fb": { - "output": "1f44d-1f3fb", - "name": "thumbs up: light skin tone", - "alpha_code": ":thumbsup_tone1:", - "aliases": ":+1_tone1:|:thumbup_tone1:" - }, - "1f44d-1f3fc": { - "output": "1f44d-1f3fc", - "name": "thumbs up: medium-light skin tone", - "alpha_code": ":thumbsup_tone2:", - "aliases": ":+1_tone2:|:thumbup_tone2:" - }, - "1f44d-1f3fd": { - "output": "1f44d-1f3fd", - "name": "thumbs up: medium skin tone", - "alpha_code": ":thumbsup_tone3:", - "aliases": ":+1_tone3:|:thumbup_tone3:" - }, - "1f44d-1f3fe": { - "output": "1f44d-1f3fe", - "name": "thumbs up: medium-dark skin tone", - "alpha_code": ":thumbsup_tone4:", - "aliases": ":+1_tone4:|:thumbup_tone4:" - }, - "1f44d-1f3ff": { - "output": "1f44d-1f3ff", - "name": "thumbs up: dark skin tone", - "alpha_code": ":thumbsup_tone5:", - "aliases": ":+1_tone5:|:thumbup_tone5:" - }, - "1f44e-1f3fb": { - "output": "1f44e-1f3fb", - "name": "thumbs down: light skin tone", - "alpha_code": ":thumbsdown_tone1:", - "aliases": ":-1_tone1:|:thumbdown_tone1:" - }, - "1f44e-1f3fc": { - "output": "1f44e-1f3fc", - "name": "thumbs down: medium-light skin tone", - "alpha_code": ":thumbsdown_tone2:", - "aliases": ":-1_tone2:|:thumbdown_tone2:" - }, - "1f44e-1f3fd": { - "output": "1f44e-1f3fd", - "name": "thumbs down: medium skin tone", - "alpha_code": ":thumbsdown_tone3:", - "aliases": ":-1_tone3:|:thumbdown_tone3:" - }, - "1f44e-1f3fe": { - "output": "1f44e-1f3fe", - "name": "thumbs down: medium-dark skin tone", - "alpha_code": ":thumbsdown_tone4:", - "aliases": ":-1_tone4:|:thumbdown_tone4:" - }, - "1f44e-1f3ff": { - "output": "1f44e-1f3ff", - "name": "thumbs down: dark skin tone", - "alpha_code": ":thumbsdown_tone5:", - "aliases": ":-1_tone5:|:thumbdown_tone5:" - }, - "261d-1f3fb": { - "output": "261d-1f3fb", - "name": "index pointing up: light skin tone", - "alpha_code": ":point_up_tone1:", - "aliases": "" - }, - "261d-1f3fc": { - "output": "261d-1f3fc", - "name": "index pointing up: medium-light skin tone", - "alpha_code": ":point_up_tone2:", - "aliases": "" - }, - "261d-1f3fd": { - "output": "261d-1f3fd", - "name": "index pointing up: medium skin tone", - "alpha_code": ":point_up_tone3:", - "aliases": "" - }, - "261d-1f3fe": { - "output": "261d-1f3fe", - "name": "index pointing up: medium-dark skin tone", - "alpha_code": ":point_up_tone4:", - "aliases": "" - }, - "261d-1f3ff": { - "output": "261d-1f3ff", - "name": "index pointing up: dark skin tone", - "alpha_code": ":point_up_tone5:", - "aliases": "" - }, - "1f446-1f3fb": { - "output": "1f446-1f3fb", - "name": "backhand index pointing up: light skin tone", - "alpha_code": ":point_up_2_tone1:", - "aliases": "" - }, - "1f446-1f3fc": { - "output": "1f446-1f3fc", - "name": "backhand index pointing up: medium-light skin tone", - "alpha_code": ":point_up_2_tone2:", - "aliases": "" - }, - "1f446-1f3fd": { - "output": "1f446-1f3fd", - "name": "backhand index pointing up: medium skin tone", - "alpha_code": ":point_up_2_tone3:", - "aliases": "" - }, - "1f446-1f3fe": { - "output": "1f446-1f3fe", - "name": "backhand index pointing up: medium-dark skin tone", - "alpha_code": ":point_up_2_tone4:", - "aliases": "" - }, - "1f446-1f3ff": { - "output": "1f446-1f3ff", - "name": "backhand index pointing up: dark skin tone", - "alpha_code": ":point_up_2_tone5:", - "aliases": "" - }, - "1f447-1f3fb": { - "output": "1f447-1f3fb", - "name": "backhand index pointing down: light skin tone", - "alpha_code": ":point_down_tone1:", - "aliases": "" - }, - "1f447-1f3fc": { - "output": "1f447-1f3fc", - "name": "backhand index pointing down: medium-light skin tone", - "alpha_code": ":point_down_tone2:", - "aliases": "" - }, - "1f447-1f3fd": { - "output": "1f447-1f3fd", - "name": "backhand index pointing down: medium skin tone", - "alpha_code": ":point_down_tone3:", - "aliases": "" - }, - "1f447-1f3fe": { - "output": "1f447-1f3fe", - "name": "backhand index pointing down: medium-dark skin tone", - "alpha_code": ":point_down_tone4:", - "aliases": "" - }, - "1f447-1f3ff": { - "output": "1f447-1f3ff", - "name": "backhand index pointing down: dark skin tone", - "alpha_code": ":point_down_tone5:", - "aliases": "" - }, - "1f448-1f3fb": { - "output": "1f448-1f3fb", - "name": "backhand index pointing left: light skin tone", - "alpha_code": ":point_left_tone1:", - "aliases": "" - }, - "1f448-1f3fc": { - "output": "1f448-1f3fc", - "name": "backhand index pointing left: medium-light skin tone", - "alpha_code": ":point_left_tone2:", - "aliases": "" - }, - "1f448-1f3fd": { - "output": "1f448-1f3fd", - "name": "backhand index pointing left: medium skin tone", - "alpha_code": ":point_left_tone3:", - "aliases": "" - }, - "1f448-1f3fe": { - "output": "1f448-1f3fe", - "name": "backhand index pointing left: medium-dark skin tone", - "alpha_code": ":point_left_tone4:", - "aliases": "" - }, - "1f448-1f3ff": { - "output": "1f448-1f3ff", - "name": "backhand index pointing left: dark skin tone", - "alpha_code": ":point_left_tone5:", - "aliases": "" - }, - "1f449-1f3fb": { - "output": "1f449-1f3fb", - "name": "backhand index pointing right: light skin tone", - "alpha_code": ":point_right_tone1:", - "aliases": "" - }, - "1f449-1f3fc": { - "output": "1f449-1f3fc", - "name": "backhand index pointing right: medium-light skin tone", - "alpha_code": ":point_right_tone2:", - "aliases": "" - }, - "1f449-1f3fd": { - "output": "1f449-1f3fd", - "name": "backhand index pointing right: medium skin tone", - "alpha_code": ":point_right_tone3:", - "aliases": "" - }, - "1f449-1f3fe": { - "output": "1f449-1f3fe", - "name": "backhand index pointing right: medium-dark skin tone", - "alpha_code": ":point_right_tone4:", - "aliases": "" - }, - "1f449-1f3ff": { - "output": "1f449-1f3ff", - "name": "backhand index pointing right: dark skin tone", - "alpha_code": ":point_right_tone5:", - "aliases": "" - }, - "1f44c-1f3fb": { - "output": "1f44c-1f3fb", - "name": "OK hand: light skin tone", - "alpha_code": ":ok_hand_tone1:", - "aliases": "" - }, - "1f44c-1f3fc": { - "output": "1f44c-1f3fc", - "name": "OK hand: medium-light skin tone", - "alpha_code": ":ok_hand_tone2:", - "aliases": "" - }, - "1f93d-1f3fd": { - "output": "1f93d-1f3fd", - "name": "person playing water polo: medium skin tone", - "alpha_code": ":person_playing_water_polo_tone3:", - "aliases": ":water_polo_tone3:" - }, - "1f44c-1f3fd": { - "output": "1f44c-1f3fd", - "name": "OK hand: medium skin tone", - "alpha_code": ":ok_hand_tone3:", - "aliases": "" - }, - "1f44c-1f3fe": { - "output": "1f44c-1f3fe", - "name": "OK hand: medium-dark skin tone", - "alpha_code": ":ok_hand_tone4:", - "aliases": "" - }, - "1f93d-1f3fe": { - "output": "1f93d-1f3fe", - "name": "person playing water polo: medium-dark skin tone", - "alpha_code": ":person_playing_water_polo_tone4:", - "aliases": ":water_polo_tone4:" - }, - "1f44c-1f3ff": { - "output": "1f44c-1f3ff", - "name": "OK hand: dark skin tone", - "alpha_code": ":ok_hand_tone5:", - "aliases": "" - }, - "270c-1f3fb": { - "output": "270c-1f3fb", - "name": "victory hand: light skin tone", - "alpha_code": ":v_tone1:", - "aliases": "" - }, - "270c-1f3fc": { - "output": "270c-1f3fc", - "name": "victory hand: medium-light skin tone", - "alpha_code": ":v_tone2:", - "aliases": "" - }, - "270c-1f3fd": { - "output": "270c-1f3fd", - "name": "victory hand: medium skin tone", - "alpha_code": ":v_tone3:", - "aliases": "" - }, - "270c-1f3fe": { - "output": "270c-1f3fe", - "name": "victory hand: medium-dark skin tone", - "alpha_code": ":v_tone4:", - "aliases": "" - }, - "270c-1f3ff": { - "output": "270c-1f3ff", - "name": "victory hand: dark skin tone", - "alpha_code": ":v_tone5:", - "aliases": "" - }, - "1f44a-1f3fb": { - "output": "1f44a-1f3fb", - "name": "oncoming fist: light skin tone", - "alpha_code": ":punch_tone1:", - "aliases": "" - }, - "1f44a-1f3fc": { - "output": "1f44a-1f3fc", - "name": "oncoming fist: medium-light skin tone", - "alpha_code": ":punch_tone2:", - "aliases": "" - }, - "1f44a-1f3fd": { - "output": "1f44a-1f3fd", - "name": "oncoming fist: medium skin tone", - "alpha_code": ":punch_tone3:", - "aliases": "" - }, - "1f44a-1f3fe": { - "output": "1f44a-1f3fe", - "name": "oncoming fist: medium-dark skin tone", - "alpha_code": ":punch_tone4:", - "aliases": "" - }, - "1f44a-1f3ff": { - "output": "1f44a-1f3ff", - "name": "oncoming fist: dark skin tone", - "alpha_code": ":punch_tone5:", - "aliases": "" - }, - "270a-1f3fb": { - "output": "270a-1f3fb", - "name": "raised fist: light skin tone", - "alpha_code": ":fist_tone1:", - "aliases": "" - }, - "270a-1f3fc": { - "output": "270a-1f3fc", - "name": "raised fist: medium-light skin tone", - "alpha_code": ":fist_tone2:", - "aliases": "" - }, - "270a-1f3fd": { - "output": "270a-1f3fd", - "name": "raised fist: medium skin tone", - "alpha_code": ":fist_tone3:", - "aliases": "" - }, - "270a-1f3fe": { - "output": "270a-1f3fe", - "name": "raised fist: medium-dark skin tone", - "alpha_code": ":fist_tone4:", - "aliases": "" - }, - "270a-1f3ff": { - "output": "270a-1f3ff", - "name": "raised fist: dark skin tone", - "alpha_code": ":fist_tone5:", - "aliases": "" - }, - "270b-1f3fb": { - "output": "270b-1f3fb", - "name": "raised hand: light skin tone", - "alpha_code": ":raised_hand_tone1:", - "aliases": "" - }, - "270b-1f3fc": { - "output": "270b-1f3fc", - "name": "raised hand: medium-light skin tone", - "alpha_code": ":raised_hand_tone2:", - "aliases": "" - }, - "270b-1f3fd": { - "output": "270b-1f3fd", - "name": "raised hand: medium skin tone", - "alpha_code": ":raised_hand_tone3:", - "aliases": "" - }, - "270b-1f3fe": { - "output": "270b-1f3fe", - "name": "raised hand: medium-dark skin tone", - "alpha_code": ":raised_hand_tone4:", - "aliases": "" - }, - "270b-1f3ff": { - "output": "270b-1f3ff", - "name": "raised hand: dark skin tone", - "alpha_code": ":raised_hand_tone5:", - "aliases": "" - }, - "1f4aa-1f3fb": { - "output": "1f4aa-1f3fb", - "name": "flexed biceps: light skin tone", - "alpha_code": ":muscle_tone1:", - "aliases": "" - }, - "1f4aa-1f3fc": { - "output": "1f4aa-1f3fc", - "name": "flexed biceps: medium-light skin tone", - "alpha_code": ":muscle_tone2:", - "aliases": "" - }, - "1f4aa-1f3fd": { - "output": "1f4aa-1f3fd", - "name": "flexed biceps: medium skin tone", - "alpha_code": ":muscle_tone3:", - "aliases": "" - }, - "1f4aa-1f3fe": { - "output": "1f4aa-1f3fe", - "name": "flexed biceps: medium-dark skin tone", - "alpha_code": ":muscle_tone4:", - "aliases": "" - }, - "1f4aa-1f3ff": { - "output": "1f4aa-1f3ff", - "name": "flexed biceps: dark skin tone", - "alpha_code": ":muscle_tone5:", - "aliases": "" - }, - "1f450-1f3fb": { - "output": "1f450-1f3fb", - "name": "open hands: light skin tone", - "alpha_code": ":open_hands_tone1:", - "aliases": "" - }, - "1f450-1f3fc": { - "output": "1f450-1f3fc", - "name": "open hands: medium-light skin tone", - "alpha_code": ":open_hands_tone2:", - "aliases": "" - }, - "1f450-1f3fd": { - "output": "1f450-1f3fd", - "name": "open hands: medium skin tone", - "alpha_code": ":open_hands_tone3:", - "aliases": "" - }, - "1f450-1f3fe": { - "output": "1f450-1f3fe", - "name": "open hands: medium-dark skin tone", - "alpha_code": ":open_hands_tone4:", - "aliases": "" - }, - "1f450-1f3ff": { - "output": "1f450-1f3ff", - "name": "open hands: dark skin tone", - "alpha_code": ":open_hands_tone5:", - "aliases": "" - }, - "1f64f-1f3fb": { - "output": "1f64f-1f3fb", - "name": "folded hands: light skin tone", - "alpha_code": ":pray_tone1:", - "aliases": "" - }, - "1f93d-1f3ff": { - "output": "1f93d-1f3ff", - "name": "person playing water polo: dark skin tone", - "alpha_code": ":person_playing_water_polo_tone5:", - "aliases": ":water_polo_tone5:" - }, - "1f64f-1f3fc": { - "output": "1f64f-1f3fc", - "name": "folded hands: medium-light skin tone", - "alpha_code": ":pray_tone2:", - "aliases": "" - }, - "1f64f-1f3fd": { - "output": "1f64f-1f3fd", - "name": "folded hands: medium skin tone", - "alpha_code": ":pray_tone3:", - "aliases": "" - }, - "1f93e-1f3fb": { - "output": "1f93e-1f3fb", - "name": "person playing handball: light skin tone", - "alpha_code": ":person_playing_handball_tone1:", - "aliases": ":handball_tone1:" - }, - "1f64f-1f3fe": { - "output": "1f64f-1f3fe", - "name": "folded hands: medium-dark skin tone", - "alpha_code": ":pray_tone4:", - "aliases": "" - }, - "1f64f-1f3ff": { - "output": "1f64f-1f3ff", - "name": "folded hands: dark skin tone", - "alpha_code": ":pray_tone5:", - "aliases": "" - }, - "1f93e-1f3fc": { - "output": "1f93e-1f3fc", - "name": "person playing handball: medium-light skin tone", - "alpha_code": ":person_playing_handball_tone2:", - "aliases": ":handball_tone2:" - }, - "1f3c3-1f3fb": { - "output": "1f3c3-1f3fb", - "name": "person running: light skin tone", - "alpha_code": ":person_running_tone1:", - "aliases": ":runner_tone1:" - }, - "1f3c3-1f3fc": { - "output": "1f3c3-1f3fc", - "name": "person running: medium-light skin tone", - "alpha_code": ":person_running_tone2:", - "aliases": ":runner_tone2:" - }, - "1f3c3-1f3fd": { - "output": "1f3c3-1f3fd", - "name": "person running: medium skin tone", - "alpha_code": ":person_running_tone3:", - "aliases": ":runner_tone3:" - }, - "1f3c3-1f3fe": { - "output": "1f3c3-1f3fe", - "name": "person running: medium-dark skin tone", - "alpha_code": ":person_running_tone4:", - "aliases": ":runner_tone4:" - }, - "1f93e-1f3fd": { - "output": "1f93e-1f3fd", - "name": "person playing handball: medium skin tone", - "alpha_code": ":person_playing_handball_tone3:", - "aliases": ":handball_tone3:" - }, - "1f3c3-1f3ff": { - "output": "1f3c3-1f3ff", - "name": "person running: dark skin tone", - "alpha_code": ":person_running_tone5:", - "aliases": ":runner_tone5:" - }, - "1f6b6-1f3fb": { - "output": "1f6b6-1f3fb", - "name": "person walking: light skin tone", - "alpha_code": ":person_walking_tone1:", - "aliases": ":walking_tone1:" - }, - "1f6b6-1f3fc": { - "output": "1f6b6-1f3fc", - "name": "person walking: medium-light skin tone", - "alpha_code": ":person_walking_tone2:", - "aliases": ":walking_tone2:" - }, - "1f6b6-1f3fd": { - "output": "1f6b6-1f3fd", - "name": "person walking: medium skin tone", - "alpha_code": ":person_walking_tone3:", - "aliases": ":walking_tone3:" - }, - "1f6b6-1f3fe": { - "output": "1f6b6-1f3fe", - "name": "person walking: medium-dark skin tone", - "alpha_code": ":person_walking_tone4:", - "aliases": ":walking_tone4:" - }, - "1f6b6-1f3ff": { - "output": "1f6b6-1f3ff", - "name": "person walking: dark skin tone", - "alpha_code": ":person_walking_tone5:", - "aliases": ":walking_tone5:" - }, - "1f483-1f3fb": { - "output": "1f483-1f3fb", - "name": "woman dancing: light skin tone", - "alpha_code": ":dancer_tone1:", - "aliases": "" - }, - "1f93e-1f3fe": { - "output": "1f93e-1f3fe", - "name": "person playing handball: medium-dark skin tone", - "alpha_code": ":person_playing_handball_tone4:", - "aliases": ":handball_tone4:" - }, - "1f483-1f3fc": { - "output": "1f483-1f3fc", - "name": "woman dancing: medium-light skin tone", - "alpha_code": ":dancer_tone2:", - "aliases": "" - }, - "1f483-1f3fd": { - "output": "1f483-1f3fd", - "name": "woman dancing: medium skin tone", - "alpha_code": ":dancer_tone3:", - "aliases": "" - }, - "1f93e-1f3ff": { - "output": "1f93e-1f3ff", - "name": "person playing handball: dark skin tone", - "alpha_code": ":person_playing_handball_tone5:", - "aliases": ":handball_tone5:" - }, - "1f483-1f3fe": { - "output": "1f483-1f3fe", - "name": "woman dancing: medium-dark skin tone", - "alpha_code": ":dancer_tone4:", - "aliases": "" - }, - "1f483-1f3ff": { - "output": "1f483-1f3ff", - "name": "woman dancing: dark skin tone", - "alpha_code": ":dancer_tone5:", - "aliases": "" - }, - "1f939-1f3fb": { - "output": "1f939-1f3fb", - "name": "person juggling: light skin tone", - "alpha_code": ":person_juggling_tone1:", - "aliases": ":juggling_tone1:|:juggler_tone1:" - }, - "1f6a3-1f3fb": { - "output": "1f6a3-1f3fb", - "name": "person rowing boat: light skin tone", - "alpha_code": ":person_rowing_boat_tone1:", - "aliases": ":rowboat_tone1:" - }, - "1f6a3-1f3fc": { - "output": "1f6a3-1f3fc", - "name": "person rowing boat: medium-light skin tone", - "alpha_code": ":person_rowing_boat_tone2:", - "aliases": ":rowboat_tone2:" - }, - "1f6a3-1f3fd": { - "output": "1f6a3-1f3fd", - "name": "person rowing boat: medium skin tone", - "alpha_code": ":person_rowing_boat_tone3:", - "aliases": ":rowboat_tone3:" - }, - "1f6a3-1f3fe": { - "output": "1f6a3-1f3fe", - "name": "person rowing boat: medium-dark skin tone", - "alpha_code": ":person_rowing_boat_tone4:", - "aliases": ":rowboat_tone4:" - }, - "1f6a3-1f3ff": { - "output": "1f6a3-1f3ff", - "name": "person rowing boat: dark skin tone", - "alpha_code": ":person_rowing_boat_tone5:", - "aliases": ":rowboat_tone5:" - }, - "1f3ca-1f3fb": { - "output": "1f3ca-1f3fb", - "name": "person swimming: light skin tone", - "alpha_code": ":person_swimming_tone1:", - "aliases": ":swimmer_tone1:" - }, - "1f3ca-1f3fc": { - "output": "1f3ca-1f3fc", - "name": "person swimming: medium-light skin tone", - "alpha_code": ":person_swimming_tone2:", - "aliases": ":swimmer_tone2:" - }, - "1f939-1f3fc": { - "output": "1f939-1f3fc", - "name": "person juggling: medium-light skin tone", - "alpha_code": ":person_juggling_tone2:", - "aliases": ":juggling_tone2:|:juggler_tone2:" - }, - "1f3ca-1f3fd": { - "output": "1f3ca-1f3fd", - "name": "person swimming: medium skin tone", - "alpha_code": ":person_swimming_tone3:", - "aliases": ":swimmer_tone3:" - }, - "1f3ca-1f3fe": { - "output": "1f3ca-1f3fe", - "name": "person swimming: medium-dark skin tone", - "alpha_code": ":person_swimming_tone4:", - "aliases": ":swimmer_tone4:" - }, - "1f3ca-1f3ff": { - "output": "1f3ca-1f3ff", - "name": "person swimming: dark skin tone", - "alpha_code": ":person_swimming_tone5:", - "aliases": ":swimmer_tone5:" - }, - "1f939-1f3fd": { - "output": "1f939-1f3fd", - "name": "person juggling: medium skin tone", - "alpha_code": ":person_juggling_tone3:", - "aliases": ":juggling_tone3:|:juggler_tone3:" - }, - "1f3c4-1f3fb": { - "output": "1f3c4-1f3fb", - "name": "person surfing: light skin tone", - "alpha_code": ":person_surfing_tone1:", - "aliases": ":surfer_tone1:" - }, - "1f3c4-1f3fc": { - "output": "1f3c4-1f3fc", - "name": "person surfing: medium-light skin tone", - "alpha_code": ":person_surfing_tone2:", - "aliases": ":surfer_tone2:" - }, - "1f3c4-1f3fd": { - "output": "1f3c4-1f3fd", - "name": "person surfing: medium skin tone", - "alpha_code": ":person_surfing_tone3:", - "aliases": ":surfer_tone3:" - }, - "1f3c4-1f3fe": { - "output": "1f3c4-1f3fe", - "name": "person surfing: medium-dark skin tone", - "alpha_code": ":person_surfing_tone4:", - "aliases": ":surfer_tone4:" - }, - "1f3c4-1f3ff": { - "output": "1f3c4-1f3ff", - "name": "person surfing: dark skin tone", - "alpha_code": ":person_surfing_tone5:", - "aliases": ":surfer_tone5:" - }, - "1f6c0-1f3fb": { - "output": "1f6c0-1f3fb", - "name": "person taking bath: light skin tone", - "alpha_code": ":bath_tone1:", - "aliases": "" - }, - "1f6c0-1f3fc": { - "output": "1f6c0-1f3fc", - "name": "person taking bath: medium-light skin tone", - "alpha_code": ":bath_tone2:", - "aliases": "" - }, - "1f939-1f3fe": { - "output": "1f939-1f3fe", - "name": "person juggling: medium-dark skin tone", - "alpha_code": ":person_juggling_tone4:", - "aliases": ":juggling_tone4:|:juggler_tone4:" - }, - "1f6c0-1f3fd": { - "output": "1f6c0-1f3fd", - "name": "person taking bath: medium skin tone", - "alpha_code": ":bath_tone3:", - "aliases": "" - }, - "1f6c0-1f3fe": { - "output": "1f6c0-1f3fe", - "name": "person taking bath: medium-dark skin tone", - "alpha_code": ":bath_tone4:", - "aliases": "" - }, - "1f6c0-1f3ff": { - "output": "1f6c0-1f3ff", - "name": "person taking bath: dark skin tone", - "alpha_code": ":bath_tone5:", - "aliases": "" - }, - "1f939-1f3ff": { - "output": "1f939-1f3ff", - "name": "person juggling: dark skin tone", - "alpha_code": ":person_juggling_tone5:", - "aliases": ":juggling_tone5:|:juggler_tone5:" - }, - "1f6b4-1f3fb": { - "output": "1f6b4-1f3fb", - "name": "person biking: light skin tone", - "alpha_code": ":person_biking_tone1:", - "aliases": ":bicyclist_tone1:" - }, - "1f6b4-1f3fc": { - "output": "1f6b4-1f3fc", - "name": "person biking: medium-light skin tone", - "alpha_code": ":person_biking_tone2:", - "aliases": ":bicyclist_tone2:" - }, - "1f6b4-1f3fd": { - "output": "1f6b4-1f3fd", - "name": "person biking: medium skin tone", - "alpha_code": ":person_biking_tone3:", - "aliases": ":bicyclist_tone3:" - }, - "1f3f3-1f308": { - "output": "1f3f3-fe0f-200d-1f308", - "name": "rainbow flag", - "alpha_code": ":rainbow_flag:", - "aliases": ":gay_pride_flag:" - }, - "1f6b4-1f3fe": { - "output": "1f6b4-1f3fe", - "name": "person biking: medium-dark skin tone", - "alpha_code": ":person_biking_tone4:", - "aliases": ":bicyclist_tone4:" - }, - "1f6b4-1f3ff": { - "output": "1f6b4-1f3ff", - "name": "person biking: dark skin tone", - "alpha_code": ":person_biking_tone5:", - "aliases": ":bicyclist_tone5:" - }, - "1f6b5-1f3fb": { - "output": "1f6b5-1f3fb", - "name": "person mountain biking: light skin tone", - "alpha_code": ":person_mountain_biking_tone1:", - "aliases": ":mountain_bicyclist_tone1:" - }, - "1f6b5-1f3fc": { - "output": "1f6b5-1f3fc", - "name": "person mountain biking: medium-light skin tone", - "alpha_code": ":person_mountain_biking_tone2:", - "aliases": ":mountain_bicyclist_tone2:" - }, - "1f6b5-1f3fd": { - "output": "1f6b5-1f3fd", - "name": "person mountain biking: medium skin tone", - "alpha_code": ":person_mountain_biking_tone3:", - "aliases": ":mountain_bicyclist_tone3:" - }, - "1f6b5-1f3fe": { - "output": "1f6b5-1f3fe", - "name": "person mountain biking: medium-dark skin tone", - "alpha_code": ":person_mountain_biking_tone4:", - "aliases": ":mountain_bicyclist_tone4:" - }, - "1f6b5-1f3ff": { - "output": "1f6b5-1f3ff", - "name": "person mountain biking: dark skin tone", - "alpha_code": ":person_mountain_biking_tone5:", - "aliases": ":mountain_bicyclist_tone5:" - }, - "1f3c7-1f3fb": { - "output": "1f3c7-1f3fb", - "name": "horse racing: light skin tone", - "alpha_code": ":horse_racing_tone1:", - "aliases": "" - }, - "1f3c7-1f3fc": { - "output": "1f3c7-1f3fc", - "name": "horse racing: medium-light skin tone", - "alpha_code": ":horse_racing_tone2:", - "aliases": "" - }, - "1f3c7-1f3fd": { - "output": "1f3c7-1f3fd", - "name": "horse racing: medium skin tone", - "alpha_code": ":horse_racing_tone3:", - "aliases": "" - }, - "1f3c7-1f3fe": { - "output": "1f3c7-1f3fe", - "name": "horse racing: medium-dark skin tone", - "alpha_code": ":horse_racing_tone4:", - "aliases": "" - }, - "1f3c7-1f3ff": { - "output": "1f3c7-1f3ff", - "name": "horse racing: dark skin tone", - "alpha_code": ":horse_racing_tone5:", - "aliases": "" - }, - "270d-1f3fb": { - "output": "270d-1f3fb", - "name": "writing hand: light skin tone", - "alpha_code": ":writing_hand_tone1:", - "aliases": "" - }, - "270d-1f3fc": { - "output": "270d-1f3fc", - "name": "writing hand: medium-light skin tone", - "alpha_code": ":writing_hand_tone2:", - "aliases": "" - }, - "270d-1f3fd": { - "output": "270d-1f3fd", - "name": "writing hand: medium skin tone", - "alpha_code": ":writing_hand_tone3:", - "aliases": "" - }, - "270d-1f3fe": { - "output": "270d-1f3fe", - "name": "writing hand: medium-dark skin tone", - "alpha_code": ":writing_hand_tone4:", - "aliases": "" - }, - "270d-1f3ff": { - "output": "270d-1f3ff", - "name": "writing hand: dark skin tone", - "alpha_code": ":writing_hand_tone5:", - "aliases": "" - }, - "1f590-1f3fb": { - "output": "1f590-1f3fb", - "name": "raised hand with fingers splayed: light skin tone", - "alpha_code": ":hand_splayed_tone1:", - "aliases": ":raised_hand_with_fingers_splayed_tone1:" - }, - "1f590-1f3fc": { - "output": "1f590-1f3fc", - "name": "raised hand with fingers splayed: medium-light skin tone", - "alpha_code": ":hand_splayed_tone2:", - "aliases": ":raised_hand_with_fingers_splayed_tone2:" - }, - "1f590-1f3fd": { - "output": "1f590-1f3fd", - "name": "raised hand with fingers splayed: medium skin tone", - "alpha_code": ":hand_splayed_tone3:", - "aliases": ":raised_hand_with_fingers_splayed_tone3:" - }, - "1f590-1f3fe": { - "output": "1f590-1f3fe", - "name": "raised hand with fingers splayed: medium-dark skin tone", - "alpha_code": ":hand_splayed_tone4:", - "aliases": ":raised_hand_with_fingers_splayed_tone4:" - }, - "1f590-1f3ff": { - "output": "1f590-1f3ff", - "name": "raised hand with fingers splayed: dark skin tone", - "alpha_code": ":hand_splayed_tone5:", - "aliases": ":raised_hand_with_fingers_splayed_tone5:" - }, - "1f595-1f3fb": { - "output": "1f595-1f3fb", - "name": "middle finger: light skin tone", - "alpha_code": ":middle_finger_tone1:", - "aliases": ":reversed_hand_with_middle_finger_extended_tone1:" - }, - "1f595-1f3fc": { - "output": "1f595-1f3fc", - "name": "middle finger: medium-light skin tone", - "alpha_code": ":middle_finger_tone2:", - "aliases": ":reversed_hand_with_middle_finger_extended_tone2:" - }, - "1f595-1f3fd": { - "output": "1f595-1f3fd", - "name": "middle finger: medium skin tone", - "alpha_code": ":middle_finger_tone3:", - "aliases": ":reversed_hand_with_middle_finger_extended_tone3:" - }, - "1f595-1f3fe": { - "output": "1f595-1f3fe", - "name": "middle finger: medium-dark skin tone", - "alpha_code": ":middle_finger_tone4:", - "aliases": ":reversed_hand_with_middle_finger_extended_tone4:" - }, - "1f595-1f3ff": { - "output": "1f595-1f3ff", - "name": "middle finger: dark skin tone", - "alpha_code": ":middle_finger_tone5:", - "aliases": ":reversed_hand_with_middle_finger_extended_tone5:" - }, - "1f596-1f3fb": { - "output": "1f596-1f3fb", - "name": "vulcan salute: light skin tone", - "alpha_code": ":vulcan_tone1:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers_tone1:" - }, - "1f596-1f3fc": { - "output": "1f596-1f3fc", - "name": "vulcan salute: medium-light skin tone", - "alpha_code": ":vulcan_tone2:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers_tone2:" - }, - "1f596-1f3fd": { - "output": "1f596-1f3fd", - "name": "vulcan salute: medium skin tone", - "alpha_code": ":vulcan_tone3:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers_tone3:" - }, - "1f596-1f3fe": { - "output": "1f596-1f3fe", - "name": "vulcan salute: medium-dark skin tone", - "alpha_code": ":vulcan_tone4:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers_tone4:" - }, - "1f596-1f3ff": { - "output": "1f596-1f3ff", - "name": "vulcan salute: dark skin tone", - "alpha_code": ":vulcan_tone5:", - "aliases": ":raised_hand_with_part_between_middle_and_ring_fingers_tone5:" - }, - "1f468-1f468-1f466": { - "output": "1f468-200d-1f468-200d-1f466", - "name": "family: man, man, boy", - "alpha_code": ":family_mmb:", - "aliases": "" - }, - "1f468-1f468-1f466-1f466": { - "output": "1f468-200d-1f468-200d-1f466-200d-1f466", - "name": "family: man, man, boy, boy", - "alpha_code": ":family_mmbb:", - "aliases": "" - }, - "1f468-1f468-1f467": { - "output": "1f468-200d-1f468-200d-1f467", - "name": "family: man, man, girl", - "alpha_code": ":family_mmg:", - "aliases": "" - }, - "1f468-1f468-1f467-1f466": { - "output": "1f468-200d-1f468-200d-1f467-200d-1f466", - "name": "family: man, man, girl, boy", - "alpha_code": ":family_mmgb:", - "aliases": "" - }, - "1f468-1f468-1f467-1f467": { - "output": "1f468-200d-1f468-200d-1f467-200d-1f467", - "name": "family: man, man, girl, girl", - "alpha_code": ":family_mmgg:", - "aliases": "" - }, - "1f468-1f469-1f466-1f466": { - "output": "1f468-200d-1f469-200d-1f466-200d-1f466", - "name": "family: man, woman, boy, boy", - "alpha_code": ":family_mwbb:", - "aliases": "" - }, - "1f468-1f469-1f467": { - "output": "1f468-200d-1f469-200d-1f467", - "name": "family: man, woman, girl", - "alpha_code": ":family_mwg:", - "aliases": "" - }, - "1f468-1f469-1f467-1f466": { - "output": "1f468-200d-1f469-200d-1f467-200d-1f466", - "name": "family: man, woman, girl, boy", - "alpha_code": ":family_mwgb:", - "aliases": "" - }, - "1f468-1f469-1f467-1f467": { - "output": "1f468-200d-1f469-200d-1f467-200d-1f467", - "name": "family: man, woman, girl, girl", - "alpha_code": ":family_mwgg:", - "aliases": "" - }, - "1f469-1f469-1f466": { - "output": "1f469-200d-1f469-200d-1f466", - "name": "family: woman, woman, boy", - "alpha_code": ":family_wwb:", - "aliases": "" - }, - "1f469-1f469-1f466-1f466": { - "output": "1f469-200d-1f469-200d-1f466-200d-1f466", - "name": "family: woman, woman, boy, boy", - "alpha_code": ":family_wwbb:", - "aliases": "" - }, - "1f469-1f469-1f467": { - "output": "1f469-200d-1f469-200d-1f467", - "name": "family: woman, woman, girl", - "alpha_code": ":family_wwg:", - "aliases": "" - }, - "1f469-1f469-1f467-1f466": { - "output": "1f469-200d-1f469-200d-1f467-200d-1f466", - "name": "family: woman, woman, girl, boy", - "alpha_code": ":family_wwgb:", - "aliases": "" - }, - "1f469-1f469-1f467-1f467": { - "output": "1f469-200d-1f469-200d-1f467-200d-1f467", - "name": "family: woman, woman, girl, girl", - "alpha_code": ":family_wwgg:", - "aliases": "" - }, - "1f469-2764-1f469": { - "output": "1f469-200d-2764-fe0f-200d-1f469", - "name": "couple with heart: woman, woman", - "alpha_code": ":couple_ww:", - "aliases": ":couple_with_heart_ww:" - }, - "1f468-2764-1f468": { - "output": "1f468-200d-2764-fe0f-200d-1f468", - "name": "couple with heart: man, man", - "alpha_code": ":couple_mm:", - "aliases": ":couple_with_heart_mm:" - }, - "1f469-2764-1f48b-1f469": { - "output": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f469", - "name": "kiss: woman, woman", - "alpha_code": ":kiss_ww:", - "aliases": ":couplekiss_ww:" - }, - "1f468-2764-1f48b-1f468": { - "output": "1f468-200d-2764-fe0f-200d-1f48b-200d-1f468", - "name": "kiss: man, man", - "alpha_code": ":kiss_mm:", - "aliases": ":couplekiss_mm:" - }, - "1f3fb": { - "output": "1f3fb", - "name": "light skin tone", - "alpha_code": ":tone1:", - "aliases": "" - }, - "1f3fc": { - "output": "1f3fc", - "name": "medium-light skin tone", - "alpha_code": ":tone2:", - "aliases": "" - }, - "1f3fd": { - "output": "1f3fd", - "name": "medium skin tone", - "alpha_code": ":tone3:", - "aliases": "" - }, - "1f3fe": { - "output": "1f3fe", - "name": "medium-dark skin tone", - "alpha_code": ":tone4:", - "aliases": "" - }, - "1f3ff": { - "output": "1f3ff", - "name": "dark skin tone", - "alpha_code": ":tone5:", - "aliases": "" - }, - "002a-20e3": { - "output": "002a-fe0f-20e3", - "name": "keycap: *", - "alpha_code": ":asterisk:", - "aliases": ":keycap_asterisk:" - }, - "23cf": { - "output": "23cf-fe0f", - "name": "eject button", - "alpha_code": ":eject:", - "aliases": ":eject_symbol:" - }, - "23ed": { - "output": "23ed-fe0f", - "name": "next track button", - "alpha_code": ":track_next:", - "aliases": ":next_track:" - }, - "23ee": { - "output": "23ee-fe0f", - "name": "last track button", - "alpha_code": ":track_previous:", - "aliases": ":previous_track:" - }, - "23ef": { - "output": "23ef-fe0f", - "name": "play or pause button", - "alpha_code": ":play_pause:", - "aliases": "" - }, - "1f441-1f5e8": { - "output": "1f441-fe0f-200d-1f5e8-fe0f", - "name": "eye in speech bubble", - "alpha_code": ":eye_in_speech_bubble:", - "aliases": "" - }, - "23f1": { - "output": "23f1-fe0f", - "name": "stopwatch", - "alpha_code": ":stopwatch:", - "aliases": "" - }, - "23f2": { - "output": "23f2-fe0f", - "name": "timer clock", - "alpha_code": ":timer:", - "aliases": ":timer_clock:" - }, - "23f8": { - "output": "23f8-fe0f", - "name": "pause button", - "alpha_code": ":pause_button:", - "aliases": ":double_vertical_bar:" - }, - "23f9": { - "output": "23f9-fe0f", - "name": "stop button", - "alpha_code": ":stop_button:", - "aliases": "" - }, - "23fa": { - "output": "23fa-fe0f", - "name": "record button", - "alpha_code": ":record_button:", - "aliases": "" - }, - "2602": { - "output": "2602-fe0f", - "name": "umbrella", - "alpha_code": ":umbrella2:", - "aliases": "" - }, - "2603": { - "output": "2603-fe0f", - "name": "snowman", - "alpha_code": ":snowman2:", - "aliases": "" - }, - "2604": { - "output": "2604-fe0f", - "name": "comet", - "alpha_code": ":comet:", - "aliases": "" - }, - "2618": { - "output": "2618-fe0f", - "name": "shamrock", - "alpha_code": ":shamrock:", - "aliases": "" - }, - "2620": { - "output": "2620-fe0f", - "name": "skull and crossbones", - "alpha_code": ":skull_crossbones:", - "aliases": ":skull_and_crossbones:" - }, - "2622": { - "output": "2622-fe0f", - "name": "radioactive", - "alpha_code": ":radioactive:", - "aliases": ":radioactive_sign:" - }, - "2623": { - "output": "2623-fe0f", - "name": "biohazard", - "alpha_code": ":biohazard:", - "aliases": ":biohazard_sign:" - }, - "2626": { - "output": "2626-fe0f", - "name": "orthodox cross", - "alpha_code": ":orthodox_cross:", - "aliases": "" - }, - "262a": { - "output": "262a-fe0f", - "name": "star and crescent", - "alpha_code": ":star_and_crescent:", - "aliases": "" - }, - "262e": { - "output": "262e-fe0f", - "name": "peace symbol", - "alpha_code": ":peace:", - "aliases": ":peace_symbol:" - }, - "262f": { - "output": "262f-fe0f", - "name": "yin yang", - "alpha_code": ":yin_yang:", - "aliases": "" - }, - "2638": { - "output": "2638-fe0f", - "name": "wheel of dharma", - "alpha_code": ":wheel_of_dharma:", - "aliases": "" - }, - "2639": { - "output": "2639-fe0f", - "name": "frowning face", - "alpha_code": ":frowning2:", - "aliases": ":white_frowning_face:" - }, - "2692": { - "output": "2692-fe0f", - "name": "hammer and pick", - "alpha_code": ":hammer_pick:", - "aliases": ":hammer_and_pick:" - }, - "2694": { - "output": "2694-fe0f", - "name": "crossed swords", - "alpha_code": ":crossed_swords:", - "aliases": "" - }, - "2696": { - "output": "2696-fe0f", - "name": "balance scale", - "alpha_code": ":scales:", - "aliases": "" - }, - "2697": { - "output": "2697-fe0f", - "name": "alembic", - "alpha_code": ":alembic:", - "aliases": "" - }, - "2699": { - "output": "2699-fe0f", - "name": "gear", - "alpha_code": ":gear:", - "aliases": "" - }, - "269b": { - "output": "269b-fe0f", - "name": "atom symbol", - "alpha_code": ":atom:", - "aliases": ":atom_symbol:" - }, - "269c": { - "output": "269c-fe0f", - "name": "fleur-de-lis", - "alpha_code": ":fleur-de-lis:", - "aliases": "" - }, - "26b0": { - "output": "26b0-fe0f", - "name": "coffin", - "alpha_code": ":coffin:", - "aliases": "" - }, - "26b1": { - "output": "26b1-fe0f", - "name": "funeral urn", - "alpha_code": ":urn:", - "aliases": ":funeral_urn:" - }, - "26c8": { - "output": "26c8-fe0f", - "name": "cloud with lightning and rain", - "alpha_code": ":thunder_cloud_rain:", - "aliases": ":thunder_cloud_and_rain:" - }, - "26cf": { - "output": "26cf-fe0f", - "name": "pick", - "alpha_code": ":pick:", - "aliases": "" - }, - "26d1": { - "output": "26d1-fe0f", - "name": "rescue worker\u2019s helmet", - "alpha_code": ":helmet_with_cross:", - "aliases": ":helmet_with_white_cross:" - }, - "26d3": { - "output": "26d3-fe0f", - "name": "chains", - "alpha_code": ":chains:", - "aliases": "" - }, - "26e9": { - "output": "26e9-fe0f", - "name": "shinto shrine", - "alpha_code": ":shinto_shrine:", - "aliases": "" - }, - "26f0": { - "output": "26f0-fe0f", - "name": "mountain", - "alpha_code": ":mountain:", - "aliases": "" - }, - "26f1": { - "output": "26f1-fe0f", - "name": "umbrella on ground", - "alpha_code": ":beach_umbrella:", - "aliases": ":umbrella_on_ground:" - }, - "26f4": { - "output": "26f4-fe0f", - "name": "ferry", - "alpha_code": ":ferry:", - "aliases": "" - }, - "26f7": { - "output": "26f7-fe0f", - "name": "skier", - "alpha_code": ":skier:", - "aliases": "" - }, - "26f8": { - "output": "26f8-fe0f", - "name": "ice skate", - "alpha_code": ":ice_skate:", - "aliases": "" - }, - "26f9": { - "output": "26f9-fe0f", - "name": "person bouncing ball", - "alpha_code": ":person_bouncing_ball:", - "aliases": ":basketball_player:|:person_with_ball:" - }, - "2721": { - "output": "2721-fe0f", - "name": "star of David", - "alpha_code": ":star_of_david:", - "aliases": "" - }, - "2763": { - "output": "2763-fe0f", - "name": "heavy heart exclamation", - "alpha_code": ":heart_exclamation:", - "aliases": ":heavy_heart_exclamation_mark_ornament:" - }, - "1f324": { - "output": "1f324-fe0f", - "name": "sun behind small cloud", - "alpha_code": ":white_sun_small_cloud:", - "aliases": ":white_sun_with_small_cloud:" - }, - "1f325": { - "output": "1f325-fe0f", - "name": "sun behind large cloud", - "alpha_code": ":white_sun_cloud:", - "aliases": ":white_sun_behind_cloud:" - }, - "1f326": { - "output": "1f326-fe0f", - "name": "sun behind rain cloud", - "alpha_code": ":white_sun_rain_cloud:", - "aliases": ":white_sun_behind_cloud_with_rain:" - }, - "1f5b1": { - "output": "1f5b1-fe0f", - "name": "computer mouse", - "alpha_code": ":mouse_three_button:", - "aliases": ":three_button_mouse:" - }, - "1f385-1f3fb": { - "output": "1f385-1f3fb", - "name": "Santa Claus: light skin tone", - "alpha_code": ":santa_tone1:", - "aliases": "" - }, - "1f385-1f3fc": { - "output": "1f385-1f3fc", - "name": "Santa Claus: medium-light skin tone", - "alpha_code": ":santa_tone2:", - "aliases": "" - }, - "1f385-1f3fd": { - "output": "1f385-1f3fd", - "name": "Santa Claus: medium skin tone", - "alpha_code": ":santa_tone3:", - "aliases": "" - }, - "1f385-1f3fe": { - "output": "1f385-1f3fe", - "name": "Santa Claus: medium-dark skin tone", - "alpha_code": ":santa_tone4:", - "aliases": "" - }, - "1f385-1f3ff": { - "output": "1f385-1f3ff", - "name": "Santa Claus: dark skin tone", - "alpha_code": ":santa_tone5:", - "aliases": "" - }, - "1f918-1f3fb": { - "output": "1f918-1f3fb", - "name": "sign of the horns: light skin tone", - "alpha_code": ":metal_tone1:", - "aliases": ":sign_of_the_horns_tone1:" - }, - "1f918-1f3fc": { - "output": "1f918-1f3fc", - "name": "sign of the horns: medium-light skin tone", - "alpha_code": ":metal_tone2:", - "aliases": ":sign_of_the_horns_tone2:" - }, - "1f918-1f3fd": { - "output": "1f918-1f3fd", - "name": "sign of the horns: medium skin tone", - "alpha_code": ":metal_tone3:", - "aliases": ":sign_of_the_horns_tone3:" - }, - "1f918-1f3fe": { - "output": "1f918-1f3fe", - "name": "sign of the horns: medium-dark skin tone", - "alpha_code": ":metal_tone4:", - "aliases": ":sign_of_the_horns_tone4:" - }, - "1f918-1f3ff": { - "output": "1f918-1f3ff", - "name": "sign of the horns: dark skin tone", - "alpha_code": ":metal_tone5:", - "aliases": ":sign_of_the_horns_tone5:" - }, - "1f3cb-1f3fb": { - "output": "1f3cb-1f3fb", - "name": "person lifting weights: light skin tone", - "alpha_code": ":person_lifting_weights_tone1:", - "aliases": ":lifter_tone1:|:weight_lifter_tone1:" - }, - "1f3cb-1f3fc": { - "output": "1f3cb-1f3fc", - "name": "person lifting weights: medium-light skin tone", - "alpha_code": ":person_lifting_weights_tone2:", - "aliases": ":lifter_tone2:|:weight_lifter_tone2:" - }, - "1f3cb-1f3fd": { - "output": "1f3cb-1f3fd", - "name": "person lifting weights: medium skin tone", - "alpha_code": ":person_lifting_weights_tone3:", - "aliases": ":lifter_tone3:|:weight_lifter_tone3:" - }, - "1f3cb-1f3fe": { - "output": "1f3cb-1f3fe", - "name": "person lifting weights: medium-dark skin tone", - "alpha_code": ":person_lifting_weights_tone4:", - "aliases": ":lifter_tone4:|:weight_lifter_tone4:" - }, - "1f3cb-1f3ff": { - "output": "1f3cb-1f3ff", - "name": "person lifting weights: dark skin tone", - "alpha_code": ":person_lifting_weights_tone5:", - "aliases": ":lifter_tone5:|:weight_lifter_tone5:" - }, - "26f9-1f3fb": { - "output": "26f9-1f3fb", - "name": "person bouncing ball: light skin tone", - "alpha_code": ":person_bouncing_ball_tone1:", - "aliases": ":basketball_player_tone1:|:person_with_ball_tone1:" - }, - "26f9-1f3fc": { - "output": "26f9-1f3fc", - "name": "person bouncing ball: medium-light skin tone", - "alpha_code": ":person_bouncing_ball_tone2:", - "aliases": ":basketball_player_tone2:|:person_with_ball_tone2:" - }, - "26f9-1f3fd": { - "output": "26f9-1f3fd", - "name": "person bouncing ball: medium skin tone", - "alpha_code": ":person_bouncing_ball_tone3:", - "aliases": ":basketball_player_tone3:|:person_with_ball_tone3:" - }, - "26f9-1f3fe": { - "output": "26f9-1f3fe", - "name": "person bouncing ball: medium-dark skin tone", - "alpha_code": ":person_bouncing_ball_tone4:", - "aliases": ":basketball_player_tone4:|:person_with_ball_tone4:" - }, - "26f9-1f3ff": { - "output": "26f9-1f3ff", - "name": "person bouncing ball: dark skin tone", - "alpha_code": ":person_bouncing_ball_tone5:", - "aliases": ":basketball_player_tone5:|:person_with_ball_tone5:" - }, - "1f643": { - "output": "1f643", - "name": "upside-down face", - "alpha_code": ":upside_down:", - "aliases": ":upside_down_face:" - }, - "1f911": { - "output": "1f911", - "name": "money-mouth face", - "alpha_code": ":money_mouth:", - "aliases": ":money_mouth_face:" - }, - "1f913": { - "output": "1f913", - "name": "nerd face", - "alpha_code": ":nerd:", - "aliases": ":nerd_face:" - }, - "1f917": { - "output": "1f917", - "name": "hugging face", - "alpha_code": ":hugging:", - "aliases": ":hugging_face:" - }, - "1f644": { - "output": "1f644", - "name": "face with rolling eyes", - "alpha_code": ":rolling_eyes:", - "aliases": ":face_with_rolling_eyes:" - }, - "1f914": { - "output": "1f914", - "name": "thinking face", - "alpha_code": ":thinking:", - "aliases": ":thinking_face:" - }, - "1f910": { - "output": "1f910", - "name": "zipper-mouth face", - "alpha_code": ":zipper_mouth:", - "aliases": ":zipper_mouth_face:" - }, - "1f912": { - "output": "1f912", - "name": "face with thermometer", - "alpha_code": ":thermometer_face:", - "aliases": ":face_with_thermometer:" - }, - "1f915": { - "output": "1f915", - "name": "face with head-bandage", - "alpha_code": ":head_bandage:", - "aliases": ":face_with_head_bandage:" - }, - "1f916": { - "output": "1f916", - "name": "robot face", - "alpha_code": ":robot:", - "aliases": ":robot_face:" - }, - "1f981": { - "output": "1f981", - "name": "lion face", - "alpha_code": ":lion_face:", - "aliases": ":lion:" - }, - "1f984": { - "output": "1f984", - "name": "unicorn face", - "alpha_code": ":unicorn:", - "aliases": ":unicorn_face:" - }, - "1f982": { - "output": "1f982", - "name": "scorpion", - "alpha_code": ":scorpion:", - "aliases": "" - }, - "1f980": { - "output": "1f980", - "name": "crab", - "alpha_code": ":crab:", - "aliases": "" - }, - "1f983": { - "output": "1f983", - "name": "turkey", - "alpha_code": ":turkey:", - "aliases": "" - }, - "1f9c0": { - "output": "1f9c0", - "name": "cheese wedge", - "alpha_code": ":cheese:", - "aliases": ":cheese_wedge:" - }, - "1f32d": { - "output": "1f32d", - "name": "hot dog", - "alpha_code": ":hotdog:", - "aliases": ":hot_dog:" - }, - "1f32e": { - "output": "1f32e", - "name": "taco", - "alpha_code": ":taco:", - "aliases": "" - }, - "1f32f": { - "output": "1f32f", - "name": "burrito", - "alpha_code": ":burrito:", - "aliases": "" - }, - "1f37f": { - "output": "1f37f", - "name": "popcorn", - "alpha_code": ":popcorn:", - "aliases": "" - }, - "1f37e": { - "output": "1f37e", - "name": "bottle with popping cork", - "alpha_code": ":champagne:", - "aliases": ":bottle_with_popping_cork:" - }, - "1f3f9": { - "output": "1f3f9", - "name": "bow and arrow", - "alpha_code": ":bow_and_arrow:", - "aliases": ":archery:" - }, - "1f3fa": { - "output": "1f3fa", - "name": "amphora", - "alpha_code": ":amphora:", - "aliases": "" - }, - "1f6d0": { - "output": "1f6d0", - "name": "place of worship", - "alpha_code": ":place_of_worship:", - "aliases": ":worship_symbol:" - }, - "1f54b": { - "output": "1f54b", - "name": "kaaba", - "alpha_code": ":kaaba:", - "aliases": "" - }, - "1f54c": { - "output": "1f54c", - "name": "mosque", - "alpha_code": ":mosque:", - "aliases": "" - }, - "1f54d": { - "output": "1f54d", - "name": "synagogue", - "alpha_code": ":synagogue:", - "aliases": "" - }, - "1f54e": { - "output": "1f54e", - "name": "menorah", - "alpha_code": ":menorah:", - "aliases": "" - }, - "1f4ff": { - "output": "1f4ff", - "name": "prayer beads", - "alpha_code": ":prayer_beads:", - "aliases": "" - }, - "1f3cf": { - "output": "1f3cf", - "name": "cricket game", - "alpha_code": ":cricket_game:", - "aliases": ":cricket_bat_ball:" - }, - "1f3d0": { - "output": "1f3d0", - "name": "volleyball", - "alpha_code": ":volleyball:", - "aliases": "" - }, - "1f3d1": { - "output": "1f3d1", - "name": "field hockey", - "alpha_code": ":field_hockey:", - "aliases": "" - }, - "1f3d2": { - "output": "1f3d2", - "name": "ice hockey", - "alpha_code": ":hockey:", - "aliases": "" - }, - "1f3d3": { - "output": "1f3d3", - "name": "ping pong", - "alpha_code": ":ping_pong:", - "aliases": ":table_tennis:" - }, - "1f3f8": { - "output": "1f3f8", - "name": "badminton", - "alpha_code": ":badminton:", - "aliases": "" - }, - "1f1e6-1f1fd": { - "output": "1f1e6-1f1fd", - "name": "\u00c5land Islands", - "alpha_code": ":flag_ax:", - "aliases": ":ax:" - }, - "1f1f9-1f1e6": { - "output": "1f1f9-1f1e6", - "name": "Tristan da Cunha", - "alpha_code": ":flag_ta:", - "aliases": ":ta:" - }, - "1f1ee-1f1f4": { - "output": "1f1ee-1f1f4", - "name": "British Indian Ocean Territory", - "alpha_code": ":flag_io:", - "aliases": ":io:" - }, - "1f1e7-1f1f6": { - "output": "1f1e7-1f1f6", - "name": "Caribbean Netherlands", - "alpha_code": ":flag_bq:", - "aliases": ":bq:" - }, - "1f1e8-1f1fd": { - "output": "1f1e8-1f1fd", - "name": "Christmas Island", - "alpha_code": ":flag_cx:", - "aliases": ":cx:" - }, - "1f1e8-1f1e8": { - "output": "1f1e8-1f1e8", - "name": "Cocos (Keeling) Islands", - "alpha_code": ":flag_cc:", - "aliases": ":cc:" - }, - "1f1ec-1f1ec": { - "output": "1f1ec-1f1ec", - "name": "Guernsey", - "alpha_code": ":flag_gg:", - "aliases": ":gg:" - }, - "1f1ee-1f1f2": { - "output": "1f1ee-1f1f2", - "name": "Isle of Man", - "alpha_code": ":flag_im:", - "aliases": ":im:" - }, - "1f1fe-1f1f9": { - "output": "1f1fe-1f1f9", - "name": "Mayotte", - "alpha_code": ":flag_yt:", - "aliases": ":yt:" - }, - "1f1f3-1f1eb": { - "output": "1f1f3-1f1eb", - "name": "Norfolk Island", - "alpha_code": ":flag_nf:", - "aliases": ":nf:" - }, - "1f1f5-1f1f3": { - "output": "1f1f5-1f1f3", - "name": "Pitcairn Islands", - "alpha_code": ":flag_pn:", - "aliases": ":pn:" - }, - "1f1e7-1f1f1": { - "output": "1f1e7-1f1f1", - "name": "St. Barth\u00e9lemy", - "alpha_code": ":flag_bl:", - "aliases": ":bl:" - }, - "1f1f5-1f1f2": { - "output": "1f1f5-1f1f2", - "name": "St. Pierre & Miquelon", - "alpha_code": ":flag_pm:", - "aliases": ":pm:" - }, - "1f1ec-1f1f8": { - "output": "1f1ec-1f1f8", - "name": "South Georgia & South Sandwich Islands", - "alpha_code": ":flag_gs:", - "aliases": ":gs:" - }, - "1f1f9-1f1f0": { - "output": "1f1f9-1f1f0", - "name": "Tokelau", - "alpha_code": ":flag_tk:", - "aliases": ":tk:" - }, - "1f1e7-1f1fb": { - "output": "1f1e7-1f1fb", - "name": "Bouvet Island", - "alpha_code": ":flag_bv:", - "aliases": ":bv:" - }, - "1f1ed-1f1f2": { - "output": "1f1ed-1f1f2", - "name": "Heard & McDonald Islands", - "alpha_code": ":flag_hm:", - "aliases": ":hm:" - }, - "1f1f8-1f1ef": { - "output": "1f1f8-1f1ef", - "name": "Svalbard & Jan Mayen", - "alpha_code": ":flag_sj:", - "aliases": ":sj:" - }, - "1f1fa-1f1f2": { - "output": "1f1fa-1f1f2", - "name": "U.S. Outlying Islands", - "alpha_code": ":flag_um:", - "aliases": ":um:" - }, - "1f1ee-1f1e8": { - "output": "1f1ee-1f1e8", - "name": "Canary Islands", - "alpha_code": ":flag_ic:", - "aliases": ":ic:" - }, - "1f1ea-1f1e6": { - "output": "1f1ea-1f1e6", - "name": "Ceuta & Melilla", - "alpha_code": ":flag_ea:", - "aliases": ":ea:" - }, - "1f1e8-1f1f5": { - "output": "1f1e8-1f1f5", - "name": "Clipperton Island", - "alpha_code": ":flag_cp:", - "aliases": ":cp:" - }, - "1f1e9-1f1ec": { - "output": "1f1e9-1f1ec", - "name": "Diego Garcia", - "alpha_code": ":flag_dg:", - "aliases": ":dg:" - }, - "1f1e6-1f1f8": { - "output": "1f1e6-1f1f8", - "name": "American Samoa", - "alpha_code": ":flag_as:", - "aliases": ":as:" - }, - "1f1e6-1f1f6": { - "output": "1f1e6-1f1f6", - "name": "Antarctica", - "alpha_code": ":flag_aq:", - "aliases": ":aq:" - }, - "1f1fb-1f1ec": { - "output": "1f1fb-1f1ec", - "name": "British Virgin Islands", - "alpha_code": ":flag_vg:", - "aliases": ":vg:" - }, - "1f1e8-1f1f0": { - "output": "1f1e8-1f1f0", - "name": "Cook Islands", - "alpha_code": ":flag_ck:", - "aliases": ":ck:" - }, - "1f1e8-1f1fc": { - "output": "1f1e8-1f1fc", - "name": "Cura\u00e7ao", - "alpha_code": ":flag_cw:", - "aliases": ":cw:" - }, - "1f1ea-1f1fa": { - "output": "1f1ea-1f1fa", - "name": "European Union", - "alpha_code": ":flag_eu:", - "aliases": ":eu:" - }, - "1f1ec-1f1eb": { - "output": "1f1ec-1f1eb", - "name": "French Guiana", - "alpha_code": ":flag_gf:", - "aliases": ":gf:" - }, - "1f1f9-1f1eb": { - "output": "1f1f9-1f1eb", - "name": "French Southern Territories", - "alpha_code": ":flag_tf:", - "aliases": ":tf:" - }, - "1f1ec-1f1f5": { - "output": "1f1ec-1f1f5", - "name": "Guadeloupe", - "alpha_code": ":flag_gp:", - "aliases": ":gp:" - }, - "1f1f2-1f1f6": { - "output": "1f1f2-1f1f6", - "name": "Martinique", - "alpha_code": ":flag_mq:", - "aliases": ":mq:" - }, - "1f1f2-1f1f5": { - "output": "1f1f2-1f1f5", - "name": "Northern Mariana Islands", - "alpha_code": ":flag_mp:", - "aliases": ":mp:" - }, - "1f1f7-1f1ea": { - "output": "1f1f7-1f1ea", - "name": "R\u00e9union", - "alpha_code": ":flag_re:", - "aliases": ":re:" - }, - "1f1f8-1f1fd": { - "output": "1f1f8-1f1fd", - "name": "Sint Maarten", - "alpha_code": ":flag_sx:", - "aliases": ":sx:" - }, - "1f1f8-1f1f8": { - "output": "1f1f8-1f1f8", - "name": "South Sudan", - "alpha_code": ":flag_ss:", - "aliases": ":ss:" - }, - "1f1f9-1f1e8": { - "output": "1f1f9-1f1e8", - "name": "Turks & Caicos Islands", - "alpha_code": ":flag_tc:", - "aliases": ":tc:" - }, - "1f1f2-1f1eb": { - "output": "1f1f2-1f1eb", - "name": "St. Martin", - "alpha_code": ":flag_mf:", - "aliases": ":mf:" - }, - "1f575-1f3fb": { - "output": "1f575-1f3fb", - "name": "detective: light skin tone", - "alpha_code": ":detective_tone1:", - "aliases": ":spy_tone1:|:sleuth_or_spy_tone1:" - }, - "1f575-1f3fc": { - "output": "1f575-1f3fc", - "name": "detective: medium-light skin tone", - "alpha_code": ":detective_tone2:", - "aliases": ":spy_tone2:|:sleuth_or_spy_tone2:" - }, - "1f575-1f3fd": { - "output": "1f575-1f3fd", - "name": "detective: medium skin tone", - "alpha_code": ":detective_tone3:", - "aliases": ":spy_tone3:|:sleuth_or_spy_tone3:" - }, - "1f575-1f3fe": { - "output": "1f575-1f3fe", - "name": "detective: medium-dark skin tone", - "alpha_code": ":detective_tone4:", - "aliases": ":spy_tone4:|:sleuth_or_spy_tone4:" - }, - "1f575-1f3ff": { - "output": "1f575-1f3ff", - "name": "detective: dark skin tone", - "alpha_code": ":detective_tone5:", - "aliases": ":spy_tone5:|:sleuth_or_spy_tone5:" - }, - "1f941": { - "output": "1f941", - "name": "drum", - "alpha_code": ":drum:", - "aliases": ":drum_with_drumsticks:" - }, - "1f990": { - "output": "1f990", - "name": "shrimp", - "alpha_code": ":shrimp:", - "aliases": "" - }, - "1f991": { - "output": "1f991", - "name": "squid", - "alpha_code": ":squid:", - "aliases": "" - }, - "1f95a": { - "output": "1f95a", - "name": "egg", - "alpha_code": ":egg:", - "aliases": "" - }, - "1f95b": { - "output": "1f95b", - "name": "glass of milk", - "alpha_code": ":milk:", - "aliases": ":glass_of_milk:" - }, - "1f95c": { - "output": "1f95c", - "name": "peanuts", - "alpha_code": ":peanuts:", - "aliases": ":shelled_peanut:" - }, - "1f95d": { - "output": "1f95d", - "name": "kiwi fruit", - "alpha_code": ":kiwi:", - "aliases": ":kiwifruit:" - }, - "1f95e": { - "output": "1f95e", - "name": "pancakes", - "alpha_code": ":pancakes:", - "aliases": "" - }, - "1f1fc": { - "output": "1f1fc", - "name": "regional indicator symbol letter w", - "alpha_code": ":regional_indicator_w:", - "aliases": "" - }, - "1f1fb": { - "output": "1f1fb", - "name": "regional indicator symbol letter v", - "alpha_code": ":regional_indicator_v:", - "aliases": "" - }, - "1f1fa": { - "output": "1f1fa", - "name": "regional indicator symbol letter u", - "alpha_code": ":regional_indicator_u:", - "aliases": "" - }, - "1f1f9": { - "output": "1f1f9", - "name": "regional indicator symbol letter t", - "alpha_code": ":regional_indicator_t:", - "aliases": "" - }, - "1f1f8": { - "output": "1f1f8", - "name": "regional indicator symbol letter s", - "alpha_code": ":regional_indicator_s:", - "aliases": "" - }, - "1f1f7": { - "output": "1f1f7", - "name": "regional indicator symbol letter r", - "alpha_code": ":regional_indicator_r:", - "aliases": "" - }, - "1f1f6": { - "output": "1f1f6", - "name": "regional indicator symbol letter q", - "alpha_code": ":regional_indicator_q:", - "aliases": "" - }, - "1f1f5": { - "output": "1f1f5", - "name": "regional indicator symbol letter p", - "alpha_code": ":regional_indicator_p:", - "aliases": "" - }, - "1f1f4": { - "output": "1f1f4", - "name": "regional indicator symbol letter o", - "alpha_code": ":regional_indicator_o:", - "aliases": "" - }, - "1f1f3": { - "output": "1f1f3", - "name": "regional indicator symbol letter n", - "alpha_code": ":regional_indicator_n:", - "aliases": "" - }, - "1f1f2": { - "output": "1f1f2", - "name": "regional indicator symbol letter m", - "alpha_code": ":regional_indicator_m:", - "aliases": "" - }, - "1f1f1": { - "output": "1f1f1", - "name": "regional indicator symbol letter l", - "alpha_code": ":regional_indicator_l:", - "aliases": "" - }, - "1f1f0": { - "output": "1f1f0", - "name": "regional indicator symbol letter k", - "alpha_code": ":regional_indicator_k:", - "aliases": "" - }, - "1f1ef": { - "output": "1f1ef", - "name": "regional indicator symbol letter j", - "alpha_code": ":regional_indicator_j:", - "aliases": "" - }, - "1f1ee": { - "output": "1f1ee", - "name": "regional indicator symbol letter i", - "alpha_code": ":regional_indicator_i:", - "aliases": "" - }, - "1f1ed": { - "output": "1f1ed", - "name": "regional indicator symbol letter h", - "alpha_code": ":regional_indicator_h:", - "aliases": "" - }, - "1f1ec": { - "output": "1f1ec", - "name": "regional indicator symbol letter g", - "alpha_code": ":regional_indicator_g:", - "aliases": "" - }, - "1f1eb": { - "output": "1f1eb", - "name": "regional indicator symbol letter f", - "alpha_code": ":regional_indicator_f:", - "aliases": "" - }, - "1f1ea": { - "output": "1f1ea", - "name": "regional indicator symbol letter e", - "alpha_code": ":regional_indicator_e:", - "aliases": "" - }, - "1f1e9": { - "output": "1f1e9", - "name": "regional indicator symbol letter d", - "alpha_code": ":regional_indicator_d:", - "aliases": "" - }, - "1f1e8": { - "output": "1f1e8", - "name": "regional indicator symbol letter c", - "alpha_code": ":regional_indicator_c:", - "aliases": "" - }, - "1f1e7": { - "output": "1f1e7", - "name": "regional indicator symbol letter b", - "alpha_code": ":regional_indicator_b:", - "aliases": "" - }, - "1f1e6": { - "output": "1f1e6", - "name": "regional indicator symbol letter a", - "alpha_code": ":regional_indicator_a:", - "aliases": "" - }, - "0039": { - "output": "0039-fe0f", - "name": "digit nine", - "alpha_code": ":digit_nine:", - "aliases": "" - }, - "0038": { - "output": "0038-fe0f", - "name": "digit eight", - "alpha_code": ":digit_eight:", - "aliases": "" - }, - "0037": { - "output": "0037-fe0f", - "name": "digit seven", - "alpha_code": ":digit_seven:", - "aliases": "" - }, - "0036": { - "output": "0036-fe0f", - "name": "digit six", - "alpha_code": ":digit_six:", - "aliases": "" - }, - "0035": { - "output": "0035-fe0f", - "name": "digit five", - "alpha_code": ":digit_five:", - "aliases": "" - }, - "0034": { - "output": "0034-fe0f", - "name": "digit four", - "alpha_code": ":digit_four:", - "aliases": "" - }, - "0033": { - "output": "0033-fe0f", - "name": "digit three", - "alpha_code": ":digit_three:", - "aliases": "" - }, - "0032": { - "output": "0032-fe0f", - "name": "digit two", - "alpha_code": ":digit_two:", - "aliases": "" - }, - "0031": { - "output": "0031-fe0f", - "name": "digit one", - "alpha_code": ":digit_one:", - "aliases": "" - }, - "0030": { - "output": "0030-fe0f", - "name": "digit zero", - "alpha_code": ":digit_zero:", - "aliases": "" - }, - "1f46f-2642": { - "output": "1f46f-200d-2642-fe0f", - "name": "men with bunny ears partying", - "alpha_code": ":men_with_bunny_ears_partying:", - "aliases": "" - }, - "1f46f-2640": { - "output": "1f46f-200d-2640-fe0f", - "name": "women with bunny ears partying", - "alpha_code": ":women_with_bunny_ears_partying:", - "aliases": "" - }, - "1f3c2-1f3fb": { - "output": "1f3c2-1f3fb", - "name": "snowboarder: light skin tone", - "alpha_code": ":snowboarder_tone1:", - "aliases": ":snowboarder_light_skin_tone:" - }, - "1f3cc-2642": { - "output": "1f3cc-fe0f-200d-2642-fe0f", - "name": "man golfing", - "alpha_code": ":man_golfing:", - "aliases": "" - }, - "1f3cc-1f3fb-2642": { - "output": "1f3cc-1f3fb-200d-2642-fe0f", - "name": "man golfing: light skin tone", - "alpha_code": ":man_golfing_tone1:", - "aliases": ":man_golfing_light_skin_tone:" - }, - "1f3cc-1f3fc-2642": { - "output": "1f3cc-1f3fc-200d-2642-fe0f", - "name": "man golfing: medium-light skin tone", - "alpha_code": ":man_golfing_tone2:", - "aliases": ":man_golfing_medium_light_skin_tone:" - }, - "1f3cc-1f3fd-2642": { - "output": "1f3cc-1f3fd-200d-2642-fe0f", - "name": "man golfing: medium skin tone", - "alpha_code": ":man_golfing_tone3:", - "aliases": ":man_golfing_medium_skin_tone:" - }, - "1f3cc-1f3fe-2642": { - "output": "1f3cc-1f3fe-200d-2642-fe0f", - "name": "man golfing: medium-dark skin tone", - "alpha_code": ":man_golfing_tone4:", - "aliases": ":man_golfing_medium_dark_skin_tone:" - }, - "1f3cc-1f3ff-2642": { - "output": "1f3cc-1f3ff-200d-2642-fe0f", - "name": "man golfing: dark skin tone", - "alpha_code": ":man_golfing_tone5:", - "aliases": ":man_golfing_dark_skin_tone:" - }, - "1f3cc-2640": { - "output": "1f3cc-fe0f-200d-2640-fe0f", - "name": "woman golfing", - "alpha_code": ":woman_golfing:", - "aliases": "" - }, - "1f3cc-1f3fb-2640": { - "output": "1f3cc-1f3fb-200d-2640-fe0f", - "name": "woman golfing: light skin tone", - "alpha_code": ":woman_golfing_tone1:", - "aliases": ":woman_golfing_light_skin_tone:" - }, - "1f3cc-1f3fc-2640": { - "output": "1f3cc-1f3fc-200d-2640-fe0f", - "name": "woman golfing: medium-light skin tone", - "alpha_code": ":woman_golfing_tone2:", - "aliases": ":woman_golfing_medium_light_skin_tone:" - }, - "1f3cc-1f3fd-2640": { - "output": "1f3cc-1f3fd-200d-2640-fe0f", - "name": "woman golfing: medium skin tone", - "alpha_code": ":woman_golfing_tone3:", - "aliases": ":woman_golfing_medium_skin_tone:" - }, - "1f3cc-1f3fe-2640": { - "output": "1f3cc-1f3fe-200d-2640-fe0f", - "name": "woman golfing: medium-dark skin tone", - "alpha_code": ":woman_golfing_tone4:", - "aliases": ":woman_golfing_medium_dark_skin_tone:" - }, - "1f3cc-1f3ff-2640": { - "output": "1f3cc-1f3ff-200d-2640-fe0f", - "name": "woman golfing: dark skin tone", - "alpha_code": ":woman_golfing_tone5:", - "aliases": ":woman_golfing_dark_skin_tone:" - }, - "1f93c-2642": { - "output": "1f93c-200d-2642-fe0f", - "name": "men wrestling", - "alpha_code": ":men_wrestling:", - "aliases": "" - }, - "1f93c-2640": { - "output": "1f93c-200d-2640-fe0f", - "name": "women wrestling", - "alpha_code": ":women_wrestling:", - "aliases": "" - }, - "1f939-1f3ff-2642": { - "output": "1f939-1f3ff-200d-2642-fe0f", - "name": "man juggling: dark skin tone", - "alpha_code": ":man_juggling_tone5:", - "aliases": ":man_juggling_dark_skin_tone:" - }, - "1f939-1f3fe-2642": { - "output": "1f939-1f3fe-200d-2642-fe0f", - "name": "man juggling: medium-dark skin tone", - "alpha_code": ":man_juggling_tone4:", - "aliases": ":man_juggling_medium_dark_skin_tone:" - }, - "1f939-1f3fd-2642": { - "output": "1f939-1f3fd-200d-2642-fe0f", - "name": "man juggling: medium skin tone", - "alpha_code": ":man_juggling_tone3:", - "aliases": ":man_juggling_medium_skin_tone:" - }, - "1f939-1f3fc-2642": { - "output": "1f939-1f3fc-200d-2642-fe0f", - "name": "man juggling: medium-light skin tone", - "alpha_code": ":man_juggling_tone2:", - "aliases": ":man_juggling_medium_light_skin_tone:" - }, - "1f939-1f3fb-2642": { - "output": "1f939-1f3fb-200d-2642-fe0f", - "name": "man juggling: light skin tone", - "alpha_code": ":man_juggling_tone1:", - "aliases": ":man_juggling_light_skin_tone:" - }, - "1f939-2642": { - "output": "1f939-200d-2642-fe0f", - "name": "man juggling", - "alpha_code": ":man_juggling:", - "aliases": "" - }, - "1f939-1f3ff-2640": { - "output": "1f939-1f3ff-200d-2640-fe0f", - "name": "woman juggling: dark skin tone", - "alpha_code": ":woman_juggling_tone5:", - "aliases": ":woman_juggling_dark_skin_tone:" - }, - "1f939-1f3fe-2640": { - "output": "1f939-1f3fe-200d-2640-fe0f", - "name": "woman juggling: medium-dark skin tone", - "alpha_code": ":woman_juggling_tone4:", - "aliases": ":woman_juggling_medium_dark_skin_tone:" - }, - "1f939-1f3fd-2640": { - "output": "1f939-1f3fd-200d-2640-fe0f", - "name": "woman juggling: medium skin tone", - "alpha_code": ":woman_juggling_tone3:", - "aliases": ":woman_juggling_medium_skin_tone:" - }, - "1f939-1f3fc-2640": { - "output": "1f939-1f3fc-200d-2640-fe0f", - "name": "woman juggling: medium-light skin tone", - "alpha_code": ":woman_juggling_tone2:", - "aliases": ":woman_juggling_medium_light_skin_tone:" - }, - "1f939-1f3fb-2640": { - "output": "1f939-1f3fb-200d-2640-fe0f", - "name": "woman juggling: light skin tone", - "alpha_code": ":woman_juggling_tone1:", - "aliases": ":woman_juggling_light_skin_tone:" - }, - "1f939-2640": { - "output": "1f939-200d-2640-fe0f", - "name": "woman juggling", - "alpha_code": ":woman_juggling:", - "aliases": "" - }, - "1f93e-1f3ff-2642": { - "output": "1f93e-1f3ff-200d-2642-fe0f", - "name": "man playing handball: dark skin tone", - "alpha_code": ":man_playing_handball_tone5:", - "aliases": ":man_playing_handball_dark_skin_tone:" - }, - "1f93e-1f3fe-2642": { - "output": "1f93e-1f3fe-200d-2642-fe0f", - "name": "man playing handball: medium-dark skin tone", - "alpha_code": ":man_playing_handball_tone4:", - "aliases": ":man_playing_handball_medium_dark_skin_tone:" - }, - "1f93e-1f3fd-2642": { - "output": "1f93e-1f3fd-200d-2642-fe0f", - "name": "man playing handball: medium skin tone", - "alpha_code": ":man_playing_handball_tone3:", - "aliases": ":man_playing_handball_medium_skin_tone:" - }, - "1f93e-1f3fc-2642": { - "output": "1f93e-1f3fc-200d-2642-fe0f", - "name": "man playing handball: medium-light skin tone", - "alpha_code": ":man_playing_handball_tone2:", - "aliases": ":man_playing_handball_medium_light_skin_tone:" - }, - "1f93e-1f3fb-2642": { - "output": "1f93e-1f3fb-200d-2642-fe0f", - "name": "man playing handball: light skin tone", - "alpha_code": ":man_playing_handball_tone1:", - "aliases": ":man_playing_handball_light_skin_tone:" - }, - "1f93e-2642": { - "output": "1f93e-200d-2642-fe0f", - "name": "man playing handball", - "alpha_code": ":man_playing_handball:", - "aliases": "" - }, - "1f93e-1f3ff-2640": { - "output": "1f93e-1f3ff-200d-2640-fe0f", - "name": "woman playing handball: dark skin tone", - "alpha_code": ":woman_playing_handball_tone5:", - "aliases": ":woman_playing_handball_dark_skin_tone:" - }, - "1f93e-1f3fe-2640": { - "output": "1f93e-1f3fe-200d-2640-fe0f", - "name": "woman playing handball: medium-dark skin tone", - "alpha_code": ":woman_playing_handball_tone4:", - "aliases": ":woman_playing_handball_medium_dark_skin_tone:" - }, - "1f93e-1f3fd-2640": { - "output": "1f93e-1f3fd-200d-2640-fe0f", - "name": "woman playing handball: medium skin tone", - "alpha_code": ":woman_playing_handball_tone3:", - "aliases": ":woman_playing_handball_medium_skin_tone:" - }, - "1f93e-1f3fc-2640": { - "output": "1f93e-1f3fc-200d-2640-fe0f", - "name": "woman playing handball: medium-light skin tone", - "alpha_code": ":woman_playing_handball_tone2:", - "aliases": ":woman_playing_handball_medium_light_skin_tone:" - }, - "1f93e-1f3fb-2640": { - "output": "1f93e-1f3fb-200d-2640-fe0f", - "name": "woman playing handball: light skin tone", - "alpha_code": ":woman_playing_handball_tone1:", - "aliases": ":woman_playing_handball_light_skin_tone:" - }, - "1f93e-2640": { - "output": "1f93e-200d-2640-fe0f", - "name": "woman playing handball", - "alpha_code": ":woman_playing_handball:", - "aliases": "" - }, - "1f93d-1f3ff-2642": { - "output": "1f93d-1f3ff-200d-2642-fe0f", - "name": "man playing water polo: dark skin tone", - "alpha_code": ":man_playing_water_polo_tone5:", - "aliases": ":man_playing_water_polo_dark_skin_tone:" - }, - "1f93d-1f3fe-2642": { - "output": "1f93d-1f3fe-200d-2642-fe0f", - "name": "man playing water polo: medium-dark skin tone", - "alpha_code": ":man_playing_water_polo_tone4:", - "aliases": ":man_playing_water_polo_medium_dark_skin_tone:" - }, - "1f93d-1f3fd-2642": { - "output": "1f93d-1f3fd-200d-2642-fe0f", - "name": "man playing water polo: medium skin tone", - "alpha_code": ":man_playing_water_polo_tone3:", - "aliases": ":man_playing_water_polo_medium_skin_tone:" - }, - "1f93d-1f3fc-2642": { - "output": "1f93d-1f3fc-200d-2642-fe0f", - "name": "man playing water polo: medium-light skin tone", - "alpha_code": ":man_playing_water_polo_tone2:", - "aliases": ":man_playing_water_polo_medium_light_skin_tone:" - }, - "1f93d-1f3fb-2642": { - "output": "1f93d-1f3fb-200d-2642-fe0f", - "name": "man playing water polo: light skin tone", - "alpha_code": ":man_playing_water_polo_tone1:", - "aliases": ":man_playing_water_polo_light_skin_tone:" - }, - "1f93d-2642": { - "output": "1f93d-200d-2642-fe0f", - "name": "man playing water polo", - "alpha_code": ":man_playing_water_polo:", - "aliases": "" - }, - "1f93d-1f3ff-2640": { - "output": "1f93d-1f3ff-200d-2640-fe0f", - "name": "woman playing water polo: dark skin tone", - "alpha_code": ":woman_playing_water_polo_tone5:", - "aliases": ":woman_playing_water_polo_dark_skin_tone:" - }, - "1f93d-1f3fe-2640": { - "output": "1f93d-1f3fe-200d-2640-fe0f", - "name": "woman playing water polo: medium-dark skin tone", - "alpha_code": ":woman_playing_water_polo_tone4:", - "aliases": ":woman_playing_water_polo_medium_dark_skin_tone:" - }, - "1f93d-1f3fd-2640": { - "output": "1f93d-1f3fd-200d-2640-fe0f", - "name": "woman playing water polo: medium skin tone", - "alpha_code": ":woman_playing_water_polo_tone3:", - "aliases": ":woman_playing_water_polo_medium_skin_tone:" - }, - "1f93d-1f3fc-2640": { - "output": "1f93d-1f3fc-200d-2640-fe0f", - "name": "woman playing water polo: medium-light skin tone", - "alpha_code": ":woman_playing_water_polo_tone2:", - "aliases": ":woman_playing_water_polo_medium_light_skin_tone:" - }, - "1f93d-1f3fb-2640": { - "output": "1f93d-1f3fb-200d-2640-fe0f", - "name": "woman playing water polo: light skin tone", - "alpha_code": ":woman_playing_water_polo_tone1:", - "aliases": ":woman_playing_water_polo_light_skin_tone:" - }, - "1f93d-2640": { - "output": "1f93d-200d-2640-fe0f", - "name": "woman playing water polo", - "alpha_code": ":woman_playing_water_polo:", - "aliases": "" - }, - "1f938-1f3ff-2642": { - "output": "1f938-1f3ff-200d-2642-fe0f", - "name": "man cartwheeling: dark skin tone", - "alpha_code": ":man_cartwheeling_tone5:", - "aliases": ":man_cartwheeling_dark_skin_tone:" - }, - "1f938-1f3fe-2642": { - "output": "1f938-1f3fe-200d-2642-fe0f", - "name": "man cartwheeling: medium-dark skin tone", - "alpha_code": ":man_cartwheeling_tone4:", - "aliases": ":man_cartwheeling_medium_dark_skin_tone:" - }, - "1f938-1f3fd-2642": { - "output": "1f938-1f3fd-200d-2642-fe0f", - "name": "man cartwheeling: medium skin tone", - "alpha_code": ":man_cartwheeling_tone3:", - "aliases": ":man_cartwheeling_medium_skin_tone:" - }, - "1f938-1f3fc-2642": { - "output": "1f938-1f3fc-200d-2642-fe0f", - "name": "man cartwheeling: medium-light skin tone", - "alpha_code": ":man_cartwheeling_tone2:", - "aliases": ":man_cartwheeling_medium_light_skin_tone:" - }, - "1f938-1f3fb-2642": { - "output": "1f938-1f3fb-200d-2642-fe0f", - "name": "man cartwheeling: light skin tone", - "alpha_code": ":man_cartwheeling_tone1:", - "aliases": ":man_cartwheeling_light_skin_tone:" - }, - "1f938-2642": { - "output": "1f938-200d-2642-fe0f", - "name": "man cartwheeling", - "alpha_code": ":man_cartwheeling:", - "aliases": "" - }, - "1f938-1f3ff-2640": { - "output": "1f938-1f3ff-200d-2640-fe0f", - "name": "woman cartwheeling: dark skin tone", - "alpha_code": ":woman_cartwheeling_tone5:", - "aliases": ":woman_cartwheeling_dark_skin_tone:" - }, - "1f938-1f3fe-2640": { - "output": "1f938-1f3fe-200d-2640-fe0f", - "name": "woman cartwheeling: medium-dark skin tone", - "alpha_code": ":woman_cartwheeling_tone4:", - "aliases": ":woman_cartwheeling_medium_dark_skin_tone:" - }, - "1f938-1f3fd-2640": { - "output": "1f938-1f3fd-200d-2640-fe0f", - "name": "woman cartwheeling: medium skin tone", - "alpha_code": ":woman_cartwheeling_tone3:", - "aliases": ":woman_cartwheeling_medium_skin_tone:" - }, - "1f938-1f3fc-2640": { - "output": "1f938-1f3fc-200d-2640-fe0f", - "name": "woman cartwheeling: medium-light skin tone", - "alpha_code": ":woman_cartwheeling_tone2:", - "aliases": ":woman_cartwheeling_medium_light_skin_tone:" - }, - "1f938-1f3fb-2640": { - "output": "1f938-1f3fb-200d-2640-fe0f", - "name": "woman cartwheeling: light skin tone", - "alpha_code": ":woman_cartwheeling_tone1:", - "aliases": ":woman_cartwheeling_light_skin_tone:" - }, - "1f938-2640": { - "output": "1f938-200d-2640-fe0f", - "name": "woman cartwheeling", - "alpha_code": ":woman_cartwheeling:", - "aliases": "" - }, - "1f6b6-1f3ff-2642": { - "output": "1f6b6-1f3ff-200d-2642-fe0f", - "name": "man walking: dark skin tone", - "alpha_code": ":man_walking_tone5:", - "aliases": ":man_walking_dark_skin_tone:" - }, - "1f6b6-1f3fe-2642": { - "output": "1f6b6-1f3fe-200d-2642-fe0f", - "name": "man walking: medium-dark skin tone", - "alpha_code": ":man_walking_tone4:", - "aliases": ":man_walking_medium_dark_skin_tone:" - }, - "1f6b6-1f3fd-2642": { - "output": "1f6b6-1f3fd-200d-2642-fe0f", - "name": "man walking: medium skin tone", - "alpha_code": ":man_walking_tone3:", - "aliases": ":man_walking_medium_skin_tone:" - }, - "1f6b6-1f3fc-2642": { - "output": "1f6b6-1f3fc-200d-2642-fe0f", - "name": "man walking: medium-light skin tone", - "alpha_code": ":man_walking_tone2:", - "aliases": ":man_walking_medium_light_skin_tone:" - }, - "1f6b6-1f3fb-2642": { - "output": "1f6b6-1f3fb-200d-2642-fe0f", - "name": "man walking: light skin tone", - "alpha_code": ":man_walking_tone1:", - "aliases": ":man_walking_light_skin_tone:" - }, - "1f6b6-2642": { - "output": "1f6b6-200d-2642-fe0f", - "name": "man walking", - "alpha_code": ":man_walking:", - "aliases": "" - }, - "1f6b6-1f3ff-2640": { - "output": "1f6b6-1f3ff-200d-2640-fe0f", - "name": "woman walking: dark skin tone", - "alpha_code": ":woman_walking_tone5:", - "aliases": ":woman_walking_dark_skin_tone:" - }, - "1f6b6-1f3fe-2640": { - "output": "1f6b6-1f3fe-200d-2640-fe0f", - "name": "woman walking: medium-dark skin tone", - "alpha_code": ":woman_walking_tone4:", - "aliases": ":woman_walking_medium_dark_skin_tone:" - }, - "1f6b6-1f3fd-2640": { - "output": "1f6b6-1f3fd-200d-2640-fe0f", - "name": "woman walking: medium skin tone", - "alpha_code": ":woman_walking_tone3:", - "aliases": ":woman_walking_medium_skin_tone:" - }, - "1f6b6-1f3fc-2640": { - "output": "1f6b6-1f3fc-200d-2640-fe0f", - "name": "woman walking: medium-light skin tone", - "alpha_code": ":woman_walking_tone2:", - "aliases": ":woman_walking_medium_light_skin_tone:" - }, - "1f6b6-1f3fb-2640": { - "output": "1f6b6-1f3fb-200d-2640-fe0f", - "name": "woman walking: light skin tone", - "alpha_code": ":woman_walking_tone1:", - "aliases": ":woman_walking_light_skin_tone:" - }, - "1f6b6-2640": { - "output": "1f6b6-200d-2640-fe0f", - "name": "woman walking", - "alpha_code": ":woman_walking:", - "aliases": "" - }, - "1f6b5-1f3ff-2642": { - "output": "1f6b5-1f3ff-200d-2642-fe0f", - "name": "man mountain biking: dark skin tone", - "alpha_code": ":man_mountain_biking_tone5:", - "aliases": ":man_mountain_biking_dark_skin_tone:" - }, - "1f6b5-1f3fe-2642": { - "output": "1f6b5-1f3fe-200d-2642-fe0f", - "name": "man mountain biking: medium-dark skin tone", - "alpha_code": ":man_mountain_biking_tone4:", - "aliases": ":man_mountain_biking_medium_dark_skin_tone:" - }, - "1f6b5-1f3fd-2642": { - "output": "1f6b5-1f3fd-200d-2642-fe0f", - "name": "man mountain biking: medium skin tone", - "alpha_code": ":man_mountain_biking_tone3:", - "aliases": ":man_mountain_biking_medium_skin_tone:" - }, - "1f6b5-1f3fc-2642": { - "output": "1f6b5-1f3fc-200d-2642-fe0f", - "name": "man mountain biking: medium-light skin tone", - "alpha_code": ":man_mountain_biking_tone2:", - "aliases": ":man_mountain_biking_medium_light_skin_tone:" - }, - "1f6b5-1f3fb-2642": { - "output": "1f6b5-1f3fb-200d-2642-fe0f", - "name": "man mountain biking: light skin tone", - "alpha_code": ":man_mountain_biking_tone1:", - "aliases": ":man_mountain_biking_light_skin_tone:" - }, - "1f6b5-2642": { - "output": "1f6b5-200d-2642-fe0f", - "name": "man mountain biking", - "alpha_code": ":man_mountain_biking:", - "aliases": "" - }, - "1f6b5-1f3ff-2640": { - "output": "1f6b5-1f3ff-200d-2640-fe0f", - "name": "woman mountain biking: dark skin tone", - "alpha_code": ":woman_mountain_biking_tone5:", - "aliases": ":woman_mountain_biking_dark_skin_tone:" - }, - "1f6b5-1f3fe-2640": { - "output": "1f6b5-1f3fe-200d-2640-fe0f", - "name": "woman mountain biking: medium-dark skin tone", - "alpha_code": ":woman_mountain_biking_tone4:", - "aliases": ":woman_mountain_biking_medium_dark_skin_tone:" - }, - "1f6b5-1f3fd-2640": { - "output": "1f6b5-1f3fd-200d-2640-fe0f", - "name": "woman mountain biking: medium skin tone", - "alpha_code": ":woman_mountain_biking_tone3:", - "aliases": ":woman_mountain_biking_medium_skin_tone:" - }, - "1f6b5-1f3fc-2640": { - "output": "1f6b5-1f3fc-200d-2640-fe0f", - "name": "woman mountain biking: medium-light skin tone", - "alpha_code": ":woman_mountain_biking_tone2:", - "aliases": ":woman_mountain_biking_medium_light_skin_tone:" - }, - "1f6b5-1f3fb-2640": { - "output": "1f6b5-1f3fb-200d-2640-fe0f", - "name": "woman mountain biking: light skin tone", - "alpha_code": ":woman_mountain_biking_tone1:", - "aliases": ":woman_mountain_biking_light_skin_tone:" - }, - "1f6b5-2640": { - "output": "1f6b5-200d-2640-fe0f", - "name": "woman mountain biking", - "alpha_code": ":woman_mountain_biking:", - "aliases": "" - }, - "1f6b4-1f3ff-2642": { - "output": "1f6b4-1f3ff-200d-2642-fe0f", - "name": "man biking: dark skin tone", - "alpha_code": ":man_biking_tone5:", - "aliases": ":man_biking_dark_skin_tone:" - }, - "1f6b4-1f3fe-2642": { - "output": "1f6b4-1f3fe-200d-2642-fe0f", - "name": "man biking: medium-dark skin tone", - "alpha_code": ":man_biking_tone4:", - "aliases": ":man_biking_medium_dark_skin_tone:" - }, - "1f6b4-1f3fd-2642": { - "output": "1f6b4-1f3fd-200d-2642-fe0f", - "name": "man biking: medium skin tone", - "alpha_code": ":man_biking_tone3:", - "aliases": ":man_biking_medium_skin_tone:" - }, - "1f6b4-1f3fc-2642": { - "output": "1f6b4-1f3fc-200d-2642-fe0f", - "name": "man biking: medium-light skin tone", - "alpha_code": ":man_biking_tone2:", - "aliases": ":man_biking_medium_light_skin_tone:" - }, - "1f6b4-1f3fb-2642": { - "output": "1f6b4-1f3fb-200d-2642-fe0f", - "name": "man biking: light skin tone", - "alpha_code": ":man_biking_tone1:", - "aliases": ":man_biking_light_skin_tone:" - }, - "1f6b4-2642": { - "output": "1f6b4-200d-2642-fe0f", - "name": "man biking", - "alpha_code": ":man_biking:", - "aliases": "" - }, - "1f6b4-1f3ff-2640": { - "output": "1f6b4-1f3ff-200d-2640-fe0f", - "name": "woman biking: dark skin tone", - "alpha_code": ":woman_biking_tone5:", - "aliases": ":woman_biking_dark_skin_tone:" - }, - "1f6b4-1f3fe-2640": { - "output": "1f6b4-1f3fe-200d-2640-fe0f", - "name": "woman biking: medium-dark skin tone", - "alpha_code": ":woman_biking_tone4:", - "aliases": ":woman_biking_medium_dark_skin_tone:" - }, - "1f6b4-1f3fd-2640": { - "output": "1f6b4-1f3fd-200d-2640-fe0f", - "name": "woman biking: medium skin tone", - "alpha_code": ":woman_biking_tone3:", - "aliases": ":woman_biking_medium_skin_tone:" - }, - "1f6b4-1f3fc-2640": { - "output": "1f6b4-1f3fc-200d-2640-fe0f", - "name": "woman biking: medium-light skin tone", - "alpha_code": ":woman_biking_tone2:", - "aliases": ":woman_biking_medium_light_skin_tone:" - }, - "1f6b4-1f3fb-2640": { - "output": "1f6b4-1f3fb-200d-2640-fe0f", - "name": "woman biking: light skin tone", - "alpha_code": ":woman_biking_tone1:", - "aliases": ":woman_biking_light_skin_tone:" - }, - "1f6b4-2640": { - "output": "1f6b4-200d-2640-fe0f", - "name": "woman biking", - "alpha_code": ":woman_biking:", - "aliases": "" - }, - "1f6a3-1f3ff-2642": { - "output": "1f6a3-1f3ff-200d-2642-fe0f", - "name": "man rowing boat: dark skin tone", - "alpha_code": ":man_rowing_boat_tone5:", - "aliases": ":man_rowing_boat_dark_skin_tone:" - }, - "1f6a3-1f3fe-2642": { - "output": "1f6a3-1f3fe-200d-2642-fe0f", - "name": "man rowing boat: medium-dark skin tone", - "alpha_code": ":man_rowing_boat_tone4:", - "aliases": ":man_rowing_boat_medium_dark_skin_tone:" - }, - "1f6a3-1f3fd-2642": { - "output": "1f6a3-1f3fd-200d-2642-fe0f", - "name": "man rowing boat: medium skin tone", - "alpha_code": ":man_rowing_boat_tone3:", - "aliases": ":man_rowing_boat_medium_skin_tone:" - }, - "1f6a3-1f3fc-2642": { - "output": "1f6a3-1f3fc-200d-2642-fe0f", - "name": "man rowing boat: medium-light skin tone", - "alpha_code": ":man_rowing_boat_tone2:", - "aliases": ":man_rowing_boat_medium_light_skin_tone:" - }, - "1f6a3-1f3fb-2642": { - "output": "1f6a3-1f3fb-200d-2642-fe0f", - "name": "man rowing boat: light skin tone", - "alpha_code": ":man_rowing_boat_tone1:", - "aliases": ":man_rowing_boat_light_skin_tone:" - }, - "1f6a3-2642": { - "output": "1f6a3-200d-2642-fe0f", - "name": "man rowing boat", - "alpha_code": ":man_rowing_boat:", - "aliases": "" - }, - "1f6a3-1f3ff-2640": { - "output": "1f6a3-1f3ff-200d-2640-fe0f", - "name": "woman rowing boat: dark skin tone", - "alpha_code": ":woman_rowing_boat_tone5:", - "aliases": ":woman_rowing_boat_dark_skin_tone:" - }, - "1f6a3-1f3fe-2640": { - "output": "1f6a3-1f3fe-200d-2640-fe0f", - "name": "woman rowing boat: medium-dark skin tone", - "alpha_code": ":woman_rowing_boat_tone4:", - "aliases": ":woman_rowing_boat_medium_dark_skin_tone:" - }, - "1f6a3-1f3fd-2640": { - "output": "1f6a3-1f3fd-200d-2640-fe0f", - "name": "woman rowing boat: medium skin tone", - "alpha_code": ":woman_rowing_boat_tone3:", - "aliases": ":woman_rowing_boat_medium_skin_tone:" - }, - "1f6a3-1f3fc-2640": { - "output": "1f6a3-1f3fc-200d-2640-fe0f", - "name": "woman rowing boat: medium-light skin tone", - "alpha_code": ":woman_rowing_boat_tone2:", - "aliases": ":woman_rowing_boat_medium_light_skin_tone:" - }, - "1f6a3-1f3fb-2640": { - "output": "1f6a3-1f3fb-200d-2640-fe0f", - "name": "woman rowing boat: light skin tone", - "alpha_code": ":woman_rowing_boat_tone1:", - "aliases": ":woman_rowing_boat_light_skin_tone:" - }, - "1f6a3-2640": { - "output": "1f6a3-200d-2640-fe0f", - "name": "woman rowing boat", - "alpha_code": ":woman_rowing_boat:", - "aliases": "" - }, - "1f3cb-1f3ff-2642": { - "output": "1f3cb-1f3ff-200d-2642-fe0f", - "name": "man lifting weights: dark skin tone", - "alpha_code": ":man_lifting_weights_tone5:", - "aliases": ":man_lifting_weights_dark_skin_tone:" - }, - "1f3cb-1f3fe-2642": { - "output": "1f3cb-1f3fe-200d-2642-fe0f", - "name": "man lifting weights: medium-dark skin tone", - "alpha_code": ":man_lifting_weights_tone4:", - "aliases": ":man_lifting_weights_medium_dark_skin_tone:" - }, - "1f3cb-1f3fd-2642": { - "output": "1f3cb-1f3fd-200d-2642-fe0f", - "name": "man lifting weights: medium skin tone", - "alpha_code": ":man_lifting_weights_tone3:", - "aliases": ":man_lifting_weights_medium_skin_tone:" - }, - "1f3cb-1f3fc-2642": { - "output": "1f3cb-1f3fc-200d-2642-fe0f", - "name": "man lifting weights: medium-light skin tone", - "alpha_code": ":man_lifting_weights_tone2:", - "aliases": ":man_lifting_weights_medium_light_skin_tone:" - }, - "1f3cb-1f3fb-2642": { - "output": "1f3cb-1f3fb-200d-2642-fe0f", - "name": "man lifting weights: light skin tone", - "alpha_code": ":man_lifting_weights_tone1:", - "aliases": ":man_lifting_weights_light_skin_tone:" - }, - "1f3cb-2642": { - "output": "1f3cb-fe0f-200d-2642-fe0f", - "name": "man lifting weights", - "alpha_code": ":man_lifting_weights:", - "aliases": "" - }, - "1f3cb-1f3ff-2640": { - "output": "1f3cb-1f3ff-200d-2640-fe0f", - "name": "woman lifting weights: dark skin tone", - "alpha_code": ":woman_lifting_weights_tone5:", - "aliases": ":woman_lifting_weights_dark_skin_tone:" - }, - "1f3cb-1f3fe-2640": { - "output": "1f3cb-1f3fe-200d-2640-fe0f", - "name": "woman lifting weights: medium-dark skin tone", - "alpha_code": ":woman_lifting_weights_tone4:", - "aliases": ":woman_lifting_weights_medium_dark_skin_tone:" - }, - "1f3cb-1f3fd-2640": { - "output": "1f3cb-1f3fd-200d-2640-fe0f", - "name": "woman lifting weights: medium skin tone", - "alpha_code": ":woman_lifting_weights_tone3:", - "aliases": ":woman_lifting_weights_medium_skin_tone:" - }, - "1f3cb-1f3fc-2640": { - "output": "1f3cb-1f3fc-200d-2640-fe0f", - "name": "woman lifting weights: medium-light skin tone", - "alpha_code": ":woman_lifting_weights_tone2:", - "aliases": ":woman_lifting_weights_medium_light_skin_tone:" - }, - "1f3cb-1f3fb-2640": { - "output": "1f3cb-1f3fb-200d-2640-fe0f", - "name": "woman lifting weights: light skin tone", - "alpha_code": ":woman_lifting_weights_tone1:", - "aliases": ":woman_lifting_weights_light_skin_tone:" - }, - "1f3cb-2640": { - "output": "1f3cb-fe0f-200d-2640-fe0f", - "name": "woman lifting weights", - "alpha_code": ":woman_lifting_weights:", - "aliases": "" - }, - "1f3ca-1f3ff-2642": { - "output": "1f3ca-1f3ff-200d-2642-fe0f", - "name": "man swimming: dark skin tone", - "alpha_code": ":man_swimming_tone5:", - "aliases": ":man_swimming_dark_skin_tone:" - }, - "1f3ca-1f3fe-2642": { - "output": "1f3ca-1f3fe-200d-2642-fe0f", - "name": "man swimming: medium-dark skin tone", - "alpha_code": ":man_swimming_tone4:", - "aliases": ":man_swimming_medium_dark_skin_tone:" - }, - "1f3ca-1f3fd-2642": { - "output": "1f3ca-1f3fd-200d-2642-fe0f", - "name": "man swimming: medium skin tone", - "alpha_code": ":man_swimming_tone3:", - "aliases": ":man_swimming_medium_skin_tone:" - }, - "1f3ca-1f3fc-2642": { - "output": "1f3ca-1f3fc-200d-2642-fe0f", - "name": "man swimming: medium-light skin tone", - "alpha_code": ":man_swimming_tone2:", - "aliases": ":man_swimming_medium_light_skin_tone:" - }, - "1f3ca-1f3fb-2642": { - "output": "1f3ca-1f3fb-200d-2642-fe0f", - "name": "man swimming: light skin tone", - "alpha_code": ":man_swimming_tone1:", - "aliases": ":man_swimming_light_skin_tone:" - }, - "1f3ca-2642": { - "output": "1f3ca-200d-2642-fe0f", - "name": "man swimming", - "alpha_code": ":man_swimming:", - "aliases": "" - }, - "1f3ca-1f3ff-2640": { - "output": "1f3ca-1f3ff-200d-2640-fe0f", - "name": "woman swimming: dark skin tone", - "alpha_code": ":woman_swimming_tone5:", - "aliases": ":woman_swimming_dark_skin_tone:" - }, - "1f3ca-1f3fe-2640": { - "output": "1f3ca-1f3fe-200d-2640-fe0f", - "name": "woman swimming: medium-dark skin tone", - "alpha_code": ":woman_swimming_tone4:", - "aliases": ":woman_swimming_medium_dark_skin_tone:" - }, - "1f3ca-1f3fd-2640": { - "output": "1f3ca-1f3fd-200d-2640-fe0f", - "name": "woman swimming: medium skin tone", - "alpha_code": ":woman_swimming_tone3:", - "aliases": ":woman_swimming_medium_skin_tone:" - }, - "1f3ca-1f3fc-2640": { - "output": "1f3ca-1f3fc-200d-2640-fe0f", - "name": "woman swimming: medium-light skin tone", - "alpha_code": ":woman_swimming_tone2:", - "aliases": ":woman_swimming_medium_light_skin_tone:" - }, - "1f3ca-1f3fb-2640": { - "output": "1f3ca-1f3fb-200d-2640-fe0f", - "name": "woman swimming: light skin tone", - "alpha_code": ":woman_swimming_tone1:", - "aliases": ":woman_swimming_light_skin_tone:" - }, - "1f3ca-2640": { - "output": "1f3ca-200d-2640-fe0f", - "name": "woman swimming", - "alpha_code": ":woman_swimming:", - "aliases": "" - }, - "1f3c4-1f3ff-2642": { - "output": "1f3c4-1f3ff-200d-2642-fe0f", - "name": "man surfing: dark skin tone", - "alpha_code": ":man_surfing_tone5:", - "aliases": ":man_surfing_dark_skin_tone:" - }, - "1f3c4-1f3fe-2642": { - "output": "1f3c4-1f3fe-200d-2642-fe0f", - "name": "man surfing: medium-dark skin tone", - "alpha_code": ":man_surfing_tone4:", - "aliases": ":man_surfing_medium_dark_skin_tone:" - }, - "1f3c4-1f3fd-2642": { - "output": "1f3c4-1f3fd-200d-2642-fe0f", - "name": "man surfing: medium skin tone", - "alpha_code": ":man_surfing_tone3:", - "aliases": ":man_surfing_medium_skin_tone:" - }, - "1f3c4-1f3fc-2642": { - "output": "1f3c4-1f3fc-200d-2642-fe0f", - "name": "man surfing: medium-light skin tone", - "alpha_code": ":man_surfing_tone2:", - "aliases": ":man_surfing_medium_light_skin_tone:" - }, - "1f3c4-1f3fb-2642": { - "output": "1f3c4-1f3fb-200d-2642-fe0f", - "name": "man surfing: light skin tone", - "alpha_code": ":man_surfing_tone1:", - "aliases": ":man_surfing_light_skin_tone:" - }, - "1f3c4-2642": { - "output": "1f3c4-200d-2642-fe0f", - "name": "man surfing", - "alpha_code": ":man_surfing:", - "aliases": "" - }, - "1f3c4-1f3ff-2640": { - "output": "1f3c4-1f3ff-200d-2640-fe0f", - "name": "woman surfing: dark skin tone", - "alpha_code": ":woman_surfing_tone5:", - "aliases": ":woman_surfing_dark_skin_tone:" - }, - "1f3c4-1f3fe-2640": { - "output": "1f3c4-1f3fe-200d-2640-fe0f", - "name": "woman surfing: medium-dark skin tone", - "alpha_code": ":woman_surfing_tone4:", - "aliases": ":woman_surfing_medium_dark_skin_tone:" - }, - "1f3c4-1f3fd-2640": { - "output": "1f3c4-1f3fd-200d-2640-fe0f", - "name": "woman surfing: medium skin tone", - "alpha_code": ":woman_surfing_tone3:", - "aliases": ":woman_surfing_medium_skin_tone:" - }, - "1f3c4-1f3fc-2640": { - "output": "1f3c4-1f3fc-200d-2640-fe0f", - "name": "woman surfing: medium-light skin tone", - "alpha_code": ":woman_surfing_tone2:", - "aliases": ":woman_surfing_medium_light_skin_tone:" - }, - "1f3c4-1f3fb-2640": { - "output": "1f3c4-1f3fb-200d-2640-fe0f", - "name": "woman surfing: light skin tone", - "alpha_code": ":woman_surfing_tone1:", - "aliases": ":woman_surfing_light_skin_tone:" - }, - "1f3c4-2640": { - "output": "1f3c4-200d-2640-fe0f", - "name": "woman surfing", - "alpha_code": ":woman_surfing:", - "aliases": "" - }, - "1f3c3-1f3ff-2642": { - "output": "1f3c3-1f3ff-200d-2642-fe0f", - "name": "man running: dark skin tone", - "alpha_code": ":man_running_tone5:", - "aliases": ":man_running_dark_skin_tone:" - }, - "1f3c3-1f3fe-2642": { - "output": "1f3c3-1f3fe-200d-2642-fe0f", - "name": "man running: medium-dark skin tone", - "alpha_code": ":man_running_tone4:", - "aliases": ":man_running_medium_dark_skin_tone:" - }, - "1f3c3-1f3fd-2642": { - "output": "1f3c3-1f3fd-200d-2642-fe0f", - "name": "man running: medium skin tone", - "alpha_code": ":man_running_tone3:", - "aliases": ":man_running_medium_skin_tone:" - }, - "1f3c3-1f3fc-2642": { - "output": "1f3c3-1f3fc-200d-2642-fe0f", - "name": "man running: medium-light skin tone", - "alpha_code": ":man_running_tone2:", - "aliases": ":man_running_medium_light_skin_tone:" - }, - "1f3c3-1f3fb-2642": { - "output": "1f3c3-1f3fb-200d-2642-fe0f", - "name": "man running: light skin tone", - "alpha_code": ":man_running_tone1:", - "aliases": ":man_running_light_skin_tone:" - }, - "1f3c3-2642": { - "output": "1f3c3-200d-2642-fe0f", - "name": "man running", - "alpha_code": ":man_running:", - "aliases": "" - }, - "1f3c3-1f3ff-2640": { - "output": "1f3c3-1f3ff-200d-2640-fe0f", - "name": "woman running: dark skin tone", - "alpha_code": ":woman_running_tone5:", - "aliases": ":woman_running_dark_skin_tone:" - }, - "1f3c3-1f3fe-2640": { - "output": "1f3c3-1f3fe-200d-2640-fe0f", - "name": "woman running: medium-dark skin tone", - "alpha_code": ":woman_running_tone4:", - "aliases": ":woman_running_medium_dark_skin_tone:" - }, - "1f3c3-1f3fd-2640": { - "output": "1f3c3-1f3fd-200d-2640-fe0f", - "name": "woman running: medium skin tone", - "alpha_code": ":woman_running_tone3:", - "aliases": ":woman_running_medium_skin_tone:" - }, - "1f3c3-1f3fc-2640": { - "output": "1f3c3-1f3fc-200d-2640-fe0f", - "name": "woman running: medium-light skin tone", - "alpha_code": ":woman_running_tone2:", - "aliases": ":woman_running_medium_light_skin_tone:" - }, - "1f3c3-1f3fb-2640": { - "output": "1f3c3-1f3fb-200d-2640-fe0f", - "name": "woman running: light skin tone", - "alpha_code": ":woman_running_tone1:", - "aliases": ":woman_running_light_skin_tone:" - }, - "1f3c3-2640": { - "output": "1f3c3-200d-2640-fe0f", - "name": "woman running", - "alpha_code": ":woman_running:", - "aliases": "" - }, - "26f9-1f3ff-2642": { - "output": "26f9-1f3ff-200d-2642-fe0f", - "name": "man bouncing ball: dark skin tone", - "alpha_code": ":man_bouncing_ball_tone5:", - "aliases": ":man_bouncing_ball_dark_skin_tone:" - }, - "26f9-1f3fe-2642": { - "output": "26f9-1f3fe-200d-2642-fe0f", - "name": "man bouncing ball: medium-dark skin tone", - "alpha_code": ":man_bouncing_ball_tone4:", - "aliases": ":man_bouncing_ball_medium_dark_skin_tone:" - }, - "26f9-1f3fd-2642": { - "output": "26f9-1f3fd-200d-2642-fe0f", - "name": "man bouncing ball: medium skin tone", - "alpha_code": ":man_bouncing_ball_tone3:", - "aliases": ":man_bouncing_ball_medium_skin_tone:" - }, - "26f9-1f3fc-2642": { - "output": "26f9-1f3fc-200d-2642-fe0f", - "name": "man bouncing ball: medium-light skin tone", - "alpha_code": ":man_bouncing_ball_tone2:", - "aliases": ":man_bouncing_ball_medium_light_skin_tone:" - }, - "26f9-1f3fb-2642": { - "output": "26f9-1f3fb-200d-2642-fe0f", - "name": "man bouncing ball: light skin tone", - "alpha_code": ":man_bouncing_ball_tone1:", - "aliases": ":man_bouncing_ball_light_skin_tone:" - }, - "26f9-2642": { - "output": "26f9-fe0f-200d-2642-fe0f", - "name": "man bouncing ball", - "alpha_code": ":man_bouncing_ball:", - "aliases": "" - }, - "26f9-1f3ff-2640": { - "output": "26f9-1f3ff-200d-2640-fe0f", - "name": "woman bouncing ball: dark skin tone", - "alpha_code": ":woman_bouncing_ball_tone5:", - "aliases": ":woman_bouncing_ball_dark_skin_tone:" - }, - "26f9-1f3fe-2640": { - "output": "26f9-1f3fe-200d-2640-fe0f", - "name": "woman bouncing ball: medium-dark skin tone", - "alpha_code": ":woman_bouncing_ball_tone4:", - "aliases": ":woman_bouncing_ball_medium_dark_skin_tone:" - }, - "26f9-1f3fd-2640": { - "output": "26f9-1f3fd-200d-2640-fe0f", - "name": "woman bouncing ball: medium skin tone", - "alpha_code": ":woman_bouncing_ball_tone3:", - "aliases": ":woman_bouncing_ball_medium_skin_tone:" - }, - "26f9-1f3fc-2640": { - "output": "26f9-1f3fc-200d-2640-fe0f", - "name": "woman bouncing ball: medium-light skin tone", - "alpha_code": ":woman_bouncing_ball_tone2:", - "aliases": ":woman_bouncing_ball_medium_light_skin_tone:" - }, - "26f9-1f3fb-2640": { - "output": "26f9-1f3fb-200d-2640-fe0f", - "name": "woman bouncing ball: light skin tone", - "alpha_code": ":woman_bouncing_ball_tone1:", - "aliases": ":woman_bouncing_ball_light_skin_tone:" - }, - "26f9-2640": { - "output": "26f9-fe0f-200d-2640-fe0f", - "name": "woman bouncing ball", - "alpha_code": ":woman_bouncing_ball:", - "aliases": "" - }, - "1f937-1f3ff-2642": { - "output": "1f937-1f3ff-200d-2642-fe0f", - "name": "man shrugging: dark skin tone", - "alpha_code": ":man_shrugging_tone5:", - "aliases": ":man_shrugging_dark_skin_tone:" - }, - "1f937-1f3fe-2642": { - "output": "1f937-1f3fe-200d-2642-fe0f", - "name": "man shrugging: medium-dark skin tone", - "alpha_code": ":man_shrugging_tone4:", - "aliases": ":man_shrugging_medium_dark_skin_tone:" - }, - "1f937-1f3fd-2642": { - "output": "1f937-1f3fd-200d-2642-fe0f", - "name": "man shrugging: medium skin tone", - "alpha_code": ":man_shrugging_tone3:", - "aliases": ":man_shrugging_medium_skin_tone:" - }, - "1f937-1f3fc-2642": { - "output": "1f937-1f3fc-200d-2642-fe0f", - "name": "man shrugging: medium-light skin tone", - "alpha_code": ":man_shrugging_tone2:", - "aliases": ":man_shrugging_medium_light_skin_tone:" - }, - "1f937-1f3fb-2642": { - "output": "1f937-1f3fb-200d-2642-fe0f", - "name": "man shrugging: light skin tone", - "alpha_code": ":man_shrugging_tone1:", - "aliases": ":man_shrugging_light_skin_tone:" - }, - "1f937-2642": { - "output": "1f937-200d-2642-fe0f", - "name": "man shrugging", - "alpha_code": ":man_shrugging:", - "aliases": "" - }, - "1f937-1f3ff-2640": { - "output": "1f937-1f3ff-200d-2640-fe0f", - "name": "woman shrugging: dark skin tone", - "alpha_code": ":woman_shrugging_tone5:", - "aliases": ":woman_shrugging_dark_skin_tone:" - }, - "1f937-1f3fe-2640": { - "output": "1f937-1f3fe-200d-2640-fe0f", - "name": "woman shrugging: medium-dark skin tone", - "alpha_code": ":woman_shrugging_tone4:", - "aliases": ":woman_shrugging_medium_dark_skin_tone:" - }, - "1f937-1f3fd-2640": { - "output": "1f937-1f3fd-200d-2640-fe0f", - "name": "woman shrugging: medium skin tone", - "alpha_code": ":woman_shrugging_tone3:", - "aliases": ":woman_shrugging_medium_skin_tone:" - }, - "1f937-1f3fc-2640": { - "output": "1f937-1f3fc-200d-2640-fe0f", - "name": "woman shrugging: medium-light skin tone", - "alpha_code": ":woman_shrugging_tone2:", - "aliases": ":woman_shrugging_medium_light_skin_tone:" - }, - "1f937-1f3fb-2640": { - "output": "1f937-1f3fb-200d-2640-fe0f", - "name": "woman shrugging: light skin tone", - "alpha_code": ":woman_shrugging_tone1:", - "aliases": ":woman_shrugging_light_skin_tone:" - }, - "1f937-2640": { - "output": "1f937-200d-2640-fe0f", - "name": "woman shrugging", - "alpha_code": ":woman_shrugging:", - "aliases": "" - }, - "1f926-1f3ff-2642": { - "output": "1f926-1f3ff-200d-2642-fe0f", - "name": "man facepalming: dark skin tone", - "alpha_code": ":man_facepalming_tone5:", - "aliases": ":man_facepalming_dark_skin_tone:" - }, - "1f926-1f3fe-2642": { - "output": "1f926-1f3fe-200d-2642-fe0f", - "name": "man facepalming: medium-dark skin tone", - "alpha_code": ":man_facepalming_tone4:", - "aliases": ":man_facepalming_medium_dark_skin_tone:" - }, - "1f926-1f3fd-2642": { - "output": "1f926-1f3fd-200d-2642-fe0f", - "name": "man facepalming: medium skin tone", - "alpha_code": ":man_facepalming_tone3:", - "aliases": ":man_facepalming_medium_skin_tone:" - }, - "1f926-1f3fc-2642": { - "output": "1f926-1f3fc-200d-2642-fe0f", - "name": "man facepalming: medium-light skin tone", - "alpha_code": ":man_facepalming_tone2:", - "aliases": ":man_facepalming_medium_light_skin_tone:" - }, - "1f926-1f3fb-2642": { - "output": "1f926-1f3fb-200d-2642-fe0f", - "name": "man facepalming: light skin tone", - "alpha_code": ":man_facepalming_tone1:", - "aliases": ":man_facepalming_light_skin_tone:" - }, - "1f926-2642": { - "output": "1f926-200d-2642-fe0f", - "name": "man facepalming", - "alpha_code": ":man_facepalming:", - "aliases": "" - }, - "1f926-1f3ff-2640": { - "output": "1f926-1f3ff-200d-2640-fe0f", - "name": "woman facepalming: dark skin tone", - "alpha_code": ":woman_facepalming_tone5:", - "aliases": ":woman_facepalming_dark_skin_tone:" - }, - "1f926-1f3fe-2640": { - "output": "1f926-1f3fe-200d-2640-fe0f", - "name": "woman facepalming: medium-dark skin tone", - "alpha_code": ":woman_facepalming_tone4:", - "aliases": ":woman_facepalming_medium_dark_skin_tone:" - }, - "1f926-1f3fd-2640": { - "output": "1f926-1f3fd-200d-2640-fe0f", - "name": "woman facepalming: medium skin tone", - "alpha_code": ":woman_facepalming_tone3:", - "aliases": ":woman_facepalming_medium_skin_tone:" - }, - "1f926-1f3fc-2640": { - "output": "1f926-1f3fc-200d-2640-fe0f", - "name": "woman facepalming: medium-light skin tone", - "alpha_code": ":woman_facepalming_tone2:", - "aliases": ":woman_facepalming_medium_light_skin_tone:" - }, - "1f926-1f3fb-2640": { - "output": "1f926-1f3fb-200d-2640-fe0f", - "name": "woman facepalming: light skin tone", - "alpha_code": ":woman_facepalming_tone1:", - "aliases": ":woman_facepalming_light_skin_tone:" - }, - "1f926-2640": { - "output": "1f926-200d-2640-fe0f", - "name": "woman facepalming", - "alpha_code": ":woman_facepalming:", - "aliases": "" - }, - "1f64e-1f3ff-2642": { - "output": "1f64e-1f3ff-200d-2642-fe0f", - "name": "man pouting: dark skin tone", - "alpha_code": ":man_pouting_tone5:", - "aliases": ":man_pouting_dark_skin_tone:" - }, - "1f64e-1f3fe-2642": { - "output": "1f64e-1f3fe-200d-2642-fe0f", - "name": "man pouting: medium-dark skin tone", - "alpha_code": ":man_pouting_tone4:", - "aliases": ":man_pouting_medium_dark_skin_tone:" - }, - "1f64e-1f3fd-2642": { - "output": "1f64e-1f3fd-200d-2642-fe0f", - "name": "man pouting: medium skin tone", - "alpha_code": ":man_pouting_tone3:", - "aliases": ":man_pouting_medium_skin_tone:" - }, - "1f64e-1f3fc-2642": { - "output": "1f64e-1f3fc-200d-2642-fe0f", - "name": "man pouting: medium-light skin tone", - "alpha_code": ":man_pouting_tone2:", - "aliases": ":man_pouting_medium_light_skin_tone:" - }, - "1f64e-1f3fb-2642": { - "output": "1f64e-1f3fb-200d-2642-fe0f", - "name": "man pouting: light skin tone", - "alpha_code": ":man_pouting_tone1:", - "aliases": ":man_pouting_light_skin_tone:" - }, - "1f64e-2642": { - "output": "1f64e-200d-2642-fe0f", - "name": "man pouting", - "alpha_code": ":man_pouting:", - "aliases": "" - }, - "1f64e-1f3ff-2640": { - "output": "1f64e-1f3ff-200d-2640-fe0f", - "name": "woman pouting: dark skin tone", - "alpha_code": ":woman_pouting_tone5:", - "aliases": ":woman_pouting_dark_skin_tone:" - }, - "1f64e-1f3fe-2640": { - "output": "1f64e-1f3fe-200d-2640-fe0f", - "name": "woman pouting: medium-dark skin tone", - "alpha_code": ":woman_pouting_tone4:", - "aliases": ":woman_pouting_medium_dark_skin_tone:" - }, - "1f64e-1f3fd-2640": { - "output": "1f64e-1f3fd-200d-2640-fe0f", - "name": "woman pouting: medium skin tone", - "alpha_code": ":woman_pouting_tone3:", - "aliases": ":woman_pouting_medium_skin_tone:" - }, - "1f64e-1f3fc-2640": { - "output": "1f64e-1f3fc-200d-2640-fe0f", - "name": "woman pouting: medium-light skin tone", - "alpha_code": ":woman_pouting_tone2:", - "aliases": ":woman_pouting_medium_light_skin_tone:" - }, - "1f64e-1f3fb-2640": { - "output": "1f64e-1f3fb-200d-2640-fe0f", - "name": "woman pouting: light skin tone", - "alpha_code": ":woman_pouting_tone1:", - "aliases": ":woman_pouting_light_skin_tone:" - }, - "1f64e-2640": { - "output": "1f64e-200d-2640-fe0f", - "name": "woman pouting", - "alpha_code": ":woman_pouting:", - "aliases": "" - }, - "1f64d-1f3ff-2642": { - "output": "1f64d-1f3ff-200d-2642-fe0f", - "name": "man frowning: dark skin tone", - "alpha_code": ":man_frowning_tone5:", - "aliases": ":man_frowning_dark_skin_tone:" - }, - "1f64d-1f3fe-2642": { - "output": "1f64d-1f3fe-200d-2642-fe0f", - "name": "man frowning: medium-dark skin tone", - "alpha_code": ":man_frowning_tone4:", - "aliases": ":man_frowning_medium_dark_skin_tone:" - }, - "1f64d-1f3fd-2642": { - "output": "1f64d-1f3fd-200d-2642-fe0f", - "name": "man frowning: medium skin tone", - "alpha_code": ":man_frowning_tone3:", - "aliases": ":man_frowning_medium_skin_tone:" - }, - "1f64d-1f3fc-2642": { - "output": "1f64d-1f3fc-200d-2642-fe0f", - "name": "man frowning: medium-light skin tone", - "alpha_code": ":man_frowning_tone2:", - "aliases": ":man_frowning_medium_light_skin_tone:" - }, - "1f64d-1f3fb-2642": { - "output": "1f64d-1f3fb-200d-2642-fe0f", - "name": "man frowning: light skin tone", - "alpha_code": ":man_frowning_tone1:", - "aliases": ":man_frowning_light_skin_tone:" - }, - "1f64d-2642": { - "output": "1f64d-200d-2642-fe0f", - "name": "man frowning", - "alpha_code": ":man_frowning:", - "aliases": "" - }, - "1f64d-1f3ff-2640": { - "output": "1f64d-1f3ff-200d-2640-fe0f", - "name": "woman frowning: dark skin tone", - "alpha_code": ":woman_frowning_tone5:", - "aliases": ":woman_frowning_dark_skin_tone:" - }, - "1f64d-1f3fe-2640": { - "output": "1f64d-1f3fe-200d-2640-fe0f", - "name": "woman frowning: medium-dark skin tone", - "alpha_code": ":woman_frowning_tone4:", - "aliases": ":woman_frowning_medium_dark_skin_tone:" - }, - "1f64d-1f3fd-2640": { - "output": "1f64d-1f3fd-200d-2640-fe0f", - "name": "woman frowning: medium skin tone", - "alpha_code": ":woman_frowning_tone3:", - "aliases": ":woman_frowning_medium_skin_tone:" - }, - "1f64d-1f3fc-2640": { - "output": "1f64d-1f3fc-200d-2640-fe0f", - "name": "woman frowning: medium-light skin tone", - "alpha_code": ":woman_frowning_tone2:", - "aliases": ":woman_frowning_medium_light_skin_tone:" - }, - "1f64d-1f3fb-2640": { - "output": "1f64d-1f3fb-200d-2640-fe0f", - "name": "woman frowning: light skin tone", - "alpha_code": ":woman_frowning_tone1:", - "aliases": ":woman_frowning_light_skin_tone:" - }, - "1f64d-2640": { - "output": "1f64d-200d-2640-fe0f", - "name": "woman frowning", - "alpha_code": ":woman_frowning:", - "aliases": "" - }, - "1f64b-1f3ff-2642": { - "output": "1f64b-1f3ff-200d-2642-fe0f", - "name": "man raising hand: dark skin tone", - "alpha_code": ":man_raising_hand_tone5:", - "aliases": ":man_raising_hand_dark_skin_tone:" - }, - "1f64b-1f3fe-2642": { - "output": "1f64b-1f3fe-200d-2642-fe0f", - "name": "man raising hand: medium-dark skin tone", - "alpha_code": ":man_raising_hand_tone4:", - "aliases": ":man_raising_hand_medium_dark_skin_tone:" - }, - "1f64b-1f3fd-2642": { - "output": "1f64b-1f3fd-200d-2642-fe0f", - "name": "man raising hand: medium skin tone", - "alpha_code": ":man_raising_hand_tone3:", - "aliases": ":man_raising_hand_medium_skin_tone:" - }, - "1f64b-1f3fc-2642": { - "output": "1f64b-1f3fc-200d-2642-fe0f", - "name": "man raising hand: medium-light skin tone", - "alpha_code": ":man_raising_hand_tone2:", - "aliases": ":man_raising_hand_medium_light_skin_tone:" - }, - "1f64b-1f3fb-2642": { - "output": "1f64b-1f3fb-200d-2642-fe0f", - "name": "man raising hand: light skin tone", - "alpha_code": ":man_raising_hand_tone1:", - "aliases": ":man_raising_hand_light_skin_tone:" - }, - "1f64b-2642": { - "output": "1f64b-200d-2642-fe0f", - "name": "man raising hand", - "alpha_code": ":man_raising_hand:", - "aliases": "" - }, - "1f64b-1f3ff-2640": { - "output": "1f64b-1f3ff-200d-2640-fe0f", - "name": "woman raising hand: dark skin tone", - "alpha_code": ":woman_raising_hand_tone5:", - "aliases": ":woman_raising_hand_dark_skin_tone:" - }, - "1f64b-1f3fe-2640": { - "output": "1f64b-1f3fe-200d-2640-fe0f", - "name": "woman raising hand: medium-dark skin tone", - "alpha_code": ":woman_raising_hand_tone4:", - "aliases": ":woman_raising_hand_medium_dark_skin_tone:" - }, - "1f64b-1f3fd-2640": { - "output": "1f64b-1f3fd-200d-2640-fe0f", - "name": "woman raising hand: medium skin tone", - "alpha_code": ":woman_raising_hand_tone3:", - "aliases": ":woman_raising_hand_medium_skin_tone:" - }, - "1f64b-1f3fc-2640": { - "output": "1f64b-1f3fc-200d-2640-fe0f", - "name": "woman raising hand: medium-light skin tone", - "alpha_code": ":woman_raising_hand_tone2:", - "aliases": ":woman_raising_hand_medium_light_skin_tone:" - }, - "1f64b-1f3fb-2640": { - "output": "1f64b-1f3fb-200d-2640-fe0f", - "name": "woman raising hand: light skin tone", - "alpha_code": ":woman_raising_hand_tone1:", - "aliases": ":woman_raising_hand_light_skin_tone:" - }, - "1f64b-2640": { - "output": "1f64b-200d-2640-fe0f", - "name": "woman raising hand", - "alpha_code": ":woman_raising_hand:", - "aliases": "" - }, - "1f647-1f3ff-2642": { - "output": "1f647-1f3ff-200d-2642-fe0f", - "name": "man bowing: dark skin tone", - "alpha_code": ":man_bowing_tone5:", - "aliases": ":man_bowing_dark_skin_tone:" - }, - "1f647-1f3fe-2642": { - "output": "1f647-1f3fe-200d-2642-fe0f", - "name": "man bowing: medium-dark skin tone", - "alpha_code": ":man_bowing_tone4:", - "aliases": ":man_bowing_medium_dark_skin_tone:" - }, - "1f647-1f3fd-2642": { - "output": "1f647-1f3fd-200d-2642-fe0f", - "name": "man bowing: medium skin tone", - "alpha_code": ":man_bowing_tone3:", - "aliases": ":man_bowing_medium_skin_tone:" - }, - "1f647-1f3fc-2642": { - "output": "1f647-1f3fc-200d-2642-fe0f", - "name": "man bowing: medium-light skin tone", - "alpha_code": ":man_bowing_tone2:", - "aliases": ":man_bowing_medium_light_skin_tone:" - }, - "1f647-1f3fb-2642": { - "output": "1f647-1f3fb-200d-2642-fe0f", - "name": "man bowing: light skin tone", - "alpha_code": ":man_bowing_tone1:", - "aliases": ":man_bowing_light_skin_tone:" - }, - "1f647-2642": { - "output": "1f647-200d-2642-fe0f", - "name": "man bowing", - "alpha_code": ":man_bowing:", - "aliases": "" - }, - "1f647-1f3ff-2640": { - "output": "1f647-1f3ff-200d-2640-fe0f", - "name": "woman bowing: dark skin tone", - "alpha_code": ":woman_bowing_tone5:", - "aliases": ":woman_bowing_dark_skin_tone:" - }, - "1f647-1f3fe-2640": { - "output": "1f647-1f3fe-200d-2640-fe0f", - "name": "woman bowing: medium-dark skin tone", - "alpha_code": ":woman_bowing_tone4:", - "aliases": ":woman_bowing_medium_dark_skin_tone:" - }, - "1f647-1f3fd-2640": { - "output": "1f647-1f3fd-200d-2640-fe0f", - "name": "woman bowing: medium skin tone", - "alpha_code": ":woman_bowing_tone3:", - "aliases": ":woman_bowing_medium_skin_tone:" - }, - "1f647-1f3fc-2640": { - "output": "1f647-1f3fc-200d-2640-fe0f", - "name": "woman bowing: medium-light skin tone", - "alpha_code": ":woman_bowing_tone2:", - "aliases": ":woman_bowing_medium_light_skin_tone:" - }, - "1f647-1f3fb-2640": { - "output": "1f647-1f3fb-200d-2640-fe0f", - "name": "woman bowing: light skin tone", - "alpha_code": ":woman_bowing_tone1:", - "aliases": ":woman_bowing_light_skin_tone:" - }, - "1f647-2640": { - "output": "1f647-200d-2640-fe0f", - "name": "woman bowing", - "alpha_code": ":woman_bowing:", - "aliases": "" - }, - "1f646-1f3ff-2642": { - "output": "1f646-1f3ff-200d-2642-fe0f", - "name": "man gesturing OK: dark skin tone", - "alpha_code": ":man_gesturing_ok_tone5:", - "aliases": ":man_gesturing_ok_dark_skin_tone:" - }, - "1f646-1f3fe-2642": { - "output": "1f646-1f3fe-200d-2642-fe0f", - "name": "man gesturing OK: medium-dark skin tone", - "alpha_code": ":man_gesturing_ok_tone4:", - "aliases": ":man_gesturing_ok_medium_dark_skin_tone:" - }, - "1f646-1f3fd-2642": { - "output": "1f646-1f3fd-200d-2642-fe0f", - "name": "man gesturing OK: medium skin tone", - "alpha_code": ":man_gesturing_ok_tone3:", - "aliases": ":man_gesturing_ok_medium_skin_tone:" - }, - "1f646-1f3fc-2642": { - "output": "1f646-1f3fc-200d-2642-fe0f", - "name": "man gesturing OK: medium-light skin tone", - "alpha_code": ":man_gesturing_ok_tone2:", - "aliases": ":man_gesturing_ok_medium_light_skin_tone:" - }, - "1f646-1f3fb-2642": { - "output": "1f646-1f3fb-200d-2642-fe0f", - "name": "man gesturing OK: light skin tone", - "alpha_code": ":man_gesturing_ok_tone1:", - "aliases": ":man_gesturing_ok_light_skin_tone:" - }, - "1f646-2642": { - "output": "1f646-200d-2642-fe0f", - "name": "man gesturing OK", - "alpha_code": ":man_gesturing_ok:", - "aliases": "" - }, - "1f646-1f3ff-2640": { - "output": "1f646-1f3ff-200d-2640-fe0f", - "name": "woman gesturing OK: dark skin tone", - "alpha_code": ":woman_gesturing_ok_tone5:", - "aliases": ":woman_gesturing_ok_dark_skin_tone:" - }, - "1f646-1f3fe-2640": { - "output": "1f646-1f3fe-200d-2640-fe0f", - "name": "woman gesturing OK: medium-dark skin tone", - "alpha_code": ":woman_gesturing_ok_tone4:", - "aliases": ":woman_gesturing_ok_medium_dark_skin_tone:" - }, - "1f646-1f3fd-2640": { - "output": "1f646-1f3fd-200d-2640-fe0f", - "name": "woman gesturing OK: medium skin tone", - "alpha_code": ":woman_gesturing_ok_tone3:", - "aliases": ":woman_gesturing_ok_medium_skin_tone:" - }, - "1f646-1f3fc-2640": { - "output": "1f646-1f3fc-200d-2640-fe0f", - "name": "woman gesturing OK: medium-light skin tone", - "alpha_code": ":woman_gesturing_ok_tone2:", - "aliases": ":woman_gesturing_ok_medium_light_skin_tone:" - }, - "1f646-1f3fb-2640": { - "output": "1f646-1f3fb-200d-2640-fe0f", - "name": "woman gesturing OK: light skin tone", - "alpha_code": ":woman_gesturing_ok_tone1:", - "aliases": ":woman_gesturing_ok_light_skin_tone:" - }, - "1f646-2640": { - "output": "1f646-200d-2640-fe0f", - "name": "woman gesturing OK", - "alpha_code": ":woman_gesturing_ok:", - "aliases": "" - }, - "1f645-1f3ff-2642": { - "output": "1f645-1f3ff-200d-2642-fe0f", - "name": "man gesturing NO: dark skin tone", - "alpha_code": ":man_gesturing_no_tone5:", - "aliases": ":man_gesturing_no_dark_skin_tone:" - }, - "1f645-1f3fe-2642": { - "output": "1f645-1f3fe-200d-2642-fe0f", - "name": "man gesturing NO: medium-dark skin tone", - "alpha_code": ":man_gesturing_no_tone4:", - "aliases": ":man_gesturing_no_medium_dark_skin_tone:" - }, - "1f645-1f3fd-2642": { - "output": "1f645-1f3fd-200d-2642-fe0f", - "name": "man gesturing NO: medium skin tone", - "alpha_code": ":man_gesturing_no_tone3:", - "aliases": ":man_gesturing_no_medium_skin_tone:" - }, - "1f645-1f3fc-2642": { - "output": "1f645-1f3fc-200d-2642-fe0f", - "name": "man gesturing NO: medium-light skin tone", - "alpha_code": ":man_gesturing_no_tone2:", - "aliases": ":man_gesturing_no_medium_light_skin_tone:" - }, - "1f645-1f3fb-2642": { - "output": "1f645-1f3fb-200d-2642-fe0f", - "name": "man gesturing NO: light skin tone", - "alpha_code": ":man_gesturing_no_tone1:", - "aliases": ":man_gesturing_no_light_skin_tone:" - }, - "1f645-2642": { - "output": "1f645-200d-2642-fe0f", - "name": "man gesturing NO", - "alpha_code": ":man_gesturing_no:", - "aliases": "" - }, - "1f645-1f3ff-2640": { - "output": "1f645-1f3ff-200d-2640-fe0f", - "name": "woman gesturing NO: dark skin tone", - "alpha_code": ":woman_gesturing_no_tone5:", - "aliases": ":woman_gesturing_no_dark_skin_tone:" - }, - "1f645-1f3fe-2640": { - "output": "1f645-1f3fe-200d-2640-fe0f", - "name": "woman gesturing NO: medium-dark skin tone", - "alpha_code": ":woman_gesturing_no_tone4:", - "aliases": ":woman_gesturing_no_medium_dark_skin_tone:" - }, - "1f645-1f3fd-2640": { - "output": "1f645-1f3fd-200d-2640-fe0f", - "name": "woman gesturing NO: medium skin tone", - "alpha_code": ":woman_gesturing_no_tone3:", - "aliases": ":woman_gesturing_no_medium_skin_tone:" - }, - "1f645-1f3fc-2640": { - "output": "1f645-1f3fc-200d-2640-fe0f", - "name": "woman gesturing NO: medium-light skin tone", - "alpha_code": ":woman_gesturing_no_tone2:", - "aliases": ":woman_gesturing_no_medium_light_skin_tone:" - }, - "1f645-1f3fb-2640": { - "output": "1f645-1f3fb-200d-2640-fe0f", - "name": "woman gesturing NO: light skin tone", - "alpha_code": ":woman_gesturing_no_tone1:", - "aliases": ":woman_gesturing_no_light_skin_tone:" - }, - "1f645-2640": { - "output": "1f645-200d-2640-fe0f", - "name": "woman gesturing NO", - "alpha_code": ":woman_gesturing_no:", - "aliases": "" - }, - "1f487-1f3ff-2642": { - "output": "1f487-1f3ff-200d-2642-fe0f", - "name": "man getting haircut: dark skin tone", - "alpha_code": ":man_getting_haircut_tone5:", - "aliases": ":man_getting_haircut_dark_skin_tone:" - }, - "1f487-1f3fe-2642": { - "output": "1f487-1f3fe-200d-2642-fe0f", - "name": "man getting haircut: medium-dark skin tone", - "alpha_code": ":man_getting_haircut_tone4:", - "aliases": ":man_getting_haircut_medium_dark_skin_tone:" - }, - "1f487-1f3fd-2642": { - "output": "1f487-1f3fd-200d-2642-fe0f", - "name": "man getting haircut: medium skin tone", - "alpha_code": ":man_getting_haircut_tone3:", - "aliases": ":man_getting_haircut_medium_skin_tone:" - }, - "1f487-1f3fc-2642": { - "output": "1f487-1f3fc-200d-2642-fe0f", - "name": "man getting haircut: medium-light skin tone", - "alpha_code": ":man_getting_haircut_tone2:", - "aliases": ":man_getting_haircut_medium_light_skin_tone:" - }, - "1f487-1f3fb-2642": { - "output": "1f487-1f3fb-200d-2642-fe0f", - "name": "man getting haircut: light skin tone", - "alpha_code": ":man_getting_haircut_tone1:", - "aliases": ":man_getting_haircut_light_skin_tone:" - }, - "1f487-2642": { - "output": "1f487-200d-2642-fe0f", - "name": "man getting haircut", - "alpha_code": ":man_getting_haircut:", - "aliases": "" - }, - "1f487-1f3ff-2640": { - "output": "1f487-1f3ff-200d-2640-fe0f", - "name": "woman getting haircut: dark skin tone", - "alpha_code": ":woman_getting_haircut_tone5:", - "aliases": ":woman_getting_haircut_dark_skin_tone:" - }, - "1f487-1f3fe-2640": { - "output": "1f487-1f3fe-200d-2640-fe0f", - "name": "woman getting haircut: medium-dark skin tone", - "alpha_code": ":woman_getting_haircut_tone4:", - "aliases": ":woman_getting_haircut_medium_dark_skin_tone:" - }, - "1f487-1f3fd-2640": { - "output": "1f487-1f3fd-200d-2640-fe0f", - "name": "woman getting haircut: medium skin tone", - "alpha_code": ":woman_getting_haircut_tone3:", - "aliases": ":woman_getting_haircut_medium_skin_tone:" - }, - "1f487-1f3fc-2640": { - "output": "1f487-1f3fc-200d-2640-fe0f", - "name": "woman getting haircut: medium-light skin tone", - "alpha_code": ":woman_getting_haircut_tone2:", - "aliases": ":woman_getting_haircut_medium_light_skin_tone:" - }, - "1f487-1f3fb-2640": { - "output": "1f487-1f3fb-200d-2640-fe0f", - "name": "woman getting haircut: light skin tone", - "alpha_code": ":woman_getting_haircut_tone1:", - "aliases": ":woman_getting_haircut_light_skin_tone:" - }, - "1f487-2640": { - "output": "1f487-200d-2640-fe0f", - "name": "woman getting haircut", - "alpha_code": ":woman_getting_haircut:", - "aliases": "" - }, - "1f486-1f3ff-2642": { - "output": "1f486-1f3ff-200d-2642-fe0f", - "name": "man getting massage: dark skin tone", - "alpha_code": ":man_getting_face_massage_tone5:", - "aliases": ":man_getting_face_massage_dark_skin_tone:" - }, - "1f486-1f3fe-2642": { - "output": "1f486-1f3fe-200d-2642-fe0f", - "name": "man getting massage: medium-dark skin tone", - "alpha_code": ":man_getting_face_massage_tone4:", - "aliases": ":man_getting_face_massage_medium_dark_skin_tone:" - }, - "1f486-1f3fd-2642": { - "output": "1f486-1f3fd-200d-2642-fe0f", - "name": "man getting massage: medium skin tone", - "alpha_code": ":man_getting_face_massage_tone3:", - "aliases": ":man_getting_face_massage_medium_skin_tone:" - }, - "1f486-1f3fc-2642": { - "output": "1f486-1f3fc-200d-2642-fe0f", - "name": "man getting massage: medium-light skin tone", - "alpha_code": ":man_getting_face_massage_tone2:", - "aliases": ":man_getting_face_massage_medium_light_skin_tone:" - }, - "1f486-1f3fb-2642": { - "output": "1f486-1f3fb-200d-2642-fe0f", - "name": "man getting massage: light skin tone", - "alpha_code": ":man_getting_face_massage_tone1:", - "aliases": ":man_getting_face_massage_light_skin_tone:" - }, - "1f486-2642": { - "output": "1f486-200d-2642-fe0f", - "name": "man getting massage", - "alpha_code": ":man_getting_face_massage:", - "aliases": "" - }, - "1f486-1f3ff-2640": { - "output": "1f486-1f3ff-200d-2640-fe0f", - "name": "woman getting massage: dark skin tone", - "alpha_code": ":woman_getting_face_massage_tone5:", - "aliases": ":woman_getting_face_massage_dark_skin_tone:" - }, - "1f486-1f3fe-2640": { - "output": "1f486-1f3fe-200d-2640-fe0f", - "name": "woman getting massage: medium-dark skin tone", - "alpha_code": ":woman_getting_face_massage_tone4:", - "aliases": ":woman_getting_face_massage_medium_dark_skin_tone:" - }, - "1f486-1f3fd-2640": { - "output": "1f486-1f3fd-200d-2640-fe0f", - "name": "woman getting massage: medium skin tone", - "alpha_code": ":woman_getting_face_massage_tone3:", - "aliases": ":woman_getting_face_massage_medium_skin_tone:" - }, - "1f486-1f3fc-2640": { - "output": "1f486-1f3fc-200d-2640-fe0f", - "name": "woman getting massage: medium-light skin tone", - "alpha_code": ":woman_getting_face_massage_tone2:", - "aliases": ":woman_getting_face_massage_medium_light_skin_tone:" - }, - "1f486-1f3fb-2640": { - "output": "1f486-1f3fb-200d-2640-fe0f", - "name": "woman getting massage: light skin tone", - "alpha_code": ":woman_getting_face_massage_tone1:", - "aliases": ":woman_getting_face_massage_light_skin_tone:" - }, - "1f486-2640": { - "output": "1f486-200d-2640-fe0f", - "name": "woman getting massage", - "alpha_code": ":woman_getting_face_massage:", - "aliases": "" - }, - "1f481-1f3ff-2642": { - "output": "1f481-1f3ff-200d-2642-fe0f", - "name": "man tipping hand: dark skin tone", - "alpha_code": ":man_tipping_hand_tone5:", - "aliases": ":man_tipping_hand_dark_skin_tone:" - }, - "1f481-1f3fe-2642": { - "output": "1f481-1f3fe-200d-2642-fe0f", - "name": "man tipping hand: medium-dark skin tone", - "alpha_code": ":man_tipping_hand_tone4:", - "aliases": ":man_tipping_hand_medium_dark_skin_tone:" - }, - "1f481-1f3fd-2642": { - "output": "1f481-1f3fd-200d-2642-fe0f", - "name": "man tipping hand: medium skin tone", - "alpha_code": ":man_tipping_hand_tone3:", - "aliases": ":man_tipping_hand_medium_skin_tone:" - }, - "1f481-1f3fc-2642": { - "output": "1f481-1f3fc-200d-2642-fe0f", - "name": "man tipping hand: medium-light skin tone", - "alpha_code": ":man_tipping_hand_tone2:", - "aliases": ":man_tipping_hand_medium_light_skin_tone:" - }, - "1f481-1f3fb-2642": { - "output": "1f481-1f3fb-200d-2642-fe0f", - "name": "man tipping hand: light skin tone", - "alpha_code": ":man_tipping_hand_tone1:", - "aliases": ":man_tipping_hand_light_skin_tone:" - }, - "1f481-2642": { - "output": "1f481-200d-2642-fe0f", - "name": "man tipping hand", - "alpha_code": ":man_tipping_hand:", - "aliases": "" - }, - "1f481-1f3ff-2640": { - "output": "1f481-1f3ff-200d-2640-fe0f", - "name": "woman tipping hand: dark skin tone", - "alpha_code": ":woman_tipping_hand_tone5:", - "aliases": ":woman_tipping_hand_dark_skin_tone:" - }, - "1f481-1f3fe-2640": { - "output": "1f481-1f3fe-200d-2640-fe0f", - "name": "woman tipping hand: medium-dark skin tone", - "alpha_code": ":woman_tipping_hand_tone4:", - "aliases": ":woman_tipping_hand_medium_dark_skin_tone:" - }, - "1f481-1f3fd-2640": { - "output": "1f481-1f3fd-200d-2640-fe0f", - "name": "woman tipping hand: medium skin tone", - "alpha_code": ":woman_tipping_hand_tone3:", - "aliases": ":woman_tipping_hand_medium_skin_tone:" - }, - "1f481-1f3fc-2640": { - "output": "1f481-1f3fc-200d-2640-fe0f", - "name": "woman tipping hand: medium-light skin tone", - "alpha_code": ":woman_tipping_hand_tone2:", - "aliases": ":woman_tipping_hand_medium_light_skin_tone:" - }, - "1f481-1f3fb-2640": { - "output": "1f481-1f3fb-200d-2640-fe0f", - "name": "woman tipping hand: light skin tone", - "alpha_code": ":woman_tipping_hand_tone1:", - "aliases": ":woman_tipping_hand_light_skin_tone:" - }, - "1f481-2640": { - "output": "1f481-200d-2640-fe0f", - "name": "woman tipping hand", - "alpha_code": ":woman_tipping_hand:", - "aliases": "" - }, - "1f471-1f3ff-2642": { - "output": "1f471-1f3ff-200d-2642-fe0f", - "name": "blond-haired man: dark skin tone", - "alpha_code": ":blond-haired_man_tone5:", - "aliases": ":blond-haired_man_dark_skin_tone:" - }, - "1f471-1f3fe-2642": { - "output": "1f471-1f3fe-200d-2642-fe0f", - "name": "blond-haired man: medium-dark skin tone", - "alpha_code": ":blond-haired_man_tone4:", - "aliases": ":blond-haired_man_medium_dark_skin_tone:" - }, - "1f471-1f3fd-2642": { - "output": "1f471-1f3fd-200d-2642-fe0f", - "name": "blond-haired man: medium skin tone", - "alpha_code": ":blond-haired_man_tone3:", - "aliases": ":blond-haired_man_medium_skin_tone:" - }, - "1f471-1f3fc-2642": { - "output": "1f471-1f3fc-200d-2642-fe0f", - "name": "blond-haired man: medium-light skin tone", - "alpha_code": ":blond-haired_man_tone2:", - "aliases": ":blond-haired_man_medium_light_skin_tone:" - }, - "1f471-1f3fb-2642": { - "output": "1f471-1f3fb-200d-2642-fe0f", - "name": "blond-haired man: light skin tone", - "alpha_code": ":blond-haired_man_tone1:", - "aliases": ":blond-haired_man_light_skin_tone:" - }, - "1f471-2642": { - "output": "1f471-200d-2642-fe0f", - "name": "blond-haired man", - "alpha_code": ":blond-haired_man:", - "aliases": "" - }, - "1f471-1f3ff-2640": { - "output": "1f471-1f3ff-200d-2640-fe0f", - "name": "blond-haired woman: dark skin tone", - "alpha_code": ":blond-haired_woman_tone5:", - "aliases": ":blond-haired_woman_dark_skin_tone:" - }, - "1f471-1f3fe-2640": { - "output": "1f471-1f3fe-200d-2640-fe0f", - "name": "blond-haired woman: medium-dark skin tone", - "alpha_code": ":blond-haired_woman_tone4:", - "aliases": ":blond-haired_woman_medium_dark_skin_tone:" - }, - "1f471-1f3fd-2640": { - "output": "1f471-1f3fd-200d-2640-fe0f", - "name": "blond-haired woman: medium skin tone", - "alpha_code": ":blond-haired_woman_tone3:", - "aliases": ":blond-haired_woman_medium_skin_tone:" - }, - "1f471-1f3fc-2640": { - "output": "1f471-1f3fc-200d-2640-fe0f", - "name": "blond-haired woman: medium-light skin tone", - "alpha_code": ":blond-haired_woman_tone2:", - "aliases": ":blond-haired_woman_medium_light_skin_tone:" - }, - "1f471-1f3fb-2640": { - "output": "1f471-1f3fb-200d-2640-fe0f", - "name": "blond-haired woman: light skin tone", - "alpha_code": ":blond-haired_woman_tone1:", - "aliases": ":blond-haired_woman_light_skin_tone:" - }, - "1f471-2640": { - "output": "1f471-200d-2640-fe0f", - "name": "blond-haired woman", - "alpha_code": ":blond-haired_woman:", - "aliases": "" - }, - "1f473-1f3ff-2642": { - "output": "1f473-1f3ff-200d-2642-fe0f", - "name": "man wearing turban: dark skin tone", - "alpha_code": ":man_wearing_turban_tone5:", - "aliases": ":man_wearing_turban_dark_skin_tone:" - }, - "1f473-1f3fe-2642": { - "output": "1f473-1f3fe-200d-2642-fe0f", - "name": "man wearing turban: medium-dark skin tone", - "alpha_code": ":man_wearing_turban_tone4:", - "aliases": ":man_wearing_turban_medium_dark_skin_tone:" - }, - "1f473-1f3fd-2642": { - "output": "1f473-1f3fd-200d-2642-fe0f", - "name": "man wearing turban: medium skin tone", - "alpha_code": ":man_wearing_turban_tone3:", - "aliases": ":man_wearing_turban_medium_skin_tone:" - }, - "1f473-1f3fc-2642": { - "output": "1f473-1f3fc-200d-2642-fe0f", - "name": "man wearing turban: medium-light skin tone", - "alpha_code": ":man_wearing_turban_tone2:", - "aliases": ":man_wearing_turban_medium_light_skin_tone:" - }, - "1f473-1f3fb-2642": { - "output": "1f473-1f3fb-200d-2642-fe0f", - "name": "man wearing turban: light skin tone", - "alpha_code": ":man_wearing_turban_tone1:", - "aliases": ":man_wearing_turban_light_skin_tone:" - }, - "1f473-2642": { - "output": "1f473-200d-2642-fe0f", - "name": "man wearing turban", - "alpha_code": ":man_wearing_turban:", - "aliases": "" - }, - "1f473-1f3ff-2640": { - "output": "1f473-1f3ff-200d-2640-fe0f", - "name": "woman wearing turban: dark skin tone", - "alpha_code": ":woman_wearing_turban_tone5:", - "aliases": ":woman_wearing_turban_dark_skin_tone:" - }, - "1f473-1f3fe-2640": { - "output": "1f473-1f3fe-200d-2640-fe0f", - "name": "woman wearing turban: medium-dark skin tone", - "alpha_code": ":woman_wearing_turban_tone4:", - "aliases": ":woman_wearing_turban_medium_dark_skin_tone:" - }, - "1f473-1f3fd-2640": { - "output": "1f473-1f3fd-200d-2640-fe0f", - "name": "woman wearing turban: medium skin tone", - "alpha_code": ":woman_wearing_turban_tone3:", - "aliases": ":woman_wearing_turban_medium_skin_tone:" - }, - "1f473-1f3fc-2640": { - "output": "1f473-1f3fc-200d-2640-fe0f", - "name": "woman wearing turban: medium-light skin tone", - "alpha_code": ":woman_wearing_turban_tone2:", - "aliases": ":woman_wearing_turban_medium_light_skin_tone:" - }, - "1f473-1f3fb-2640": { - "output": "1f473-1f3fb-200d-2640-fe0f", - "name": "woman wearing turban: light skin tone", - "alpha_code": ":woman_wearing_turban_tone1:", - "aliases": ":woman_wearing_turban_light_skin_tone:" - }, - "1f473-2640": { - "output": "1f473-200d-2640-fe0f", - "name": "woman wearing turban", - "alpha_code": ":woman_wearing_turban:", - "aliases": "" - }, - "1f482-1f3ff-2642": { - "output": "1f482-1f3ff-200d-2642-fe0f", - "name": "man guard: dark skin tone", - "alpha_code": ":man_guard_tone5:", - "aliases": ":man_guard_dark_skin_tone:" - }, - "1f482-1f3fe-2642": { - "output": "1f482-1f3fe-200d-2642-fe0f", - "name": "man guard: medium-dark skin tone", - "alpha_code": ":man_guard_tone4:", - "aliases": ":man_guard_medium_dark_skin_tone:" - }, - "1f482-1f3fd-2642": { - "output": "1f482-1f3fd-200d-2642-fe0f", - "name": "man guard: medium skin tone", - "alpha_code": ":man_guard_tone3:", - "aliases": ":man_guard_medium_skin_tone:" - }, - "1f482-1f3fc-2642": { - "output": "1f482-1f3fc-200d-2642-fe0f", - "name": "man guard: medium-light skin tone", - "alpha_code": ":man_guard_tone2:", - "aliases": ":man_guard_medium_light_skin_tone:" - }, - "1f482-1f3fb-2642": { - "output": "1f482-1f3fb-200d-2642-fe0f", - "name": "man guard: light skin tone", - "alpha_code": ":man_guard_tone1:", - "aliases": ":man_guard_light_skin_tone:" - }, - "1f482-2642": { - "output": "1f482-200d-2642-fe0f", - "name": "man guard", - "alpha_code": ":man_guard:", - "aliases": "" - }, - "1f482-1f3ff-2640": { - "output": "1f482-1f3ff-200d-2640-fe0f", - "name": "woman guard: dark skin tone", - "alpha_code": ":woman_guard_tone5:", - "aliases": ":woman_guard_dark_skin_tone:" - }, - "1f482-1f3fe-2640": { - "output": "1f482-1f3fe-200d-2640-fe0f", - "name": "woman guard: medium-dark skin tone", - "alpha_code": ":woman_guard_tone4:", - "aliases": ":woman_guard_medium_dark_skin_tone:" - }, - "1f482-1f3fd-2640": { - "output": "1f482-1f3fd-200d-2640-fe0f", - "name": "woman guard: medium skin tone", - "alpha_code": ":woman_guard_tone3:", - "aliases": ":woman_guard_medium_skin_tone:" - }, - "1f482-1f3fc-2640": { - "output": "1f482-1f3fc-200d-2640-fe0f", - "name": "woman guard: medium-light skin tone", - "alpha_code": ":woman_guard_tone2:", - "aliases": ":woman_guard_medium_light_skin_tone:" - }, - "1f482-1f3fb-2640": { - "output": "1f482-1f3fb-200d-2640-fe0f", - "name": "woman guard: light skin tone", - "alpha_code": ":woman_guard_tone1:", - "aliases": ":woman_guard_light_skin_tone:" - }, - "1f482-2640": { - "output": "1f482-200d-2640-fe0f", - "name": "woman guard", - "alpha_code": ":woman_guard:", - "aliases": "" - }, - "1f575-1f3ff-2642": { - "output": "1f575-1f3ff-200d-2642-fe0f", - "name": "man detective: dark skin tone", - "alpha_code": ":man_detective_tone5:", - "aliases": ":man_detective_dark_skin_tone:" - }, - "1f575-1f3fe-2642": { - "output": "1f575-1f3fe-200d-2642-fe0f", - "name": "man detective: medium-dark skin tone", - "alpha_code": ":man_detective_tone4:", - "aliases": ":man_detective_medium_dark_skin_tone:" - }, - "1f575-1f3fd-2642": { - "output": "1f575-1f3fd-200d-2642-fe0f", - "name": "man detective: medium skin tone", - "alpha_code": ":man_detective_tone3:", - "aliases": ":man_detective_medium_skin_tone:" - }, - "1f575-1f3fc-2642": { - "output": "1f575-1f3fc-200d-2642-fe0f", - "name": "man detective: medium-light skin tone", - "alpha_code": ":man_detective_tone2:", - "aliases": ":man_detective_medium_light_skin_tone:" - }, - "1f575-1f3fb-2642": { - "output": "1f575-1f3fb-200d-2642-fe0f", - "name": "man detective: light skin tone", - "alpha_code": ":man_detective_tone1:", - "aliases": ":man_detective_light_skin_tone:" - }, - "1f575-2642": { - "output": "1f575-fe0f-200d-2642-fe0f", - "name": "man detective", - "alpha_code": ":man_detective:", - "aliases": "" - }, - "1f575-1f3ff-2640": { - "output": "1f575-1f3ff-200d-2640-fe0f", - "name": "woman detective: dark skin tone", - "alpha_code": ":woman_detective_tone5:", - "aliases": ":woman_detective_dark_skin_tone:" - }, - "1f575-1f3fe-2640": { - "output": "1f575-1f3fe-200d-2640-fe0f", - "name": "woman detective: medium-dark skin tone", - "alpha_code": ":woman_detective_tone4:", - "aliases": ":woman_detective_medium_dark_skin_tone:" - }, - "1f575-1f3fd-2640": { - "output": "1f575-1f3fd-200d-2640-fe0f", - "name": "woman detective: medium skin tone", - "alpha_code": ":woman_detective_tone3:", - "aliases": ":woman_detective_medium_skin_tone:" - }, - "1f575-1f3fc-2640": { - "output": "1f575-1f3fc-200d-2640-fe0f", - "name": "woman detective: medium-light skin tone", - "alpha_code": ":woman_detective_tone2:", - "aliases": ":woman_detective_medium_light_skin_tone:" - }, - "1f575-1f3fb-2640": { - "output": "1f575-1f3fb-200d-2640-fe0f", - "name": "woman detective: light skin tone", - "alpha_code": ":woman_detective_tone1:", - "aliases": ":woman_detective_light_skin_tone:" - }, - "1f575-2640": { - "output": "1f575-fe0f-200d-2640-fe0f", - "name": "woman detective", - "alpha_code": ":woman_detective:", - "aliases": "" - }, - "1f477-1f3ff-2642": { - "output": "1f477-1f3ff-200d-2642-fe0f", - "name": "man construction worker: dark skin tone", - "alpha_code": ":man_construction_worker_tone5:", - "aliases": ":man_construction_worker_dark_skin_tone:" - }, - "1f477-1f3fe-2642": { - "output": "1f477-1f3fe-200d-2642-fe0f", - "name": "man construction worker: medium-dark skin tone", - "alpha_code": ":man_construction_worker_tone4:", - "aliases": ":man_construction_worker_medium_dark_skin_tone:" - }, - "1f477-1f3fd-2642": { - "output": "1f477-1f3fd-200d-2642-fe0f", - "name": "man construction worker: medium skin tone", - "alpha_code": ":man_construction_worker_tone3:", - "aliases": ":man_construction_worker_medium_skin_tone:" - }, - "1f477-1f3fc-2642": { - "output": "1f477-1f3fc-200d-2642-fe0f", - "name": "man construction worker: medium-light skin tone", - "alpha_code": ":man_construction_worker_tone2:", - "aliases": ":man_construction_worker_medium_light_skin_tone:" - }, - "1f477-1f3fb-2642": { - "output": "1f477-1f3fb-200d-2642-fe0f", - "name": "man construction worker: light skin tone", - "alpha_code": ":man_construction_worker_tone1:", - "aliases": ":man_construction_worker_light_skin_tone:" - }, - "1f477-2642": { - "output": "1f477-200d-2642-fe0f", - "name": "man construction worker", - "alpha_code": ":man_construction_worker:", - "aliases": "" - }, - "1f477-1f3ff-2640": { - "output": "1f477-1f3ff-200d-2640-fe0f", - "name": "woman construction worker: dark skin tone", - "alpha_code": ":woman_construction_worker_tone5:", - "aliases": ":woman_construction_worker_dark_skin_tone:" - }, - "1f477-1f3fe-2640": { - "output": "1f477-1f3fe-200d-2640-fe0f", - "name": "woman construction worker: medium-dark skin tone", - "alpha_code": ":woman_construction_worker_tone4:", - "aliases": ":woman_construction_worker_medium_dark_skin_tone:" - }, - "1f477-1f3fd-2640": { - "output": "1f477-1f3fd-200d-2640-fe0f", - "name": "woman construction worker: medium skin tone", - "alpha_code": ":woman_construction_worker_tone3:", - "aliases": ":woman_construction_worker_medium_skin_tone:" - }, - "1f477-1f3fc-2640": { - "output": "1f477-1f3fc-200d-2640-fe0f", - "name": "woman construction worker: medium-light skin tone", - "alpha_code": ":woman_construction_worker_tone2:", - "aliases": ":woman_construction_worker_medium_light_skin_tone:" - }, - "1f477-1f3fb-2640": { - "output": "1f477-1f3fb-200d-2640-fe0f", - "name": "woman construction worker: light skin tone", - "alpha_code": ":woman_construction_worker_tone1:", - "aliases": ":woman_construction_worker_light_skin_tone:" - }, - "1f477-2640": { - "output": "1f477-200d-2640-fe0f", - "name": "woman construction worker", - "alpha_code": ":woman_construction_worker:", - "aliases": "" - }, - "1f46e-1f3ff-2642": { - "output": "1f46e-1f3ff-200d-2642-fe0f", - "name": "man police officer: dark skin tone", - "alpha_code": ":man_police_officer_tone5:", - "aliases": ":man_police_officer_dark_skin_tone:" - }, - "1f46e-1f3fe-2642": { - "output": "1f46e-1f3fe-200d-2642-fe0f", - "name": "man police officer: medium-dark skin tone", - "alpha_code": ":man_police_officer_tone4:", - "aliases": ":man_police_officer_medium_dark_skin_tone:" - }, - "1f46e-1f3fd-2642": { - "output": "1f46e-1f3fd-200d-2642-fe0f", - "name": "man police officer: medium skin tone", - "alpha_code": ":man_police_officer_tone3:", - "aliases": ":man_police_officer_medium_skin_tone:" - }, - "1f46e-1f3fc-2642": { - "output": "1f46e-1f3fc-200d-2642-fe0f", - "name": "man police officer: medium-light skin tone", - "alpha_code": ":man_police_officer_tone2:", - "aliases": ":man_police_officer_medium_light_skin_tone:" - }, - "1f46e-1f3fb-2642": { - "output": "1f46e-1f3fb-200d-2642-fe0f", - "name": "man police officer: light skin tone", - "alpha_code": ":man_police_officer_tone1:", - "aliases": ":man_police_officer_light_skin_tone:" - }, - "1f46e-2642": { - "output": "1f46e-200d-2642-fe0f", - "name": "man police officer", - "alpha_code": ":man_police_officer:", - "aliases": "" - }, - "1f46e-1f3ff-2640": { - "output": "1f46e-1f3ff-200d-2640-fe0f", - "name": "woman police officer: dark skin tone", - "alpha_code": ":woman_police_officer_tone5:", - "aliases": ":woman_police_officer_dark_skin_tone:" - }, - "1f46e-1f3fe-2640": { - "output": "1f46e-1f3fe-200d-2640-fe0f", - "name": "woman police officer: medium-dark skin tone", - "alpha_code": ":woman_police_officer_tone4:", - "aliases": ":woman_police_officer_medium_dark_skin_tone:" - }, - "1f46e-1f3fd-2640": { - "output": "1f46e-1f3fd-200d-2640-fe0f", - "name": "woman police officer: medium skin tone", - "alpha_code": ":woman_police_officer_tone3:", - "aliases": ":woman_police_officer_medium_skin_tone:" - }, - "1f46e-1f3fc-2640": { - "output": "1f46e-1f3fc-200d-2640-fe0f", - "name": "woman police officer: medium-light skin tone", - "alpha_code": ":woman_police_officer_tone2:", - "aliases": ":woman_police_officer_medium_light_skin_tone:" - }, - "1f46e-1f3fb-2640": { - "output": "1f46e-1f3fb-200d-2640-fe0f", - "name": "woman police officer: light skin tone", - "alpha_code": ":woman_police_officer_tone1:", - "aliases": ":woman_police_officer_light_skin_tone:" - }, - "1f46e-2640": { - "output": "1f46e-200d-2640-fe0f", - "name": "woman police officer", - "alpha_code": ":woman_police_officer:", - "aliases": "" - }, - "1f468-1f3ff-1f4bb": { - "output": "1f468-1f3ff-200d-1f4bb", - "name": "man technologist: dark skin tone", - "alpha_code": ":man_technologist_tone5:", - "aliases": ":man_technologist_dark_skin_tone:" - }, - "1f468-1f3fe-1f4bb": { - "output": "1f468-1f3fe-200d-1f4bb", - "name": "man technologist: medium-dark skin tone", - "alpha_code": ":man_technologist_tone4:", - "aliases": ":man_technologist_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f4bb": { - "output": "1f468-1f3fd-200d-1f4bb", - "name": "man technologist: medium skin tone", - "alpha_code": ":man_technologist_tone3:", - "aliases": ":man_technologist_medium_skin_tone:" - }, - "1f468-1f3fc-1f4bb": { - "output": "1f468-1f3fc-200d-1f4bb", - "name": "man technologist: medium-light skin tone", - "alpha_code": ":man_technologist_tone2:", - "aliases": ":man_technologist_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f4bb": { - "output": "1f468-1f3fb-200d-1f4bb", - "name": "man technologist: light skin tone", - "alpha_code": ":man_technologist_tone1:", - "aliases": ":man_technologist_light_skin_tone:" - }, - "1f468-1f4bb": { - "output": "1f468-200d-1f4bb", - "name": "man technologist", - "alpha_code": ":man_technologist:", - "aliases": "" - }, - "1f469-1f3ff-1f4bb": { - "output": "1f469-1f3ff-200d-1f4bb", - "name": "woman technologist: dark skin tone", - "alpha_code": ":woman_technologist_tone5:", - "aliases": ":woman_technologist_dark_skin_tone:" - }, - "1f469-1f3fe-1f4bb": { - "output": "1f469-1f3fe-200d-1f4bb", - "name": "woman technologist: medium-dark skin tone", - "alpha_code": ":woman_technologist_tone4:", - "aliases": ":woman_technologist_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f4bb": { - "output": "1f469-1f3fd-200d-1f4bb", - "name": "woman technologist: medium skin tone", - "alpha_code": ":woman_technologist_tone3:", - "aliases": ":woman_technologist_medium_skin_tone:" - }, - "1f469-1f3fc-1f4bb": { - "output": "1f469-1f3fc-200d-1f4bb", - "name": "woman technologist: medium-light skin tone", - "alpha_code": ":woman_technologist_tone2:", - "aliases": ":woman_technologist_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f4bb": { - "output": "1f469-1f3fb-200d-1f4bb", - "name": "woman technologist: light skin tone", - "alpha_code": ":woman_technologist_tone1:", - "aliases": ":woman_technologist_light_skin_tone:" - }, - "1f469-1f4bb": { - "output": "1f469-200d-1f4bb", - "name": "woman technologist", - "alpha_code": ":woman_technologist:", - "aliases": "" - }, - "1f468-1f3ff-1f3eb": { - "output": "1f468-1f3ff-200d-1f3eb", - "name": "man teacher: dark skin tone", - "alpha_code": ":man_teacher_tone5:", - "aliases": ":man_teacher_dark_skin_tone:" - }, - "1f468-1f3fe-1f3eb": { - "output": "1f468-1f3fe-200d-1f3eb", - "name": "man teacher: medium-dark skin tone", - "alpha_code": ":man_teacher_tone4:", - "aliases": ":man_teacher_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f3eb": { - "output": "1f468-1f3fd-200d-1f3eb", - "name": "man teacher: medium skin tone", - "alpha_code": ":man_teacher_tone3:", - "aliases": ":man_teacher_medium_skin_tone:" - }, - "1f468-1f3fc-1f3eb": { - "output": "1f468-1f3fc-200d-1f3eb", - "name": "man teacher: medium-light skin tone", - "alpha_code": ":man_teacher_tone2:", - "aliases": ":man_teacher_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f3eb": { - "output": "1f468-1f3fb-200d-1f3eb", - "name": "man teacher: light skin tone", - "alpha_code": ":man_teacher_tone1:", - "aliases": ":man_teacher_light_skin_tone:" - }, - "1f468-1f3eb": { - "output": "1f468-200d-1f3eb", - "name": "man teacher", - "alpha_code": ":man_teacher:", - "aliases": "" - }, - "1f469-1f3ff-1f3eb": { - "output": "1f469-1f3ff-200d-1f3eb", - "name": "woman teacher: dark skin tone", - "alpha_code": ":woman_teacher_tone5:", - "aliases": ":woman_teacher_dark_skin_tone:" - }, - "1f469-1f3fe-1f3eb": { - "output": "1f469-1f3fe-200d-1f3eb", - "name": "woman teacher: medium-dark skin tone", - "alpha_code": ":woman_teacher_tone4:", - "aliases": ":woman_teacher_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f3eb": { - "output": "1f469-1f3fd-200d-1f3eb", - "name": "woman teacher: medium skin tone", - "alpha_code": ":woman_teacher_tone3:", - "aliases": ":woman_teacher_medium_skin_tone:" - }, - "1f469-1f3fc-1f3eb": { - "output": "1f469-1f3fc-200d-1f3eb", - "name": "woman teacher: medium-light skin tone", - "alpha_code": ":woman_teacher_tone2:", - "aliases": ":woman_teacher_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f3eb": { - "output": "1f469-1f3fb-200d-1f3eb", - "name": "woman teacher: light skin tone", - "alpha_code": ":woman_teacher_tone1:", - "aliases": ":woman_teacher_light_skin_tone:" - }, - "1f469-1f3eb": { - "output": "1f469-200d-1f3eb", - "name": "woman teacher", - "alpha_code": ":woman_teacher:", - "aliases": "" - }, - "1f468-1f3ff-1f393": { - "output": "1f468-1f3ff-200d-1f393", - "name": "man student: dark skin tone", - "alpha_code": ":man_student_tone5:", - "aliases": ":man_student_dark_skin_tone:" - }, - "1f468-1f3fe-1f393": { - "output": "1f468-1f3fe-200d-1f393", - "name": "man student: medium-dark skin tone", - "alpha_code": ":man_student_tone4:", - "aliases": ":man_student_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f393": { - "output": "1f468-1f3fd-200d-1f393", - "name": "man student: medium skin tone", - "alpha_code": ":man_student_tone3:", - "aliases": ":man_student_medium_skin_tone:" - }, - "1f468-1f3fc-1f393": { - "output": "1f468-1f3fc-200d-1f393", - "name": "man student: medium-light skin tone", - "alpha_code": ":man_student_tone2:", - "aliases": ":man_student_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f393": { - "output": "1f468-1f3fb-200d-1f393", - "name": "man student: light skin tone", - "alpha_code": ":man_student_tone1:", - "aliases": ":man_student_light_skin_tone:" - }, - "1f468-1f393": { - "output": "1f468-200d-1f393", - "name": "man student", - "alpha_code": ":man_student:", - "aliases": "" - }, - "1f469-1f3ff-1f393": { - "output": "1f469-1f3ff-200d-1f393", - "name": "woman student: dark skin tone", - "alpha_code": ":woman_student_tone5:", - "aliases": ":woman_student_dark_skin_tone:" - }, - "1f469-1f3fe-1f393": { - "output": "1f469-1f3fe-200d-1f393", - "name": "woman student: medium-dark skin tone", - "alpha_code": ":woman_student_tone4:", - "aliases": ":woman_student_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f393": { - "output": "1f469-1f3fd-200d-1f393", - "name": "woman student: medium skin tone", - "alpha_code": ":woman_student_tone3:", - "aliases": ":woman_student_medium_skin_tone:" - }, - "1f469-1f3fc-1f393": { - "output": "1f469-1f3fc-200d-1f393", - "name": "woman student: medium-light skin tone", - "alpha_code": ":woman_student_tone2:", - "aliases": ":woman_student_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f393": { - "output": "1f469-1f3fb-200d-1f393", - "name": "woman student: light skin tone", - "alpha_code": ":woman_student_tone1:", - "aliases": ":woman_student_light_skin_tone:" - }, - "1f469-1f393": { - "output": "1f469-200d-1f393", - "name": "woman student", - "alpha_code": ":woman_student:", - "aliases": "" - }, - "1f468-1f3ff-1f3a4": { - "output": "1f468-1f3ff-200d-1f3a4", - "name": "man singer: dark skin tone", - "alpha_code": ":man_singer_tone5:", - "aliases": ":man_singer_dark_skin_tone:" - }, - "1f468-1f3fe-1f3a4": { - "output": "1f468-1f3fe-200d-1f3a4", - "name": "man singer: medium-dark skin tone", - "alpha_code": ":man_singer_tone4:", - "aliases": ":man_singer_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f3a4": { - "output": "1f468-1f3fd-200d-1f3a4", - "name": "man singer: medium skin tone", - "alpha_code": ":man_singer_tone3:", - "aliases": ":man_singer_medium_skin_tone:" - }, - "1f468-1f3fc-1f3a4": { - "output": "1f468-1f3fc-200d-1f3a4", - "name": "man singer: medium-light skin tone", - "alpha_code": ":man_singer_tone2:", - "aliases": ":man_singer_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f3a4": { - "output": "1f468-1f3fb-200d-1f3a4", - "name": "man singer: light skin tone", - "alpha_code": ":man_singer_tone1:", - "aliases": ":man_singer_light_skin_tone:" - }, - "1f468-1f3a4": { - "output": "1f468-200d-1f3a4", - "name": "man singer", - "alpha_code": ":man_singer:", - "aliases": "" - }, - "1f469-1f3ff-1f3a4": { - "output": "1f469-1f3ff-200d-1f3a4", - "name": "woman singer: dark skin tone", - "alpha_code": ":woman_singer_tone5:", - "aliases": ":woman_singer_dark_skin_tone:" - }, - "1f469-1f3fe-1f3a4": { - "output": "1f469-1f3fe-200d-1f3a4", - "name": "woman singer: medium-dark skin tone", - "alpha_code": ":woman_singer_tone4:", - "aliases": ":woman_singer_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f3a4": { - "output": "1f469-1f3fd-200d-1f3a4", - "name": "woman singer: medium skin tone", - "alpha_code": ":woman_singer_tone3:", - "aliases": ":woman_singer_medium_skin_tone:" - }, - "1f469-1f3fc-1f3a4": { - "output": "1f469-1f3fc-200d-1f3a4", - "name": "woman singer: medium-light skin tone", - "alpha_code": ":woman_singer_tone2:", - "aliases": ":woman_singer_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f3a4": { - "output": "1f469-1f3fb-200d-1f3a4", - "name": "woman singer: light skin tone", - "alpha_code": ":woman_singer_tone1:", - "aliases": ":woman_singer_light_skin_tone:" - }, - "1f469-1f3a4": { - "output": "1f469-200d-1f3a4", - "name": "woman singer", - "alpha_code": ":woman_singer:", - "aliases": "" - }, - "1f468-1f3ff-1f52c": { - "output": "1f468-1f3ff-200d-1f52c", - "name": "man scientist: dark skin tone", - "alpha_code": ":man_scientist_tone5:", - "aliases": ":man_scientist_dark_skin_tone:" - }, - "1f468-1f3fe-1f52c": { - "output": "1f468-1f3fe-200d-1f52c", - "name": "man scientist: medium-dark skin tone", - "alpha_code": ":man_scientist_tone4:", - "aliases": ":man_scientist_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f52c": { - "output": "1f468-1f3fd-200d-1f52c", - "name": "man scientist: medium skin tone", - "alpha_code": ":man_scientist_tone3:", - "aliases": ":man_scientist_medium_skin_tone:" - }, - "1f468-1f3fc-1f52c": { - "output": "1f468-1f3fc-200d-1f52c", - "name": "man scientist: medium-light skin tone", - "alpha_code": ":man_scientist_tone2:", - "aliases": ":man_scientist_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f52c": { - "output": "1f468-1f3fb-200d-1f52c", - "name": "man scientist: light skin tone", - "alpha_code": ":man_scientist_tone1:", - "aliases": ":man_scientist_light_skin_tone:" - }, - "1f468-1f52c": { - "output": "1f468-200d-1f52c", - "name": "man scientist", - "alpha_code": ":man_scientist:", - "aliases": "" - }, - "1f469-1f3ff-1f52c": { - "output": "1f469-1f3ff-200d-1f52c", - "name": "woman scientist: dark skin tone", - "alpha_code": ":woman_scientist_tone5:", - "aliases": ":woman_scientist_dark_skin_tone:" - }, - "1f469-1f3fe-1f52c": { - "output": "1f469-1f3fe-200d-1f52c", - "name": "woman scientist: medium-dark skin tone", - "alpha_code": ":woman_scientist_tone4:", - "aliases": ":woman_scientist_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f52c": { - "output": "1f469-1f3fd-200d-1f52c", - "name": "woman scientist: medium skin tone", - "alpha_code": ":woman_scientist_tone3:", - "aliases": ":woman_scientist_medium_skin_tone:" - }, - "1f469-1f3fc-1f52c": { - "output": "1f469-1f3fc-200d-1f52c", - "name": "woman scientist: medium-light skin tone", - "alpha_code": ":woman_scientist_tone2:", - "aliases": ":woman_scientist_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f52c": { - "output": "1f469-1f3fb-200d-1f52c", - "name": "woman scientist: light skin tone", - "alpha_code": ":woman_scientist_tone1:", - "aliases": ":woman_scientist_light_skin_tone:" - }, - "1f469-1f52c": { - "output": "1f469-200d-1f52c", - "name": "woman scientist", - "alpha_code": ":woman_scientist:", - "aliases": "" - }, - "1f468-1f3ff-1f4bc": { - "output": "1f468-1f3ff-200d-1f4bc", - "name": "man office worker: dark skin tone", - "alpha_code": ":man_office_worker_tone5:", - "aliases": ":man_office_worker_dark_skin_tone:" - }, - "1f468-1f3fe-1f4bc": { - "output": "1f468-1f3fe-200d-1f4bc", - "name": "man office worker: medium-dark skin tone", - "alpha_code": ":man_office_worker_tone4:", - "aliases": ":man_office_worker_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f4bc": { - "output": "1f468-1f3fd-200d-1f4bc", - "name": "man office worker: medium skin tone", - "alpha_code": ":man_office_worker_tone3:", - "aliases": ":man_office_worker_medium_skin_tone:" - }, - "1f468-1f3fc-1f4bc": { - "output": "1f468-1f3fc-200d-1f4bc", - "name": "man office worker: medium-light skin tone", - "alpha_code": ":man_office_worker_tone2:", - "aliases": ":man_office_worker_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f4bc": { - "output": "1f468-1f3fb-200d-1f4bc", - "name": "man office worker: light skin tone", - "alpha_code": ":man_office_worker_tone1:", - "aliases": ":man_office_worker_light_skin_tone:" - }, - "1f468-1f4bc": { - "output": "1f468-200d-1f4bc", - "name": "man office worker", - "alpha_code": ":man_office_worker:", - "aliases": "" - }, - "1f469-1f3ff-1f4bc": { - "output": "1f469-1f3ff-200d-1f4bc", - "name": "woman office worker: dark skin tone", - "alpha_code": ":woman_office_worker_tone5:", - "aliases": ":woman_office_worker_dark_skin_tone:" - }, - "1f469-1f3fe-1f4bc": { - "output": "1f469-1f3fe-200d-1f4bc", - "name": "woman office worker: medium-dark skin tone", - "alpha_code": ":woman_office_worker_tone4:", - "aliases": ":woman_office_worker_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f4bc": { - "output": "1f469-1f3fd-200d-1f4bc", - "name": "woman office worker: medium skin tone", - "alpha_code": ":woman_office_worker_tone3:", - "aliases": ":woman_office_worker_medium_skin_tone:" - }, - "1f469-1f3fc-1f4bc": { - "output": "1f469-1f3fc-200d-1f4bc", - "name": "woman office worker: medium-light skin tone", - "alpha_code": ":woman_office_worker_tone2:", - "aliases": ":woman_office_worker_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f4bc": { - "output": "1f469-1f3fb-200d-1f4bc", - "name": "woman office worker: light skin tone", - "alpha_code": ":woman_office_worker_tone1:", - "aliases": ":woman_office_worker_light_skin_tone:" - }, - "1f469-1f4bc": { - "output": "1f469-200d-1f4bc", - "name": "woman office worker", - "alpha_code": ":woman_office_worker:", - "aliases": "" - }, - "1f468-1f3ff-1f527": { - "output": "1f468-1f3ff-200d-1f527", - "name": "man mechanic: dark skin tone", - "alpha_code": ":man_mechanic_tone5:", - "aliases": ":man_mechanic_dark_skin_tone:" - }, - "1f468-1f3fe-1f527": { - "output": "1f468-1f3fe-200d-1f527", - "name": "man mechanic: medium-dark skin tone", - "alpha_code": ":man_mechanic_tone4:", - "aliases": ":man_mechanic_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f527": { - "output": "1f468-1f3fd-200d-1f527", - "name": "man mechanic: medium skin tone", - "alpha_code": ":man_mechanic_tone3:", - "aliases": ":man_mechanic_medium_skin_tone:" - }, - "1f468-1f3fc-1f527": { - "output": "1f468-1f3fc-200d-1f527", - "name": "man mechanic: medium-light skin tone", - "alpha_code": ":man_mechanic_tone2:", - "aliases": ":man_mechanic_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f527": { - "output": "1f468-1f3fb-200d-1f527", - "name": "man mechanic: light skin tone", - "alpha_code": ":man_mechanic_tone1:", - "aliases": ":man_mechanic_light_skin_tone:" - }, - "1f468-1f527": { - "output": "1f468-200d-1f527", - "name": "man mechanic", - "alpha_code": ":man_mechanic:", - "aliases": "" - }, - "1f469-1f3ff-1f527": { - "output": "1f469-1f3ff-200d-1f527", - "name": "woman mechanic: dark skin tone", - "alpha_code": ":woman_mechanic_tone5:", - "aliases": ":woman_mechanic_dark_skin_tone:" - }, - "1f469-1f3fe-1f527": { - "output": "1f469-1f3fe-200d-1f527", - "name": "woman mechanic: medium-dark skin tone", - "alpha_code": ":woman_mechanic_tone4:", - "aliases": ":woman_mechanic_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f527": { - "output": "1f469-1f3fd-200d-1f527", - "name": "woman mechanic: medium skin tone", - "alpha_code": ":woman_mechanic_tone3:", - "aliases": ":woman_mechanic_medium_skin_tone:" - }, - "1f469-1f3fc-1f527": { - "output": "1f469-1f3fc-200d-1f527", - "name": "woman mechanic: medium-light skin tone", - "alpha_code": ":woman_mechanic_tone2:", - "aliases": ":woman_mechanic_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f527": { - "output": "1f469-1f3fb-200d-1f527", - "name": "woman mechanic: light skin tone", - "alpha_code": ":woman_mechanic_tone1:", - "aliases": ":woman_mechanic_light_skin_tone:" - }, - "1f469-1f527": { - "output": "1f469-200d-1f527", - "name": "woman mechanic", - "alpha_code": ":woman_mechanic:", - "aliases": "" - }, - "1f468-1f3ff-2695": { - "output": "1f468-1f3ff-200d-2695-fe0f", - "name": "man health worker: dark skin tone", - "alpha_code": ":man_health_worker_tone5:", - "aliases": ":man_health_worker_dark_skin_tone:" - }, - "1f468-1f3fe-2695": { - "output": "1f468-1f3fe-200d-2695-fe0f", - "name": "man health worker: medium-dark skin tone", - "alpha_code": ":man_health_worker_tone4:", - "aliases": ":man_health_worker_medium_dark_skin_tone:" - }, - "1f468-1f3fd-2695": { - "output": "1f468-1f3fd-200d-2695-fe0f", - "name": "man health worker: medium skin tone", - "alpha_code": ":man_health_worker_tone3:", - "aliases": ":man_health_worker_medium_skin_tone:" - }, - "1f468-1f3fc-2695": { - "output": "1f468-1f3fc-200d-2695-fe0f", - "name": "man health worker: medium-light skin tone", - "alpha_code": ":man_health_worker_tone2:", - "aliases": ":man_health_worker_medium_light_skin_tone:" - }, - "1f468-1f3fb-2695": { - "output": "1f468-1f3fb-200d-2695-fe0f", - "name": "man health worker: light skin tone", - "alpha_code": ":man_health_worker_tone1:", - "aliases": ":man_health_worker_light_skin_tone:" - }, - "1f468-2695": { - "output": "1f468-200d-2695-fe0f", - "name": "man health worker", - "alpha_code": ":man_health_worker:", - "aliases": "" - }, - "1f469-1f3ff-2695": { - "output": "1f469-1f3ff-200d-2695-fe0f", - "name": "woman health worker: dark skin tone", - "alpha_code": ":woman_health_worker_tone5:", - "aliases": ":woman_health_worker_dark_skin_tone:" - }, - "1f469-1f3fe-2695": { - "output": "1f469-1f3fe-200d-2695-fe0f", - "name": "woman health worker: medium-dark skin tone", - "alpha_code": ":woman_health_worker_tone4:", - "aliases": ":woman_health_worker_medium_dark_skin_tone:" - }, - "1f469-1f3fd-2695": { - "output": "1f469-1f3fd-200d-2695-fe0f", - "name": "woman health worker: medium skin tone", - "alpha_code": ":woman_health_worker_tone3:", - "aliases": ":woman_health_worker_medium_skin_tone:" - }, - "1f469-1f3fc-2695": { - "output": "1f469-1f3fc-200d-2695-fe0f", - "name": "woman health worker: medium-light skin tone", - "alpha_code": ":woman_health_worker_tone2:", - "aliases": ":woman_health_worker_medium_light_skin_tone:" - }, - "1f469-1f3fb-2695": { - "output": "1f469-1f3fb-200d-2695-fe0f", - "name": "woman health worker: light skin tone", - "alpha_code": ":woman_health_worker_tone1:", - "aliases": ":woman_health_worker_light_skin_tone:" - }, - "1f469-2695": { - "output": "1f469-200d-2695-fe0f", - "name": "woman health worker", - "alpha_code": ":woman_health_worker:", - "aliases": "" - }, - "1f468-1f3ff-1f3ed": { - "output": "1f468-1f3ff-200d-1f3ed", - "name": "man factory worker: dark skin tone", - "alpha_code": ":man_factory_worker_tone5:", - "aliases": ":man_factory_worker_dark_skin_tone:" - }, - "1f468-1f3fe-1f3ed": { - "output": "1f468-1f3fe-200d-1f3ed", - "name": "man factory worker: medium-dark skin tone", - "alpha_code": ":man_factory_worker_tone4:", - "aliases": ":man_factory_worker_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f3ed": { - "output": "1f468-1f3fd-200d-1f3ed", - "name": "man factory worker: medium skin tone", - "alpha_code": ":man_factory_worker_tone3:", - "aliases": ":man_factory_worker_medium_skin_tone:" - }, - "1f468-1f3fc-1f3ed": { - "output": "1f468-1f3fc-200d-1f3ed", - "name": "man factory worker: medium-light skin tone", - "alpha_code": ":man_factory_worker_tone2:", - "aliases": ":man_factory_worker_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f3ed": { - "output": "1f468-1f3fb-200d-1f3ed", - "name": "man factory worker: light skin tone", - "alpha_code": ":man_factory_worker_tone1:", - "aliases": ":man_factory_worker_light_skin_tone:" - }, - "1f468-1f3ed": { - "output": "1f468-200d-1f3ed", - "name": "man factory worker", - "alpha_code": ":man_factory_worker:", - "aliases": "" - }, - "1f469-1f3ff-1f3ed": { - "output": "1f469-1f3ff-200d-1f3ed", - "name": "woman factory worker: dark skin tone", - "alpha_code": ":woman_factory_worker_tone5:", - "aliases": ":woman_factory_worker_dark_skin_tone:" - }, - "1f469-1f3fe-1f3ed": { - "output": "1f469-1f3fe-200d-1f3ed", - "name": "woman factory worker: medium-dark skin tone", - "alpha_code": ":woman_factory_worker_tone4:", - "aliases": ":woman_factory_worker_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f3ed": { - "output": "1f469-1f3fd-200d-1f3ed", - "name": "woman factory worker: medium skin tone", - "alpha_code": ":woman_factory_worker_tone3:", - "aliases": ":woman_factory_worker_medium_skin_tone:" - }, - "1f469-1f3fc-1f3ed": { - "output": "1f469-1f3fc-200d-1f3ed", - "name": "woman factory worker: medium-light skin tone", - "alpha_code": ":woman_factory_worker_tone2:", - "aliases": ":woman_factory_worker_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f3ed": { - "output": "1f469-1f3fb-200d-1f3ed", - "name": "woman factory worker: light skin tone", - "alpha_code": ":woman_factory_worker_tone1:", - "aliases": ":woman_factory_worker_light_skin_tone:" - }, - "1f469-1f3ed": { - "output": "1f469-200d-1f3ed", - "name": "woman factory worker", - "alpha_code": ":woman_factory_worker:", - "aliases": "" - }, - "1f468-1f3ff-1f373": { - "output": "1f468-1f3ff-200d-1f373", - "name": "man cook: dark skin tone", - "alpha_code": ":man_cook_tone5:", - "aliases": ":man_cook_dark_skin_tone:" - }, - "1f468-1f3fe-1f373": { - "output": "1f468-1f3fe-200d-1f373", - "name": "man cook: medium-dark skin tone", - "alpha_code": ":man_cook_tone4:", - "aliases": ":man_cook_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f373": { - "output": "1f468-1f3fd-200d-1f373", - "name": "man cook: medium skin tone", - "alpha_code": ":man_cook_tone3:", - "aliases": ":man_cook_medium_skin_tone:" - }, - "1f468-1f3fc-1f373": { - "output": "1f468-1f3fc-200d-1f373", - "name": "man cook: medium-light skin tone", - "alpha_code": ":man_cook_tone2:", - "aliases": ":man_cook_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f373": { - "output": "1f468-1f3fb-200d-1f373", - "name": "man cook: light skin tone", - "alpha_code": ":man_cook_tone1:", - "aliases": ":man_cook_light_skin_tone:" - }, - "1f468-1f373": { - "output": "1f468-200d-1f373", - "name": "man cook", - "alpha_code": ":man_cook:", - "aliases": "" - }, - "1f469-1f3ff-1f373": { - "output": "1f469-1f3ff-200d-1f373", - "name": "woman cook: dark skin tone", - "alpha_code": ":woman_cook_tone5:", - "aliases": ":woman_cook_dark_skin_tone:" - }, - "1f469-1f3fe-1f373": { - "output": "1f469-1f3fe-200d-1f373", - "name": "woman cook: medium-dark skin tone", - "alpha_code": ":woman_cook_tone4:", - "aliases": ":woman_cook_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f373": { - "output": "1f469-1f3fd-200d-1f373", - "name": "woman cook: medium skin tone", - "alpha_code": ":woman_cook_tone3:", - "aliases": ":woman_cook_medium_skin_tone:" - }, - "1f469-1f3fc-1f373": { - "output": "1f469-1f3fc-200d-1f373", - "name": "woman cook: medium-light skin tone", - "alpha_code": ":woman_cook_tone2:", - "aliases": ":woman_cook_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f373": { - "output": "1f469-1f3fb-200d-1f373", - "name": "woman cook: light skin tone", - "alpha_code": ":woman_cook_tone1:", - "aliases": ":woman_cook_light_skin_tone:" - }, - "1f469-1f373": { - "output": "1f469-200d-1f373", - "name": "woman cook", - "alpha_code": ":woman_cook:", - "aliases": "" - }, - "1f468-1f3ff-1f33e": { - "output": "1f468-1f3ff-200d-1f33e", - "name": "man farmer: dark skin tone", - "alpha_code": ":man_farmer_tone5:", - "aliases": ":man_farmer_dark_skin_tone:" - }, - "1f468-1f3fe-1f33e": { - "output": "1f468-1f3fe-200d-1f33e", - "name": "man farmer: medium-dark skin tone", - "alpha_code": ":man_farmer_tone4:", - "aliases": ":man_farmer_medium_dark_skin_tone:" - }, - "1f468-1f3fd-1f33e": { - "output": "1f468-1f3fd-200d-1f33e", - "name": "man farmer: medium skin tone", - "alpha_code": ":man_farmer_tone3:", - "aliases": ":man_farmer_medium_skin_tone:" - }, - "1f468-1f3fc-1f33e": { - "output": "1f468-1f3fc-200d-1f33e", - "name": "man farmer: medium-light skin tone", - "alpha_code": ":man_farmer_tone2:", - "aliases": ":man_farmer_medium_light_skin_tone:" - }, - "1f468-1f3fb-1f33e": { - "output": "1f468-1f3fb-200d-1f33e", - "name": "man farmer: light skin tone", - "alpha_code": ":man_farmer_tone1:", - "aliases": ":man_farmer_light_skin_tone:" - }, - "1f468-1f33e": { - "output": "1f468-200d-1f33e", - "name": "man farmer", - "alpha_code": ":man_farmer:", - "aliases": "" - }, - "1f469-1f3ff-1f33e": { - "output": "1f469-1f3ff-200d-1f33e", - "name": "woman farmer: dark skin tone", - "alpha_code": ":woman_farmer_tone5:", - "aliases": ":woman_farmer_dark_skin_tone:" - }, - "1f469-1f3fe-1f33e": { - "output": "1f469-1f3fe-200d-1f33e", - "name": "woman farmer: medium-dark skin tone", - "alpha_code": ":woman_farmer_tone4:", - "aliases": ":woman_farmer_medium_dark_skin_tone:" - }, - "1f469-1f3fd-1f33e": { - "output": "1f469-1f3fd-200d-1f33e", - "name": "woman farmer: medium skin tone", - "alpha_code": ":woman_farmer_tone3:", - "aliases": ":woman_farmer_medium_skin_tone:" - }, - "1f469-1f3fc-1f33e": { - "output": "1f469-1f3fc-200d-1f33e", - "name": "woman farmer: medium-light skin tone", - "alpha_code": ":woman_farmer_tone2:", - "aliases": ":woman_farmer_medium_light_skin_tone:" - }, - "1f469-1f3fb-1f33e": { - "output": "1f469-1f3fb-200d-1f33e", - "name": "woman farmer: light skin tone", - "alpha_code": ":woman_farmer_tone1:", - "aliases": ":woman_farmer_light_skin_tone:" - }, - "1f469-1f33e": { - "output": "1f469-200d-1f33e", - "name": "woman farmer", - "alpha_code": ":woman_farmer:", - "aliases": "" - }, - "1f574-1f3fb": { - "output": "1f574-1f3fb", - "name": "man in business suit levitating: light skin tone", - "alpha_code": ":man_in_business_suit_levitating_tone1:", - "aliases": ":man_in_business_suit_levitating_light_skin_tone:" - }, - "1f574-1f3fc": { - "output": "1f574-1f3fc", - "name": "man in business suit levitating: medium-light skin tone", - "alpha_code": ":man_in_business_suit_levitating_tone2:", - "aliases": ":man_in_business_suit_levitating_medium_light_skin_tone:" - }, - "1f574-1f3fd": { - "output": "1f574-1f3fd", - "name": "man in business suit levitating: medium skin tone", - "alpha_code": ":man_in_business_suit_levitating_tone3:", - "aliases": ":man_in_business_suit_levitating_medium_skin_tone:" - }, - "1f574-1f3fe": { - "output": "1f574-1f3fe", - "name": "man in business suit levitating: medium-dark skin tone", - "alpha_code": ":man_in_business_suit_levitating_tone4:", - "aliases": ":man_in_business_suit_levitating_medium_dark_skin_tone:" - }, - "1f574-1f3ff": { - "output": "1f574-1f3ff", - "name": "man in business suit levitating: dark skin tone", - "alpha_code": ":man_in_business_suit_levitating_tone5:", - "aliases": ":man_in_business_suit_levitating_dark_skin_tone:" - }, - "1f6cc-1f3fb": { - "output": "1f6cc-1f3fb", - "name": "person in bed: light skin tone", - "alpha_code": ":person_in_bed_tone1:", - "aliases": ":person_in_bed_light_skin_tone:" - }, - "1f6cc-1f3fc": { - "output": "1f6cc-1f3fc", - "name": "person in bed: medium-light skin tone", - "alpha_code": ":person_in_bed_tone2:", - "aliases": ":person_in_bed_medium_light_skin_tone:" - }, - "1f6cc-1f3fd": { - "output": "1f6cc-1f3fd", - "name": "person in bed: medium skin tone", - "alpha_code": ":person_in_bed_tone3:", - "aliases": ":person_in_bed_medium_skin_tone:" - }, - "1f6cc-1f3fe": { - "output": "1f6cc-1f3fe", - "name": "person in bed: medium-dark skin tone", - "alpha_code": ":person_in_bed_tone4:", - "aliases": ":person_in_bed_medium_dark_skin_tone:" - }, - "1f6cc-1f3ff": { - "output": "1f6cc-1f3ff", - "name": "person in bed: dark skin tone", - "alpha_code": ":person_in_bed_tone5:", - "aliases": ":person_in_bed_dark_skin_tone:" - }, - "1f468-1f466": { - "output": "1f468-200d-1f466", - "name": "family: man, boy", - "alpha_code": ":family_man_boy:", - "aliases": "" - }, - "1f468-1f466-1f466": { - "output": "1f468-200d-1f466-200d-1f466", - "name": "family: man, boy, boy", - "alpha_code": ":family_man_boy_boy:", - "aliases": "" - }, - "1f468-1f467": { - "output": "1f468-200d-1f467", - "name": "family: man, girl", - "alpha_code": ":family_man_girl:", - "aliases": "" - }, - "1f468-1f467-1f466": { - "output": "1f468-200d-1f467-200d-1f466", - "name": "family: man, girl, boy", - "alpha_code": ":family_man_girl_boy:", - "aliases": "" - }, - "1f469-1f466": { - "output": "1f469-200d-1f466", - "name": "family: woman, boy", - "alpha_code": ":family_woman_boy:", - "aliases": "" - }, - "1f469-1f466-1f466": { - "output": "1f469-200d-1f466-200d-1f466", - "name": "family: woman, boy, boy", - "alpha_code": ":family_woman_boy_boy:", - "aliases": "" - }, - "1f469-1f467": { - "output": "1f469-200d-1f467", - "name": "family: woman, girl", - "alpha_code": ":family_woman_girl:", - "aliases": "" - }, - "1f469-1f467-1f466": { - "output": "1f469-200d-1f467-200d-1f466", - "name": "family: woman, girl, boy", - "alpha_code": ":family_woman_girl_boy:", - "aliases": "" - }, - "1f469-1f467-1f467": { - "output": "1f469-200d-1f467-200d-1f467", - "name": "family: woman, girl, girl", - "alpha_code": ":family_woman_girl_girl:", - "aliases": "" - }, - "1f468-2696": { - "output": "1f468-200d-2696-fe0f", - "name": "man judge", - "alpha_code": ":man_judge:", - "aliases": "" - }, - "1f468-1f3fb-2696": { - "output": "1f468-1f3fb-200d-2696-fe0f", - "name": "man judge: light skin tone", - "alpha_code": ":man_judge_tone1:", - "aliases": ":man_judge_light_skin_tone:" - }, - "1f468-1f3fc-2696": { - "output": "1f468-1f3fc-200d-2696-fe0f", - "name": "man judge: medium-light skin tone", - "alpha_code": ":man_judge_tone2:", - "aliases": ":man_judge_medium_light_skin_tone:" - }, - "1f468-1f3fd-2696": { - "output": "1f468-1f3fd-200d-2696-fe0f", - "name": "man judge: medium skin tone", - "alpha_code": ":man_judge_tone3:", - "aliases": ":man_judge_medium_skin_tone:" - }, - "1f468-1f3fe-2696": { - "output": "1f468-1f3fe-200d-2696-fe0f", - "name": "man judge: medium-dark skin tone", - "alpha_code": ":man_judge_tone4:", - "aliases": ":man_judge_medium_dark_skin_tone:" - }, - "1f468-1f3ff-2696": { - "output": "1f468-1f3ff-200d-2696-fe0f", - "name": "man judge: dark skin tone", - "alpha_code": ":man_judge_tone5:", - "aliases": ":man_judge_dark_skin_tone:" - }, - "1f469-2696": { - "output": "1f469-200d-2696-fe0f", - "name": "woman judge", - "alpha_code": ":woman_judge:", - "aliases": "" - }, - "1f469-1f3fb-2696": { - "output": "1f469-1f3fb-200d-2696-fe0f", - "name": "woman judge: light skin tone", - "alpha_code": ":woman_judge_tone1:", - "aliases": ":woman_judge_light_skin_tone:" - }, - "1f469-1f3fc-2696": { - "output": "1f469-1f3fc-200d-2696-fe0f", - "name": "woman judge: medium-light skin tone", - "alpha_code": ":woman_judge_tone2:", - "aliases": ":woman_judge_medium_light_skin_tone:" - }, - "1f469-1f3fd-2696": { - "output": "1f469-1f3fd-200d-2696-fe0f", - "name": "woman judge: medium skin tone", - "alpha_code": ":woman_judge_tone3:", - "aliases": ":woman_judge_medium_skin_tone:" - }, - "1f469-1f3fe-2696": { - "output": "1f469-1f3fe-200d-2696-fe0f", - "name": "woman judge: medium-dark skin tone", - "alpha_code": ":woman_judge_tone4:", - "aliases": ":woman_judge_medium_dark_skin_tone:" - }, - "1f469-1f3ff-2696": { - "output": "1f469-1f3ff-200d-2696-fe0f", - "name": "woman judge: dark skin tone", - "alpha_code": ":woman_judge_tone5:", - "aliases": ":woman_judge_dark_skin_tone:" - }, - "1f468-2708": { - "output": "1f468-200d-2708-fe0f", - "name": "man pilot", - "alpha_code": ":man_pilot:", - "aliases": "" - }, - "1f468-1f3fb-2708": { - "output": "1f468-1f3fb-200d-2708-fe0f", - "name": "man pilot: light skin tone", - "alpha_code": ":man_pilot_tone1:", - "aliases": ":man_pilot_light_skin_tone:" - }, - "1f468-1f3fc-2708": { - "output": "1f468-1f3fc-200d-2708-fe0f", - "name": "man pilot: medium-light skin tone", - "alpha_code": ":man_pilot_tone2:", - "aliases": ":man_pilot_medium_light_skin_tone:" - }, - "1f468-1f3fd-2708": { - "output": "1f468-1f3fd-200d-2708-fe0f", - "name": "man pilot: medium skin tone", - "alpha_code": ":man_pilot_tone3:", - "aliases": ":man_pilot_medium_skin_tone:" - }, - "1f468-1f3fe-2708": { - "output": "1f468-1f3fe-200d-2708-fe0f", - "name": "man pilot: medium-dark skin tone", - "alpha_code": ":man_pilot_tone4:", - "aliases": ":man_pilot_medium_dark_skin_tone:" - }, - "1f468-1f3ff-2708": { - "output": "1f468-1f3ff-200d-2708-fe0f", - "name": "man pilot: dark skin tone", - "alpha_code": ":man_pilot_tone5:", - "aliases": ":man_pilot_dark_skin_tone:" - }, - "1f469-2708": { - "output": "1f469-200d-2708-fe0f", - "name": "woman pilot", - "alpha_code": ":woman_pilot:", - "aliases": "" - }, - "1f469-1f3fb-2708": { - "output": "1f469-1f3fb-200d-2708-fe0f", - "name": "woman pilot: light skin tone", - "alpha_code": ":woman_pilot_tone1:", - "aliases": ":woman_pilot_light_skin_tone:" - }, - "1f469-1f3fc-2708": { - "output": "1f469-1f3fc-200d-2708-fe0f", - "name": "woman pilot: medium-light skin tone", - "alpha_code": ":woman_pilot_tone2:", - "aliases": ":woman_pilot_medium_light_skin_tone:" - }, - "1f469-1f3fd-2708": { - "output": "1f469-1f3fd-200d-2708-fe0f", - "name": "woman pilot: medium skin tone", - "alpha_code": ":woman_pilot_tone3:", - "aliases": ":woman_pilot_medium_skin_tone:" - }, - "1f469-1f3fe-2708": { - "output": "1f469-1f3fe-200d-2708-fe0f", - "name": "woman pilot: medium-dark skin tone", - "alpha_code": ":woman_pilot_tone4:", - "aliases": ":woman_pilot_medium_dark_skin_tone:" - }, - "1f469-1f3ff-2708": { - "output": "1f469-1f3ff-200d-2708-fe0f", - "name": "woman pilot: dark skin tone", - "alpha_code": ":woman_pilot_tone5:", - "aliases": ":woman_pilot_dark_skin_tone:" - }, - "1f468-1f3a8": { - "output": "1f468-200d-1f3a8", - "name": "man artist", - "alpha_code": ":man_artist:", - "aliases": "" - }, - "1f468-1f3fb-1f3a8": { - "output": "1f468-1f3fb-200d-1f3a8", - "name": "man artist: light skin tone", - "alpha_code": ":man_artist_tone1:", - "aliases": ":man_artist_light_skin_tone:" - }, - "1f468-1f3fc-1f3a8": { - "output": "1f468-1f3fc-200d-1f3a8", - "name": "man artist: medium-light skin tone", - "alpha_code": ":man_artist_tone2:", - "aliases": ":man_artist_medium_light_skin_tone:" - }, - "1f468-1f3fd-1f3a8": { - "output": "1f468-1f3fd-200d-1f3a8", - "name": "man artist: medium skin tone", - "alpha_code": ":man_artist_tone3:", - "aliases": ":man_artist_medium_skin_tone:" - }, - "1f468-1f3fe-1f3a8": { - "output": "1f468-1f3fe-200d-1f3a8", - "name": "man artist: medium-dark skin tone", - "alpha_code": ":man_artist_tone4:", - "aliases": ":man_artist_medium_dark_skin_tone:" - }, - "1f468-1f3ff-1f3a8": { - "output": "1f468-1f3ff-200d-1f3a8", - "name": "man artist: dark skin tone", - "alpha_code": ":man_artist_tone5:", - "aliases": ":man_artist_dark_skin_tone:" - }, - "1f469-1f3a8": { - "output": "1f469-200d-1f3a8", - "name": "woman artist", - "alpha_code": ":woman_artist:", - "aliases": "" - }, - "1f469-1f3fb-1f3a8": { - "output": "1f469-1f3fb-200d-1f3a8", - "name": "woman artist: light skin tone", - "alpha_code": ":woman_artist_tone1:", - "aliases": ":woman_artist_light_skin_tone:" - }, - "1f469-1f3fc-1f3a8": { - "output": "1f469-1f3fc-200d-1f3a8", - "name": "woman artist: medium-light skin tone", - "alpha_code": ":woman_artist_tone2:", - "aliases": ":woman_artist_medium_light_skin_tone:" - }, - "1f469-1f3fd-1f3a8": { - "output": "1f469-1f3fd-200d-1f3a8", - "name": "woman artist: medium skin tone", - "alpha_code": ":woman_artist_tone3:", - "aliases": ":woman_artist_medium_skin_tone:" - }, - "1f469-1f3fe-1f3a8": { - "output": "1f469-1f3fe-200d-1f3a8", - "name": "woman artist: medium-dark skin tone", - "alpha_code": ":woman_artist_tone4:", - "aliases": ":woman_artist_medium_dark_skin_tone:" - }, - "1f469-1f3ff-1f3a8": { - "output": "1f469-1f3ff-200d-1f3a8", - "name": "woman artist: dark skin tone", - "alpha_code": ":woman_artist_tone5:", - "aliases": ":woman_artist_dark_skin_tone:" - }, - "1f468-1f680": { - "output": "1f468-200d-1f680", - "name": "man astronaut", - "alpha_code": ":man_astronaut:", - "aliases": "" - }, - "1f468-1f3fb-1f680": { - "output": "1f468-1f3fb-200d-1f680", - "name": "man astronaut: light skin tone", - "alpha_code": ":man_astronaut_tone1:", - "aliases": ":man_astronaut_light_skin_tone:" - }, - "1f468-1f3fc-1f680": { - "output": "1f468-1f3fc-200d-1f680", - "name": "man astronaut: medium-light skin tone", - "alpha_code": ":man_astronaut_tone2:", - "aliases": ":man_astronaut_medium_light_skin_tone:" - }, - "1f468-1f3fd-1f680": { - "output": "1f468-1f3fd-200d-1f680", - "name": "man astronaut: medium skin tone", - "alpha_code": ":man_astronaut_tone3:", - "aliases": ":man_astronaut_medium_skin_tone:" - }, - "1f468-1f3fe-1f680": { - "output": "1f468-1f3fe-200d-1f680", - "name": "man astronaut: medium-dark skin tone", - "alpha_code": ":man_astronaut_tone4:", - "aliases": ":man_astronaut_medium_dark_skin_tone:" - }, - "1f468-1f3ff-1f680": { - "output": "1f468-1f3ff-200d-1f680", - "name": "man astronaut: dark skin tone", - "alpha_code": ":man_astronaut_tone5:", - "aliases": ":man_astronaut_dark_skin_tone:" - }, - "1f469-1f680": { - "output": "1f469-200d-1f680", - "name": "woman astronaut", - "alpha_code": ":woman_astronaut:", - "aliases": "" - }, - "1f469-1f3fb-1f680": { - "output": "1f469-1f3fb-200d-1f680", - "name": "woman astronaut: light skin tone", - "alpha_code": ":woman_astronaut_tone1:", - "aliases": ":woman_astronaut_light_skin_tone:" - }, - "1f469-1f3fc-1f680": { - "output": "1f469-1f3fc-200d-1f680", - "name": "woman astronaut: medium-light skin tone", - "alpha_code": ":woman_astronaut_tone2:", - "aliases": ":woman_astronaut_medium_light_skin_tone:" - }, - "1f469-1f3fd-1f680": { - "output": "1f469-1f3fd-200d-1f680", - "name": "woman astronaut: medium skin tone", - "alpha_code": ":woman_astronaut_tone3:", - "aliases": ":woman_astronaut_medium_skin_tone:" - }, - "1f469-1f3fe-1f680": { - "output": "1f469-1f3fe-200d-1f680", - "name": "woman astronaut: medium-dark skin tone", - "alpha_code": ":woman_astronaut_tone4:", - "aliases": ":woman_astronaut_medium_dark_skin_tone:" - }, - "1f469-1f3ff-1f680": { - "output": "1f469-1f3ff-200d-1f680", - "name": "woman astronaut: dark skin tone", - "alpha_code": ":woman_astronaut_tone5:", - "aliases": ":woman_astronaut_dark_skin_tone:" - }, - "1f468-1f692": { - "output": "1f468-200d-1f692", - "name": "man firefighter", - "alpha_code": ":man_firefighter:", - "aliases": "" - }, - "1f468-1f3fb-1f692": { - "output": "1f468-1f3fb-200d-1f692", - "name": "man firefighter: light skin tone", - "alpha_code": ":man_firefighter_tone1:", - "aliases": ":man_firefighter_light_skin_tone:" - }, - "1f468-1f3fc-1f692": { - "output": "1f468-1f3fc-200d-1f692", - "name": "man firefighter: medium-light skin tone", - "alpha_code": ":man_firefighter_tone2:", - "aliases": ":man_firefighter_medium_light_skin_tone:" - }, - "1f468-1f3fd-1f692": { - "output": "1f468-1f3fd-200d-1f692", - "name": "man firefighter: medium skin tone", - "alpha_code": ":man_firefighter_tone3:", - "aliases": ":man_firefighter_medium_skin_tone:" - }, - "1f468-1f3fe-1f692": { - "output": "1f468-1f3fe-200d-1f692", - "name": "man firefighter: medium-dark skin tone", - "alpha_code": ":man_firefighter_tone4:", - "aliases": ":man_firefighter_medium_dark_skin_tone:" - }, - "1f468-1f3ff-1f692": { - "output": "1f468-1f3ff-200d-1f692", - "name": "man firefighter: dark skin tone", - "alpha_code": ":man_firefighter_tone5:", - "aliases": ":man_firefighter_dark_skin_tone:" - }, - "1f469-1f692": { - "output": "1f469-200d-1f692", - "name": "woman firefighter", - "alpha_code": ":woman_firefighter:", - "aliases": "" - }, - "1f469-1f3fb-1f692": { - "output": "1f469-1f3fb-200d-1f692", - "name": "woman firefighter: light skin tone", - "alpha_code": ":woman_firefighter_tone1:", - "aliases": ":woman_firefighter_light_skin_tone:" - }, - "1f469-1f3fc-1f692": { - "output": "1f469-1f3fc-200d-1f692", - "name": "woman firefighter: medium-light skin tone", - "alpha_code": ":woman_firefighter_tone2:", - "aliases": ":woman_firefighter_medium_light_skin_tone:" - }, - "1f469-1f3fd-1f692": { - "output": "1f469-1f3fd-200d-1f692", - "name": "woman firefighter: medium skin tone", - "alpha_code": ":woman_firefighter_tone3:", - "aliases": ":woman_firefighter_medium_skin_tone:" - }, - "1f469-1f3fe-1f692": { - "output": "1f469-1f3fe-200d-1f692", - "name": "woman firefighter: medium-dark skin tone", - "alpha_code": ":woman_firefighter_tone4:", - "aliases": ":woman_firefighter_medium_dark_skin_tone:" - }, - "1f469-1f3ff-1f692": { - "output": "1f469-1f3ff-200d-1f692", - "name": "woman firefighter: dark skin tone", - "alpha_code": ":woman_firefighter_tone5:", - "aliases": ":woman_firefighter_dark_skin_tone:" - }, - "2640": { - "output": "2640-fe0f", - "name": "female sign", - "alpha_code": ":female_sign:", - "aliases": "" - }, - "2642": { - "output": "2642-fe0f", - "name": "male sign", - "alpha_code": ":male_sign:", - "aliases": "" - }, - "2695": { - "output": "2695-fe0f", - "name": "medical symbol", - "alpha_code": ":medical_symbol:", - "aliases": "" - }, - "1f1fa-1f1f3": { - "output": "1f1fa-1f1f3", - "name": "United Nations", - "alpha_code": ":united_nations:", - "aliases": "" - }, - "1f3c2-1f3fc": { - "output": "1f3c2-1f3fc", - "name": "snowboarder: medium-light skin tone", - "alpha_code": ":snowboarder_tone2:", - "aliases": ":snowboarder_medium_light_skin_tone:" - }, - "1f3c2-1f3fd": { - "output": "1f3c2-1f3fd", - "name": "snowboarder: medium skin tone", - "alpha_code": ":snowboarder_tone3:", - "aliases": ":snowboarder_medium_skin_tone:" - }, - "1f3c2-1f3fe": { - "output": "1f3c2-1f3fe", - "name": "snowboarder: medium-dark skin tone", - "alpha_code": ":snowboarder_tone4:", - "aliases": ":snowboarder_medium_dark_skin_tone:" - }, - "1f3c2-1f3ff": { - "output": "1f3c2-1f3ff", - "name": "snowboarder: dark skin tone", - "alpha_code": ":snowboarder_tone5:", - "aliases": ":snowboarder_dark_skin_tone:" - }, - "1f3cc-1f3fb": { - "output": "1f3cc-1f3fb", - "name": "person golfing: light skin tone", - "alpha_code": ":person_golfing_tone1:", - "aliases": ":person_golfing_light_skin_tone:" - }, - "1f3cc-1f3fc": { - "output": "1f3cc-1f3fc", - "name": "person golfing: medium-light skin tone", - "alpha_code": ":person_golfing_tone2:", - "aliases": ":person_golfing_medium_light_skin_tone:" - }, - "1f3cc-1f3fd": { - "output": "1f3cc-1f3fd", - "name": "person golfing: medium skin tone", - "alpha_code": ":person_golfing_tone3:", - "aliases": ":person_golfing_medium_skin_tone:" - }, - "1f3cc-1f3fe": { - "output": "1f3cc-1f3fe", - "name": "person golfing: medium-dark skin tone", - "alpha_code": ":person_golfing_tone4:", - "aliases": ":person_golfing_medium_dark_skin_tone:" - }, - "1f3cc-1f3ff": { - "output": "1f3cc-1f3ff", - "name": "person golfing: dark skin tone", - "alpha_code": ":person_golfing_tone5:", - "aliases": ":person_golfing_dark_skin_tone:" - }, - "1f468-1f467-1f467": { - "output": "1f468-200d-1f467-200d-1f467", - "name": "family: man, girl, girl", - "alpha_code": ":family_man_girl_girl:", - "aliases": "" - }, - "1f468-1f469-1f466": { - "output": "1f468-200d-1f469-200d-1f466", - "name": "family: man, woman, boy", - "alpha_code": ":family_man_woman_boy:", - "aliases": "" - }, - "1f469-2764-1f468": { - "output": "1f469-200d-2764-fe0f-200d-1f468", - "name": "couple with heart: woman, man", - "alpha_code": ":couple_with_heart_woman_man:", - "aliases": "" - }, - "1f469-2764-1f48b-1f468": { - "output": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f468", - "name": "kiss: woman, man", - "alpha_code": ":kiss_woman_man:", - "aliases": "" - }, - "1f6f7": { - "output": "1f6f7", - "name": "sled", - "alpha_code": ":sled:", - "aliases": "" - }, - "1f6f8": { - "output": "1f6f8", - "name": "flying saucer", - "alpha_code": ":flying_saucer:", - "aliases": "" - }, - "1f91f": { - "output": "1f91f", - "name": "love-you gesture", - "alpha_code": ":love_you_gesture:", - "aliases": "" - }, - "1f928": { - "output": "1f928", - "name": "face with raised eyebrow", - "alpha_code": ":face_with_raised_eyebrow:", - "aliases": "" - }, - "1f929": { - "output": "1f929", - "name": "star-struck", - "alpha_code": ":star_struck:", - "aliases": "" - }, - "1f92a": { - "output": "1f92a", - "name": "crazy face", - "alpha_code": ":crazy_face:", - "aliases": "" - }, - "1f92b": { - "output": "1f92b", - "name": "shushing face", - "alpha_code": ":shushing_face:", - "aliases": "" - }, - "1f92c": { - "output": "1f92c", - "name": "face with symbols over mouth", - "alpha_code": ":face_with_symbols_over_mouth:", - "aliases": "" - }, - "1f92d": { - "output": "1f92d", - "name": "face with hand over mouth", - "alpha_code": ":face_with_hand_over_mouth:", - "aliases": "" - }, - "1f92e": { - "output": "1f92e", - "name": "face vomiting", - "alpha_code": ":face_vomiting:", - "aliases": "" - }, - "1f92f": { - "output": "1f92f", - "name": "exploding head", - "alpha_code": ":exploding_head:", - "aliases": "" - }, - "1f931": { - "output": "1f931", - "name": "breast-feeding", - "alpha_code": ":breast_feeding:", - "aliases": "" - }, - "1f932": { - "output": "1f932", - "name": "palms up together", - "alpha_code": ":palms_up_together:", - "aliases": "" - }, - "1f94c": { - "output": "1f94c", - "name": "curling stone", - "alpha_code": ":curling_stone:", - "aliases": "" - }, - "1f95f": { - "output": "1f95f", - "name": "dumpling", - "alpha_code": ":dumpling:", - "aliases": "" - }, - "1f960": { - "output": "1f960", - "name": "fortune cookie", - "alpha_code": ":fortune_cookie:", - "aliases": "" - }, - "1f961": { - "output": "1f961", - "name": "takeout box", - "alpha_code": ":takeout_box:", - "aliases": "" - }, - "1f962": { - "output": "1f962", - "name": "chopsticks", - "alpha_code": ":chopsticks:", - "aliases": "" - }, - "1f963": { - "output": "1f963", - "name": "bowl with spoon", - "alpha_code": ":bowl_with_spoon:", - "aliases": "" - }, - "1f964": { - "output": "1f964", - "name": "cup with straw", - "alpha_code": ":cup_with_straw:", - "aliases": "" - }, - "1f965": { - "output": "1f965", - "name": "coconut", - "alpha_code": ":coconut:", - "aliases": "" - }, - "1f966": { - "output": "1f966", - "name": "broccoli", - "alpha_code": ":broccoli:", - "aliases": "" - }, - "1f967": { - "output": "1f967", - "name": "pie", - "alpha_code": ":pie:", - "aliases": "" - }, - "1f968": { - "output": "1f968", - "name": "pretzel", - "alpha_code": ":pretzel:", - "aliases": "" - }, - "1f969": { - "output": "1f969", - "name": "cut of meat", - "alpha_code": ":cut_of_meat:", - "aliases": "" - }, - "1f96a": { - "output": "1f96a", - "name": "sandwich", - "alpha_code": ":sandwich:", - "aliases": "" - }, - "1f96b": { - "output": "1f96b", - "name": "canned food", - "alpha_code": ":canned_food:", - "aliases": "" - }, - "1f992": { - "output": "1f992", - "name": "giraffe", - "alpha_code": ":giraffe:", - "aliases": "" - }, - "1f993": { - "output": "1f993", - "name": "zebra", - "alpha_code": ":zebra:", - "aliases": "" - }, - "1f994": { - "output": "1f994", - "name": "hedgehog", - "alpha_code": ":hedgehog:", - "aliases": "" - }, - "1f995": { - "output": "1f995", - "name": "sauropod", - "alpha_code": ":sauropod:", - "aliases": "" - }, - "1f996": { - "output": "1f996", - "name": "T-Rex", - "alpha_code": ":t_rex:", - "aliases": "" - }, - "1f997": { - "output": "1f997", - "name": "cricket", - "alpha_code": ":cricket:", - "aliases": "" - }, - "1f9d0": { - "output": "1f9d0", - "name": "face with monocle", - "alpha_code": ":face_with_monocle:", - "aliases": "" - }, - "1f9d1": { - "output": "1f9d1", - "name": "adult", - "alpha_code": ":adult:", - "aliases": "" - }, - "1f9d2": { - "output": "1f9d2", - "name": "child", - "alpha_code": ":child:", - "aliases": "" - }, - "1f9d3": { - "output": "1f9d3", - "name": "older adult", - "alpha_code": ":older_adult:", - "aliases": "" - }, - "1f9d4": { - "output": "1f9d4", - "name": "bearded person", - "alpha_code": ":bearded_person:", - "aliases": "" - }, - "1f9d5": { - "output": "1f9d5", - "name": "woman with headscarf", - "alpha_code": ":woman_with_headscarf:", - "aliases": "" - }, - "1f9d6": { - "output": "1f9d6", - "name": "person in steamy room", - "alpha_code": ":person_in_steamy_room:", - "aliases": "" - }, - "1f9d7": { - "output": "1f9d7", - "name": "person climbing", - "alpha_code": ":person_climbing:", - "aliases": "" - }, - "1f9d8": { - "output": "1f9d8", - "name": "person in lotus position", - "alpha_code": ":person_in_lotus_position:", - "aliases": "" - }, - "1f9d9": { - "output": "1f9d9", - "name": "mage", - "alpha_code": ":mage:", - "aliases": "" - }, - "1f9da": { - "output": "1f9da", - "name": "fairy", - "alpha_code": ":fairy:", - "aliases": "" - }, - "1f9db": { - "output": "1f9db", - "name": "vampire", - "alpha_code": ":vampire:", - "aliases": "" - }, - "1f9dc": { - "output": "1f9dc", - "name": "merperson", - "alpha_code": ":merperson:", - "aliases": "" - }, - "1f9dd": { - "output": "1f9dd", - "name": "elf", - "alpha_code": ":elf:", - "aliases": "" - }, - "1f9de": { - "output": "1f9de", - "name": "genie", - "alpha_code": ":genie:", - "aliases": "" - }, - "1f9df": { - "output": "1f9df", - "name": "zombie", - "alpha_code": ":zombie:", - "aliases": "" - }, - "1f9e0": { - "output": "1f9e0", - "name": "brain", - "alpha_code": ":brain:", - "aliases": "" - }, - "1f9e1": { - "output": "1f9e1", - "name": "orange heart", - "alpha_code": ":orange_heart:", - "aliases": "" - }, - "1f9e2": { - "output": "1f9e2", - "name": "billed cap", - "alpha_code": ":billed_cap:", - "aliases": "" - }, - "1f9e3": { - "output": "1f9e3", - "name": "scarf", - "alpha_code": ":scarf:", - "aliases": "" - }, - "1f9e4": { - "output": "1f9e4", - "name": "gloves", - "alpha_code": ":gloves:", - "aliases": "" - }, - "1f9e5": { - "output": "1f9e5", - "name": "coat", - "alpha_code": ":coat:", - "aliases": "" - }, - "1f9e6": { - "output": "1f9e6", - "name": "socks", - "alpha_code": ":socks:", - "aliases": "" - }, - "1f3f4-e0067-e0062-e0065-e006e-e0067-e007f": { - "output": "1f3f4-e0067-e0062-e0065-e006e-e0067-e007f", - "name": "England", - "alpha_code": ":england:", - "aliases": "" - }, - "1f3f4-e0067-e0062-e0073-e0063-e0074-e007f": { - "output": "1f3f4-e0067-e0062-e0073-e0063-e0074-e007f", - "name": "Scotland", - "alpha_code": ":scotland:", - "aliases": "" - }, - "1f3f4-e0067-e0062-e0077-e006c-e0073-e007f": { - "output": "1f3f4-e0067-e0062-e0077-e006c-e0073-e007f", - "name": "Wales", - "alpha_code": ":wales:", - "aliases": "" - }, - "1f91f-1f3fb": { - "output": "1f91f-1f3fb", - "name": "love-you gesture: light skin tone", - "alpha_code": ":love_you_gesture_tone1:", - "aliases": ":love_you_gesture_light_skin_tone:" - }, - "1f91f-1f3fc": { - "output": "1f91f-1f3fc", - "name": "love-you gesture: medium-light skin tone", - "alpha_code": ":love_you_gesture_tone2:", - "aliases": ":love_you_gesture_medium_light_skin_tone:" - }, - "1f91f-1f3fd": { - "output": "1f91f-1f3fd", - "name": "love-you gesture: medium skin tone", - "alpha_code": ":love_you_gesture_tone3:", - "aliases": ":love_you_gesture_medium_skin_tone:" - }, - "1f91f-1f3fe": { - "output": "1f91f-1f3fe", - "name": "love-you gesture: medium-dark skin tone", - "alpha_code": ":love_you_gesture_tone4:", - "aliases": ":love_you_gesture_medium_dark_skin_tone:" - }, - "1f91f-1f3ff": { - "output": "1f91f-1f3ff", - "name": "love-you gesture: dark skin tone", - "alpha_code": ":love_you_gesture_tone5:", - "aliases": ":love_you_gesture_dark_skin_tone:" - }, - "1f931-1f3fb": { - "output": "1f931-1f3fb", - "name": "breast-feeding: light skin tone", - "alpha_code": ":breast_feeding_tone1:", - "aliases": ":breast_feeding_light_skin_tone:" - }, - "1f931-1f3fc": { - "output": "1f931-1f3fc", - "name": "breast-feeding: medium-light skin tone", - "alpha_code": ":breast_feeding_tone2:", - "aliases": ":breast_feeding_medium_light_skin_tone:" - }, - "1f931-1f3fd": { - "output": "1f931-1f3fd", - "name": "breast-feeding: medium skin tone", - "alpha_code": ":breast_feeding_tone3:", - "aliases": ":breast_feeding_medium_skin_tone:" - }, - "1f931-1f3fe": { - "output": "1f931-1f3fe", - "name": "breast-feeding: medium-dark skin tone", - "alpha_code": ":breast_feeding_tone4:", - "aliases": ":breast_feeding_medium_dark_skin_tone:" - }, - "1f931-1f3ff": { - "output": "1f931-1f3ff", - "name": "breast-feeding: dark skin tone", - "alpha_code": ":breast_feeding_tone5:", - "aliases": ":breast_feeding_dark_skin_tone:" - }, - "1f932-1f3fb": { - "output": "1f932-1f3fb", - "name": "palms up together: light skin tone", - "alpha_code": ":palms_up_together_tone1:", - "aliases": ":palms_up_together_light_skin_tone:" - }, - "1f932-1f3fc": { - "output": "1f932-1f3fc", - "name": "palms up together: medium-light skin tone", - "alpha_code": ":palms_up_together_tone2:", - "aliases": ":palms_up_together_medium_light_skin_tone:" - }, - "1f932-1f3fd": { - "output": "1f932-1f3fd", - "name": "palms up together: medium skin tone", - "alpha_code": ":palms_up_together_tone3:", - "aliases": ":palms_up_together_medium_skin_tone:" - }, - "1f932-1f3fe": { - "output": "1f932-1f3fe", - "name": "palms up together: medium-dark skin tone", - "alpha_code": ":palms_up_together_tone4:", - "aliases": ":palms_up_together_medium_dark_skin_tone:" - }, - "1f932-1f3ff": { - "output": "1f932-1f3ff", - "name": "palms up together: dark skin tone", - "alpha_code": ":palms_up_together_tone5:", - "aliases": ":palms_up_together_dark_skin_tone:" - }, - "1f9d1-1f3fb": { - "output": "1f9d1-1f3fb", - "name": "adult: light skin tone", - "alpha_code": ":adult_tone1:", - "aliases": ":adult_light_skin_tone:" - }, - "1f9d1-1f3fc": { - "output": "1f9d1-1f3fc", - "name": "adult: medium-light skin tone", - "alpha_code": ":adult_tone2:", - "aliases": ":adult_medium_light_skin_tone:" - }, - "1f9d1-1f3fd": { - "output": "1f9d1-1f3fd", - "name": "adult: medium skin tone", - "alpha_code": ":adult_tone3:", - "aliases": ":adult_medium_skin_tone:" - }, - "1f9d1-1f3fe": { - "output": "1f9d1-1f3fe", - "name": "adult: medium-dark skin tone", - "alpha_code": ":adult_tone4:", - "aliases": ":adult_medium_dark_skin_tone:" - }, - "1f9d1-1f3ff": { - "output": "1f9d1-1f3ff", - "name": "adult: dark skin tone", - "alpha_code": ":adult_tone5:", - "aliases": ":adult_dark_skin_tone:" - }, - "1f9d2-1f3fb": { - "output": "1f9d2-1f3fb", - "name": "child: light skin tone", - "alpha_code": ":child_tone1:", - "aliases": ":child_light_skin_tone:" - }, - "1f9d2-1f3fc": { - "output": "1f9d2-1f3fc", - "name": "child: medium-light skin tone", - "alpha_code": ":child_tone2:", - "aliases": ":child_medium_light_skin_tone:" - }, - "1f9d2-1f3fd": { - "output": "1f9d2-1f3fd", - "name": "child: medium skin tone", - "alpha_code": ":child_tone3:", - "aliases": ":child_medium_skin_tone:" - }, - "1f9d2-1f3fe": { - "output": "1f9d2-1f3fe", - "name": "child: medium-dark skin tone", - "alpha_code": ":child_tone4:", - "aliases": ":child_medium_dark_skin_tone:" - }, - "1f9d2-1f3ff": { - "output": "1f9d2-1f3ff", - "name": "child: dark skin tone", - "alpha_code": ":child_tone5:", - "aliases": ":child_dark_skin_tone:" - }, - "1f9d3-1f3fb": { - "output": "1f9d3-1f3fb", - "name": "older adult: light skin tone", - "alpha_code": ":older_adult_tone1:", - "aliases": ":older_adult_light_skin_tone:" - }, - "1f9d3-1f3fc": { - "output": "1f9d3-1f3fc", - "name": "older adult: medium-light skin tone", - "alpha_code": ":older_adult_tone2:", - "aliases": ":older_adult_medium_light_skin_tone:" - }, - "1f9d3-1f3fd": { - "output": "1f9d3-1f3fd", - "name": "older adult: medium skin tone", - "alpha_code": ":older_adult_tone3:", - "aliases": ":older_adult_medium_skin_tone:" - }, - "1f9d3-1f3fe": { - "output": "1f9d3-1f3fe", - "name": "older adult: medium-dark skin tone", - "alpha_code": ":older_adult_tone4:", - "aliases": ":older_adult_medium_dark_skin_tone:" - }, - "1f9d3-1f3ff": { - "output": "1f9d3-1f3ff", - "name": "older adult: dark skin tone", - "alpha_code": ":older_adult_tone5:", - "aliases": ":older_adult_dark_skin_tone:" - }, - "1f9d4-1f3fb": { - "output": "1f9d4-1f3fb", - "name": "bearded person: light skin tone", - "alpha_code": ":bearded_person_tone1:", - "aliases": ":bearded_person_light_skin_tone:" - }, - "1f9d4-1f3fc": { - "output": "1f9d4-1f3fc", - "name": "bearded person: medium-light skin tone", - "alpha_code": ":bearded_person_tone2:", - "aliases": ":bearded_person_medium_light_skin_tone:" - }, - "1f9d4-1f3fd": { - "output": "1f9d4-1f3fd", - "name": "bearded person: medium skin tone", - "alpha_code": ":bearded_person_tone3:", - "aliases": ":bearded_person_medium_skin_tone:" - }, - "1f9d4-1f3fe": { - "output": "1f9d4-1f3fe", - "name": "bearded person: medium-dark skin tone", - "alpha_code": ":bearded_person_tone4:", - "aliases": ":bearded_person_medium_dark_skin_tone:" - }, - "1f9d4-1f3ff": { - "output": "1f9d4-1f3ff", - "name": "bearded person: dark skin tone", - "alpha_code": ":bearded_person_tone5:", - "aliases": ":bearded_person_dark_skin_tone:" - }, - "1f9d5-1f3fb": { - "output": "1f9d5-1f3fb", - "name": "woman with headscarf: light skin tone", - "alpha_code": ":woman_with_headscarf_tone1:", - "aliases": ":woman_with_headscarf_light_skin_tone:" - }, - "1f9d5-1f3fc": { - "output": "1f9d5-1f3fc", - "name": "woman with headscarf: medium-light skin tone", - "alpha_code": ":woman_with_headscarf_tone2:", - "aliases": ":woman_with_headscarf_medium_light_skin_tone:" - }, - "1f9d5-1f3fd": { - "output": "1f9d5-1f3fd", - "name": "woman with headscarf: medium skin tone", - "alpha_code": ":woman_with_headscarf_tone3:", - "aliases": ":woman_with_headscarf_medium_skin_tone:" - }, - "1f9d5-1f3fe": { - "output": "1f9d5-1f3fe", - "name": "woman with headscarf: medium-dark skin tone", - "alpha_code": ":woman_with_headscarf_tone4:", - "aliases": ":woman_with_headscarf_medium_dark_skin_tone:" - }, - "1f9d5-1f3ff": { - "output": "1f9d5-1f3ff", - "name": "woman with headscarf: dark skin tone", - "alpha_code": ":woman_with_headscarf_tone5:", - "aliases": ":woman_with_headscarf_dark_skin_tone:" - }, - "1f9d6-1f3fb": { - "output": "1f9d6-1f3fb", - "name": "person in steamy room: light skin tone", - "alpha_code": ":person_in_steamy_room_tone1:", - "aliases": ":person_in_steamy_room_light_skin_tone:" - }, - "1f9d6-1f3fc": { - "output": "1f9d6-1f3fc", - "name": "person in steamy room: medium-light skin tone", - "alpha_code": ":person_in_steamy_room_tone2:", - "aliases": ":person_in_steamy_room_medium_light_skin_tone:" - }, - "1f9d6-1f3fd": { - "output": "1f9d6-1f3fd", - "name": "person in steamy room: medium skin tone", - "alpha_code": ":person_in_steamy_room_tone3:", - "aliases": ":person_in_steamy_room_medium_skin_tone:" - }, - "1f9d6-1f3fe": { - "output": "1f9d6-1f3fe", - "name": "person in steamy room: medium-dark skin tone", - "alpha_code": ":person_in_steamy_room_tone4:", - "aliases": ":person_in_steamy_room_medium_dark_skin_tone:" - }, - "1f9d6-1f3ff": { - "output": "1f9d6-1f3ff", - "name": "person in steamy room: dark skin tone", - "alpha_code": ":person_in_steamy_room_tone5:", - "aliases": ":person_in_steamy_room_dark_skin_tone:" - }, - "1f9d7-1f3fb": { - "output": "1f9d7-1f3fb", - "name": "person climbing: light skin tone", - "alpha_code": ":person_climbing_tone1:", - "aliases": ":person_climbing_light_skin_tone:" - }, - "1f9d7-1f3fc": { - "output": "1f9d7-1f3fc", - "name": "person climbing: medium-light skin tone", - "alpha_code": ":person_climbing_tone2:", - "aliases": ":person_climbing_medium_light_skin_tone:" - }, - "1f9d7-1f3fd": { - "output": "1f9d7-1f3fd", - "name": "person climbing: medium skin tone", - "alpha_code": ":person_climbing_tone3:", - "aliases": ":person_climbing_medium_skin_tone:" - }, - "1f9d7-1f3fe": { - "output": "1f9d7-1f3fe", - "name": "person climbing: medium-dark skin tone", - "alpha_code": ":person_climbing_tone4:", - "aliases": ":person_climbing_medium_dark_skin_tone:" - }, - "1f9d7-1f3ff": { - "output": "1f9d7-1f3ff", - "name": "person climbing: dark skin tone", - "alpha_code": ":person_climbing_tone5:", - "aliases": ":person_climbing_dark_skin_tone:" - }, - "1f9d8-1f3fb": { - "output": "1f9d8-1f3fb", - "name": "person in lotus position: light skin tone", - "alpha_code": ":person_in_lotus_position_tone1:", - "aliases": ":person_in_lotus_position_light_skin_tone:" - }, - "1f9d8-1f3fc": { - "output": "1f9d8-1f3fc", - "name": "person in lotus position: medium-light skin tone", - "alpha_code": ":person_in_lotus_position_tone2:", - "aliases": ":person_in_lotus_position_medium_light_skin_tone:" - }, - "1f9d8-1f3fd": { - "output": "1f9d8-1f3fd", - "name": "person in lotus position: medium skin tone", - "alpha_code": ":person_in_lotus_position_tone3:", - "aliases": ":person_in_lotus_position_medium_skin_tone:" - }, - "1f9d8-1f3fe": { - "output": "1f9d8-1f3fe", - "name": "person in lotus position: medium-dark skin tone", - "alpha_code": ":person_in_lotus_position_tone4:", - "aliases": ":person_in_lotus_position_medium_dark_skin_tone:" - }, - "1f9d8-1f3ff": { - "output": "1f9d8-1f3ff", - "name": "person in lotus position: dark skin tone", - "alpha_code": ":person_in_lotus_position_tone5:", - "aliases": ":person_in_lotus_position_dark_skin_tone:" - }, - "1f9d9-1f3fb": { - "output": "1f9d9-1f3fb", - "name": "mage: light skin tone", - "alpha_code": ":mage_tone1:", - "aliases": ":mage_light_skin_tone:" - }, - "1f9d9-1f3fc": { - "output": "1f9d9-1f3fc", - "name": "mage: medium-light skin tone", - "alpha_code": ":mage_tone2:", - "aliases": ":mage_medium_light_skin_tone:" - }, - "1f9d9-1f3fd": { - "output": "1f9d9-1f3fd", - "name": "mage: medium skin tone", - "alpha_code": ":mage_tone3:", - "aliases": ":mage_medium_skin_tone:" - }, - "1f9d9-1f3fe": { - "output": "1f9d9-1f3fe", - "name": "mage: medium-dark skin tone", - "alpha_code": ":mage_tone4:", - "aliases": ":mage_medium_dark_skin_tone:" - }, - "1f9d9-1f3ff": { - "output": "1f9d9-1f3ff", - "name": "mage: dark skin tone", - "alpha_code": ":mage_tone5:", - "aliases": ":mage_dark_skin_tone:" - }, - "1f9da-1f3fb": { - "output": "1f9da-1f3fb", - "name": "fairy: light skin tone", - "alpha_code": ":fairy_tone1:", - "aliases": ":fairy_light_skin_tone:" - }, - "1f9da-1f3fc": { - "output": "1f9da-1f3fc", - "name": "fairy: medium-light skin tone", - "alpha_code": ":fairy_tone2:", - "aliases": ":fairy_medium_light_skin_tone:" - }, - "1f9da-1f3fd": { - "output": "1f9da-1f3fd", - "name": "fairy: medium skin tone", - "alpha_code": ":fairy_tone3:", - "aliases": ":fairy_medium_skin_tone:" - }, - "1f9da-1f3fe": { - "output": "1f9da-1f3fe", - "name": "fairy: medium-dark skin tone", - "alpha_code": ":fairy_tone4:", - "aliases": ":fairy_medium_dark_skin_tone:" - }, - "1f9da-1f3ff": { - "output": "1f9da-1f3ff", - "name": "fairy: dark skin tone", - "alpha_code": ":fairy_tone5:", - "aliases": ":fairy_dark_skin_tone:" - }, - "1f9db-1f3fb": { - "output": "1f9db-1f3fb", - "name": "vampire: light skin tone", - "alpha_code": ":vampire_tone1:", - "aliases": ":vampire_light_skin_tone:" - }, - "1f9db-1f3fc": { - "output": "1f9db-1f3fc", - "name": "vampire: medium-light skin tone", - "alpha_code": ":vampire_tone2:", - "aliases": ":vampire_medium_light_skin_tone:" - }, - "1f9db-1f3fd": { - "output": "1f9db-1f3fd", - "name": "vampire: medium skin tone", - "alpha_code": ":vampire_tone3:", - "aliases": ":vampire_medium_skin_tone:" - }, - "1f9db-1f3fe": { - "output": "1f9db-1f3fe", - "name": "vampire: medium-dark skin tone", - "alpha_code": ":vampire_tone4:", - "aliases": ":vampire_medium_dark_skin_tone:" - }, - "1f9db-1f3ff": { - "output": "1f9db-1f3ff", - "name": "vampire: dark skin tone", - "alpha_code": ":vampire_tone5:", - "aliases": ":vampire_dark_skin_tone:" - }, - "1f9dc-1f3fb": { - "output": "1f9dc-1f3fb", - "name": "merperson: light skin tone", - "alpha_code": ":merperson_tone1:", - "aliases": ":merperson_light_skin_tone:" - }, - "1f9dc-1f3fc": { - "output": "1f9dc-1f3fc", - "name": "merperson: medium-light skin tone", - "alpha_code": ":merperson_tone2:", - "aliases": ":merperson_medium_light_skin_tone:" - }, - "1f9dc-1f3fd": { - "output": "1f9dc-1f3fd", - "name": "merperson: medium skin tone", - "alpha_code": ":merperson_tone3:", - "aliases": ":merperson_medium_skin_tone:" - }, - "1f9dc-1f3fe": { - "output": "1f9dc-1f3fe", - "name": "merperson: medium-dark skin tone", - "alpha_code": ":merperson_tone4:", - "aliases": ":merperson_medium_dark_skin_tone:" - }, - "1f9dc-1f3ff": { - "output": "1f9dc-1f3ff", - "name": "merperson: dark skin tone", - "alpha_code": ":merperson_tone5:", - "aliases": ":merperson_dark_skin_tone:" - }, - "1f9dd-1f3fb": { - "output": "1f9dd-1f3fb", - "name": "elf: light skin tone", - "alpha_code": ":elf_tone1:", - "aliases": ":elf_light_skin_tone:" - }, - "1f9dd-1f3fc": { - "output": "1f9dd-1f3fc", - "name": "elf: medium-light skin tone", - "alpha_code": ":elf_tone2:", - "aliases": ":elf_medium_light_skin_tone:" - }, - "1f9dd-1f3fd": { - "output": "1f9dd-1f3fd", - "name": "elf: medium skin tone", - "alpha_code": ":elf_tone3:", - "aliases": ":elf_medium_skin_tone:" - }, - "1f9dd-1f3fe": { - "output": "1f9dd-1f3fe", - "name": "elf: medium-dark skin tone", - "alpha_code": ":elf_tone4:", - "aliases": ":elf_medium_dark_skin_tone:" - }, - "1f9dd-1f3ff": { - "output": "1f9dd-1f3ff", - "name": "elf: dark skin tone", - "alpha_code": ":elf_tone5:", - "aliases": ":elf_dark_skin_tone:" - }, - "1f9d9-2640": { - "output": "1f9d9-200d-2640-fe0f", - "name": "woman mage", - "alpha_code": ":woman_mage:", - "aliases": "" - }, - "1f9d9-2642": { - "output": "1f9d9-200d-2642-fe0f", - "name": "man mage", - "alpha_code": ":man_mage:", - "aliases": "" - }, - "1f9d9-1f3fb-2640": { - "output": "1f9d9-1f3fb-200d-2640-fe0f", - "name": "woman mage: light skin tone", - "alpha_code": ":woman_mage_tone1:", - "aliases": ":woman_mage_light_skin_tone:" - }, - "1f9d9-1f3fb-2642": { - "output": "1f9d9-1f3fb-200d-2642-fe0f", - "name": "man mage: light skin tone", - "alpha_code": ":man_mage_tone1:", - "aliases": ":man_mage_light_skin_tone:" - }, - "1f9d9-1f3fc-2640": { - "output": "1f9d9-1f3fc-200d-2640-fe0f", - "name": "woman mage: medium-light skin tone", - "alpha_code": ":woman_mage_tone2:", - "aliases": ":woman_mage_medium_light_skin_tone:" - }, - "1f9d9-1f3fc-2642": { - "output": "1f9d9-1f3fc-200d-2642-fe0f", - "name": "man mage: medium-light skin tone", - "alpha_code": ":man_mage_tone2:", - "aliases": ":man_mage_medium_light_skin_tone:" - }, - "1f9d9-1f3fd-2640": { - "output": "1f9d9-1f3fd-200d-2640-fe0f", - "name": "woman mage: medium skin tone", - "alpha_code": ":woman_mage_tone3:", - "aliases": ":woman_mage_medium_skin_tone:" - }, - "1f9d9-1f3fd-2642": { - "output": "1f9d9-1f3fd-200d-2642-fe0f", - "name": "man mage: medium skin tone", - "alpha_code": ":man_mage_tone3:", - "aliases": ":man_mage_medium_skin_tone:" - }, - "1f9d9-1f3fe-2640": { - "output": "1f9d9-1f3fe-200d-2640-fe0f", - "name": "woman mage: medium-dark skin tone", - "alpha_code": ":woman_mage_tone4:", - "aliases": ":woman_mage_medium_dark_skin_tone:" - }, - "1f9d9-1f3fe-2642": { - "output": "1f9d9-1f3fe-200d-2642-fe0f", - "name": "man mage: medium-dark skin tone", - "alpha_code": ":man_mage_tone4:", - "aliases": ":man_mage_medium_dark_skin_tone:" - }, - "1f9d9-1f3ff-2640": { - "output": "1f9d9-1f3ff-200d-2640-fe0f", - "name": "woman mage: dark skin tone", - "alpha_code": ":woman_mage_tone5:", - "aliases": ":woman_mage_dark_skin_tone:" - }, - "1f9d9-1f3ff-2642": { - "output": "1f9d9-1f3ff-200d-2642-fe0f", - "name": "man mage: dark skin tone", - "alpha_code": ":man_mage_tone5:", - "aliases": ":man_mage_dark_skin_tone:" - }, - "1f9da-2640": { - "output": "1f9da-200d-2640-fe0f", - "name": "woman fairy", - "alpha_code": ":woman_fairy:", - "aliases": "" - }, - "1f9da-2642": { - "output": "1f9da-200d-2642-fe0f", - "name": "man fairy", - "alpha_code": ":man_fairy:", - "aliases": "" - }, - "1f9da-1f3fb-2640": { - "output": "1f9da-1f3fb-200d-2640-fe0f", - "name": "woman fairy: light skin tone", - "alpha_code": ":woman_fairy_tone1:", - "aliases": ":woman_fairy_light_skin_tone:" - }, - "1f9da-1f3fb-2642": { - "output": "1f9da-1f3fb-200d-2642-fe0f", - "name": "man fairy: light skin tone", - "alpha_code": ":man_fairy_tone1:", - "aliases": ":man_fairy_light_skin_tone:" - }, - "1f9da-1f3fc-2640": { - "output": "1f9da-1f3fc-200d-2640-fe0f", - "name": "woman fairy: medium-light skin tone", - "alpha_code": ":woman_fairy_tone2:", - "aliases": ":woman_fairy_medium_light_skin_tone:" - }, - "1f9da-1f3fc-2642": { - "output": "1f9da-1f3fc-200d-2642-fe0f", - "name": "man fairy: medium-light skin tone", - "alpha_code": ":man_fairy_tone2:", - "aliases": ":man_fairy_medium_light_skin_tone:" - }, - "1f9da-1f3fd-2640": { - "output": "1f9da-1f3fd-200d-2640-fe0f", - "name": "woman fairy: medium skin tone", - "alpha_code": ":woman_fairy_tone3:", - "aliases": ":woman_fairy_medium_skin_tone:" - }, - "1f9da-1f3fd-2642": { - "output": "1f9da-1f3fd-200d-2642-fe0f", - "name": "man fairy: medium skin tone", - "alpha_code": ":man_fairy_tone3:", - "aliases": ":man_fairy_medium_skin_tone:" - }, - "1f9da-1f3fe-2640": { - "output": "1f9da-1f3fe-200d-2640-fe0f", - "name": "woman fairy: medium-dark skin tone", - "alpha_code": ":woman_fairy_tone4:", - "aliases": ":woman_fairy_medium_dark_skin_tone:" - }, - "1f9da-1f3fe-2642": { - "output": "1f9da-1f3fe-200d-2642-fe0f", - "name": "man fairy: medium-dark skin tone", - "alpha_code": ":man_fairy_tone4:", - "aliases": ":man_fairy_medium_dark_skin_tone:" - }, - "1f9da-1f3ff-2640": { - "output": "1f9da-1f3ff-200d-2640-fe0f", - "name": "woman fairy: dark skin tone", - "alpha_code": ":woman_fairy_tone5:", - "aliases": ":woman_fairy_dark_skin_tone:" - }, - "1f9da-1f3ff-2642": { - "output": "1f9da-1f3ff-200d-2642-fe0f", - "name": "man fairy: dark skin tone", - "alpha_code": ":man_fairy_tone5:", - "aliases": ":man_fairy_dark_skin_tone:" - }, - "1f9db-2640": { - "output": "1f9db-200d-2640-fe0f", - "name": "woman vampire", - "alpha_code": ":woman_vampire:", - "aliases": "" - }, - "1f9db-2642": { - "output": "1f9db-200d-2642-fe0f", - "name": "man vampire", - "alpha_code": ":man_vampire:", - "aliases": "" - }, - "1f9db-1f3fb-2640": { - "output": "1f9db-1f3fb-200d-2640-fe0f", - "name": "woman vampire: light skin tone", - "alpha_code": ":woman_vampire_tone1:", - "aliases": ":woman_vampire_light_skin_tone:" - }, - "1f9db-1f3fb-2642": { - "output": "1f9db-1f3fb-200d-2642-fe0f", - "name": "man vampire: light skin tone", - "alpha_code": ":man_vampire_tone1:", - "aliases": ":man_vampire_light_skin_tone:" - }, - "1f9db-1f3fc-2640": { - "output": "1f9db-1f3fc-200d-2640-fe0f", - "name": "woman vampire: medium-light skin tone", - "alpha_code": ":woman_vampire_tone2:", - "aliases": ":woman_vampire_medium_light_skin_tone:" - }, - "1f9db-1f3fc-2642": { - "output": "1f9db-1f3fc-200d-2642-fe0f", - "name": "man vampire: medium-light skin tone", - "alpha_code": ":man_vampire_tone2:", - "aliases": ":man_vampire_medium_light_skin_tone:" - }, - "1f9db-1f3fd-2640": { - "output": "1f9db-1f3fd-200d-2640-fe0f", - "name": "woman vampire: medium skin tone", - "alpha_code": ":woman_vampire_tone3:", - "aliases": ":woman_vampire_medium_skin_tone:" - }, - "1f9db-1f3fd-2642": { - "output": "1f9db-1f3fd-200d-2642-fe0f", - "name": "man vampire: medium skin tone", - "alpha_code": ":man_vampire_tone3:", - "aliases": ":man_vampire_medium_skin_tone:" - }, - "1f9db-1f3fe-2640": { - "output": "1f9db-1f3fe-200d-2640-fe0f", - "name": "woman vampire: medium-dark skin tone", - "alpha_code": ":woman_vampire_tone4:", - "aliases": ":woman_vampire_medium_dark_skin_tone:" - }, - "1f9db-1f3fe-2642": { - "output": "1f9db-1f3fe-200d-2642-fe0f", - "name": "man vampire: medium-dark skin tone", - "alpha_code": ":man_vampire_tone4:", - "aliases": ":man_vampire_medium_dark_skin_tone:" - }, - "1f9db-1f3ff-2640": { - "output": "1f9db-1f3ff-200d-2640-fe0f", - "name": "woman vampire: dark skin tone", - "alpha_code": ":woman_vampire_tone5:", - "aliases": ":woman_vampire_dark_skin_tone:" - }, - "1f9db-1f3ff-2642": { - "output": "1f9db-1f3ff-200d-2642-fe0f", - "name": "man vampire: dark skin tone", - "alpha_code": ":man_vampire_tone5:", - "aliases": ":man_vampire_dark_skin_tone:" - }, - "1f9dc-2640": { - "output": "1f9dc-200d-2640-fe0f", - "name": "mermaid", - "alpha_code": ":mermaid:", - "aliases": "" - }, - "1f9dc-2642": { - "output": "1f9dc-200d-2642-fe0f", - "name": "merman", - "alpha_code": ":merman:", - "aliases": "" - }, - "1f9dc-1f3fb-2640": { - "output": "1f9dc-1f3fb-200d-2640-fe0f", - "name": "mermaid: light skin tone", - "alpha_code": ":mermaid_tone1:", - "aliases": ":mermaid_light_skin_tone:" - }, - "1f9dc-1f3fb-2642": { - "output": "1f9dc-1f3fb-200d-2642-fe0f", - "name": "merman: light skin tone", - "alpha_code": ":merman_tone1:", - "aliases": ":merman_light_skin_tone:" - }, - "1f9dc-1f3fc-2640": { - "output": "1f9dc-1f3fc-200d-2640-fe0f", - "name": "mermaid: medium-light skin tone", - "alpha_code": ":mermaid_tone2:", - "aliases": ":mermaid_medium_light_skin_tone:" - }, - "1f9dc-1f3fc-2642": { - "output": "1f9dc-1f3fc-200d-2642-fe0f", - "name": "merman: medium-light skin tone", - "alpha_code": ":merman_tone2:", - "aliases": ":merman_medium_light_skin_tone:" - }, - "1f9dc-1f3fd-2640": { - "output": "1f9dc-1f3fd-200d-2640-fe0f", - "name": "mermaid: medium skin tone", - "alpha_code": ":mermaid_tone3:", - "aliases": ":mermaid_medium_skin_tone:" - }, - "1f9dc-1f3fd-2642": { - "output": "1f9dc-1f3fd-200d-2642-fe0f", - "name": "merman: medium skin tone", - "alpha_code": ":merman_tone3:", - "aliases": ":merman_medium_skin_tone:" - }, - "1f9dc-1f3fe-2640": { - "output": "1f9dc-1f3fe-200d-2640-fe0f", - "name": "mermaid: medium-dark skin tone", - "alpha_code": ":mermaid_tone4:", - "aliases": ":mermaid_medium_dark_skin_tone:" - }, - "1f9dc-1f3fe-2642": { - "output": "1f9dc-1f3fe-200d-2642-fe0f", - "name": "merman: medium-dark skin tone", - "alpha_code": ":merman_tone4:", - "aliases": ":merman_medium_dark_skin_tone:" - }, - "1f9dc-1f3ff-2640": { - "output": "1f9dc-1f3ff-200d-2640-fe0f", - "name": "mermaid: dark skin tone", - "alpha_code": ":mermaid_tone5:", - "aliases": ":mermaid_dark_skin_tone:" - }, - "1f9dc-1f3ff-2642": { - "output": "1f9dc-1f3ff-200d-2642-fe0f", - "name": "merman: dark skin tone", - "alpha_code": ":merman_tone5:", - "aliases": ":merman_dark_skin_tone:" - }, - "1f9dd-2640": { - "output": "1f9dd-200d-2640-fe0f", - "name": "woman elf", - "alpha_code": ":woman_elf:", - "aliases": "" - }, - "1f9dd-2642": { - "output": "1f9dd-200d-2642-fe0f", - "name": "man elf", - "alpha_code": ":man_elf:", - "aliases": "" - }, - "1f9dd-1f3fb-2640": { - "output": "1f9dd-1f3fb-200d-2640-fe0f", - "name": "woman elf: light skin tone", - "alpha_code": ":woman_elf_tone1:", - "aliases": ":woman_elf_light_skin_tone:" - }, - "1f9dd-1f3fb-2642": { - "output": "1f9dd-1f3fb-200d-2642-fe0f", - "name": "man elf: light skin tone", - "alpha_code": ":man_elf_tone1:", - "aliases": ":man_elf_light_skin_tone:" - }, - "1f9dd-1f3fc-2640": { - "output": "1f9dd-1f3fc-200d-2640-fe0f", - "name": "woman elf: medium-light skin tone", - "alpha_code": ":woman_elf_tone2:", - "aliases": ":woman_elf_medium_light_skin_tone:" - }, - "1f9dd-1f3fc-2642": { - "output": "1f9dd-1f3fc-200d-2642-fe0f", - "name": "man elf: medium-light skin tone", - "alpha_code": ":man_elf_tone2:", - "aliases": ":man_elf_medium_light_skin_tone:" - }, - "1f9dd-1f3fd-2640": { - "output": "1f9dd-1f3fd-200d-2640-fe0f", - "name": "woman elf: medium skin tone", - "alpha_code": ":woman_elf_tone3:", - "aliases": ":woman_elf_medium_skin_tone:" - }, - "1f9dd-1f3fd-2642": { - "output": "1f9dd-1f3fd-200d-2642-fe0f", - "name": "man elf: medium skin tone", - "alpha_code": ":man_elf_tone3:", - "aliases": ":man_elf_medium_skin_tone:" - }, - "1f9dd-1f3fe-2640": { - "output": "1f9dd-1f3fe-200d-2640-fe0f", - "name": "woman elf: medium-dark skin tone", - "alpha_code": ":woman_elf_tone4:", - "aliases": ":woman_elf_medium_dark_skin_tone:" - }, - "1f9dd-1f3fe-2642": { - "output": "1f9dd-1f3fe-200d-2642-fe0f", - "name": "man elf: medium-dark skin tone", - "alpha_code": ":man_elf_tone4:", - "aliases": ":man_elf_medium_dark_skin_tone:" - }, - "1f9dd-1f3ff-2640": { - "output": "1f9dd-1f3ff-200d-2640-fe0f", - "name": "woman elf: dark skin tone", - "alpha_code": ":woman_elf_tone5:", - "aliases": ":woman_elf_dark_skin_tone:" - }, - "1f9dd-1f3ff-2642": { - "output": "1f9dd-1f3ff-200d-2642-fe0f", - "name": "man elf: dark skin tone", - "alpha_code": ":man_elf_tone5:", - "aliases": ":man_elf_dark_skin_tone:" - }, - "1f9de-2640": { - "output": "1f9de-200d-2640-fe0f", - "name": "woman genie", - "alpha_code": ":woman_genie:", - "aliases": "" - }, - "1f9de-2642": { - "output": "1f9de-200d-2642-fe0f", - "name": "man genie", - "alpha_code": ":man_genie:", - "aliases": "" - }, - "1f9df-2640": { - "output": "1f9df-200d-2640-fe0f", - "name": "woman zombie", - "alpha_code": ":woman_zombie:", - "aliases": "" - }, - "1f9df-2642": { - "output": "1f9df-200d-2642-fe0f", - "name": "man zombie", - "alpha_code": ":man_zombie:", - "aliases": "" - }, - "1f9d6-2640": { - "output": "1f9d6-200d-2640-fe0f", - "name": "woman in steamy room", - "alpha_code": ":woman_in_steamy_room:", - "aliases": "" - }, - "1f9d6-2642": { - "output": "1f9d6-200d-2642-fe0f", - "name": "man in steamy room", - "alpha_code": ":man_in_steamy_room:", - "aliases": "" - }, - "1f9d6-1f3fb-2640": { - "output": "1f9d6-1f3fb-200d-2640-fe0f", - "name": "woman in steamy room: light skin tone", - "alpha_code": ":woman_in_steamy_room_tone1:", - "aliases": ":woman_in_steamy_room_light_skin_tone:" - }, - "1f9d6-1f3fb-2642": { - "output": "1f9d6-1f3fb-200d-2642-fe0f", - "name": "man in steamy room: light skin tone", - "alpha_code": ":man_in_steamy_room_tone1:", - "aliases": ":man_in_steamy_room_light_skin_tone:" - }, - "1f9d6-1f3fc-2640": { - "output": "1f9d6-1f3fc-200d-2640-fe0f", - "name": "woman in steamy room: medium-light skin tone", - "alpha_code": ":woman_in_steamy_room_tone2:", - "aliases": ":woman_in_steamy_room_medium_light_skin_tone:" - }, - "1f9d6-1f3fc-2642": { - "output": "1f9d6-1f3fc-200d-2642-fe0f", - "name": "man in steamy room: medium-light skin tone", - "alpha_code": ":man_in_steamy_room_tone2:", - "aliases": ":man_in_steamy_room_medium_light_skin_tone:" - }, - "1f9d6-1f3fd-2640": { - "output": "1f9d6-1f3fd-200d-2640-fe0f", - "name": "woman in steamy room: medium skin tone", - "alpha_code": ":woman_in_steamy_room_tone3:", - "aliases": ":woman_in_steamy_room_medium_skin_tone:" - }, - "1f9d6-1f3fd-2642": { - "output": "1f9d6-1f3fd-200d-2642-fe0f", - "name": "man in steamy room: medium skin tone", - "alpha_code": ":man_in_steamy_room_tone3:", - "aliases": ":man_in_steamy_room_medium_skin_tone:" - }, - "1f9d6-1f3fe-2640": { - "output": "1f9d6-1f3fe-200d-2640-fe0f", - "name": "woman in steamy room: medium-dark skin tone", - "alpha_code": ":woman_in_steamy_room_tone4:", - "aliases": ":woman_in_steamy_room_medium_dark_skin_tone:" - }, - "1f9d6-1f3fe-2642": { - "output": "1f9d6-1f3fe-200d-2642-fe0f", - "name": "man in steamy room: medium-dark skin tone", - "alpha_code": ":man_in_steamy_room_tone4:", - "aliases": ":man_in_steamy_room_medium_dark_skin_tone:" - }, - "1f9d6-1f3ff-2640": { - "output": "1f9d6-1f3ff-200d-2640-fe0f", - "name": "woman in steamy room: dark skin tone", - "alpha_code": ":woman_in_steamy_room_tone5:", - "aliases": ":woman_in_steamy_room_dark_skin_tone:" - }, - "1f9d6-1f3ff-2642": { - "output": "1f9d6-1f3ff-200d-2642-fe0f", - "name": "man in steamy room: dark skin tone", - "alpha_code": ":man_in_steamy_room_tone5:", - "aliases": ":man_in_steamy_room_dark_skin_tone:" - }, - "1f9d7-2640": { - "output": "1f9d7-200d-2640-fe0f", - "name": "woman climbing", - "alpha_code": ":woman_climbing:", - "aliases": "" - }, - "1f9d7-2642": { - "output": "1f9d7-200d-2642-fe0f", - "name": "man climbing", - "alpha_code": ":man_climbing:", - "aliases": "" - }, - "1f9d7-1f3fb-2640": { - "output": "1f9d7-1f3fb-200d-2640-fe0f", - "name": "woman climbing: light skin tone", - "alpha_code": ":woman_climbing_tone1:", - "aliases": ":woman_climbing_light_skin_tone:" - }, - "1f9d7-1f3fb-2642": { - "output": "1f9d7-1f3fb-200d-2642-fe0f", - "name": "man climbing: light skin tone", - "alpha_code": ":man_climbing_tone1:", - "aliases": ":man_climbing_light_skin_tone:" - }, - "1f9d7-1f3fc-2640": { - "output": "1f9d7-1f3fc-200d-2640-fe0f", - "name": "woman climbing: medium-light skin tone", - "alpha_code": ":woman_climbing_tone2:", - "aliases": ":woman_climbing_medium_light_skin_tone:" - }, - "1f9d7-1f3fc-2642": { - "output": "1f9d7-1f3fc-200d-2642-fe0f", - "name": "man climbing: medium-light skin tone", - "alpha_code": ":man_climbing_tone2:", - "aliases": ":man_climbing_medium_light_skin_tone:" - }, - "1f9d7-1f3fd-2640": { - "output": "1f9d7-1f3fd-200d-2640-fe0f", - "name": "woman climbing: medium skin tone", - "alpha_code": ":woman_climbing_tone3:", - "aliases": ":woman_climbing_medium_skin_tone:" - }, - "1f9d7-1f3fd-2642": { - "output": "1f9d7-1f3fd-200d-2642-fe0f", - "name": "man climbing: medium skin tone", - "alpha_code": ":man_climbing_tone3:", - "aliases": ":man_climbing_medium_skin_tone:" - }, - "1f9d7-1f3fe-2640": { - "output": "1f9d7-1f3fe-200d-2640-fe0f", - "name": "woman climbing: medium-dark skin tone", - "alpha_code": ":woman_climbing_tone4:", - "aliases": ":woman_climbing_medium_dark_skin_tone:" - }, - "1f9d7-1f3fe-2642": { - "output": "1f9d7-1f3fe-200d-2642-fe0f", - "name": "man climbing: medium-dark skin tone", - "alpha_code": ":man_climbing_tone4:", - "aliases": ":man_climbing_medium_dark_skin_tone:" - }, - "1f9d7-1f3ff-2640": { - "output": "1f9d7-1f3ff-200d-2640-fe0f", - "name": "woman climbing: dark skin tone", - "alpha_code": ":woman_climbing_tone5:", - "aliases": ":woman_climbing_dark_skin_tone:" - }, - "1f9d7-1f3ff-2642": { - "output": "1f9d7-1f3ff-200d-2642-fe0f", - "name": "man climbing: dark skin tone", - "alpha_code": ":man_climbing_tone5:", - "aliases": ":man_climbing_dark_skin_tone:" - }, - "1f9d8-2640": { - "output": "1f9d8-200d-2640-fe0f", - "name": "woman in lotus position", - "alpha_code": ":woman_in_lotus_position:", - "aliases": "" - }, - "1f9d8-2642": { - "output": "1f9d8-200d-2642-fe0f", - "name": "man in lotus position", - "alpha_code": ":man_in_lotus_position:", - "aliases": "" - }, - "1f9d8-1f3fb-2640": { - "output": "1f9d8-1f3fb-200d-2640-fe0f", - "name": "woman in lotus position: light skin tone", - "alpha_code": ":woman_in_lotus_position_tone1:", - "aliases": ":woman_in_lotus_position_light_skin_tone:" - }, - "1f9d8-1f3fb-2642": { - "output": "1f9d8-1f3fb-200d-2642-fe0f", - "name": "man in lotus position: light skin tone", - "alpha_code": ":man_in_lotus_position_tone1:", - "aliases": ":man_in_lotus_position_light_skin_tone:" - }, - "1f9d8-1f3fc-2640": { - "output": "1f9d8-1f3fc-200d-2640-fe0f", - "name": "woman in lotus position: medium-light skin tone", - "alpha_code": ":woman_in_lotus_position_tone2:", - "aliases": ":woman_in_lotus_position_medium_light_skin_tone:" - }, - "1f9d8-1f3fc-2642": { - "output": "1f9d8-1f3fc-200d-2642-fe0f", - "name": "man in lotus position: medium-light skin tone", - "alpha_code": ":man_in_lotus_position_tone2:", - "aliases": ":man_in_lotus_position_medium_light_skin_tone:" - }, - "1f9d8-1f3fd-2640": { - "output": "1f9d8-1f3fd-200d-2640-fe0f", - "name": "woman in lotus position: medium skin tone", - "alpha_code": ":woman_in_lotus_position_tone3:", - "aliases": ":woman_in_lotus_position_medium_skin_tone:" - }, - "1f9d8-1f3fd-2642": { - "output": "1f9d8-1f3fd-200d-2642-fe0f", - "name": "man in lotus position: medium skin tone", - "alpha_code": ":man_in_lotus_position_tone3:", - "aliases": ":man_in_lotus_position_medium_skin_tone:" - }, - "1f9d8-1f3fe-2640": { - "output": "1f9d8-1f3fe-200d-2640-fe0f", - "name": "woman in lotus position: medium-dark skin tone", - "alpha_code": ":woman_in_lotus_position_tone4:", - "aliases": ":woman_in_lotus_position_medium_dark_skin_tone:" - }, - "1f9d8-1f3fe-2642": { - "output": "1f9d8-1f3fe-200d-2642-fe0f", - "name": "man in lotus position: medium-dark skin tone", - "alpha_code": ":man_in_lotus_position_tone4:", - "aliases": ":man_in_lotus_position_medium_dark_skin_tone:" - }, - "1f9d8-1f3ff-2640": { - "output": "1f9d8-1f3ff-200d-2640-fe0f", - "name": "woman in lotus position: dark skin tone", - "alpha_code": ":woman_in_lotus_position_tone5:", - "aliases": ":woman_in_lotus_position_dark_skin_tone:" - }, - "1f9d8-1f3ff-2642": { - "output": "1f9d8-1f3ff-200d-2642-fe0f", - "name": "man in lotus position: dark skin tone", - "alpha_code": ":man_in_lotus_position_tone5:", - "aliases": ":man_in_lotus_position_dark_skin_tone:" - } -} \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/about_box.cpp b/Telegram/SourceFiles/boxes/about_box.cpp index 45f82a765..38595e765 100644 --- a/Telegram/SourceFiles/boxes/about_box.cpp +++ b/Telegram/SourceFiles/boxes/about_box.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/text/text_utilities.h" #include "platform/platform_file_utilities.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "core/click_handler_types.h" #include "core/update_checker.h" #include "styles/style_boxes.h" diff --git a/Telegram/SourceFiles/boxes/abstract_box.cpp b/Telegram/SourceFiles/boxes/abstract_box.cpp index f4869e7d2..76bfe2582 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.cpp +++ b/Telegram/SourceFiles/boxes/abstract_box.cpp @@ -634,4 +634,14 @@ bool isLayerShown() { return false; } +int DividerLabel::naturalWidth() const { + return -1; +} + +void DividerLabel::resizeEvent(QResizeEvent *e) { + _background->lower(); + _background->setGeometry(rect()); + return PaddingWrap::resizeEvent(e); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h index 0a38b109d..d08b97c22 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.h +++ b/Telegram/SourceFiles/boxes/abstract_box.h @@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/layer_widget.h" #include "base/unique_qptr.h" #include "base/flags.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/widgets/labels.h" #include "ui/effects/animation_value.h" #include "ui/text/text_entity.h" #include "ui/rp_widget.h" @@ -455,4 +457,19 @@ void hideLayer(anim::type animated = anim::type::normal); void hideSettingsAndLayer(anim::type animated = anim::type::normal); bool isLayerShown(); +class DividerLabel : public PaddingWrap { +public: + using PaddingWrap::PaddingWrap; + + int naturalWidth() const override; + +protected: + void resizeEvent(QResizeEvent *e) override; + +private: + object_ptr _background + = object_ptr(this); + +}; + } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 5e151ca73..50d25deb7 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp index ebd3810d6..7d7782478 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "core/click_handler_types.h" // UrlClickHandler #include "base/qthelp_url.h" // qthelp::url_encode -#include "platform/platform_info.h" // Platform::SystemVersionPretty +#include "base/platform/base_platform_info.h" #include "mainwidget.h" #include "numbers.h" #include "app.h" diff --git a/Telegram/SourceFiles/boxes/edit_color_box.cpp b/Telegram/SourceFiles/boxes/edit_color_box.cpp index fae3a3574..b421aa800 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_color_box.cpp @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/widgets/input_fields.h" #include "ui/ui_utility.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "app.h" #include "styles/style_boxes.h" #include "styles/style_mediaview.h" diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 89a38ef8a..2aaeb9f4e 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "window/window.style"; diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 6b7b6fb9f..428ebf94f 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/openssl_help.h" #include "mtproto/connection.h" #include "media/audio/media_audio_track.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "calls/calls_panel.h" #include "data/data_user.h" #include "data/data_session.h" diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 54ab0796e..06bc2196e 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp index eb8f4f150..16803d6fb 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp @@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "chat_helpers/emoji_keywords.h" -#include "chat_helpers/emoji_suggestions_helper.h" +#include "emoji_suggestions_helper.h" #include "lang/lang_instance.h" #include "lang/lang_cloud_manager.h" #include "core/application.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "ui/emoji_config.h" #include "main/main_account.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_helper.h b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_helper.h deleted file mode 100644 index 0b4b55796..000000000 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_helper.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "emoji_suggestions.h" -#include "emoji_suggestions_data.h" - -namespace Ui { -namespace Emoji { - -inline utf16string QStringToUTF16(const QString &string) { - return utf16string( - reinterpret_cast(string.constData()), - string.size()); -} - -inline QString QStringFromUTF16(utf16string string) { - return QString::fromRawData( - reinterpret_cast(string.data()), - string.size()); -} - -constexpr auto kSuggestionMaxLength = internal::kReplacementMaxLength; - -} // namespace Emoji -} // namespace Ui diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index d2b763a4f..394691007 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_keywords.h" -#include "chat_helpers/emoji_suggestions_helper.h" +#include "emoji_suggestions_helper.h" #include "ui/effects/ripple_animation.h" #include "ui/widgets/shadow.h" #include "ui/widgets/inner_dropdown.h" diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp deleted file mode 100644 index 6c98a2f5c..000000000 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* -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 "codegen/common/basic_tokenized_file.h" - -#include "codegen/common/logging.h" -#include "codegen/common/clean_file_reader.h" -#include "codegen/common/checked_utf8_string.h" - -using Token = codegen::common::BasicTokenizedFile::Token; -using Type = Token::Type; - -namespace codegen { -namespace common { -namespace { - -constexpr int kErrorUnterminatedStringLiteral = 201; -constexpr int kErrorIncorrectUtf8String = 202; -constexpr int kErrorIncorrectToken = 203; -constexpr int kErrorUnexpectedToken = 204; - -bool isDigitChar(char ch) { - return (ch >= '0') && (ch <= '9'); -} - -bool isNameChar(char ch) { - return isDigitChar(ch) || ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || (ch == '_'); -} - -bool isWhitespaceChar(char ch) { - return (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'); -} - -Token invalidToken() { - return { Type::Invalid, QString(), ConstUtf8String(nullptr, 0), false }; -} - -} // namespace - -BasicTokenizedFile::BasicTokenizedFile(const QString &filepath) : reader_(filepath) { -} - -BasicTokenizedFile::BasicTokenizedFile(const QByteArray &content, const QString &filepath) : reader_(content, filepath) { -} - -bool BasicTokenizedFile::putBack() { - if (currentToken_ > 0) { - --currentToken_; - return true; - } - return false; -} - -Token BasicTokenizedFile::getAnyToken() { - if (currentToken_ >= tokens_.size()) { - if (readToken() == Type::Invalid) { - return invalidToken(); - } - } - return tokens_.at(currentToken_++); -} - -Token BasicTokenizedFile::getToken(Type typeCondition) { - if (auto token = getAnyToken()) { - if (token.type == typeCondition) { - return token; - } - putBack(); - } - return invalidToken(); -} - -Type BasicTokenizedFile::readToken() { - auto result = readOneToken(StartWithWhitespace::Allow); - - // Try to read double token. - if (result == Type::Int) { - if (readOneToken(StartWithWhitespace::Deny) == Type::Dot) { - // We got int and dot, so it is double already. - result = uniteLastTokens(Type::Double); - - // Try to read one more int (after dot). - if (readOneToken(StartWithWhitespace::Deny) == Type::Int) { - result = uniteLastTokens(Type::Double); - } - } - } else if (result == Type::Dot) { - if (readOneToken(StartWithWhitespace::Deny) == Type::Int) { - //We got dot and int, so it is double. - result = uniteLastTokens(Type::Double); - } - } - return result; -} - -Type BasicTokenizedFile::readOneToken(StartWithWhitespace condition) { - skipWhitespaces(); - if (tokenStartWhitespace_ && condition == StartWithWhitespace::Deny) { - return Type::Invalid; - } - if (reader_.atEnd()) { - return Type::Invalid; - } - - auto ch = reader_.currentChar(); - if (ch == '"') { - return readString(); - } else if (isNameChar(ch)) { - return readNameOrNumber(); - } - return readSingleLetter(); -} - -Type BasicTokenizedFile::saveToken(Type type, const QString &value) { - ConstUtf8String original = { tokenStart_, reader_.currentPtr() }; - tokens_.push_back({ type, value, original, tokenStartWhitespace_ }); - return type; -} - -Type BasicTokenizedFile::uniteLastTokens(Type type) { - auto size = tokens_.size(); - if (size < 2) { - return Type::Invalid; - } - - auto &token(tokens_[size - 2]); - auto originalFrom = token.original.data(); - auto originalTill = tokens_.back().original.end(); - token.type = type; - token.original = { originalFrom, originalTill }; - token.value += tokens_.back().value; - tokens_.pop_back(); - return type; -} - -QString BasicTokenizedFile::getCurrentLineComment() { - if (lineNumber_ > singleLineComments_.size()) { - reader_.logError(kErrorInternal, lineNumber_) << "internal tokenizer error (line number larger than comments list size)."; - failed_ = true; - return QString(); - } - auto commentBytes = singleLineComments_[lineNumber_ - 1].mid(2); // Skip "//" - CheckedUtf8String comment(commentBytes); - if (!comment.isValid()) { - reader_.logError(kErrorIncorrectUtf8String, lineNumber_) << "incorrect UTF-8 string in the comment."; - failed_ = true; - return QString(); - } - return comment.toString().trimmed(); -} - -Type BasicTokenizedFile::readNameOrNumber() { - while (!reader_.atEnd()) { - if (!isDigitChar(reader_.currentChar())) { - break; - } - reader_.skipChar(); - } - bool onlyDigits = true; - while (!reader_.atEnd()) { - if (!isNameChar(reader_.currentChar())) { - break; - } - onlyDigits = false; - reader_.skipChar(); - } - return saveToken(onlyDigits ? Type::Int : Type::Name); -} - -Type BasicTokenizedFile::readString() { - reader_.skipChar(); - auto offset = reader_.currentPtr(); - - QByteArray value; - while (!reader_.atEnd()) { - auto ch = reader_.currentChar(); - if (ch == '"') { - if (reader_.currentPtr() > offset) { - value.append(offset, reader_.currentPtr() - offset); - } - break; - } - if (ch == '\n') { - reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal."; - failed_ = true; - return Type::Invalid; - } - if (ch == '\\') { - if (reader_.currentPtr() > offset) { - value.append(offset, reader_.currentPtr() - offset); - } - reader_.skipChar(); - ch = reader_.currentChar(); - if (reader_.atEnd() || ch == '\n') { - reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal."; - failed_ = true; - return Type::Invalid; - } - offset = reader_.currentPtr() + 1; - if (ch == 'n') { - value.append('\n'); - } else if (ch == 't') { - value.append('\t'); - } else if (ch == '"') { - value.append('"'); - } else if (ch == '\\') { - value.append('\\'); - } - } - reader_.skipChar(); - } - if (reader_.atEnd()) { - reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal."; - failed_ = true; - return Type::Invalid; - } - CheckedUtf8String checked(value); - if (!checked.isValid()) { - reader_.logError(kErrorIncorrectUtf8String, lineNumber_) << "incorrect UTF-8 string literal."; - failed_ = true; - return Type::Invalid; - } - reader_.skipChar(); - return saveToken(Type::String, checked.toString()); -} - -Type BasicTokenizedFile::readSingleLetter() { - auto type = singleLetterTokens_.value(reader_.currentChar(), Type::Invalid); - if (type == Type::Invalid) { - reader_.logError(kErrorIncorrectToken, lineNumber_) << "incorrect token '" << reader_.currentChar() << "'"; - return Type::Invalid; - } - - reader_.skipChar(); - return saveToken(type); -} - -void BasicTokenizedFile::skipWhitespaces() { - if (reader_.atEnd()) return; - - auto ch = reader_.currentChar(); - tokenStartWhitespace_ = isWhitespaceChar(ch); - if (tokenStartWhitespace_) { - do { - if (ch == '\n') { - ++lineNumber_; - } - reader_.skipChar(); - ch = reader_.currentChar(); - } while (!reader_.atEnd() && isWhitespaceChar(ch)); - } - tokenStart_ = reader_.currentPtr(); -} - -LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type) { - const char *value = "'invalid'"; - switch (type) { - case Type::Invalid: break; - case Type::Int: value = "'int'"; break; - case Type::Double: value = "'double'"; break; - case Type::String: value = "'string'"; break; - case Type::LeftParenthesis: value = "'('"; break; - case Type::RightParenthesis: value = "')'"; break; - case Type::LeftBrace: value = "'{'"; break; - case Type::RightBrace: value = "'}'"; break; - case Type::LeftBracket: value = "'['"; break; - case Type::RightBracket: value = "']'"; break; - case Type::Colon: value = "':'"; break; - case Type::Semicolon: value = "';'"; break; - case Type::Comma: value = "','"; break; - case Type::Dot: value = "'.'"; break; - case Type::Number: value = "'#'"; break; - case Type::Plus: value = "'+'"; break; - case Type::Minus: value = "'-'"; break; - case Type::Equals: value = "'='"; break; - case Type::Name: value = "'identifier'"; break; - } - return std::forward(stream) << value; -} - -LogStream BasicTokenizedFile::logError(int code) const { - return reader_.logError(code, lineNumber_); -} - -LogStream BasicTokenizedFile::logErrorUnexpectedToken() const { - if (currentToken_ < tokens_.size()) { - auto token = tokens_.at(currentToken_).original.toStdString(); - return logError(kErrorUnexpectedToken) << "unexpected token '" << token << "', expected "; - } - return logError(kErrorUnexpectedToken) << "unexpected token, expected "; -} - -BasicTokenizedFile::~BasicTokenizedFile() = default; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h deleted file mode 100644 index 55c736683..000000000 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h +++ /dev/null @@ -1,156 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include - -#include "codegen/common/const_utf8_string.h" -#include "codegen/common/clean_file_reader.h" - -namespace codegen { -namespace common { - -class LogStream; - -// Interface for reading a cleaned from comments file by basic tokens. -class BasicTokenizedFile { -public: - explicit BasicTokenizedFile(const QString &filepath); - explicit BasicTokenizedFile(const QByteArray &content, const QString &filepath = QString()); - BasicTokenizedFile(const BasicTokenizedFile &other) = delete; - BasicTokenizedFile &operator=(const BasicTokenizedFile &other) = delete; - - struct Token { - // String - utf8 string converted to QString. - enum class Type { - Invalid = 0, - Int, - Double, - String, - LeftParenthesis, - RightParenthesis, - LeftBrace, - RightBrace, - LeftBracket, - RightBracket, - Colon, - Semicolon, - Comma, - Dot, - Number, - Plus, - Minus, - Equals, - And, - Or, - Name, // [0-9a-zA-Z_]+ with at least one letter. - }; - Type type; - QString value; - ConstUtf8String original; - bool hasLeftWhitespace; - - explicit operator bool() const { - return (type != Type::Invalid); - } - }; - - bool read() { - if (reader_.read()) { - singleLineComments_ = reader_.singleLineComments(); - return true; - } - return false; - } - bool atEnd() const { - return reader_.atEnd(); - } - - Token getAnyToken(); - Token getToken(Token::Type typeCondition); - bool putBack(); - bool failed() const { - return failed_; - } - - QString getCurrentLineComment(); - - // Log error to std::cerr with 'code' at the current position in file. - LogStream logError(int code) const; - LogStream logErrorUnexpectedToken() const; - - ~BasicTokenizedFile(); - -private: - using Type = Token::Type; - - void skipWhitespaces(); - - // Reads a token, including complex tokens, like double numbers. - Type readToken(); - - // Read exactly one token, applying condition on the whitespaces. - enum class StartWithWhitespace { - Allow, - Deny, - }; - Type readOneToken(StartWithWhitespace condition); - - // helpers - Type readNameOrNumber(); - Type readString(); - Type readSingleLetter(); - - Type saveToken(Type type, const QString &value = QString()); - Type uniteLastTokens(Type type); - - CleanFileReader reader_; - QList tokens_; - int currentToken_ = 0; - int lineNumber_ = 1; - bool failed_ = false; - QVector singleLineComments_; - - // Where the last (currently read) token has started. - const char *tokenStart_ = nullptr; - - // Did the last (currently read) token start with a whitespace. - bool tokenStartWhitespace_ = false; - - const QMap singleLetterTokens_ = { - { '(', Type::LeftParenthesis }, - { ')', Type::RightParenthesis }, - { '{', Type::LeftBrace }, - { '}', Type::RightBrace }, - { '[', Type::LeftBracket }, - { ']', Type::RightBracket }, - { ':', Type::Colon }, - { ';', Type::Semicolon }, - { ',', Type::Comma }, - { '.', Type::Dot }, - { '#', Type::Number }, - { '+', Type::Plus }, - { '-', Type::Minus }, - { '=', Type::Equals }, - { '&', Type::And }, - { '|', Type::Or }, - }; - -}; - -LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type); -template <> -LogStream operator<< (LogStream &&stream, BasicTokenizedFile::Token::Type &&value) = delete; -template <> -LogStream operator<< (LogStream &&stream, const BasicTokenizedFile::Token::Type &value) = delete; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/checked_utf8_string.cpp b/Telegram/SourceFiles/codegen/common/checked_utf8_string.cpp deleted file mode 100644 index 412671ac8..000000000 --- a/Telegram/SourceFiles/codegen/common/checked_utf8_string.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* -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 "codegen/common/checked_utf8_string.h" - -#include -#include - -#include "codegen/common/const_utf8_string.h" - -namespace codegen { -namespace common { - -CheckedUtf8String::CheckedUtf8String(const char *string, int size) { - if (size < 0) { - size = strlen(string); - } - if (!size) { // Valid empty string - return; - } - - QTextCodec::ConverterState state; - QTextCodec *codec = QTextCodec::codecForName("UTF-8"); - string_ = codec->toUnicode(string, size, &state); - if (state.invalidChars > 0) { - valid_ = false; - } -} - -CheckedUtf8String::CheckedUtf8String(const QByteArray &string) : CheckedUtf8String(string.constData(), string.size()) { -} - -CheckedUtf8String::CheckedUtf8String(const ConstUtf8String &string) : CheckedUtf8String(string.data(), string.size()) { -} - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/checked_utf8_string.h b/Telegram/SourceFiles/codegen/common/checked_utf8_string.h deleted file mode 100644 index a5f287e67..000000000 --- a/Telegram/SourceFiles/codegen/common/checked_utf8_string.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -class QByteArray; - -namespace codegen { -namespace common { - -class ConstUtf8String; - -// Parses a char sequence to a QString using UTF-8 codec. -// You can check for invalid UTF-8 sequence by isValid() method. -class CheckedUtf8String { -public: - CheckedUtf8String(const CheckedUtf8String &other) = default; - CheckedUtf8String &operator=(const CheckedUtf8String &other) = default; - - explicit CheckedUtf8String(const char *string, int size = -1); - explicit CheckedUtf8String(const QByteArray &string); - explicit CheckedUtf8String(const ConstUtf8String &string); - - bool isValid() const { - return valid_; - } - const QString &toString() const { - return string_; - } - -private: - QString string_; - bool valid_ = true; - -}; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/clean_file.cpp b/Telegram/SourceFiles/codegen/common/clean_file.cpp deleted file mode 100644 index d95ea1ef9..000000000 --- a/Telegram/SourceFiles/codegen/common/clean_file.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* -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 "codegen/common/clean_file.h" - -#include -#include - -#include "codegen/common/logging.h" - -namespace codegen { -namespace common { -namespace { - -bool readFile(const QString &filepath, QByteArray *outResult) { - QFile f(filepath); - if (!f.exists()) { - logError(kErrorFileNotFound, filepath) << ": error: file does not exist."; - return false; - } - auto limit = CleanFile::MaxSize; - if (f.size() > limit) { - logError(kErrorFileTooLarge, filepath) << "' is too large, size=" << f.size() << " > maxsize=" << limit; - return false; - } - if (!f.open(QIODevice::ReadOnly)) { - logError(kErrorFileNotOpened, filepath) << "' for read."; - return false; - } - *outResult = f.readAll(); - return true; -} - -} // namespace - - -CleanFile::CleanFile(const QString &filepath) -: filepath_(filepath) -, read_(true) { -} - -CleanFile::CleanFile(const QByteArray &content, const QString &filepath) -: filepath_(filepath) -, content_(content) -, read_(false) { -} - -bool CleanFile::read() { - if (read_) { - if (!readFile(filepath_, &content_)) { - return false; - } - } - filepath_ = QFileInfo(filepath_).absoluteFilePath(); - - enum class InsideComment { - None, - SingleLine, - MultiLine, - }; - auto insideComment = InsideComment::None; - bool insideString = false; - - const char *begin = content_.cbegin(), *end = content_.cend(), *offset = begin; - auto feedContent = [this, &offset, end](const char *ch) { - if (ch > offset) { - if (result_.isEmpty()) result_.reserve(end - offset - 2); - result_.append(offset, ch - offset); - offset = ch; - } - }; - - auto lineNumber = 0; - auto feedComment = [this, &offset, end, &lineNumber](const char *ch, bool save = false) { - if (ch > offset) { - if (save) { - singleLineComments_.resize(lineNumber + 1); - singleLineComments_[lineNumber] = QByteArray(offset, ch - offset); - } - if (result_.isEmpty()) { - result_.reserve(end - offset - 2); - } - result_.append(' '); - offset = ch; - } - }; - for (const char *ch = offset; ch != end;) { - char currentChar = *ch; - char nextChar = (ch + 1 == end) ? 0 : *(ch + 1); - - if (insideComment == InsideComment::None && currentChar == '"') { - bool escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\'); - if (!escaped) { - insideString = !insideString; - } - } - if (insideString) { - if (currentChar == '\n') { - ++lineNumber; - } - ++ch; - continue; - } - - if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') { - feedContent(ch); - insideComment = InsideComment::SingleLine; - ch += 2; - } else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') { - feedComment(ch, true); - ch += 2; - ++lineNumber; - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::SingleLine && currentChar == '\n') { - feedComment(ch, true); - ++ch; - ++lineNumber; - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') { - feedContent(ch); - ch += 2; - insideComment = InsideComment::MultiLine; - } else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') { - ch += 2; - feedComment(ch); - insideComment = InsideComment::None; - } else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') { - feedComment(ch); - ch += 2; - ++lineNumber; - feedContent(ch); - } else if (insideComment == InsideComment::MultiLine && currentChar == '\n') { - feedComment(ch); - ++ch; - ++lineNumber; - feedContent(ch); - } else { - if (currentChar == '\n') { - ++lineNumber; - } - ++ch; - } - } - singleLineComments_.resize(lineNumber + 1); - - if (insideComment == InsideComment::MultiLine) { - common::logError(kErrorUnexpectedEndOfFile, filepath_); - return false; - } - if (insideComment == InsideComment::None && end > offset) { - if (result_.isEmpty()) { - result_ = content_; - } else { - result_.append(offset, end - offset); - } - } - return true; -} - -QVector CleanFile::singleLineComments() const { - return singleLineComments_; -} - -LogStream CleanFile::logError(int code, int line) const { - return common::logError(code, filepath_, line); -} - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/clean_file.h b/Telegram/SourceFiles/codegen/common/clean_file.h deleted file mode 100644 index e3936aa21..000000000 --- a/Telegram/SourceFiles/codegen/common/clean_file.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include - -#include "codegen/common/logging.h" - -namespace codegen { -namespace common { - -// Reads a file removing all C-style comments. -class CleanFile { -public: - explicit CleanFile(const QString &filepath); - explicit CleanFile(const QByteArray &content, const QString &filepath = QString()); - CleanFile(const CleanFile &other) = delete; - CleanFile &operator=(const CleanFile &other) = delete; - - bool read(); - QVector singleLineComments() const; - - const char *data() const { - return result_.constData(); - } - const char *end() const { - return result_.constEnd(); - } - - static constexpr int MaxSize = 10 * 1024 * 1024; - - // Log error to std::cerr with 'code' at line number 'line' in data(). - LogStream logError(int code, int line) const; - -private: - QString filepath_; - QByteArray content_, result_; - bool read_; - - QVector singleLineComments_; - -}; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/clean_file_reader.h b/Telegram/SourceFiles/codegen/common/clean_file_reader.h deleted file mode 100644 index be555d3e0..000000000 --- a/Telegram/SourceFiles/codegen/common/clean_file_reader.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -#include "codegen/common/clean_file.h" - -namespace codegen { -namespace common { - -// Wrapper allows you to read forward the CleanFile without overflow checks. -class CleanFileReader { -public: - explicit CleanFileReader(const QString &filepath) : file_(filepath) { - } - explicit CleanFileReader(const QByteArray &content, const QString &filepath = QString()) : file_(content, filepath) { - } - - bool read() { - if (!file_.read()) { - return false; - } - pos_ = file_.data(); - end_ = file_.end(); - return true; - } - bool atEnd() const { - return (pos_ == end_); - } - char currentChar() const { - return atEnd() ? 0 : *pos_; - } - bool skipChar() { - if (atEnd()) { - return false; - } - ++pos_; - return true; - } - const char *currentPtr() const { - return pos_; - } - int charsLeft() const { - return (end_ - pos_); - } - - QVector singleLineComments() const { - return file_.singleLineComments(); - } - - // Log error to std::cerr with 'code' at line number 'line' in data(). - LogStream logError(int code, int line) const { - return std::forward(file_.logError(code, line)); - } - - -private: - CleanFile file_; - const char *pos_ = nullptr; - const char *end_ = nullptr; - -}; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/const_utf8_string.h b/Telegram/SourceFiles/codegen/common/const_utf8_string.h deleted file mode 100644 index 1ac8af471..000000000 --- a/Telegram/SourceFiles/codegen/common/const_utf8_string.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include - -namespace codegen { -namespace common { - -// This is a simple wrapper around (const char*, size). -// Not null-terminated! It does not hold any ownership. -class ConstUtf8String { -public: - explicit ConstUtf8String(const char *string, int size = -1) : string_(string) { - if (size < 0) { - size = strlen(string); - } - size_ = size; - } - ConstUtf8String(const char *string, const char *end) : ConstUtf8String(string, end - string) { - } - - QByteArray toByteArray() const { - return QByteArray(string_, size_); - } - std::string toStdString() const { - return std::string(string_, size_); - } - QString toStringUnchecked() const { - return QString::fromUtf8(string_, size_); - } - bool empty() const { - return size_ == 0; - } - const char *data() const { - return string_; - } - int size() const { - return size_; - } - const char *end() const { - return data() + size(); - } - ConstUtf8String mid(int pos, int size = -1) { - return ConstUtf8String(string_ + pos, std::max(std::min(size, size_ - pos), 0)); - } - -private: - const char *string_; - int size_; - -}; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/cpp_file.cpp b/Telegram/SourceFiles/codegen/common/cpp_file.cpp deleted file mode 100644 index 842f1e8d2..000000000 --- a/Telegram/SourceFiles/codegen/common/cpp_file.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -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 "codegen/common/cpp_file.h" - -#include -#include - -namespace codegen { -namespace common { -namespace { - -void writeLicense(QTextStream &stream, const ProjectInfo &project) { - stream << "\ -/*\n\ -WARNING! All changes made in this file will be lost!\n\ -Created from '" << project.source << "' by '" << project.name << "'\n\ -\n\ -This file is part of Telegram Desktop,\n\ -the official desktop application for the Telegram messaging service.\n\ -\n\ -For license and copyright information please follow this link:\n\ -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL\n\ -*/\n"; -} - -} // namespace - -CppFile::CppFile(const QString &path, const ProjectInfo &project) -: stream_(&content_) -, forceReGenerate_(project.forceReGenerate) { - bool cpp = path.toLower().endsWith(".cpp"); - - QFileInfo info(path); - info.dir().mkpath("."); - filepath_ = info.absoluteFilePath(); - - writeLicense(stream_, project); - if (cpp) { - include(info.baseName() + ".h").newline(); - } else { - stream() << "#pragma once"; - newline().newline(); - } -} - -CppFile &CppFile::include(const QString &header) { - stream() << "#include \"" << header << "\""; - return newline(); -} - -CppFile &CppFile::includeFromLibrary(const QString &header) { - stream() << "#include <" << header << ">"; - return newline(); -} - -CppFile &CppFile::pushNamespace(const QString &name) { - namespaces_.push_back(name); - - stream() << "namespace"; - if (!name.isEmpty()) { - stream() << ' ' << name; - } - stream() << " {"; - return newline(); -} - -CppFile &CppFile::popNamespace() { - if (namespaces_.isEmpty()) { - return *this; - } - auto name = namespaces_.back(); - namespaces_.pop_back(); - - stream() << "} // namespace"; - if (!name.isEmpty()) { - stream() << ' ' << name; - } - return newline(); -} - -bool CppFile::finalize() { - while (!namespaces_.isEmpty()) { - popNamespace(); - } - stream_.flush(); - - QFile file(filepath_); - if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) { - if (file.readAll() == content_) { - file.close(); - return true; - } - file.close(); - } - - if (!file.open(QIODevice::WriteOnly)) { - return false; - } - if (file.write(content_) != content_.size()) { - return false; - } - return true; -} - -} // namespace common -} // namespace codegen \ No newline at end of file diff --git a/Telegram/SourceFiles/codegen/common/cpp_file.h b/Telegram/SourceFiles/codegen/common/cpp_file.h deleted file mode 100644 index e2731b32a..000000000 --- a/Telegram/SourceFiles/codegen/common/cpp_file.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include - -namespace codegen { -namespace common { - -struct ProjectInfo { - QString name; - QString source; - bool forceReGenerate; -}; - -// Creates a file with license header and codegen warning. -class CppFile { -public: - // If "basepath" is empty the folder containing "path" will be chosen. - // File ending with .cpp will be treated as source, otherwise like header. - CppFile(const QString &path, const ProjectInfo &project); - - QTextStream &stream() { - return stream_; - } - - CppFile &newline() { - stream() << "\n"; - return *this; - } - CppFile &include(const QString &header); - CppFile &includeFromLibrary(const QString &header); - - // Empty name adds anonymous namespace. - CppFile &pushNamespace(const QString &name = QString()); - CppFile &popNamespace(); - - bool finalize(); - -private: - QString filepath_; - QByteArray content_; - QTextStream stream_; - QVector namespaces_; - bool forceReGenerate_; - -}; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/logging.cpp b/Telegram/SourceFiles/codegen/common/logging.cpp deleted file mode 100644 index 9e029e10c..000000000 --- a/Telegram/SourceFiles/codegen/common/logging.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 "codegen/common/logging.h" - -#include -#include - -namespace codegen { -namespace common { -namespace { - -QString WorkingPath = "."; - -std::string relativeLocalPath(const QString &filepath) { - auto name = QFile::encodeName(QDir(WorkingPath).relativeFilePath(filepath)); - return name.constData(); -} - -} // namespace - -LogStream logError(int code, const QString &filepath, int line) { - std::cerr << relativeLocalPath(filepath); - if (line > 0) { - std::cerr << '(' << line << ')'; - } - std::cerr << ": error " << code << ": "; - return LogStream(std::cerr); -} - -void logSetWorkingPath(const QString &workingpath) { - WorkingPath = workingpath; -} - -} // namespace common -} // namespace codegen \ No newline at end of file diff --git a/Telegram/SourceFiles/codegen/common/logging.h b/Telegram/SourceFiles/codegen/common/logging.h deleted file mode 100644 index dc5e714cf..000000000 --- a/Telegram/SourceFiles/codegen/common/logging.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace codegen { -namespace common { - -// Common error codes. -constexpr int kErrorFileNotFound = 101; -constexpr int kErrorFileTooLarge = 102; -constexpr int kErrorFileNotOpened = 103; -constexpr int kErrorUnexpectedEndOfFile = 104; - -// Wrapper around std::ostream that adds '\n' to the end of the logging line. -class LogStream { -public: - enum NullType { - Null, - }; - explicit LogStream(NullType) : final_(false) { - } - explicit LogStream(std::ostream &stream) : stream_(&stream) { - } - LogStream(LogStream &&other) : stream_(other.stream_), final_(other.final_) { - other.final_ = false; - } - std::ostream *stream() const { - return stream_; - } - ~LogStream() { - if (final_) { - *stream_ << '\n'; - } - } - -private: - std::ostream *stream_ = nullptr; - bool final_ = true; - -}; - -template -LogStream operator<<(LogStream &&stream, T &&value) { - if (auto ostream = stream.stream()) { - *ostream << std::forward(value); - } - return std::forward(stream); -} - -// Outputs file name, line number and error code to std::err. Usage: -// logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size; -LogStream logError(int code, const QString &filepath, int line = 0); - -void logSetWorkingPath(const QString &workingpath); - -static constexpr int kErrorInternal = 666; - -} // namespace common -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/data.cpp b/Telegram/SourceFiles/codegen/emoji/data.cpp deleted file mode 100644 index d60972171..000000000 --- a/Telegram/SourceFiles/codegen/emoji/data.cpp +++ /dev/null @@ -1,4030 +0,0 @@ -/* -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 "codegen/emoji/data.h" - -namespace codegen { -namespace emoji { -namespace { - -using uint16 = quint16; -using uint32 = quint32; -using uint64 = quint64; - -using std::vector; -using std::map; -using std::set; -using std::find; -using std::make_pair; -using std::move; -using std::begin; -using std::end; - -using InputId = vector; -using InputCategory = vector; - -namespace old { -extern InputCategory ColoredEmoji; -extern InputCategory Category1; -extern InputCategory Category2; -extern InputCategory Category3; -extern InputCategory Category4; -extern InputCategory Category5; -extern InputCategory Category6; -extern InputCategory Category7; -} // namespace old - -// copied from emoji_box.cpp -struct Replace { - InputId code; - const char *replace; -}; - -Replace Replaces[] = { - { { 0xD83DDE0AU }, ":-)" }, - { { 0xD83DDE0DU }, "8-)" }, - { { 0x2764U }, "<3" }, -// { { 0xD83DDC8BU }, ":kiss:" }, -// { { 0xD83DDE01U }, ":grin:" }, -// { { 0xD83DDE02U }, ":joy:" }, - { { 0xD83DDE1AU }, ":-*" }, -// { { 0xD83DDE06U }, "xD" }, // Conflicts with typing xDDD... -// { { 0xD83DDC4DU }, ":like:" }, -// { { 0xD83DDC4EU }, ":dislike:" }, -// { { 0x261DU }, ":up:" }, -// { { 0x270CU }, ":v:" }, -// { { 0xD83DDC4CU }, ":ok:" }, - { { 0xD83DDE0EU }, "B-)" }, - { { 0xD83DDE03U }, ":-D" }, - { { 0xD83DDE09U }, ";-)" }, - { { 0xD83DDE1CU }, ";-P" }, - { { 0xD83DDE0BU }, ":-p" }, - { { 0xD83DDE14U }, "3(" }, - { { 0xD83DDE1EU }, ":-(" }, - { { 0xD83DDE0FU }, ":]" }, - { { 0xD83DDE22U }, ":'(" }, - { { 0xD83DDE2DU }, ":_(" }, - { { 0xD83DDE29U }, ":((" }, -// { { 0xD83DDE28U }, ":o" }, // Conflicts with typing :ok... - { { 0xD83DDE10U }, ":|" }, - { { 0xD83DDE0CU }, "3-)" }, - { { 0xD83DDE20U }, ">(" }, - { { 0xD83DDE21U }, ">((" }, - { { 0xD83DDE07U }, "O:)" }, - { { 0xD83DDE30U }, ";o" }, - { { 0xD83DDE33U }, "8|" }, - { { 0xD83DDE32U }, "8o" }, - { { 0xD83DDE37U }, ":X" }, - { { 0xD83DDE08U }, "}:)" }, -}; - -InputCategory PostfixRequired = { - { 0x2122U, 0xFE0FU, }, - { 0xA9U, 0xFE0FU, }, - { 0xAEU, 0xFE0FU, }, -}; - -using ColorId = uint32; -ColorId Colors[] = { - 0xD83CDFFBU, - 0xD83CDFFCU, - 0xD83CDFFDU, - 0xD83CDFFEU, - 0xD83CDFFFU, -}; - -constexpr auto ColorMask = 0xD83CDFFBU; -InputCategory ColoredEmoji = { - { 0xD83EDD32U, 0xD83CDFFBU, }, - { 0xD83DDC50U, 0xD83CDFFBU, }, - { 0xD83DDE4CU, 0xD83CDFFBU, }, - { 0xD83DDC4FU, 0xD83CDFFBU, }, - { 0xD83DDC4DU, 0xD83CDFFBU, }, - { 0xD83DDC4EU, 0xD83CDFFBU, }, - { 0xD83DDC4AU, 0xD83CDFFBU, }, - { 0x270AU, 0xD83CDFFBU, }, - { 0xD83EDD1BU, 0xD83CDFFBU, }, - { 0xD83EDD1CU, 0xD83CDFFBU, }, - { 0xD83EDD1EU, 0xD83CDFFBU, }, - { 0x270CU, 0xD83CDFFBU, }, - { 0xD83EDD1FU, 0xD83CDFFBU, }, - { 0xD83EDD18U, 0xD83CDFFBU, }, - { 0xD83DDC4CU, 0xD83CDFFBU, }, - { 0xD83DDC48U, 0xD83CDFFBU, }, - { 0xD83DDC49U, 0xD83CDFFBU, }, - { 0xD83DDC46U, 0xD83CDFFBU, }, - { 0xD83DDC47U, 0xD83CDFFBU, }, - { 0x261DU, 0xD83CDFFBU, }, - { 0x270BU, 0xD83CDFFBU, }, - { 0xD83EDD1AU, 0xD83CDFFBU, }, - { 0xD83DDD90U, 0xD83CDFFBU, }, - { 0xD83DDD96U, 0xD83CDFFBU, }, - { 0xD83DDC4BU, 0xD83CDFFBU, }, - { 0xD83EDD19U, 0xD83CDFFBU, }, - { 0xD83DDCAAU, 0xD83CDFFBU, }, - { 0xD83DDD95U, 0xD83CDFFBU, }, - { 0x270DU, 0xD83CDFFBU, }, - { 0xD83DDE4FU, 0xD83CDFFBU, }, - { 0xD83EDDB6U, 0xD83CDFFBU, }, - { 0xD83EDDB5U, 0xD83CDFFBU, }, - { 0xD83DDC42U, 0xD83CDFFBU, }, - { 0xD83DDC43U, 0xD83CDFFBU, }, - { 0xD83DDC76U, 0xD83CDFFBU, }, - { 0xD83DDC67U, 0xD83CDFFBU, }, - { 0xD83EDDD2U, 0xD83CDFFBU, }, - { 0xD83DDC66U, 0xD83CDFFBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, }, - { 0xD83EDDD1U, 0xD83CDFFBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB1U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB1U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB0U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB0U, }, - { 0xD83DDC71U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC71U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB3U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB3U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB2U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83EDDB2U, }, - { 0xD83EDDD4U, 0xD83CDFFBU, }, - { 0xD83DDC75U, 0xD83CDFFBU, }, - { 0xD83EDDD3U, 0xD83CDFFBU, }, - { 0xD83DDC74U, 0xD83CDFFBU, }, - { 0xD83DDC72U, 0xD83CDFFBU, }, - { 0xD83DDC73U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC73U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD5U, 0xD83CDFFBU, }, - { 0xD83DDC6EU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC6EU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC77U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC77U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC82U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC82U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDD75U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDD75U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC70U, 0xD83CDFFBU, }, - { 0xD83EDD35U, 0xD83CDFFBU, }, - { 0xD83DDC78U, 0xD83CDFFBU, }, - { 0xD83EDD34U, 0xD83CDFFBU, }, - { 0xD83EDDB8U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDB8U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDB9U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDB9U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD36U, 0xD83CDFFBU, }, - { 0xD83CDF85U, 0xD83CDFFBU, }, - { 0xD83EDDD9U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD9U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDDU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDDU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDBU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDBU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDCU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDCU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDAU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDAU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC7CU, 0xD83CDFFBU, }, - { 0xD83EDD30U, 0xD83CDFFBU, }, - { 0xD83EDD31U, 0xD83CDFFBU, }, - { 0xD83DDE47U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE47U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC81U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC81U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE45U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE45U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE46U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE46U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4BU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4BU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD26U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD26U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD37U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD37U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4EU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4EU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4DU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4DU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC87U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC87U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC86U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC86U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD6U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD6U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC85U, 0xD83CDFFBU, }, - { 0xD83EDD33U, 0xD83CDFFBU, }, - { 0xD83DDC83U, 0xD83CDFFBU, }, - { 0xD83DDD7AU, 0xD83CDFFBU, }, - { 0xD83DDD74U, 0xD83CDFFBU, }, - { 0xD83DDEB6U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB6U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC3U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC3U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCBU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCBU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD38U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD38U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0x26F9U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0x26F9U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD3EU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3EU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC7U, 0xD83CDFFBU, }, - { 0xD83EDDD8U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD8U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC4U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC4U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCAU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCAU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD3DU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3DU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEA3U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEA3U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD7U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD7U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEB5U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB5U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEB4U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB4U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD39U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD39U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEC0U, 0xD83CDFFBU, }, -}; - -InputCategory Category1 = { - { 0xD83DDE00U, }, - { 0xD83DDE03U, }, - { 0xD83DDE04U, }, - { 0xD83DDE01U, }, - { 0xD83DDE06U, }, - { 0xD83DDE05U, }, - { 0xD83DDE02U, }, - { 0xD83EDD23U, }, - { 0x263AU, 0xFE0FU, }, - { 0xD83DDE0AU, }, - { 0xD83DDE07U, }, - { 0xD83DDE42U, }, - { 0xD83DDE43U, }, - { 0xD83DDE09U, }, - { 0xD83DDE0CU, }, - { 0xD83DDE0DU, }, - { 0xD83EDD70U, }, - { 0xD83DDE18U, }, - { 0xD83DDE17U, }, - { 0xD83DDE19U, }, - { 0xD83DDE1AU, }, - { 0xD83DDE0BU, }, - { 0xD83DDE1BU, }, - { 0xD83DDE1DU, }, - { 0xD83DDE1CU, }, - { 0xD83EDD2AU, }, - { 0xD83EDD28U, }, - { 0xD83EDDD0U, }, - { 0xD83EDD13U, }, - { 0xD83DDE0EU, }, - { 0xD83EDD29U, }, - { 0xD83EDD73U, }, - { 0xD83DDE0FU, }, - { 0xD83DDE12U, }, - { 0xD83DDE1EU, }, - { 0xD83DDE14U, }, - { 0xD83DDE1FU, }, - { 0xD83DDE15U, }, - { 0xD83DDE41U, }, - { 0x2639U, 0xFE0FU, }, - { 0xD83DDE23U, }, - { 0xD83DDE16U, }, - { 0xD83DDE2BU, }, - { 0xD83DDE29U, }, - { 0xD83EDD7AU, }, - { 0xD83DDE22U, }, - { 0xD83DDE2DU, }, - { 0xD83DDE24U, }, - { 0xD83DDE20U, }, - { 0xD83DDE21U, }, - { 0xD83EDD2CU, }, - { 0xD83EDD2FU, }, - { 0xD83DDE33U, }, - { 0xD83EDD75U, }, - { 0xD83EDD76U, }, - { 0xD83DDE31U, }, - { 0xD83DDE28U, }, - { 0xD83DDE30U, }, - { 0xD83DDE25U, }, - { 0xD83DDE13U, }, - { 0xD83EDD17U, }, - { 0xD83EDD14U, }, - { 0xD83EDD2DU, }, - { 0xD83EDD2BU, }, - { 0xD83EDD25U, }, - { 0xD83DDE36U, }, - { 0xD83DDE10U, }, - { 0xD83DDE11U, }, - { 0xD83DDE2CU, }, - { 0xD83DDE44U, }, - { 0xD83DDE2FU, }, - { 0xD83DDE26U, }, - { 0xD83DDE27U, }, - { 0xD83DDE2EU, }, - { 0xD83DDE32U, }, - { 0xD83DDE34U, }, - { 0xD83EDD24U, }, - { 0xD83DDE2AU, }, - { 0xD83DDE35U, }, - { 0xD83EDD10U, }, - { 0xD83EDD74U, }, - { 0xD83EDD22U, }, - { 0xD83EDD2EU, }, - { 0xD83EDD27U, }, - { 0xD83DDE37U, }, - { 0xD83EDD12U, }, - { 0xD83EDD15U, }, - { 0xD83EDD11U, }, - { 0xD83EDD20U, }, - { 0xD83DDE08U, }, - { 0xD83DDC7FU, }, - { 0xD83DDC79U, }, - { 0xD83DDC7AU, }, - { 0xD83EDD21U, }, - { 0xD83DDCA9U, }, - { 0xD83DDC7BU, }, - { 0xD83DDC80U, }, - { 0x2620U, 0xFE0FU, }, - { 0xD83DDC7DU, }, - { 0xD83DDC7EU, }, - { 0xD83EDD16U, }, - { 0xD83CDF83U, }, - { 0xD83DDE3AU, }, - { 0xD83DDE38U, }, - { 0xD83DDE39U, }, - { 0xD83DDE3BU, }, - { 0xD83DDE3CU, }, - { 0xD83DDE3DU, }, - { 0xD83DDE40U, }, - { 0xD83DDE3FU, }, - { 0xD83DDE3EU, }, - { 0xD83EDD32U, }, - { 0xD83DDC50U, }, - { 0xD83DDE4CU, }, - { 0xD83DDC4FU, }, - { 0xD83EDD1DU, }, - { 0xD83DDC4DU, }, - { 0xD83DDC4EU, }, - { 0xD83DDC4AU, }, - { 0x270AU, }, - { 0xD83EDD1BU, }, - { 0xD83EDD1CU, }, - { 0xD83EDD1EU, }, - { 0x270CU, 0xFE0FU, }, - { 0xD83EDD1FU, }, - { 0xD83EDD18U, }, - { 0xD83DDC4CU, }, - { 0xD83DDC48U, }, - { 0xD83DDC49U, }, - { 0xD83DDC46U, }, - { 0xD83DDC47U, }, - { 0x261DU, 0xFE0FU, }, - { 0x270BU, }, - { 0xD83EDD1AU, }, - { 0xD83DDD90U, }, - { 0xD83DDD96U, }, - { 0xD83DDC4BU, }, - { 0xD83EDD19U, }, - { 0xD83DDCAAU, }, - { 0xD83DDD95U, }, - { 0x270DU, 0xFE0FU, }, - { 0xD83DDE4FU, }, - { 0xD83EDDB6U, }, - { 0xD83EDDB5U, }, - { 0xD83DDC84U, }, - { 0xD83DDC8BU, }, - { 0xD83DDC44U, }, - { 0xD83EDDB7U, }, - { 0xD83DDC45U, }, - { 0xD83DDC42U, }, - { 0xD83DDC43U, }, - { 0xD83DDC63U, }, - { 0xD83DDC41U, }, - { 0xD83DDC40U, }, - { 0xD83EDDE0U, }, - { 0xD83DDDE3U, }, - { 0xD83DDC64U, }, - { 0xD83DDC65U, }, - { 0xD83DDC76U, }, - { 0xD83DDC67U, }, - { 0xD83EDDD2U, }, - { 0xD83DDC66U, }, - { 0xD83DDC69U, }, - { 0xD83EDDD1U, }, - { 0xD83DDC68U, }, - { 0xD83DDC69U, 0x200DU, 0xD83EDDB1U, }, - { 0xD83DDC68U, 0x200DU, 0xD83EDDB1U, }, - { 0xD83DDC69U, 0x200DU, 0xD83EDDB0U, }, - { 0xD83DDC68U, 0x200DU, 0xD83EDDB0U, }, - { 0xD83DDC71U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC71U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0xD83EDDB3U, }, - { 0xD83DDC68U, 0x200DU, 0xD83EDDB3U, }, - { 0xD83DDC69U, 0x200DU, 0xD83EDDB2U, }, - { 0xD83DDC68U, 0x200DU, 0xD83EDDB2U, }, - { 0xD83EDDD4U, }, - { 0xD83DDC75U, }, - { 0xD83EDDD3U, }, - { 0xD83DDC74U, }, - { 0xD83DDC72U, }, - { 0xD83DDC73U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC73U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD5U, }, - { 0xD83DDC6EU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC6EU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC77U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC77U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC82U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC82U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDD75U, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDD75U, 0xFE0FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC69U, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC69U, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC70U, }, - { 0xD83EDD35U, }, - { 0xD83DDC78U, }, - { 0xD83EDD34U, }, - { 0xD83EDDB8U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDB8U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDB9U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDB9U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD36U, }, - { 0xD83CDF85U, }, - { 0xD83EDDD9U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD9U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDDU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDDU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDFU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDFU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDEU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDEU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDCU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDCU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDDAU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDDAU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC7CU, }, - { 0xD83EDD30U, }, - { 0xD83EDD31U, }, - { 0xD83DDE47U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE47U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC81U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC81U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE45U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE45U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE46U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE46U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4BU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4BU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD26U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD26U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD37U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD37U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4EU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4EU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4DU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE4DU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC87U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC87U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC86U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC86U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD6U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD6U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC85U, }, - { 0xD83EDD33U, }, - { 0xD83DDC83U, }, - { 0xD83DDD7AU, }, - { 0xD83DDC6FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC6FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDD74U, }, - { 0xD83DDEB6U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB6U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC3U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC3U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC6BU, }, - { 0xD83DDC6DU, }, - { 0xD83DDC6CU, }, - { 0xD83DDC91U, }, - { 0xD83DDC69U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC69U, }, - { 0xD83DDC68U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC68U, }, - { 0xD83DDC8FU, }, - { 0xD83DDC69U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC8BU, 0x200DU, 0xD83DDC69U, }, - { 0xD83DDC68U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC8BU, 0x200DU, 0xD83DDC68U, }, - { 0xD83DDC6AU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83EDDF6U, }, - { 0xD83EDDF5U, }, - { 0xD83EDDE5U, }, - { 0xD83EDD7CU, }, - { 0xD83DDC5AU, }, - { 0xD83DDC55U, }, - { 0xD83DDC56U, }, - { 0xD83DDC54U, }, - { 0xD83DDC57U, }, - { 0xD83DDC59U, }, - { 0xD83DDC58U, }, - { 0xD83EDD7FU, }, - { 0xD83DDC60U, }, - { 0xD83DDC61U, }, - { 0xD83DDC62U, }, - { 0xD83DDC5EU, }, - { 0xD83DDC5FU, }, - { 0xD83EDD7EU, }, - { 0xD83EDDE6U, }, - { 0xD83EDDE4U, }, - { 0xD83EDDE3U, }, - { 0xD83CDFA9U, }, - { 0xD83EDDE2U, }, - { 0xD83DDC52U, }, - { 0xD83CDF93U, }, - { 0x26D1U, }, - { 0xD83DDC51U, }, - { 0xD83DDC8DU, }, - { 0xD83DDC5DU, }, - { 0xD83DDC5BU, }, - { 0xD83DDC5CU, }, - { 0xD83DDCBCU, }, - { 0xD83CDF92U, }, - { 0xD83EDDF3U, }, - { 0xD83DDC53U, }, - { 0xD83DDD76U, }, - { 0xD83EDD7DU, }, - { 0xD83CDF02U, }, -}; - -InputCategory Category2 = { - { 0xD83DDC36U, }, - { 0xD83DDC31U, }, - { 0xD83DDC2DU, }, - { 0xD83DDC39U, }, - { 0xD83DDC30U, }, - { 0xD83EDD8AU, }, - { 0xD83DDC3BU, }, - { 0xD83DDC3CU, }, - { 0xD83DDC28U, }, - { 0xD83DDC2FU, }, - { 0xD83EDD81U, }, - { 0xD83DDC2EU, }, - { 0xD83DDC37U, }, - { 0xD83DDC3DU, }, - { 0xD83DDC38U, }, - { 0xD83DDC35U, }, - { 0xD83DDE48U, }, - { 0xD83DDE49U, }, - { 0xD83DDE4AU, }, - { 0xD83DDC12U, }, - { 0xD83DDC14U, }, - { 0xD83DDC27U, }, - { 0xD83DDC26U, }, - { 0xD83DDC24U, }, - { 0xD83DDC23U, }, - { 0xD83DDC25U, }, - { 0xD83EDD86U, }, - { 0xD83EDD85U, }, - { 0xD83EDD89U, }, - { 0xD83EDD87U, }, - { 0xD83DDC3AU, }, - { 0xD83DDC17U, }, - { 0xD83DDC34U, }, - { 0xD83EDD84U, }, - { 0xD83DDC1DU, }, - { 0xD83DDC1BU, }, - { 0xD83EDD8BU, }, - { 0xD83DDC0CU, }, - { 0xD83DDC1EU, }, - { 0xD83DDC1CU, }, - { 0xD83EDD9FU, }, - { 0xD83EDD97U, }, - { 0xD83DDD77U, }, - { 0xD83DDD78U, }, - { 0xD83EDD82U, }, - { 0xD83DDC22U, }, - { 0xD83DDC0DU, }, - { 0xD83EDD8EU, }, - { 0xD83EDD96U, }, - { 0xD83EDD95U, }, - { 0xD83DDC19U, }, - { 0xD83EDD91U, }, - { 0xD83EDD90U, }, - { 0xD83EDD9EU, }, - { 0xD83EDD80U, }, - { 0xD83DDC21U, }, - { 0xD83DDC20U, }, - { 0xD83DDC1FU, }, - { 0xD83DDC2CU, }, - { 0xD83DDC33U, }, - { 0xD83DDC0BU, }, - { 0xD83EDD88U, }, - { 0xD83DDC0AU, }, - { 0xD83DDC05U, }, - { 0xD83DDC06U, }, - { 0xD83EDD93U, }, - { 0xD83EDD8DU, }, - { 0xD83DDC18U, }, - { 0xD83EDD9BU, }, - { 0xD83EDD8FU, }, - { 0xD83DDC2AU, }, - { 0xD83DDC2BU, }, - { 0xD83EDD92U, }, - { 0xD83EDD98U, }, - { 0xD83DDC03U, }, - { 0xD83DDC02U, }, - { 0xD83DDC04U, }, - { 0xD83DDC0EU, }, - { 0xD83DDC16U, }, - { 0xD83DDC0FU, }, - { 0xD83DDC11U, }, - { 0xD83EDD99U, }, - { 0xD83DDC10U, }, - { 0xD83EDD8CU, }, - { 0xD83DDC15U, }, - { 0xD83DDC29U, }, - { 0xD83DDC08U, }, - { 0xD83DDC13U, }, - { 0xD83EDD83U, }, - { 0xD83EDD9AU, }, - { 0xD83EDD9CU, }, - { 0xD83EDDA2U, }, - { 0xD83DDD4AU, }, - { 0xD83DDC07U, }, - { 0xD83EDD9DU, }, - { 0xD83EDDA1U, }, - { 0xD83DDC01U, }, - { 0xD83DDC00U, }, - { 0xD83DDC3FU, }, - { 0xD83EDD94U, }, - { 0xD83DDC3EU, }, - { 0xD83DDC09U, }, - { 0xD83DDC32U, }, - { 0xD83CDF35U, }, - { 0xD83CDF84U, }, - { 0xD83CDF32U, }, - { 0xD83CDF33U, }, - { 0xD83CDF34U, }, - { 0xD83CDF31U, }, - { 0xD83CDF3FU, }, - { 0x2618U, 0xFE0FU, }, - { 0xD83CDF40U, }, - { 0xD83CDF8DU, }, - { 0xD83CDF8BU, }, - { 0xD83CDF43U, }, - { 0xD83CDF42U, }, - { 0xD83CDF41U, }, - { 0xD83CDF44U, }, - { 0xD83DDC1AU, }, - { 0xD83CDF3EU, }, - { 0xD83DDC90U, }, - { 0xD83CDF37U, }, - { 0xD83CDF39U, }, - { 0xD83EDD40U, }, - { 0xD83CDF3AU, }, - { 0xD83CDF38U, }, - { 0xD83CDF3CU, }, - { 0xD83CDF3BU, }, - { 0xD83CDF1EU, }, - { 0xD83CDF1DU, }, - { 0xD83CDF1BU, }, - { 0xD83CDF1CU, }, - { 0xD83CDF1AU, }, - { 0xD83CDF15U, }, - { 0xD83CDF16U, }, - { 0xD83CDF17U, }, - { 0xD83CDF18U, }, - { 0xD83CDF11U, }, - { 0xD83CDF12U, }, - { 0xD83CDF13U, }, - { 0xD83CDF14U, }, - { 0xD83CDF19U, }, - { 0xD83CDF0EU, }, - { 0xD83CDF0DU, }, - { 0xD83CDF0FU, }, - { 0xD83DDCABU, }, - { 0x2B50U, 0xFE0FU, }, - { 0xD83CDF1FU, }, - { 0x2728U, }, - { 0x26A1U, 0xFE0FU, }, - { 0x2604U, 0xFE0FU, }, - { 0xD83DDCA5U, }, - { 0xD83DDD25U, }, - { 0xD83CDF2AU, }, - { 0xD83CDF08U, }, - { 0x2600U, 0xFE0FU, }, - { 0xD83CDF24U, }, - { 0x26C5U, 0xFE0FU, }, - { 0xD83CDF25U, }, - { 0x2601U, 0xFE0FU, }, - { 0xD83CDF26U, }, - { 0xD83CDF27U, }, - { 0x26C8U, }, - { 0xD83CDF29U, }, - { 0xD83CDF28U, }, - { 0x2744U, 0xFE0FU, }, - { 0x2603U, 0xFE0FU, }, - { 0x26C4U, 0xFE0FU, }, - { 0xD83CDF2CU, }, - { 0xD83DDCA8U, }, - { 0xD83DDCA7U, }, - { 0xD83DDCA6U, }, - { 0x2614U, 0xFE0FU, }, - { 0x2602U, 0xFE0FU, }, - { 0xD83CDF0AU, }, - { 0xD83CDF2BU, }, -}; - -InputCategory Category3 = { - { 0xD83CDF4FU, }, - { 0xD83CDF4EU, }, - { 0xD83CDF50U, }, - { 0xD83CDF4AU, }, - { 0xD83CDF4BU, }, - { 0xD83CDF4CU, }, - { 0xD83CDF49U, }, - { 0xD83CDF47U, }, - { 0xD83CDF53U, }, - { 0xD83CDF48U, }, - { 0xD83CDF52U, }, - { 0xD83CDF51U, }, - { 0xD83EDD6DU, }, - { 0xD83CDF4DU, }, - { 0xD83EDD65U, }, - { 0xD83EDD5DU, }, - { 0xD83CDF45U, }, - { 0xD83CDF46U, }, - { 0xD83EDD51U, }, - { 0xD83EDD66U, }, - { 0xD83EDD6CU, }, - { 0xD83EDD52U, }, - { 0xD83CDF36U, }, - { 0xD83CDF3DU, }, - { 0xD83EDD55U, }, - { 0xD83EDD54U, }, - { 0xD83CDF60U, }, - { 0xD83EDD50U, }, - { 0xD83EDD6FU, }, - { 0xD83CDF5EU, }, - { 0xD83EDD56U, }, - { 0xD83EDD68U, }, - { 0xD83EDDC0U, }, - { 0xD83EDD5AU, }, - { 0xD83CDF73U, }, - { 0xD83EDD5EU, }, - { 0xD83EDD53U, }, - { 0xD83EDD69U, }, - { 0xD83CDF57U, }, - { 0xD83CDF56U, }, - { 0xD83EDDB4U, }, - { 0xD83CDF2DU, }, - { 0xD83CDF54U, }, - { 0xD83CDF5FU, }, - { 0xD83CDF55U, }, - { 0xD83EDD6AU, }, - { 0xD83EDD59U, }, - { 0xD83CDF2EU, }, - { 0xD83CDF2FU, }, - { 0xD83EDD57U, }, - { 0xD83EDD58U, }, - { 0xD83EDD6BU, }, - { 0xD83CDF5DU, }, - { 0xD83CDF5CU, }, - { 0xD83CDF72U, }, - { 0xD83CDF5BU, }, - { 0xD83CDF63U, }, - { 0xD83CDF71U, }, - { 0xD83EDD5FU, }, - { 0xD83CDF64U, }, - { 0xD83CDF59U, }, - { 0xD83CDF5AU, }, - { 0xD83CDF58U, }, - { 0xD83CDF65U, }, - { 0xD83EDD60U, }, - { 0xD83EDD6EU, }, - { 0xD83CDF62U, }, - { 0xD83CDF61U, }, - { 0xD83CDF67U, }, - { 0xD83CDF68U, }, - { 0xD83CDF66U, }, - { 0xD83EDD67U, }, - { 0xD83EDDC1U, }, - { 0xD83CDF70U, }, - { 0xD83CDF82U, }, - { 0xD83CDF6EU, }, - { 0xD83CDF6DU, }, - { 0xD83CDF6CU, }, - { 0xD83CDF6BU, }, - { 0xD83CDF7FU, }, - { 0xD83CDF69U, }, - { 0xD83CDF6AU, }, - { 0xD83CDF30U, }, - { 0xD83EDD5CU, }, - { 0xD83CDF6FU, }, - { 0xD83EDD5BU, }, - { 0xD83CDF7CU, }, - { 0x2615U, 0xFE0FU, }, - { 0xD83CDF75U, }, - { 0xD83EDD64U, }, - { 0xD83CDF76U, }, - { 0xD83CDF7AU, }, - { 0xD83CDF7BU, }, - { 0xD83EDD42U, }, - { 0xD83CDF77U, }, - { 0xD83EDD43U, }, - { 0xD83CDF78U, }, - { 0xD83CDF79U, }, - { 0xD83CDF7EU, }, - { 0xD83EDD44U, }, - { 0xD83CDF74U, }, - { 0xD83CDF7DU, }, - { 0xD83EDD63U, }, - { 0xD83EDD61U, }, - { 0xD83EDD62U, }, - { 0xD83EDDC2U, }, -}; - -InputCategory Category4 = { - { 0x26BDU, 0xFE0FU, }, - { 0xD83CDFC0U, }, - { 0xD83CDFC8U, }, - { 0x26BEU, 0xFE0FU, }, - { 0xD83EDD4EU, }, - { 0xD83CDFBEU, }, - { 0xD83CDFD0U, }, - { 0xD83CDFC9U, }, - { 0xD83EDD4FU, }, - { 0xD83CDFB1U, }, - { 0xD83CDFD3U, }, - { 0xD83CDFF8U, }, - { 0xD83CDFD2U, }, - { 0xD83CDFD1U, }, - { 0xD83EDD4DU, }, - { 0xD83CDFCFU, }, - { 0xD83EDD45U, }, - { 0x26F3U, 0xFE0FU, }, - { 0xD83CDFF9U, }, - { 0xD83CDFA3U, }, - { 0xD83EDD4AU, }, - { 0xD83EDD4BU, }, - { 0xD83CDFBDU, }, - { 0xD83DDEF9U, }, - { 0xD83DDEF7U, }, - { 0x26F8U, }, - { 0xD83EDD4CU, }, - { 0xD83CDFBFU, }, - { 0x26F7U, }, - { 0xD83CDFC2U, }, - { 0xD83CDFCBU, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCBU, 0xFE0FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD3CU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3CU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD38U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD38U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0x26F9U, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0x26F9U, 0xFE0FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD3AU, }, - { 0xD83EDD3EU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3EU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xFE0FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC7U, }, - { 0xD83EDDD8U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD8U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC4U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC4U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCAU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCAU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD3DU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3DU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEA3U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEA3U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDDD7U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDDD7U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEB5U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB5U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEB4U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB4U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFC6U, }, - { 0xD83EDD47U, }, - { 0xD83EDD48U, }, - { 0xD83EDD49U, }, - { 0xD83CDFC5U, }, - { 0xD83CDF96U, }, - { 0xD83CDFF5U, }, - { 0xD83CDF97U, }, - { 0xD83CDFABU, }, - { 0xD83CDF9FU, }, - { 0xD83CDFAAU, }, - { 0xD83EDD39U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD39U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFADU, }, - { 0xD83CDFA8U, }, - { 0xD83CDFACU, }, - { 0xD83CDFA4U, }, - { 0xD83CDFA7U, }, - { 0xD83CDFBCU, }, - { 0xD83CDFB9U, }, - { 0xD83EDD41U, }, - { 0xD83CDFB7U, }, - { 0xD83CDFBAU, }, - { 0xD83CDFB8U, }, - { 0xD83CDFBBU, }, - { 0xD83CDFB2U, }, - { 0x265FU, }, - { 0xD83CDFAFU, }, - { 0xD83CDFB3U, }, - { 0xD83CDFAEU, }, - { 0xD83CDFB0U, }, - { 0xD83EDDE9U, }, -}; - -InputCategory Category5 = { - { 0xD83DDE97U, }, - { 0xD83DDE95U, }, - { 0xD83DDE99U, }, - { 0xD83DDE8CU, }, - { 0xD83DDE8EU, }, - { 0xD83CDFCEU, }, - { 0xD83DDE93U, }, - { 0xD83DDE91U, }, - { 0xD83DDE92U, }, - { 0xD83DDE90U, }, - { 0xD83DDE9AU, }, - { 0xD83DDE9BU, }, - { 0xD83DDE9CU, }, - { 0xD83DDEF4U, }, - { 0xD83DDEB2U, }, - { 0xD83DDEF5U, }, - { 0xD83CDFCDU, }, - { 0xD83DDEA8U, }, - { 0xD83DDE94U, }, - { 0xD83DDE8DU, }, - { 0xD83DDE98U, }, - { 0xD83DDE96U, }, - { 0xD83DDEA1U, }, - { 0xD83DDEA0U, }, - { 0xD83DDE9FU, }, - { 0xD83DDE83U, }, - { 0xD83DDE8BU, }, - { 0xD83DDE9EU, }, - { 0xD83DDE9DU, }, - { 0xD83DDE84U, }, - { 0xD83DDE85U, }, - { 0xD83DDE88U, }, - { 0xD83DDE82U, }, - { 0xD83DDE86U, }, - { 0xD83DDE87U, }, - { 0xD83DDE8AU, }, - { 0xD83DDE89U, }, - { 0x2708U, 0xFE0FU, }, - { 0xD83DDEEBU, }, - { 0xD83DDEECU, }, - { 0xD83DDEE9U, }, - { 0xD83DDCBAU, }, - { 0xD83DDEF0U, }, - { 0xD83DDE80U, }, - { 0xD83DDEF8U, }, - { 0xD83DDE81U, }, - { 0xD83DDEF6U, }, - { 0x26F5U, 0xFE0FU, }, - { 0xD83DDEA4U, }, - { 0xD83DDEE5U, }, - { 0xD83DDEF3U, }, - { 0x26F4U, }, - { 0xD83DDEA2U, }, - { 0x2693U, 0xFE0FU, }, - { 0x26FDU, 0xFE0FU, }, - { 0xD83DDEA7U, }, - { 0xD83DDEA6U, }, - { 0xD83DDEA5U, }, - { 0xD83DDE8FU, }, - { 0xD83DDDFAU, }, - { 0xD83DDDFFU, }, - { 0xD83DDDFDU, }, - { 0xD83DDDFCU, }, - { 0xD83CDFF0U, }, - { 0xD83CDFEFU, }, - { 0xD83CDFDFU, }, - { 0xD83CDFA1U, }, - { 0xD83CDFA2U, }, - { 0xD83CDFA0U, }, - { 0x26F2U, 0xFE0FU, }, - { 0x26F1U, }, - { 0xD83CDFD6U, }, - { 0xD83CDFDDU, }, - { 0xD83CDFDCU, }, - { 0xD83CDF0BU, }, - { 0x26F0U, }, - { 0xD83CDFD4U, }, - { 0xD83DDDFBU, }, - { 0xD83CDFD5U, }, - { 0x26FAU, 0xFE0FU, }, - { 0xD83CDFE0U, }, - { 0xD83CDFE1U, }, - { 0xD83CDFD8U, }, - { 0xD83CDFDAU, }, - { 0xD83CDFD7U, }, - { 0xD83CDFEDU, }, - { 0xD83CDFE2U, }, - { 0xD83CDFECU, }, - { 0xD83CDFE3U, }, - { 0xD83CDFE4U, }, - { 0xD83CDFE5U, }, - { 0xD83CDFE6U, }, - { 0xD83CDFE8U, }, - { 0xD83CDFEAU, }, - { 0xD83CDFEBU, }, - { 0xD83CDFE9U, }, - { 0xD83DDC92U, }, - { 0xD83CDFDBU, }, - { 0x26EAU, 0xFE0FU, }, - { 0xD83DDD4CU, }, - { 0xD83DDD4DU, }, - { 0xD83DDD4BU, }, - { 0x26E9U, }, - { 0xD83DDEE4U, }, - { 0xD83DDEE3U, }, - { 0xD83DDDFEU, }, - { 0xD83CDF91U, }, - { 0xD83CDFDEU, }, - { 0xD83CDF05U, }, - { 0xD83CDF04U, }, - { 0xD83CDF20U, }, - { 0xD83CDF87U, }, - { 0xD83CDF86U, }, - { 0xD83CDF07U, }, - { 0xD83CDF06U, }, - { 0xD83CDFD9U, }, - { 0xD83CDF03U, }, - { 0xD83CDF0CU, }, - { 0xD83CDF09U, }, - { 0xD83CDF01U, }, -}; - -InputCategory Category6 = { - { 0x231AU, 0xFE0FU, }, - { 0xD83DDCF1U, }, - { 0xD83DDCF2U, }, - { 0xD83DDCBBU, }, - { 0x2328U, 0xFE0FU, }, - { 0xD83DDDA5U, }, - { 0xD83DDDA8U, }, - { 0xD83DDDB1U, }, - { 0xD83DDDB2U, }, - { 0xD83DDD79U, }, - { 0xD83DDDDCU, }, - { 0xD83DDCBDU, }, - { 0xD83DDCBEU, }, - { 0xD83DDCBFU, }, - { 0xD83DDCC0U, }, - { 0xD83DDCFCU, }, - { 0xD83DDCF7U, }, - { 0xD83DDCF8U, }, - { 0xD83DDCF9U, }, - { 0xD83CDFA5U, }, - { 0xD83DDCFDU, }, - { 0xD83CDF9EU, }, - { 0xD83DDCDEU, }, - { 0x260EU, 0xFE0FU, }, - { 0xD83DDCDFU, }, - { 0xD83DDCE0U, }, - { 0xD83DDCFAU, }, - { 0xD83DDCFBU, }, - { 0xD83CDF99U, }, - { 0xD83CDF9AU, }, - { 0xD83CDF9BU, }, - { 0xD83EDDEDU, }, - { 0x23F1U, }, - { 0x23F2U, }, - { 0x23F0U, }, - { 0xD83DDD70U, }, - { 0x231BU, 0xFE0FU, }, - { 0x23F3U, }, - { 0xD83DDCE1U, }, - { 0xD83DDD0BU, }, - { 0xD83DDD0CU, }, - { 0xD83DDCA1U, }, - { 0xD83DDD26U, }, - { 0xD83DDD6FU, }, - { 0xD83EDDEFU, }, - { 0xD83DDEE2U, }, - { 0xD83DDCB8U, }, - { 0xD83DDCB5U, }, - { 0xD83DDCB4U, }, - { 0xD83DDCB6U, }, - { 0xD83DDCB7U, }, - { 0xD83DDCB0U, }, - { 0xD83DDCB3U, }, - { 0xD83DDC8EU, }, - { 0x2696U, 0xFE0FU, }, - { 0xD83EDDF0U, }, - { 0xD83DDD27U, }, - { 0xD83DDD28U, }, - { 0x2692U, }, - { 0xD83DDEE0U, }, - { 0x26CFU, }, - { 0xD83DDD29U, }, - { 0x2699U, 0xFE0FU, }, - { 0xD83EDDF1U, }, - { 0x26D3U, }, - { 0xD83EDDF2U, }, - { 0xD83DDD2BU, }, - { 0xD83DDCA3U, }, - { 0xD83EDDE8U, }, - { 0xD83DDD2AU, }, - { 0xD83DDDE1U, }, - { 0x2694U, 0xFE0FU, }, - { 0xD83DDEE1U, }, - { 0xD83DDEACU, }, - { 0x26B0U, 0xFE0FU, }, - { 0x26B1U, 0xFE0FU, }, - { 0xD83CDFFAU, }, - { 0xD83DDD2EU, }, - { 0xD83DDCFFU, }, - { 0xD83EDDFFU, }, - { 0xD83DDC88U, }, - { 0x2697U, 0xFE0FU, }, - { 0xD83DDD2DU, }, - { 0xD83DDD2CU, }, - { 0xD83DDD73U, }, - { 0xD83DDC8AU, }, - { 0xD83DDC89U, }, - { 0xD83EDDECU, }, - { 0xD83EDDA0U, }, - { 0xD83EDDEBU, }, - { 0xD83EDDEAU, }, - { 0xD83CDF21U, }, - { 0xD83EDDF9U, }, - { 0xD83EDDFAU, }, - { 0xD83EDDFBU, }, - { 0xD83DDEBDU, }, - { 0xD83DDEB0U, }, - { 0xD83DDEBFU, }, - { 0xD83DDEC1U, }, - { 0xD83DDEC0U, }, - { 0xD83EDDFCU, }, - { 0xD83EDDFDU, }, - { 0xD83EDDF4U, }, - { 0xD83DDECEU, }, - { 0xD83DDD11U, }, - { 0xD83DDDDDU, }, - { 0xD83DDEAAU, }, - { 0xD83DDECBU, }, - { 0xD83DDECFU, }, - { 0xD83DDECCU, }, - { 0xD83EDDF8U, }, - { 0xD83DDDBCU, }, - { 0xD83DDECDU, }, - { 0xD83DDED2U, }, - { 0xD83CDF81U, }, - { 0xD83CDF88U, }, - { 0xD83CDF8FU, }, - { 0xD83CDF80U, }, - { 0xD83CDF8AU, }, - { 0xD83CDF89U, }, - { 0xD83CDF8EU, }, - { 0xD83CDFEEU, }, - { 0xD83CDF90U, }, - { 0xD83EDDE7U, }, - { 0x2709U, 0xFE0FU, }, - { 0xD83DDCE9U, }, - { 0xD83DDCE8U, }, - { 0xD83DDCE7U, }, - { 0xD83DDC8CU, }, - { 0xD83DDCE5U, }, - { 0xD83DDCE4U, }, - { 0xD83DDCE6U, }, - { 0xD83CDFF7U, }, - { 0xD83DDCEAU, }, - { 0xD83DDCEBU, }, - { 0xD83DDCECU, }, - { 0xD83DDCEDU, }, - { 0xD83DDCEEU, }, - { 0xD83DDCEFU, }, - { 0xD83DDCDCU, }, - { 0xD83DDCC3U, }, - { 0xD83DDCC4U, }, - { 0xD83DDCD1U, }, - { 0xD83EDDFEU, }, - { 0xD83DDCCAU, }, - { 0xD83DDCC8U, }, - { 0xD83DDCC9U, }, - { 0xD83DDDD2U, }, - { 0xD83DDDD3U, }, - { 0xD83DDCC6U, }, - { 0xD83DDCC5U, }, - { 0xD83DDDD1U, }, - { 0xD83DDCC7U, }, - { 0xD83DDDC3U, }, - { 0xD83DDDF3U, }, - { 0xD83DDDC4U, }, - { 0xD83DDCCBU, }, - { 0xD83DDCC1U, }, - { 0xD83DDCC2U, }, - { 0xD83DDDC2U, }, - { 0xD83DDDDEU, }, - { 0xD83DDCF0U, }, - { 0xD83DDCD3U, }, - { 0xD83DDCD4U, }, - { 0xD83DDCD2U, }, - { 0xD83DDCD5U, }, - { 0xD83DDCD7U, }, - { 0xD83DDCD8U, }, - { 0xD83DDCD9U, }, - { 0xD83DDCDAU, }, - { 0xD83DDCD6U, }, - { 0xD83DDD16U, }, - { 0xD83EDDF7U, }, - { 0xD83DDD17U, }, - { 0xD83DDCCEU, }, - { 0xD83DDD87U, }, - { 0xD83DDCD0U, }, - { 0xD83DDCCFU, }, - { 0xD83EDDEEU, }, - { 0xD83DDCCCU, }, - { 0xD83DDCCDU, }, - { 0x2702U, 0xFE0FU, }, - { 0xD83DDD8AU, }, - { 0xD83DDD8BU, }, - { 0x2712U, 0xFE0FU, }, - { 0xD83DDD8CU, }, - { 0xD83DDD8DU, }, - { 0xD83DDCDDU, }, - { 0x270FU, 0xFE0FU, }, - { 0xD83DDD0DU, }, - { 0xD83DDD0EU, }, - { 0xD83DDD0FU, }, - { 0xD83DDD10U, }, - { 0xD83DDD12U, }, - { 0xD83DDD13U, }, -}; - -InputCategory Category7 = { - { 0x2764U, 0xFE0FU, }, - { 0xD83EDDE1U, }, - { 0xD83DDC9BU, }, - { 0xD83DDC9AU, }, - { 0xD83DDC99U, }, - { 0xD83DDC9CU, }, - { 0xD83DDDA4U, }, - { 0xD83DDC94U, }, - { 0x2763U, 0xFE0FU, }, - { 0xD83DDC95U, }, - { 0xD83DDC9EU, }, - { 0xD83DDC93U, }, - { 0xD83DDC97U, }, - { 0xD83DDC96U, }, - { 0xD83DDC98U, }, - { 0xD83DDC9DU, }, - { 0xD83DDC9FU, }, - { 0x262EU, 0xFE0FU, }, - { 0x271DU, 0xFE0FU, }, - { 0x262AU, 0xFE0FU, }, - { 0xD83DDD49U, }, - { 0x2638U, 0xFE0FU, }, - { 0x2721U, 0xFE0FU, }, - { 0xD83DDD2FU, }, - { 0xD83DDD4EU, }, - { 0x262FU, 0xFE0FU, }, - { 0x2626U, 0xFE0FU, }, - { 0xD83DDED0U, }, - { 0x26CEU, }, - { 0x2648U, 0xFE0FU, }, - { 0x2649U, 0xFE0FU, }, - { 0x264AU, 0xFE0FU, }, - { 0x264BU, 0xFE0FU, }, - { 0x264CU, 0xFE0FU, }, - { 0x264DU, 0xFE0FU, }, - { 0x264EU, 0xFE0FU, }, - { 0x264FU, 0xFE0FU, }, - { 0x2650U, 0xFE0FU, }, - { 0x2651U, 0xFE0FU, }, - { 0x2652U, 0xFE0FU, }, - { 0x2653U, 0xFE0FU, }, - { 0xD83CDD94U, }, - { 0x269BU, 0xFE0FU, }, - { 0xD83CDE51U, }, - { 0x2622U, 0xFE0FU, }, - { 0x2623U, 0xFE0FU, }, - { 0xD83DDCF4U, }, - { 0xD83DDCF3U, }, - { 0xD83CDE36U, }, - { 0xD83CDE1AU, 0xFE0FU, }, - { 0xD83CDE38U, }, - { 0xD83CDE3AU, }, - { 0xD83CDE37U, 0xFE0FU, }, - { 0x2734U, 0xFE0FU, }, - { 0xD83CDD9AU, }, - { 0xD83DDCAEU, }, - { 0xD83CDE50U, }, - { 0x3299U, 0xFE0FU, }, - { 0x3297U, 0xFE0FU, }, - { 0xD83CDE34U, }, - { 0xD83CDE35U, }, - { 0xD83CDE39U, }, - { 0xD83CDE32U, }, - { 0xD83CDD70U, 0xFE0FU, }, - { 0xD83CDD71U, 0xFE0FU, }, - { 0xD83CDD8EU, }, - { 0xD83CDD91U, }, - { 0xD83CDD7EU, 0xFE0FU, }, - { 0xD83CDD98U, }, - { 0x274CU, }, - { 0x2B55U, 0xFE0FU, }, - { 0xD83DDED1U, }, - { 0x26D4U, 0xFE0FU, }, - { 0xD83DDCDBU, }, - { 0xD83DDEABU, }, - { 0xD83DDCAFU, }, - { 0xD83DDCA2U, }, - { 0x2668U, 0xFE0FU, }, - { 0xD83DDEB7U, }, - { 0xD83DDEAFU, }, - { 0xD83DDEB3U, }, - { 0xD83DDEB1U, }, - { 0xD83DDD1EU, }, - { 0xD83DDCF5U, }, - { 0xD83DDEADU, }, - { 0x2757U, 0xFE0FU, }, - { 0x2755U, }, - { 0x2753U, }, - { 0x2754U, }, - { 0x203CU, 0xFE0FU, }, - { 0x2049U, 0xFE0FU, }, - { 0xD83DDD05U, }, - { 0xD83DDD06U, }, - { 0x303DU, 0xFE0FU, }, - { 0x26A0U, 0xFE0FU, }, - { 0xD83DDEB8U, }, - { 0xD83DDD31U, }, - { 0x269CU, 0xFE0FU, }, - { 0xD83DDD30U, }, - { 0x267BU, 0xFE0FU, }, - { 0x2705U, }, - { 0xD83CDE2FU, 0xFE0FU, }, - { 0xD83DDCB9U, }, - { 0x2747U, 0xFE0FU, }, - { 0x2733U, 0xFE0FU, }, - { 0x274EU, }, - { 0xD83CDF10U, }, - { 0xD83DDCA0U, }, - { 0x24C2U, 0xFE0FU, }, - { 0xD83CDF00U, }, - { 0xD83DDCA4U, }, - { 0xD83CDFE7U, }, - { 0xD83DDEBEU, }, - { 0x267FU, 0xFE0FU, }, - { 0xD83CDD7FU, 0xFE0FU, }, - { 0xD83CDE33U, }, - { 0xD83CDE02U, 0xFE0FU, }, - { 0xD83DDEC2U, }, - { 0xD83DDEC3U, }, - { 0xD83DDEC4U, }, - { 0xD83DDEC5U, }, - { 0xD83DDEB9U, }, - { 0xD83DDEBAU, }, - { 0xD83DDEBCU, }, - { 0xD83DDEBBU, }, - { 0xD83DDEAEU, }, - { 0xD83CDFA6U, }, - { 0xD83DDCF6U, }, - { 0xD83CDE01U, }, - { 0xD83DDD23U, }, - { 0x2139U, 0xFE0FU, }, - { 0xD83DDD24U, }, - { 0xD83DDD21U, }, - { 0xD83DDD20U, }, - { 0xD83CDD96U, }, - { 0xD83CDD97U, }, - { 0xD83CDD99U, }, - { 0xD83CDD92U, }, - { 0xD83CDD95U, }, - { 0xD83CDD93U, }, - { 0x30U, 0xFE0FU, 0x20E3U, }, - { 0x31U, 0xFE0FU, 0x20E3U, }, - { 0x32U, 0xFE0FU, 0x20E3U, }, - { 0x33U, 0xFE0FU, 0x20E3U, }, - { 0x34U, 0xFE0FU, 0x20E3U, }, - { 0x35U, 0xFE0FU, 0x20E3U, }, - { 0x36U, 0xFE0FU, 0x20E3U, }, - { 0x37U, 0xFE0FU, 0x20E3U, }, - { 0x38U, 0xFE0FU, 0x20E3U, }, - { 0x39U, 0xFE0FU, 0x20E3U, }, - { 0xD83DDD1FU, }, - { 0xD83DDD22U, }, - { 0x23U, 0xFE0FU, 0x20E3U, }, - { 0x2AU, 0xFE0FU, 0x20E3U, }, - { 0x23CFU, 0xFE0FU, }, - { 0x25B6U, 0xFE0FU, }, - { 0x23F8U, }, - { 0x23EFU, }, - { 0x23F9U, }, - { 0x23FAU, }, - { 0x23EDU, }, - { 0x23EEU, }, - { 0x23E9U, }, - { 0x23EAU, }, - { 0x23EBU, }, - { 0x23ECU, }, - { 0x25C0U, 0xFE0FU, }, - { 0xD83DDD3CU, }, - { 0xD83DDD3DU, }, - { 0x27A1U, 0xFE0FU, }, - { 0x2B05U, 0xFE0FU, }, - { 0x2B06U, 0xFE0FU, }, - { 0x2B07U, 0xFE0FU, }, - { 0x2197U, 0xFE0FU, }, - { 0x2198U, 0xFE0FU, }, - { 0x2199U, 0xFE0FU, }, - { 0x2196U, 0xFE0FU, }, - { 0x2195U, 0xFE0FU, }, - { 0x2194U, 0xFE0FU, }, - { 0x21AAU, 0xFE0FU, }, - { 0x21A9U, 0xFE0FU, }, - { 0x2934U, 0xFE0FU, }, - { 0x2935U, 0xFE0FU, }, - { 0xD83DDD00U, }, - { 0xD83DDD01U, }, - { 0xD83DDD02U, }, - { 0xD83DDD04U, }, - { 0xD83DDD03U, }, - { 0xD83CDFB5U, }, - { 0xD83CDFB6U, }, - { 0x2795U, }, - { 0x2796U, }, - { 0x2797U, }, - { 0x2716U, 0xFE0FU, }, - { 0x267EU, }, - { 0xD83DDCB2U, }, - { 0xD83DDCB1U, }, - { 0x2122U, 0xFE0FU, }, - { 0xA9U, 0xFE0FU, }, - { 0xAEU, 0xFE0FU, }, - { 0xD83DDC41U, 0x200DU, 0xD83DDDE8U, }, - { 0xD83DDD1AU, }, - { 0xD83DDD19U, }, - { 0xD83DDD1BU, }, - { 0xD83DDD1DU, }, - { 0xD83DDD1CU, }, - { 0x3030U, 0xFE0FU, }, - { 0x27B0U, }, - { 0x27BFU, }, - { 0x2714U, 0xFE0FU, }, - { 0x2611U, 0xFE0FU, }, - { 0xD83DDD18U, }, - { 0x26AAU, 0xFE0FU, }, - { 0x26ABU, 0xFE0FU, }, - { 0xD83DDD34U, }, - { 0xD83DDD35U, }, - { 0xD83DDD3AU, }, - { 0xD83DDD3BU, }, - { 0xD83DDD38U, }, - { 0xD83DDD39U, }, - { 0xD83DDD36U, }, - { 0xD83DDD37U, }, - { 0xD83DDD33U, }, - { 0xD83DDD32U, }, - { 0x25AAU, 0xFE0FU, }, - { 0x25ABU, 0xFE0FU, }, - { 0x25FEU, 0xFE0FU, }, - { 0x25FDU, 0xFE0FU, }, - { 0x25FCU, 0xFE0FU, }, - { 0x25FBU, 0xFE0FU, }, - { 0x2B1BU, 0xFE0FU, }, - { 0x2B1CU, 0xFE0FU, }, - { 0xD83DDD08U, }, - { 0xD83DDD07U, }, - { 0xD83DDD09U, }, - { 0xD83DDD0AU, }, - { 0xD83DDD14U, }, - { 0xD83DDD15U, }, - { 0xD83DDCE3U, }, - { 0xD83DDCE2U, }, - { 0xD83DDCACU, }, - { 0xD83DDCADU, }, - { 0xD83DDDEFU, }, - { 0x2660U, 0xFE0FU, }, - { 0x2663U, 0xFE0FU, }, - { 0x2665U, 0xFE0FU, }, - { 0x2666U, 0xFE0FU, }, - { 0xD83CDCCFU, }, - { 0xD83CDFB4U, }, - { 0xD83CDC04U, 0xFE0FU, }, - { 0xD83DDD50U, }, - { 0xD83DDD51U, }, - { 0xD83DDD52U, }, - { 0xD83DDD53U, }, - { 0xD83DDD54U, }, - { 0xD83DDD55U, }, - { 0xD83DDD56U, }, - { 0xD83DDD57U, }, - { 0xD83DDD58U, }, - { 0xD83DDD59U, }, - { 0xD83DDD5AU, }, - { 0xD83DDD5BU, }, - { 0xD83DDD5CU, }, - { 0xD83DDD5DU, }, - { 0xD83DDD5EU, }, - { 0xD83DDD5FU, }, - { 0xD83DDD60U, }, - { 0xD83DDD61U, }, - { 0xD83DDD62U, }, - { 0xD83DDD63U, }, - { 0xD83DDD64U, }, - { 0xD83DDD65U, }, - { 0xD83DDD66U, }, - { 0xD83DDD67U, }, - -//}; -// -//InputCategory Category8 = { - - { 0xD83CDFF3U, 0xFE0FU, }, - { 0xD83CDFF4U, }, - { 0xD83CDFF4U, 0x200DU, 0x2620U, 0xFE0FU, }, - { 0xD83CDFC1U, }, - { 0xD83DDEA9U, }, - { 0xD83CDFF3U, 0xFE0FU, 0x200DU, 0xD83CDF08U, }, - { 0xD83CDDFAU, 0xD83CDDF3U, }, - { 0xD83CDDE6U, 0xD83CDDEBU, }, - { 0xD83CDDE6U, 0xD83CDDFDU, }, - { 0xD83CDDE6U, 0xD83CDDF1U, }, - { 0xD83CDDE9U, 0xD83CDDFFU, }, - { 0xD83CDDE6U, 0xD83CDDF8U, }, - { 0xD83CDDE6U, 0xD83CDDE9U, }, - { 0xD83CDDE6U, 0xD83CDDF4U, }, - { 0xD83CDDE6U, 0xD83CDDEEU, }, - { 0xD83CDDE6U, 0xD83CDDF6U, }, - { 0xD83CDDE6U, 0xD83CDDECU, }, - { 0xD83CDDE6U, 0xD83CDDF7U, }, - { 0xD83CDDE6U, 0xD83CDDF2U, }, - { 0xD83CDDE6U, 0xD83CDDFCU, }, - { 0xD83CDDE6U, 0xD83CDDFAU, }, - { 0xD83CDDE6U, 0xD83CDDF9U, }, - { 0xD83CDDE6U, 0xD83CDDFFU, }, - { 0xD83CDDE7U, 0xD83CDDF8U, }, - { 0xD83CDDE7U, 0xD83CDDEDU, }, - { 0xD83CDDE7U, 0xD83CDDE9U, }, - { 0xD83CDDE7U, 0xD83CDDE7U, }, - { 0xD83CDDE7U, 0xD83CDDFEU, }, - { 0xD83CDDE7U, 0xD83CDDEAU, }, - { 0xD83CDDE7U, 0xD83CDDFFU, }, - { 0xD83CDDE7U, 0xD83CDDEFU, }, - { 0xD83CDDE7U, 0xD83CDDF2U, }, - { 0xD83CDDE7U, 0xD83CDDF9U, }, - { 0xD83CDDE7U, 0xD83CDDF4U, }, - { 0xD83CDDE7U, 0xD83CDDE6U, }, - { 0xD83CDDE7U, 0xD83CDDFCU, }, - { 0xD83CDDE7U, 0xD83CDDF7U, }, - { 0xD83CDDEEU, 0xD83CDDF4U, }, - { 0xD83CDDFBU, 0xD83CDDECU, }, - { 0xD83CDDE7U, 0xD83CDDF3U, }, - { 0xD83CDDE7U, 0xD83CDDECU, }, - { 0xD83CDDE7U, 0xD83CDDEBU, }, - { 0xD83CDDE7U, 0xD83CDDEEU, }, - { 0xD83CDDF0U, 0xD83CDDEDU, }, - { 0xD83CDDE8U, 0xD83CDDF2U, }, - { 0xD83CDDE8U, 0xD83CDDE6U, }, - { 0xD83CDDEEU, 0xD83CDDE8U, }, - { 0xD83CDDE8U, 0xD83CDDFBU, }, - { 0xD83CDDE7U, 0xD83CDDF6U, }, - { 0xD83CDDF0U, 0xD83CDDFEU, }, - { 0xD83CDDE8U, 0xD83CDDEBU, }, - { 0xD83CDDF9U, 0xD83CDDE9U, }, - { 0xD83CDDE8U, 0xD83CDDF1U, }, - { 0xD83CDDE8U, 0xD83CDDF3U, }, - { 0xD83CDDE8U, 0xD83CDDFDU, }, - { 0xD83CDDE8U, 0xD83CDDE8U, }, - { 0xD83CDDE8U, 0xD83CDDF4U, }, - { 0xD83CDDF0U, 0xD83CDDF2U, }, - { 0xD83CDDE8U, 0xD83CDDECU, }, - { 0xD83CDDE8U, 0xD83CDDE9U, }, - { 0xD83CDDE8U, 0xD83CDDF0U, }, - { 0xD83CDDE8U, 0xD83CDDF7U, }, - { 0xD83CDDE8U, 0xD83CDDEEU, }, - { 0xD83CDDEDU, 0xD83CDDF7U, }, - { 0xD83CDDE8U, 0xD83CDDFAU, }, - { 0xD83CDDE8U, 0xD83CDDFCU, }, - { 0xD83CDDE8U, 0xD83CDDFEU, }, - { 0xD83CDDE8U, 0xD83CDDFFU, }, - { 0xD83CDDE9U, 0xD83CDDF0U, }, - { 0xD83CDDE9U, 0xD83CDDEFU, }, - { 0xD83CDDE9U, 0xD83CDDF2U, }, - { 0xD83CDDE9U, 0xD83CDDF4U, }, - { 0xD83CDDEAU, 0xD83CDDE8U, }, - { 0xD83CDDEAU, 0xD83CDDECU, }, - { 0xD83CDDF8U, 0xD83CDDFBU, }, - { 0xD83CDDECU, 0xD83CDDF6U, }, - { 0xD83CDDEAU, 0xD83CDDF7U, }, - { 0xD83CDDEAU, 0xD83CDDEAU, }, - { 0xD83CDDEAU, 0xD83CDDF9U, }, - { 0xD83CDDEAU, 0xD83CDDFAU, }, - { 0xD83CDDEBU, 0xD83CDDF0U, }, - { 0xD83CDDEBU, 0xD83CDDF4U, }, - { 0xD83CDDEBU, 0xD83CDDEFU, }, - { 0xD83CDDEBU, 0xD83CDDEEU, }, - { 0xD83CDDEBU, 0xD83CDDF7U, }, - { 0xD83CDDECU, 0xD83CDDEBU, }, - { 0xD83CDDF5U, 0xD83CDDEBU, }, - { 0xD83CDDF9U, 0xD83CDDEBU, }, - { 0xD83CDDECU, 0xD83CDDE6U, }, - { 0xD83CDDECU, 0xD83CDDF2U, }, - { 0xD83CDDECU, 0xD83CDDEAU, }, - { 0xD83CDDE9U, 0xD83CDDEAU, }, - { 0xD83CDDECU, 0xD83CDDEDU, }, - { 0xD83CDDECU, 0xD83CDDEEU, }, - { 0xD83CDDECU, 0xD83CDDF7U, }, - { 0xD83CDDECU, 0xD83CDDF1U, }, - { 0xD83CDDECU, 0xD83CDDE9U, }, - { 0xD83CDDECU, 0xD83CDDF5U, }, - { 0xD83CDDECU, 0xD83CDDFAU, }, - { 0xD83CDDECU, 0xD83CDDF9U, }, - { 0xD83CDDECU, 0xD83CDDECU, }, - { 0xD83CDDECU, 0xD83CDDF3U, }, - { 0xD83CDDECU, 0xD83CDDFCU, }, - { 0xD83CDDECU, 0xD83CDDFEU, }, - { 0xD83CDDEDU, 0xD83CDDF9U, }, - { 0xD83CDDEDU, 0xD83CDDF3U, }, - { 0xD83CDDEDU, 0xD83CDDF0U, }, - { 0xD83CDDEDU, 0xD83CDDFAU, }, - { 0xD83CDDEEU, 0xD83CDDF8U, }, - { 0xD83CDDEEU, 0xD83CDDF3U, }, - { 0xD83CDDEEU, 0xD83CDDE9U, }, - { 0xD83CDDEEU, 0xD83CDDF7U, }, - { 0xD83CDDEEU, 0xD83CDDF6U, }, - { 0xD83CDDEEU, 0xD83CDDEAU, }, - { 0xD83CDDEEU, 0xD83CDDF2U, }, - { 0xD83CDDEEU, 0xD83CDDF1U, }, - { 0xD83CDDEEU, 0xD83CDDF9U, }, - { 0xD83CDDEFU, 0xD83CDDF2U, }, - { 0xD83CDDEFU, 0xD83CDDF5U, }, - { 0xD83CDF8CU, }, - { 0xD83CDDEFU, 0xD83CDDEAU, }, - { 0xD83CDDEFU, 0xD83CDDF4U, }, - { 0xD83CDDF0U, 0xD83CDDFFU, }, - { 0xD83CDDF0U, 0xD83CDDEAU, }, - { 0xD83CDDF0U, 0xD83CDDEEU, }, - { 0xD83CDDFDU, 0xD83CDDF0U, }, - { 0xD83CDDF0U, 0xD83CDDFCU, }, - { 0xD83CDDF0U, 0xD83CDDECU, }, - { 0xD83CDDF1U, 0xD83CDDE6U, }, - { 0xD83CDDF1U, 0xD83CDDFBU, }, - { 0xD83CDDF1U, 0xD83CDDE7U, }, - { 0xD83CDDF1U, 0xD83CDDF8U, }, - { 0xD83CDDF1U, 0xD83CDDF7U, }, - { 0xD83CDDF1U, 0xD83CDDFEU, }, - { 0xD83CDDF1U, 0xD83CDDEEU, }, - { 0xD83CDDF1U, 0xD83CDDF9U, }, - { 0xD83CDDF1U, 0xD83CDDFAU, }, - { 0xD83CDDF2U, 0xD83CDDF4U, }, - { 0xD83CDDF2U, 0xD83CDDF0U, }, - { 0xD83CDDF2U, 0xD83CDDECU, }, - { 0xD83CDDF2U, 0xD83CDDFCU, }, - { 0xD83CDDF2U, 0xD83CDDFEU, }, - { 0xD83CDDF2U, 0xD83CDDFBU, }, - { 0xD83CDDF2U, 0xD83CDDF1U, }, - { 0xD83CDDF2U, 0xD83CDDF9U, }, - { 0xD83CDDF2U, 0xD83CDDEDU, }, - { 0xD83CDDF2U, 0xD83CDDF6U, }, - { 0xD83CDDF2U, 0xD83CDDF7U, }, - { 0xD83CDDF2U, 0xD83CDDFAU, }, - { 0xD83CDDFEU, 0xD83CDDF9U, }, - { 0xD83CDDF2U, 0xD83CDDFDU, }, - { 0xD83CDDEBU, 0xD83CDDF2U, }, - { 0xD83CDDF2U, 0xD83CDDE9U, }, - { 0xD83CDDF2U, 0xD83CDDE8U, }, - { 0xD83CDDF2U, 0xD83CDDF3U, }, - { 0xD83CDDF2U, 0xD83CDDEAU, }, - { 0xD83CDDF2U, 0xD83CDDF8U, }, - { 0xD83CDDF2U, 0xD83CDDE6U, }, - { 0xD83CDDF2U, 0xD83CDDFFU, }, - { 0xD83CDDF2U, 0xD83CDDF2U, }, - { 0xD83CDDF3U, 0xD83CDDE6U, }, - { 0xD83CDDF3U, 0xD83CDDF7U, }, - { 0xD83CDDF3U, 0xD83CDDF5U, }, - { 0xD83CDDF3U, 0xD83CDDF1U, }, - { 0xD83CDDF3U, 0xD83CDDE8U, }, - { 0xD83CDDF3U, 0xD83CDDFFU, }, - { 0xD83CDDF3U, 0xD83CDDEEU, }, - { 0xD83CDDF3U, 0xD83CDDEAU, }, - { 0xD83CDDF3U, 0xD83CDDECU, }, - { 0xD83CDDF3U, 0xD83CDDFAU, }, - { 0xD83CDDF3U, 0xD83CDDEBU, }, - { 0xD83CDDF0U, 0xD83CDDF5U, }, - { 0xD83CDDF2U, 0xD83CDDF5U, }, - { 0xD83CDDF3U, 0xD83CDDF4U, }, - { 0xD83CDDF4U, 0xD83CDDF2U, }, - { 0xD83CDDF5U, 0xD83CDDF0U, }, - { 0xD83CDDF5U, 0xD83CDDFCU, }, - { 0xD83CDDF5U, 0xD83CDDF8U, }, - { 0xD83CDDF5U, 0xD83CDDE6U, }, - { 0xD83CDDF5U, 0xD83CDDECU, }, - { 0xD83CDDF5U, 0xD83CDDFEU, }, - { 0xD83CDDF5U, 0xD83CDDEAU, }, - { 0xD83CDDF5U, 0xD83CDDEDU, }, - { 0xD83CDDF5U, 0xD83CDDF3U, }, - { 0xD83CDDF5U, 0xD83CDDF1U, }, - { 0xD83CDDF5U, 0xD83CDDF9U, }, - { 0xD83CDDF5U, 0xD83CDDF7U, }, - { 0xD83CDDF6U, 0xD83CDDE6U, }, - { 0xD83CDDF7U, 0xD83CDDEAU, }, - { 0xD83CDDF7U, 0xD83CDDF4U, }, - { 0xD83CDDF7U, 0xD83CDDFAU, }, - { 0xD83CDDF7U, 0xD83CDDFCU, }, - { 0xD83CDDFCU, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDF2U, }, - { 0xD83CDDF8U, 0xD83CDDF9U, }, - { 0xD83CDDF8U, 0xD83CDDE6U, }, - { 0xD83CDDF8U, 0xD83CDDF3U, }, - { 0xD83CDDF7U, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDE8U, }, - { 0xD83CDDF8U, 0xD83CDDF1U, }, - { 0xD83CDDF8U, 0xD83CDDECU, }, - { 0xD83CDDF8U, 0xD83CDDFDU, }, - { 0xD83CDDF8U, 0xD83CDDF0U, }, - { 0xD83CDDF8U, 0xD83CDDEEU, }, - { 0xD83CDDECU, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDE7U, }, - { 0xD83CDDF8U, 0xD83CDDF4U, }, - { 0xD83CDDFFU, 0xD83CDDE6U, }, - { 0xD83CDDF0U, 0xD83CDDF7U, }, - { 0xD83CDDF8U, 0xD83CDDF8U, }, - { 0xD83CDDEAU, 0xD83CDDF8U, }, - { 0xD83CDDF1U, 0xD83CDDF0U, }, - { 0xD83CDDE7U, 0xD83CDDF1U, }, - { 0xD83CDDF8U, 0xD83CDDEDU, }, - { 0xD83CDDF0U, 0xD83CDDF3U, }, - { 0xD83CDDF1U, 0xD83CDDE8U, }, - { 0xD83CDDF5U, 0xD83CDDF2U, }, - { 0xD83CDDFBU, 0xD83CDDE8U, }, - { 0xD83CDDF8U, 0xD83CDDE9U, }, - { 0xD83CDDF8U, 0xD83CDDF7U, }, - { 0xD83CDDF8U, 0xD83CDDFFU, }, - { 0xD83CDDF8U, 0xD83CDDEAU, }, - { 0xD83CDDE8U, 0xD83CDDEDU, }, - { 0xD83CDDF8U, 0xD83CDDFEU, }, - { 0xD83CDDF9U, 0xD83CDDFCU, }, - { 0xD83CDDF9U, 0xD83CDDEFU, }, - { 0xD83CDDF9U, 0xD83CDDFFU, }, - { 0xD83CDDF9U, 0xD83CDDEDU, }, - { 0xD83CDDF9U, 0xD83CDDF1U, }, - { 0xD83CDDF9U, 0xD83CDDECU, }, - { 0xD83CDDF9U, 0xD83CDDF0U, }, - { 0xD83CDDF9U, 0xD83CDDF4U, }, - { 0xD83CDDF9U, 0xD83CDDF9U, }, - { 0xD83CDDF9U, 0xD83CDDF3U, }, - { 0xD83CDDF9U, 0xD83CDDF7U, }, - { 0xD83CDDF9U, 0xD83CDDF2U, }, - { 0xD83CDDF9U, 0xD83CDDE8U, }, - { 0xD83CDDF9U, 0xD83CDDFBU, }, - { 0xD83CDDFBU, 0xD83CDDEEU, }, - { 0xD83CDDFAU, 0xD83CDDECU, }, - { 0xD83CDDFAU, 0xD83CDDE6U, }, - { 0xD83CDDE6U, 0xD83CDDEAU, }, - { 0xD83CDDECU, 0xD83CDDE7U, }, - { 0xD83CDFF4U, 0xDB40DC67U, 0xDB40DC62U, 0xDB40DC65U, 0xDB40DC6EU, 0xDB40DC67U, 0xDB40DC7FU, }, - { 0xD83CDFF4U, 0xDB40DC67U, 0xDB40DC62U, 0xDB40DC73U, 0xDB40DC63U, 0xDB40DC74U, 0xDB40DC7FU, }, - { 0xD83CDFF4U, 0xDB40DC67U, 0xDB40DC62U, 0xDB40DC77U, 0xDB40DC6CU, 0xDB40DC73U, 0xDB40DC7FU, }, - { 0xD83CDDFAU, 0xD83CDDF8U, }, - { 0xD83CDDFAU, 0xD83CDDFEU, }, - { 0xD83CDDFAU, 0xD83CDDFFU, }, - { 0xD83CDDFBU, 0xD83CDDFAU, }, - { 0xD83CDDFBU, 0xD83CDDE6U, }, - { 0xD83CDDFBU, 0xD83CDDEAU, }, - { 0xD83CDDFBU, 0xD83CDDF3U, }, - { 0xD83CDDFCU, 0xD83CDDEBU, }, - { 0xD83CDDEAU, 0xD83CDDEDU, }, - { 0xD83CDDFEU, 0xD83CDDEAU, }, - { 0xD83CDDFFU, 0xD83CDDF2U, }, - { 0xD83CDDFFU, 0xD83CDDFCU, }, -}; - -// Original data has those emoji only with gender symbols. -// But they should be displayed as emoji even without gender symbols. -// So we map which gender symbol to use for an emoji without one. -std::map WithoutGenderAliases = { - { { 0xD83EDD26U, }, 0x2642U }, - { { 0xD83EDD37U, }, 0x2640U }, - { { 0xD83EDD38U, }, 0x2642U }, - { { 0xD83EDD3CU, }, 0x2640U }, - { { 0xD83EDD3DU, }, 0x2642U }, - { { 0xD83EDD3EU, }, 0x2640U }, - { { 0xD83EDD39U, }, 0x2642U }, - { { 0xD83EDDB8U, }, 0x2640U }, - { { 0xD83EDDB9U, }, 0x2640U }, - { { 0xD83EDDD6U, }, 0x2642U }, - { { 0xD83EDDD7U, }, 0x2640U }, - { { 0xD83EDDD8U, }, 0x2640U }, - { { 0xD83EDDD9U, }, 0x2640U }, - { { 0xD83EDDDAU, }, 0x2640U }, - { { 0xD83EDDDBU, }, 0x2640U }, - { { 0xD83EDDDCU, }, 0x2642U }, - { { 0xD83EDDDDU, }, 0x2642U }, - { { 0xD83EDDDEU, }, 0x2642U }, - { { 0xD83EDDDFU, }, 0x2642U }, -}; - -// Some flags are sent as one string, but are rendered as a different too. -std::map FlagAliases = { - { { 0xD83CDDE8U, 0xD83CDDF5U, }, { 0xD83CDDEBU, 0xD83CDDF7U, } }, - { { 0xD83CDDE7U, 0xD83CDDFBU, }, { 0xD83CDDF3U, 0xD83CDDF4U, } }, - { { 0xD83CDDE6U, 0xD83CDDE8U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } }, - - // This is different flag, but macOS shows that glyph :( - { { 0xD83CDDE9U, 0xD83CDDECU, }, { 0xD83CDDEEU, 0xD83CDDF4U, } }, - - { { 0xD83CDDF9U, 0xD83CDDE6U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } }, - { { 0xD83CDDF2U, 0xD83CDDEBU, }, { 0xD83CDDEBU, 0xD83CDDF7U, } }, - { { 0xD83CDDEAU, 0xD83CDDE6U, }, { 0xD83CDDEAU, 0xD83CDDF8U, } }, -}; - -std::map> Aliases; // original -> list of aliased - -void AddAlias(const Id &original, const Id &aliased) { - Aliases[original].push_back(aliased); -} - -constexpr auto kErrorBadData = 401; - -void append(Id &id, uint32 code) { - if (auto first = static_cast((code >> 16) & 0xFFFFU)) { - id.append(QChar(first)); - } - id.append(QChar(static_cast(code & 0xFFFFU))); -} - -Id BareIdFromInput(const InputId &id) { - auto result = Id(); - for (const auto unicode : id) { - if (unicode != kPostfix) { - append(result, unicode); - } - } - return result; -} - -set fillVariatedIds() { - auto result = set(); - for (const auto &row : ColoredEmoji) { - auto variatedId = Id(); - if (row.size() < 2) { - logDataError() << "colored string should have at least two characters."; - return {}; - } - for (auto i = size_t(0), size = row.size(); i != size; ++i) { - auto code = row[i]; - if (i == 1) { - if (code != ColorMask) { - logDataError() << "color code should appear at index 1."; - return {}; - } - } else if (code == ColorMask) { - logDataError() << "color code should appear only at index 1."; - return {}; - } else if (code != kPostfix) { - append(variatedId, code); - } - } - result.emplace(variatedId); - } - return result; -} - -set fillPostfixRequiredIds() { - auto result = set(); - for (const auto &row : PostfixRequired) { - result.emplace(BareIdFromInput(row)); - } - return result; -} - -void appendCategory( - Data &result, - const InputCategory &category, - const set &variatedIds, - const set &postfixRequiredIds) { - result.categories.emplace_back(); - for (auto &id : category) { - auto emoji = Emoji(); - auto bareId = BareIdFromInput(id); - auto from = id.cbegin(), to = id.cend(); - if (to - from == 2 && *(to - 1) == kPostfix) { - emoji.postfixed = true; - --to; - } - for (auto i = from; i != to; ++i) { - auto code = *i; - if (find(begin(Colors), end(Colors), code) != end(Colors)) { - logDataError() << "color code found in a category emoji."; - result = Data(); - return; - } - append(emoji.id, code); - } - if (bareId.isEmpty()) { - logDataError() << "empty emoji id found."; - result = Data(); - return; - } - - auto it = result.map.find(bareId); - if (it == result.map.cend()) { - const auto index = result.list.size(); - it = result.map.emplace(bareId, index).first; - result.list.push_back(move(emoji)); - if (const auto a = Aliases.find(bareId); a != end(Aliases)) { - for (const auto &alias : a->second) { - const auto ok = result.map.emplace(alias, index).second; - if (!ok) { - logDataError() << "some emoji alias already in the map."; - result = Data(); - return; - } - } - } - if (postfixRequiredIds.find(bareId) != end(postfixRequiredIds)) { - result.postfixRequired.emplace(index); - } - } else if (result.list[it->second].postfixed != emoji.postfixed) { - logDataError() << "same emoji found with different postfixed property."; - result = Data(); - return; - } else if (result.list[it->second].id != emoji.id) { - logDataError() << "same emoji found with different id."; - result = Data(); - return; - } - if (variatedIds.find(bareId) != end(variatedIds)) { - result.list[it->second].variated = true; - - auto baseId = Id(); - if (*from == kPostfix) { - logDataError() << "bad first symbol in emoji."; - result = Data(); - return; - } - append(baseId, *from++); - for (auto color : Colors) { - auto colored = Emoji(); - colored.id = baseId; - colored.colored = true; - append(colored.id, color); - auto bareColoredId = colored.id; - for (auto i = from; i != to; ++i) { - append(colored.id, *i); - if (*i != kPostfix) { - append(bareColoredId, *i); - } - } - auto it = result.map.find(bareColoredId); - if (it == result.map.cend()) { - const auto index = result.list.size(); - it = result.map.emplace(bareColoredId, index).first; - result.list.push_back(move(colored)); - if (const auto a = Aliases.find(bareColoredId); a != end(Aliases)) { - for (const auto &alias : a->second) { - const auto ok = result.map.emplace(alias, index).second; - if (!ok) { - logDataError() << "some emoji alias already in the map."; - result = Data(); - return; - } - } - } - if (postfixRequiredIds.find(bareColoredId) != end(postfixRequiredIds)) { - result.postfixRequired.emplace(index); - } - } else if (result.list[it->second].postfixed != colored.postfixed) { - logDataError() << "same emoji found with different postfixed property."; - result = Data(); - return; - } else if (result.list[it->second].id != colored.id) { - logDataError() << "same emoji found with different id."; - result = Data(); - return; - } - } - } - result.categories.back().push_back(it->second); - } -} - -void fillReplaces(Data &result) { - for (auto &replace : Replaces) { - auto id = Id(); - for (auto code : replace.code) { - append(id, code); - } - auto it = result.map.find(id); - if (it == result.map.cend()) { - logDataError() << "emoji from replaces not found in the map."; - result = Data(); - return; - } - result.replaces.insert(make_pair(QString::fromUtf8(replace.replace), it->second)); - } -} - -bool AddItemBeforeItem(const InputId &add, const InputId &before) { - auto addToCategory = (InputCategory*)nullptr; - auto addBeforeIterator = InputCategory::iterator(); - for (auto category : { - &Category1, - &Category2, - &Category3, - &Category4, - &Category5, - &Category6, - &Category7, - }) { - for (auto i = category->begin(), e = category->end(); i != e; ++i) { - if (*i == add) { - return true; - } else if (*i == before) { - addToCategory = category; - addBeforeIterator = i; - } - } - } - if (!addToCategory) { - return false; - } - addToCategory->insert(addBeforeIterator, add); - return true; -} - -bool CheckOldInCurrent(std::set variatedIds) { - const auto categories = { - &Category1, - &Category2, - &Category3, - &Category4, - &Category5, - &Category6, - &Category7, - }; - const auto old = { - &old::Category1, - &old::Category2, - &old::Category3, - &old::Category4, - &old::Category5, - &old::Category6, - &old::Category7, - }; - const auto genders = { 0x2640U, 0x2642U }; - const auto addGender = [](const InputId &was, uint32 gender) { - auto result = was; - result.push_back(0x200DU); - result.push_back(gender); - result.push_back(0xFE0FU); - return result; - }; - const auto addGenderByIndex = [&](const InputId &was, int index) { - return addGender(was, *(begin(genders) + index)); - }; - const auto find = []( - const InputCategory &list, - const InputId &id) { - return (std::find(begin(list), end(list), id) != end(list)); - }; - const auto findInMany = [&]( - auto &&list, - const InputId &id) { - for (const auto current : list) { - if (find(*current, id)) { - return true; - } - } - return false; - }; - const auto emplaceColoredAlias = [](const InputId &real, const InputId &alias, uint32_t color) { - if (real.size() < 2 || alias.size() < 2 || real[1] != Colors[0] || alias[1] != Colors[0]) { - return false; - } - auto key = real; - key[1] = color; - auto value = alias; - value[1] = color; - AddAlias(BareIdFromInput(key), BareIdFromInput(value)); - return true; - }; - auto result = true; - for (auto c = begin(old); c != end(old); ++c) { - const auto category = *c; - for (auto i = begin(*category); i != end(*category); ++i) { - if (findInMany(categories, *i)) { - continue; - } - - // Some emoji were ending with 0xFE0FU and now are not. - if (i->back() == 0xFE0FU) { - auto other = *i; - other.pop_back(); - if (findInMany(categories, other)) { - continue; - } - } - - // Some emoji were not ending with 0xFE0FU and now are. - if (i->back() != 0xFE0FU) { - auto other = *i; - other.push_back(0xFE0FU); - if (findInMany(categories, other)) { - continue; - } - } - - // Some emoji were without gender symbol and now have gender symbol. - // Try adding 0x200DU, 0x2640U, 0xFE0FU or 0x200DU, 0x2642U, 0xFE0FU. - const auto otherGenderIndex = [&] { - for (auto g = begin(genders); g != end(genders); ++g) { - auto altered = *i; - altered.push_back(0x200DU); - altered.push_back(*g); - altered.push_back(0xFE0FU); - if (findInMany(old, altered)) { - return int(g - begin(genders)); - } - } - return -1; - }(); - if (otherGenderIndex < 0) { - common::logError(kErrorBadData, "input") - << "Bad data: old emoji (category " - << (c - begin(old)) - << ", index " - << (i - begin(*category)) - << ") not found in current."; - result = false; - continue; - } - - const auto genderIndex = (1 - otherGenderIndex); - const auto real = addGenderByIndex(*i, genderIndex); - const auto bare = BareIdFromInput(real); - if (!findInMany(categories, real)) { - common::logError(kErrorBadData, "input") - << "Bad data: old emoji (category " - << (c - begin(old)) - << ", index " - << (i - begin(*category)) - << ") not found in current with added gender: " - << genderIndex - << "."; - result = false; - } else { - AddAlias(bare, BareIdFromInput(*i)); - } - } - } - for (auto i = begin(old::ColoredEmoji); i != end(old::ColoredEmoji); ++i) { - if (find(ColoredEmoji, *i)) { - continue; - } - - const auto otherGenderIndex = [&] { - for (auto g = begin(genders); g != end(genders); ++g) { - auto altered = *i; - altered.push_back(0x200DU); - altered.push_back(*g); - altered.push_back(0xFE0FU); - if (find(old::ColoredEmoji, altered)) { - return int(g - begin(genders)); - } - } - return -1; - }(); - if (otherGenderIndex < 0) { - common::logError(kErrorBadData, "input") - << "Bad data: old colored emoji (index " - << (i - begin(old::ColoredEmoji)) - << ") not found in current."; - result = false; - continue; - } - - const auto genderIndex = (1 - otherGenderIndex); - const auto real = addGenderByIndex(*i, genderIndex); - const auto bare = BareIdFromInput(real); - if (!find(ColoredEmoji, real)) { - common::logError(kErrorBadData, "input") - << "Bad data: old colored emoji (index " - << (i - begin(old::ColoredEmoji)) - << ") not found in current with added gender: " - << genderIndex - << "."; - result = false; - continue; - } else { - for (const auto color : Colors) { - if (!emplaceColoredAlias(real, *i, color)) { - common::logError(kErrorBadData, "input") - << "Bad data: bad colored emoji."; - result = false; - break; - } - } - } - } - - for (const auto &entry : WithoutGenderAliases) { - const auto &inputId = entry.first; - const auto &gender = entry.second; - if (findInMany(categories, inputId)) { - continue; - } - const auto real = [&] { - auto result = addGender(inputId, gender); - if (findInMany(categories, result)) { - return result; - } - result.push_back(kPostfix); - return result; - }(); - const auto bare = BareIdFromInput(real); - if (!findInMany(categories, real)) { - common::logError(kErrorBadData, "input") - << "Bad data: without gender alias not found with gender."; - result = false; - } else { - AddAlias(bare, BareIdFromInput(inputId)); - } - if (variatedIds.find(bare) != variatedIds.end()) { - auto colorReal = real; - colorReal.insert(colorReal.begin() + 1, Colors[0]); - auto colorInput = inputId; - colorInput.insert(colorInput.begin() + 1, Colors[0]); - for (const auto color : Colors) { - if (!emplaceColoredAlias(colorReal, colorInput, color)) { - common::logError(kErrorBadData, "input") - << "Bad data: bad colored emoji."; - result = false; - break; - } - } - - } - } - - for (const auto &[inputId, real] : FlagAliases) { - AddAlias(BareIdFromInput(real), BareIdFromInput(inputId)); - } - - return result; -} - -} // namespace - -common::LogStream logDataError() { - return common::logError(kErrorBadData, "input") << "Bad data: "; -} - -Data PrepareData() { - Data result; - - const auto variatedIds = fillVariatedIds(); - const auto postfixRequiredIds = fillPostfixRequiredIds(); - if (variatedIds.empty() || postfixRequiredIds.empty()) { - return Data(); - } - - // Manually add :speech_left: emoji before eye-with-speech emoji. - if (!AddItemBeforeItem({ 0xD83DDDE8U }, { 0xD83DDC41U, 0x200DU, 0xD83DDDE8U })) { - return Data(); - } - - if (!CheckOldInCurrent(variatedIds)) { - return Data(); - } - - const auto categories = { - &Category1, - &Category2, - &Category3, - &Category4, - &Category5, - &Category6, - &Category7, - }; - for (const auto category : categories) { - appendCategory(result, *category, variatedIds, postfixRequiredIds); - if (result.list.empty()) { - return Data(); - } - } - - fillReplaces(result); - if (result.list.empty()) { - return Data(); - } - - return result; -} - -namespace { -namespace old { - -InputCategory ColoredEmoji = { - { 0xD83DDC50U, 0xD83CDFFBU, }, - { 0xD83DDE4CU, 0xD83CDFFBU, }, - { 0xD83DDC4FU, 0xD83CDFFBU, }, - { 0xD83DDE4FU, 0xD83CDFFBU, }, - { 0xD83DDC4DU, 0xD83CDFFBU, }, - { 0xD83DDC4EU, 0xD83CDFFBU, }, - { 0xD83DDC4AU, 0xD83CDFFBU, }, - { 0x270AU, 0xD83CDFFBU, }, - { 0xD83EDD1BU, 0xD83CDFFBU, }, - { 0xD83EDD1CU, 0xD83CDFFBU, }, - { 0xD83EDD1EU, 0xD83CDFFBU, }, - { 0x270CU, 0xD83CDFFBU, }, - { 0xD83EDD18U, 0xD83CDFFBU, }, - { 0xD83DDC4CU, 0xD83CDFFBU, }, - { 0xD83DDC48U, 0xD83CDFFBU, }, - { 0xD83DDC49U, 0xD83CDFFBU, }, - { 0xD83DDC46U, 0xD83CDFFBU, }, - { 0xD83DDC47U, 0xD83CDFFBU, }, - { 0x261DU, 0xD83CDFFBU, }, - { 0x270BU, 0xD83CDFFBU, }, - { 0xD83EDD1AU, 0xD83CDFFBU, }, - { 0xD83DDD90U, 0xD83CDFFBU, }, - { 0xD83DDD96U, 0xD83CDFFBU, }, - { 0xD83DDC4BU, 0xD83CDFFBU, }, - { 0xD83EDD19U, 0xD83CDFFBU, }, - { 0xD83DDCAAU, 0xD83CDFFBU, }, - { 0xD83DDD95U, 0xD83CDFFBU, }, - { 0x270DU, 0xD83CDFFBU, }, - { 0xD83EDD33U, 0xD83CDFFBU, }, - { 0xD83DDC85U, 0xD83CDFFBU, }, - { 0xD83DDC42U, 0xD83CDFFBU, }, - { 0xD83DDC43U, 0xD83CDFFBU, }, - { 0xD83DDC76U, 0xD83CDFFBU, }, - { 0xD83DDC66U, 0xD83CDFFBU, }, - { 0xD83DDC67U, 0xD83CDFFBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, }, - { 0xD83DDC71U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC71U, 0xD83CDFFBU, }, - { 0xD83DDC74U, 0xD83CDFFBU, }, - { 0xD83DDC75U, 0xD83CDFFBU, }, - { 0xD83DDC72U, 0xD83CDFFBU, }, - { 0xD83DDC73U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC73U, 0xD83CDFFBU, }, - { 0xD83DDC6EU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC6EU, 0xD83CDFFBU, }, - { 0xD83DDC77U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC77U, 0xD83CDFFBU, }, - { 0xD83DDC82U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC82U, 0xD83CDFFBU, }, - { 0xD83DDD75U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDD75U, 0xD83CDFFBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC69U, 0xD83CDFFBU, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC68U, 0xD83CDFFBU, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83EDD36U, 0xD83CDFFBU, }, - { 0xD83CDF85U, 0xD83CDFFBU, }, - { 0xD83DDC78U, 0xD83CDFFBU, }, - { 0xD83EDD34U, 0xD83CDFFBU, }, - { 0xD83DDC70U, 0xD83CDFFBU, }, - { 0xD83EDD35U, 0xD83CDFFBU, }, - { 0xD83DDC7CU, 0xD83CDFFBU, }, - { 0xD83EDD30U, 0xD83CDFFBU, }, - { 0xD83DDE47U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE47U, 0xD83CDFFBU, }, - { 0xD83DDC81U, 0xD83CDFFBU, }, - { 0xD83DDC81U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE45U, 0xD83CDFFBU, }, - { 0xD83DDE45U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE46U, 0xD83CDFFBU, }, - { 0xD83DDE46U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4BU, 0xD83CDFFBU, }, - { 0xD83DDE4BU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD26U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD26U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD37U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD37U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4EU, 0xD83CDFFBU, }, - { 0xD83DDE4EU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4DU, 0xD83CDFFBU, }, - { 0xD83DDE4DU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC87U, 0xD83CDFFBU, }, - { 0xD83DDC87U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC86U, 0xD83CDFFBU, }, - { 0xD83DDC86U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDD74U, 0xD83CDFFBU, }, - { 0xD83DDC83U, 0xD83CDFFBU, }, - { 0xD83DDD7AU, 0xD83CDFFBU, }, - { 0xD83DDEB6U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB6U, 0xD83CDFFBU, }, - { 0xD83CDFC3U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC3U, 0xD83CDFFBU, }, - { 0xD83CDFCBU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCBU, 0xD83CDFFBU, }, - { 0xD83EDD38U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD38U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0x26F9U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0x26F9U, 0xD83CDFFBU, }, - { 0xD83EDD3EU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3EU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xD83CDFFBU, }, - { 0xD83CDFC4U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC4U, 0xD83CDFFBU, }, - { 0xD83CDFCAU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCAU, 0xD83CDFFBU, }, - { 0xD83EDD3DU, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3DU, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEA3U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEA3U, 0xD83CDFFBU, }, - { 0xD83CDFC7U, 0xD83CDFFBU, }, - { 0xD83DDEB4U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB4U, 0xD83CDFFBU, }, - { 0xD83DDEB5U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB5U, 0xD83CDFFBU, }, - { 0xD83EDD39U, 0xD83CDFFBU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD39U, 0xD83CDFFBU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEC0U, 0xD83CDFFBU, }, -}; - -InputCategory Category1 = { - { 0xD83DDE00U, }, - { 0xD83DDE03U, }, - { 0xD83DDE04U, }, - { 0xD83DDE01U, }, - { 0xD83DDE06U, }, - { 0xD83DDE05U, }, - { 0xD83DDE02U, }, - { 0xD83EDD23U, }, - { 0x263AU, 0xFE0FU, }, - { 0xD83DDE0AU, }, - { 0xD83DDE07U, }, - { 0xD83DDE42U, }, - { 0xD83DDE43U, }, - { 0xD83DDE09U, }, - { 0xD83DDE0CU, }, - { 0xD83DDE0DU, }, - { 0xD83DDE18U, }, - { 0xD83DDE17U, }, - { 0xD83DDE19U, }, - { 0xD83DDE1AU, }, - { 0xD83DDE0BU, }, - { 0xD83DDE1CU, }, - { 0xD83DDE1DU, }, - { 0xD83DDE1BU, }, - { 0xD83EDD11U, }, - { 0xD83EDD17U, }, - { 0xD83EDD13U, }, - { 0xD83DDE0EU, }, - { 0xD83EDD21U, }, - { 0xD83EDD20U, }, - { 0xD83DDE0FU, }, - { 0xD83DDE12U, }, - { 0xD83DDE1EU, }, - { 0xD83DDE14U, }, - { 0xD83DDE1FU, }, - { 0xD83DDE15U, }, - { 0xD83DDE41U, }, - { 0x2639U, 0xFE0FU, }, - { 0xD83DDE23U, }, - { 0xD83DDE16U, }, - { 0xD83DDE2BU, }, - { 0xD83DDE29U, }, - { 0xD83DDE24U, }, - { 0xD83DDE20U, }, - { 0xD83DDE21U, }, - { 0xD83DDE36U, }, - { 0xD83DDE10U, }, - { 0xD83DDE11U, }, - { 0xD83DDE2FU, }, - { 0xD83DDE26U, }, - { 0xD83DDE27U, }, - { 0xD83DDE2EU, }, - { 0xD83DDE32U, }, - { 0xD83DDE35U, }, - { 0xD83DDE33U, }, - { 0xD83DDE31U, }, - { 0xD83DDE28U, }, - { 0xD83DDE30U, }, - { 0xD83DDE22U, }, - { 0xD83DDE25U, }, - { 0xD83EDD24U, }, - { 0xD83DDE2DU, }, - { 0xD83DDE13U, }, - { 0xD83DDE2AU, }, - { 0xD83DDE34U, }, - { 0xD83DDE44U, }, - { 0xD83EDD14U, }, - { 0xD83EDD25U, }, - { 0xD83DDE2CU, }, - { 0xD83EDD10U, }, - { 0xD83EDD22U, }, - { 0xD83EDD27U, }, - { 0xD83DDE37U, }, - { 0xD83EDD12U, }, - { 0xD83EDD15U, }, - { 0xD83DDE08U, }, - { 0xD83DDC7FU, }, - { 0xD83DDC79U, }, - { 0xD83DDC7AU, }, - { 0xD83DDCA9U, }, - { 0xD83DDC7BU, }, - { 0xD83DDC80U, }, - { 0x2620U, 0xFE0FU, }, - { 0xD83DDC7DU, }, - { 0xD83DDC7EU, }, - { 0xD83EDD16U, }, - { 0xD83CDF83U, }, - { 0xD83DDE3AU, }, - { 0xD83DDE38U, }, - { 0xD83DDE39U, }, - { 0xD83DDE3BU, }, - { 0xD83DDE3CU, }, - { 0xD83DDE3DU, }, - { 0xD83DDE40U, }, - { 0xD83DDE3FU, }, - { 0xD83DDE3EU, }, - { 0xD83DDC50U, }, - { 0xD83DDE4CU, }, - { 0xD83DDC4FU, }, - { 0xD83DDE4FU, }, - { 0xD83EDD1DU, }, - { 0xD83DDC4DU, }, - { 0xD83DDC4EU, }, - { 0xD83DDC4AU, }, - { 0x270AU, 0xFE0FU, }, - { 0xD83EDD1BU, }, - { 0xD83EDD1CU, }, - { 0xD83EDD1EU, }, - { 0x270CU, 0xFE0FU, }, - { 0xD83EDD18U, }, - { 0xD83DDC4CU, }, - { 0xD83DDC48U, }, - { 0xD83DDC49U, }, - { 0xD83DDC46U, }, - { 0xD83DDC47U, }, - { 0x261DU, 0xFE0FU, }, - { 0x270BU, 0xFE0FU, }, - { 0xD83EDD1AU, }, - { 0xD83DDD90U, }, - { 0xD83DDD96U, }, - { 0xD83DDC4BU, }, - { 0xD83EDD19U, }, - { 0xD83DDCAAU, }, - { 0xD83DDD95U, }, - { 0x270DU, 0xFE0FU, }, - { 0xD83EDD33U, }, - { 0xD83DDC85U, }, - { 0xD83DDC8DU, }, - { 0xD83DDC84U, }, - { 0xD83DDC8BU, }, - { 0xD83DDC44U, }, - { 0xD83DDC45U, }, - { 0xD83DDC42U, }, - { 0xD83DDC43U, }, - { 0xD83DDC63U, }, - { 0xD83DDC41U, }, - { 0xD83DDC40U, }, - { 0xD83DDDE3U, }, - { 0xD83DDC64U, }, - { 0xD83DDC65U, }, - { 0xD83DDC76U, }, - { 0xD83DDC66U, }, - { 0xD83DDC67U, }, - { 0xD83DDC68U, }, - { 0xD83DDC69U, }, - { 0xD83DDC71U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC71U, }, - { 0xD83DDC74U, }, - { 0xD83DDC75U, }, - { 0xD83DDC72U, }, - { 0xD83DDC73U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC73U, }, - { 0xD83DDC6EU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC6EU, }, - { 0xD83DDC77U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC77U, }, - { 0xD83DDC82U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDC82U, }, - { 0xD83DDD75U, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDD75U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2695U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF3EU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF73U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDF93U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFA4U, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFEBU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFEDU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDCBBU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDCBCU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDD27U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDD2CU, }, - { 0xD83DDC69U, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC68U, 0x200DU, 0xD83CDFA8U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDE92U, }, - { 0xD83DDC69U, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2708U, 0xFE0FU, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDE80U, }, - { 0xD83DDC69U, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83DDC68U, 0x200DU, 0x2696U, 0xFE0FU, }, - { 0xD83EDD36U, }, - { 0xD83CDF85U, }, - { 0xD83DDC78U, }, - { 0xD83EDD34U, }, - { 0xD83DDC70U, }, - { 0xD83EDD35U, }, - { 0xD83DDC7CU, }, - { 0xD83EDD30U, }, - { 0xD83DDE47U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDE47U, }, - { 0xD83DDC81U, }, - { 0xD83DDC81U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE45U, }, - { 0xD83DDE45U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE46U, }, - { 0xD83DDE46U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4BU, }, - { 0xD83DDE4BU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD26U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD26U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD37U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD37U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4EU, }, - { 0xD83DDE4EU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDE4DU, }, - { 0xD83DDE4DU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC87U, }, - { 0xD83DDC87U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDC86U, }, - { 0xD83DDC86U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDD74U, }, - { 0xD83DDC83U, }, - { 0xD83DDD7AU, }, - { 0xD83DDC6FU, }, - { 0xD83DDC6FU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEB6U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB6U, }, - { 0xD83CDFC3U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC3U, }, - { 0xD83DDC6BU, }, - { 0xD83DDC6DU, }, - { 0xD83DDC6CU, }, - { 0xD83DDC91U, }, - { 0xD83DDC69U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC69U, }, - { 0xD83DDC68U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC68U, }, - { 0xD83DDC8FU, }, - { 0xD83DDC69U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC8BU, 0x200DU, 0xD83DDC69U, }, - { 0xD83DDC68U, 0x200DU, 0x2764U, 0xFE0FU, 0x200DU, 0xD83DDC8BU, 0x200DU, 0xD83DDC68U, }, - { 0xD83DDC6AU, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC69U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC66U, 0x200DU, 0xD83DDC66U, }, - { 0xD83DDC68U, 0x200DU, 0xD83DDC67U, 0x200DU, 0xD83DDC67U, }, - { 0xD83DDC5AU, }, - { 0xD83DDC55U, }, - { 0xD83DDC56U, }, - { 0xD83DDC54U, }, - { 0xD83DDC57U, }, - { 0xD83DDC59U, }, - { 0xD83DDC58U, }, - { 0xD83DDC60U, }, - { 0xD83DDC61U, }, - { 0xD83DDC62U, }, - { 0xD83DDC5EU, }, - { 0xD83DDC5FU, }, - { 0xD83DDC52U, }, - { 0xD83CDFA9U, }, - { 0xD83CDF93U, }, - { 0xD83DDC51U, }, - { 0x26D1U, }, - { 0xD83CDF92U, }, - { 0xD83DDC5DU, }, - { 0xD83DDC5BU, }, - { 0xD83DDC5CU, }, - { 0xD83DDCBCU, }, - { 0xD83DDC53U, }, - { 0xD83DDD76U, }, - { 0xD83CDF02U, }, - { 0x2602U, 0xFE0FU, }, -}; - -InputCategory Category2 = { - { 0xD83DDC36U, }, - { 0xD83DDC31U, }, - { 0xD83DDC2DU, }, - { 0xD83DDC39U, }, - { 0xD83DDC30U, }, - { 0xD83EDD8AU, }, - { 0xD83DDC3BU, }, - { 0xD83DDC3CU, }, - { 0xD83DDC28U, }, - { 0xD83DDC2FU, }, - { 0xD83EDD81U, }, - { 0xD83DDC2EU, }, - { 0xD83DDC37U, }, - { 0xD83DDC3DU, }, - { 0xD83DDC38U, }, - { 0xD83DDC35U, }, - { 0xD83DDE48U, }, - { 0xD83DDE49U, }, - { 0xD83DDE4AU, }, - { 0xD83DDC12U, }, - { 0xD83DDC14U, }, - { 0xD83DDC27U, }, - { 0xD83DDC26U, }, - { 0xD83DDC24U, }, - { 0xD83DDC23U, }, - { 0xD83DDC25U, }, - { 0xD83EDD86U, }, - { 0xD83EDD85U, }, - { 0xD83EDD89U, }, - { 0xD83EDD87U, }, - { 0xD83DDC3AU, }, - { 0xD83DDC17U, }, - { 0xD83DDC34U, }, - { 0xD83EDD84U, }, - { 0xD83DDC1DU, }, - { 0xD83DDC1BU, }, - { 0xD83EDD8BU, }, - { 0xD83DDC0CU, }, - { 0xD83DDC1AU, }, - { 0xD83DDC1EU, }, - { 0xD83DDC1CU, }, - { 0xD83DDD77U, }, - { 0xD83DDD78U, }, - { 0xD83DDC22U, }, - { 0xD83DDC0DU, }, - { 0xD83EDD8EU, }, - { 0xD83EDD82U, }, - { 0xD83EDD80U, }, - { 0xD83EDD91U, }, - { 0xD83DDC19U, }, - { 0xD83EDD90U, }, - { 0xD83DDC20U, }, - { 0xD83DDC1FU, }, - { 0xD83DDC21U, }, - { 0xD83DDC2CU, }, - { 0xD83EDD88U, }, - { 0xD83DDC33U, }, - { 0xD83DDC0BU, }, - { 0xD83DDC0AU, }, - { 0xD83DDC06U, }, - { 0xD83DDC05U, }, - { 0xD83DDC03U, }, - { 0xD83DDC02U, }, - { 0xD83DDC04U, }, - { 0xD83EDD8CU, }, - { 0xD83DDC2AU, }, - { 0xD83DDC2BU, }, - { 0xD83DDC18U, }, - { 0xD83EDD8FU, }, - { 0xD83EDD8DU, }, - { 0xD83DDC0EU, }, - { 0xD83DDC16U, }, - { 0xD83DDC10U, }, - { 0xD83DDC0FU, }, - { 0xD83DDC11U, }, - { 0xD83DDC15U, }, - { 0xD83DDC29U, }, - { 0xD83DDC08U, }, - { 0xD83DDC13U, }, - { 0xD83EDD83U, }, - { 0xD83DDD4AU, }, - { 0xD83DDC07U, }, - { 0xD83DDC01U, }, - { 0xD83DDC00U, }, - { 0xD83DDC3FU, }, - { 0xD83DDC3EU, }, - { 0xD83DDC09U, }, - { 0xD83DDC32U, }, - { 0xD83CDF35U, }, - { 0xD83CDF84U, }, - { 0xD83CDF32U, }, - { 0xD83CDF33U, }, - { 0xD83CDF34U, }, - { 0xD83CDF31U, }, - { 0xD83CDF3FU, }, - { 0x2618U, 0xFE0FU, }, - { 0xD83CDF40U, }, - { 0xD83CDF8DU, }, - { 0xD83CDF8BU, }, - { 0xD83CDF43U, }, - { 0xD83CDF42U, }, - { 0xD83CDF41U, }, - { 0xD83CDF44U, }, - { 0xD83CDF3EU, }, - { 0xD83DDC90U, }, - { 0xD83CDF37U, }, - { 0xD83CDF39U, }, - { 0xD83EDD40U, }, - { 0xD83CDF3BU, }, - { 0xD83CDF3CU, }, - { 0xD83CDF38U, }, - { 0xD83CDF3AU, }, - { 0xD83CDF0EU, }, - { 0xD83CDF0DU, }, - { 0xD83CDF0FU, }, - { 0xD83CDF15U, }, - { 0xD83CDF16U, }, - { 0xD83CDF17U, }, - { 0xD83CDF18U, }, - { 0xD83CDF11U, }, - { 0xD83CDF12U, }, - { 0xD83CDF13U, }, - { 0xD83CDF14U, }, - { 0xD83CDF1AU, }, - { 0xD83CDF1DU, }, - { 0xD83CDF1EU, }, - { 0xD83CDF1BU, }, - { 0xD83CDF1CU, }, - { 0xD83CDF19U, }, - { 0xD83DDCABU, }, - { 0x2B50U, 0xFE0FU, }, - { 0xD83CDF1FU, }, - { 0x2728U, }, - { 0x26A1U, 0xFE0FU, }, - { 0xD83DDD25U, }, - { 0xD83DDCA5U, }, - { 0x2604U, 0xFE0FU, }, - { 0x2600U, 0xFE0FU, }, - { 0xD83CDF24U, }, - { 0x26C5U, 0xFE0FU, }, - { 0xD83CDF25U, }, - { 0xD83CDF26U, }, - { 0xD83CDF08U, }, - { 0x2601U, 0xFE0FU, }, - { 0xD83CDF27U, }, - { 0x26C8U, }, - { 0xD83CDF29U, }, - { 0xD83CDF28U, }, - { 0x2603U, 0xFE0FU, }, - { 0x26C4U, 0xFE0FU, }, - { 0x2744U, 0xFE0FU, }, - { 0xD83CDF2CU, }, - { 0xD83DDCA8U, }, - { 0xD83CDF2AU, }, - { 0xD83CDF2BU, }, - { 0xD83CDF0AU, }, - { 0xD83DDCA7U, }, - { 0xD83DDCA6U, }, - { 0x2614U, 0xFE0FU, }, -}; - -InputCategory Category3 = { - { 0xD83CDF4FU, }, - { 0xD83CDF4EU, }, - { 0xD83CDF50U, }, - { 0xD83CDF4AU, }, - { 0xD83CDF4BU, }, - { 0xD83CDF4CU, }, - { 0xD83CDF49U, }, - { 0xD83CDF47U, }, - { 0xD83CDF53U, }, - { 0xD83CDF48U, }, - { 0xD83CDF52U, }, - { 0xD83CDF51U, }, - { 0xD83CDF4DU, }, - { 0xD83EDD5DU, }, - { 0xD83EDD51U, }, - { 0xD83CDF45U, }, - { 0xD83CDF46U, }, - { 0xD83EDD52U, }, - { 0xD83EDD55U, }, - { 0xD83CDF3DU, }, - { 0xD83CDF36U, }, - { 0xD83EDD54U, }, - { 0xD83CDF60U, }, - { 0xD83CDF30U, }, - { 0xD83EDD5CU, }, - { 0xD83CDF6FU, }, - { 0xD83EDD50U, }, - { 0xD83CDF5EU, }, - { 0xD83EDD56U, }, - { 0xD83EDDC0U, }, - { 0xD83EDD5AU, }, - { 0xD83CDF73U, }, - { 0xD83EDD53U, }, - { 0xD83EDD5EU, }, - { 0xD83CDF64U, }, - { 0xD83CDF57U, }, - { 0xD83CDF56U, }, - { 0xD83CDF55U, }, - { 0xD83CDF2DU, }, - { 0xD83CDF54U, }, - { 0xD83CDF5FU, }, - { 0xD83EDD59U, }, - { 0xD83CDF2EU, }, - { 0xD83CDF2FU, }, - { 0xD83EDD57U, }, - { 0xD83EDD58U, }, - { 0xD83CDF5DU, }, - { 0xD83CDF5CU, }, - { 0xD83CDF72U, }, - { 0xD83CDF65U, }, - { 0xD83CDF63U, }, - { 0xD83CDF71U, }, - { 0xD83CDF5BU, }, - { 0xD83CDF59U, }, - { 0xD83CDF5AU, }, - { 0xD83CDF58U, }, - { 0xD83CDF62U, }, - { 0xD83CDF61U, }, - { 0xD83CDF67U, }, - { 0xD83CDF68U, }, - { 0xD83CDF66U, }, - { 0xD83CDF70U, }, - { 0xD83CDF82U, }, - { 0xD83CDF6EU, }, - { 0xD83CDF6DU, }, - { 0xD83CDF6CU, }, - { 0xD83CDF6BU, }, - { 0xD83CDF7FU, }, - { 0xD83CDF69U, }, - { 0xD83CDF6AU, }, - { 0xD83EDD5BU, }, - { 0xD83CDF7CU, }, - { 0x2615U, 0xFE0FU, }, - { 0xD83CDF75U, }, - { 0xD83CDF76U, }, - { 0xD83CDF7AU, }, - { 0xD83CDF7BU, }, - { 0xD83EDD42U, }, - { 0xD83CDF77U, }, - { 0xD83EDD43U, }, - { 0xD83CDF78U, }, - { 0xD83CDF79U, }, - { 0xD83CDF7EU, }, - { 0xD83EDD44U, }, - { 0xD83CDF74U, }, - { 0xD83CDF7DU, }, -}; - -InputCategory Category4 = { - { 0x26BDU, 0xFE0FU, }, - { 0xD83CDFC0U, }, - { 0xD83CDFC8U, }, - { 0x26BEU, 0xFE0FU, }, - { 0xD83CDFBEU, }, - { 0xD83CDFD0U, }, - { 0xD83CDFC9U, }, - { 0xD83CDFB1U, }, - { 0xD83CDFD3U, }, - { 0xD83CDFF8U, }, - { 0xD83EDD45U, }, - { 0xD83CDFD2U, }, - { 0xD83CDFD1U, }, - { 0xD83CDFCFU, }, - { 0x26F3U, 0xFE0FU, }, - { 0xD83CDFF9U, }, - { 0xD83CDFA3U, }, - { 0xD83EDD4AU, }, - { 0xD83EDD4BU, }, - { 0x26F8U, }, - { 0xD83CDFBFU, }, - { 0x26F7U, }, - { 0xD83CDFC2U, }, - { 0xD83CDFCBU, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCBU, 0xFE0FU, }, - { 0xD83EDD3AU, }, - { 0xD83EDD3CU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3CU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83EDD38U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD38U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0x26F9U, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0x26F9U, 0xFE0FU, }, - { 0xD83EDD3EU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3EU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xFE0FU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCCU, 0xFE0FU, }, - { 0xD83CDFC4U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFC4U, }, - { 0xD83CDFCAU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83CDFCAU, }, - { 0xD83EDD3DU, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD3DU, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83DDEA3U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEA3U, }, - { 0xD83CDFC7U, }, - { 0xD83DDEB4U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB4U, }, - { 0xD83DDEB5U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83DDEB5U, }, - { 0xD83CDFBDU, }, - { 0xD83CDFC5U, }, - { 0xD83CDF96U, }, - { 0xD83EDD47U, }, - { 0xD83EDD48U, }, - { 0xD83EDD49U, }, - { 0xD83CDFC6U, }, - { 0xD83CDFF5U, }, - { 0xD83CDF97U, }, - { 0xD83CDFABU, }, - { 0xD83CDF9FU, }, - { 0xD83CDFAAU, }, - { 0xD83EDD39U, 0x200DU, 0x2640U, 0xFE0FU, }, - { 0xD83EDD39U, 0x200DU, 0x2642U, 0xFE0FU, }, - { 0xD83CDFADU, }, - { 0xD83CDFA8U, }, - { 0xD83CDFACU, }, - { 0xD83CDFA4U, }, - { 0xD83CDFA7U, }, - { 0xD83CDFBCU, }, - { 0xD83CDFB9U, }, - { 0xD83EDD41U, }, - { 0xD83CDFB7U, }, - { 0xD83CDFBAU, }, - { 0xD83CDFB8U, }, - { 0xD83CDFBBU, }, - { 0xD83CDFB2U, }, - { 0xD83CDFAFU, }, - { 0xD83CDFB3U, }, - { 0xD83CDFAEU, }, - { 0xD83CDFB0U, }, -}; - -InputCategory Category5 = { - { 0xD83DDE97U, }, - { 0xD83DDE95U, }, - { 0xD83DDE99U, }, - { 0xD83DDE8CU, }, - { 0xD83DDE8EU, }, - { 0xD83CDFCEU, }, - { 0xD83DDE93U, }, - { 0xD83DDE91U, }, - { 0xD83DDE92U, }, - { 0xD83DDE90U, }, - { 0xD83DDE9AU, }, - { 0xD83DDE9BU, }, - { 0xD83DDE9CU, }, - { 0xD83DDEF4U, }, - { 0xD83DDEB2U, }, - { 0xD83DDEF5U, }, - { 0xD83CDFCDU, }, - { 0xD83DDEA8U, }, - { 0xD83DDE94U, }, - { 0xD83DDE8DU, }, - { 0xD83DDE98U, }, - { 0xD83DDE96U, }, - { 0xD83DDEA1U, }, - { 0xD83DDEA0U, }, - { 0xD83DDE9FU, }, - { 0xD83DDE83U, }, - { 0xD83DDE8BU, }, - { 0xD83DDE9EU, }, - { 0xD83DDE9DU, }, - { 0xD83DDE84U, }, - { 0xD83DDE85U, }, - { 0xD83DDE88U, }, - { 0xD83DDE82U, }, - { 0xD83DDE86U, }, - { 0xD83DDE87U, }, - { 0xD83DDE8AU, }, - { 0xD83DDE89U, }, - { 0xD83DDE81U, }, - { 0xD83DDEE9U, }, - { 0x2708U, 0xFE0FU, }, - { 0xD83DDEEBU, }, - { 0xD83DDEECU, }, - { 0xD83DDE80U, }, - { 0xD83DDEF0U, }, - { 0xD83DDCBAU, }, - { 0xD83DDEF6U, }, - { 0x26F5U, 0xFE0FU, }, - { 0xD83DDEE5U, }, - { 0xD83DDEA4U, }, - { 0xD83DDEF3U, }, - { 0x26F4U, }, - { 0xD83DDEA2U, }, - { 0x2693U, 0xFE0FU, }, - { 0xD83DDEA7U, }, - { 0x26FDU, 0xFE0FU, }, - { 0xD83DDE8FU, }, - { 0xD83DDEA6U, }, - { 0xD83DDEA5U, }, - { 0xD83DDDFAU, }, - { 0xD83DDDFFU, }, - { 0xD83DDDFDU, }, - { 0x26F2U, 0xFE0FU, }, - { 0xD83DDDFCU, }, - { 0xD83CDFF0U, }, - { 0xD83CDFEFU, }, - { 0xD83CDFDFU, }, - { 0xD83CDFA1U, }, - { 0xD83CDFA2U, }, - { 0xD83CDFA0U, }, - { 0x26F1U, }, - { 0xD83CDFD6U, }, - { 0xD83CDFDDU, }, - { 0x26F0U, }, - { 0xD83CDFD4U, }, - { 0xD83DDDFBU, }, - { 0xD83CDF0BU, }, - { 0xD83CDFDCU, }, - { 0xD83CDFD5U, }, - { 0x26FAU, 0xFE0FU, }, - { 0xD83DDEE4U, }, - { 0xD83DDEE3U, }, - { 0xD83CDFD7U, }, - { 0xD83CDFEDU, }, - { 0xD83CDFE0U, }, - { 0xD83CDFE1U, }, - { 0xD83CDFD8U, }, - { 0xD83CDFDAU, }, - { 0xD83CDFE2U, }, - { 0xD83CDFECU, }, - { 0xD83CDFE3U, }, - { 0xD83CDFE4U, }, - { 0xD83CDFE5U, }, - { 0xD83CDFE6U, }, - { 0xD83CDFE8U, }, - { 0xD83CDFEAU, }, - { 0xD83CDFEBU, }, - { 0xD83CDFE9U, }, - { 0xD83DDC92U, }, - { 0xD83CDFDBU, }, - { 0x26EAU, 0xFE0FU, }, - { 0xD83DDD4CU, }, - { 0xD83DDD4DU, }, - { 0xD83DDD4BU, }, - { 0x26E9U, }, - { 0xD83DDDFEU, }, - { 0xD83CDF91U, }, - { 0xD83CDFDEU, }, - { 0xD83CDF05U, }, - { 0xD83CDF04U, }, - { 0xD83CDF20U, }, - { 0xD83CDF87U, }, - { 0xD83CDF86U, }, - { 0xD83CDF07U, }, - { 0xD83CDF06U, }, - { 0xD83CDFD9U, }, - { 0xD83CDF03U, }, - { 0xD83CDF0CU, }, - { 0xD83CDF09U, }, - { 0xD83CDF01U, }, -}; - -InputCategory Category6 = { - { 0x231AU, 0xFE0FU, }, - { 0xD83DDCF1U, }, - { 0xD83DDCF2U, }, - { 0xD83DDCBBU, }, - { 0x2328U, 0xFE0FU, }, - { 0xD83DDDA5U, }, - { 0xD83DDDA8U, }, - { 0xD83DDDB1U, }, - { 0xD83DDDB2U, }, - { 0xD83DDD79U, }, - { 0xD83DDDDCU, }, - { 0xD83DDCBDU, }, - { 0xD83DDCBEU, }, - { 0xD83DDCBFU, }, - { 0xD83DDCC0U, }, - { 0xD83DDCFCU, }, - { 0xD83DDCF7U, }, - { 0xD83DDCF8U, }, - { 0xD83DDCF9U, }, - { 0xD83CDFA5U, }, - { 0xD83DDCFDU, }, - { 0xD83CDF9EU, }, - { 0xD83DDCDEU, }, - { 0x260EU, 0xFE0FU, }, - { 0xD83DDCDFU, }, - { 0xD83DDCE0U, }, - { 0xD83DDCFAU, }, - { 0xD83DDCFBU, }, - { 0xD83CDF99U, }, - { 0xD83CDF9AU, }, - { 0xD83CDF9BU, }, - { 0x23F1U, }, - { 0x23F2U, }, - { 0x23F0U, }, - { 0xD83DDD70U, }, - { 0x231BU, 0xFE0FU, }, - { 0x23F3U, }, - { 0xD83DDCE1U, }, - { 0xD83DDD0BU, }, - { 0xD83DDD0CU, }, - { 0xD83DDCA1U, }, - { 0xD83DDD26U, }, - { 0xD83DDD6FU, }, - { 0xD83DDDD1U, }, - { 0xD83DDEE2U, }, - { 0xD83DDCB8U, }, - { 0xD83DDCB5U, }, - { 0xD83DDCB4U, }, - { 0xD83DDCB6U, }, - { 0xD83DDCB7U, }, - { 0xD83DDCB0U, }, - { 0xD83DDCB3U, }, - { 0xD83DDC8EU, }, - { 0x2696U, 0xFE0FU, }, - { 0xD83DDD27U, }, - { 0xD83DDD28U, }, - { 0x2692U, }, - { 0xD83DDEE0U, }, - { 0x26CFU, }, - { 0xD83DDD29U, }, - { 0x2699U, 0xFE0FU, }, - { 0x26D3U, }, - { 0xD83DDD2BU, }, - { 0xD83DDCA3U, }, - { 0xD83DDD2AU, }, - { 0xD83DDDE1U, }, - { 0x2694U, 0xFE0FU, }, - { 0xD83DDEE1U, }, - { 0xD83DDEACU, }, - { 0x26B0U, 0xFE0FU, }, - { 0x26B1U, 0xFE0FU, }, - { 0xD83CDFFAU, }, - { 0xD83DDD2EU, }, - { 0xD83DDCFFU, }, - { 0xD83DDC88U, }, - { 0x2697U, 0xFE0FU, }, - { 0xD83DDD2DU, }, - { 0xD83DDD2CU, }, - { 0xD83DDD73U, }, - { 0xD83DDC8AU, }, - { 0xD83DDC89U, }, - { 0xD83CDF21U, }, - { 0xD83DDEBDU, }, - { 0xD83DDEB0U, }, - { 0xD83DDEBFU, }, - { 0xD83DDEC1U, }, - { 0xD83DDEC0U, }, - { 0xD83DDECEU, }, - { 0xD83DDD11U, }, - { 0xD83DDDDDU, }, - { 0xD83DDEAAU, }, - { 0xD83DDECBU, }, - { 0xD83DDECFU, }, - { 0xD83DDECCU, }, - { 0xD83DDDBCU, }, - { 0xD83DDECDU, }, - { 0xD83DDED2U, }, - { 0xD83CDF81U, }, - { 0xD83CDF88U, }, - { 0xD83CDF8FU, }, - { 0xD83CDF80U, }, - { 0xD83CDF8AU, }, - { 0xD83CDF89U, }, - { 0xD83CDF8EU, }, - { 0xD83CDFEEU, }, - { 0xD83CDF90U, }, - { 0x2709U, 0xFE0FU, }, - { 0xD83DDCE9U, }, - { 0xD83DDCE8U, }, - { 0xD83DDCE7U, }, - { 0xD83DDC8CU, }, - { 0xD83DDCE5U, }, - { 0xD83DDCE4U, }, - { 0xD83DDCE6U, }, - { 0xD83CDFF7U, }, - { 0xD83DDCEAU, }, - { 0xD83DDCEBU, }, - { 0xD83DDCECU, }, - { 0xD83DDCEDU, }, - { 0xD83DDCEEU, }, - { 0xD83DDCEFU, }, - { 0xD83DDCDCU, }, - { 0xD83DDCC3U, }, - { 0xD83DDCC4U, }, - { 0xD83DDCD1U, }, - { 0xD83DDCCAU, }, - { 0xD83DDCC8U, }, - { 0xD83DDCC9U, }, - { 0xD83DDDD2U, }, - { 0xD83DDDD3U, }, - { 0xD83DDCC6U, }, - { 0xD83DDCC5U, }, - { 0xD83DDCC7U, }, - { 0xD83DDDC3U, }, - { 0xD83DDDF3U, }, - { 0xD83DDDC4U, }, - { 0xD83DDCCBU, }, - { 0xD83DDCC1U, }, - { 0xD83DDCC2U, }, - { 0xD83DDDC2U, }, - { 0xD83DDDDEU, }, - { 0xD83DDCF0U, }, - { 0xD83DDCD3U, }, - { 0xD83DDCD4U, }, - { 0xD83DDCD2U, }, - { 0xD83DDCD5U, }, - { 0xD83DDCD7U, }, - { 0xD83DDCD8U, }, - { 0xD83DDCD9U, }, - { 0xD83DDCDAU, }, - { 0xD83DDCD6U, }, - { 0xD83DDD16U, }, - { 0xD83DDD17U, }, - { 0xD83DDCCEU, }, - { 0xD83DDD87U, }, - { 0xD83DDCD0U, }, - { 0xD83DDCCFU, }, - { 0xD83DDCCCU, }, - { 0xD83DDCCDU, }, - { 0x2702U, 0xFE0FU, }, - { 0xD83DDD8AU, }, - { 0xD83DDD8BU, }, - { 0x2712U, 0xFE0FU, }, - { 0xD83DDD8CU, }, - { 0xD83DDD8DU, }, - { 0xD83DDCDDU, }, - { 0x270FU, 0xFE0FU, }, - { 0xD83DDD0DU, }, - { 0xD83DDD0EU, }, - { 0xD83DDD0FU, }, - { 0xD83DDD10U, }, - { 0xD83DDD12U, }, - { 0xD83DDD13U, }, -}; - -InputCategory Category7 = { - { 0x2764U, 0xFE0FU, }, - { 0xD83DDC9BU, }, - { 0xD83DDC9AU, }, - { 0xD83DDC99U, }, - { 0xD83DDC9CU, }, - { 0xD83DDDA4U, }, - { 0xD83DDC94U, }, - { 0x2763U, 0xFE0FU, }, - { 0xD83DDC95U, }, - { 0xD83DDC9EU, }, - { 0xD83DDC93U, }, - { 0xD83DDC97U, }, - { 0xD83DDC96U, }, - { 0xD83DDC98U, }, - { 0xD83DDC9DU, }, - { 0xD83DDC9FU, }, - { 0x262EU, 0xFE0FU, }, - { 0x271DU, 0xFE0FU, }, - { 0x262AU, 0xFE0FU, }, - { 0xD83DDD49U, }, - { 0x2638U, 0xFE0FU, }, - { 0x2721U, 0xFE0FU, }, - { 0xD83DDD2FU, }, - { 0xD83DDD4EU, }, - { 0x262FU, 0xFE0FU, }, - { 0x2626U, 0xFE0FU, }, - { 0xD83DDED0U, }, - { 0x26CEU, }, - { 0x2648U, 0xFE0FU, }, - { 0x2649U, 0xFE0FU, }, - { 0x264AU, 0xFE0FU, }, - { 0x264BU, 0xFE0FU, }, - { 0x264CU, 0xFE0FU, }, - { 0x264DU, 0xFE0FU, }, - { 0x264EU, 0xFE0FU, }, - { 0x264FU, 0xFE0FU, }, - { 0x2650U, 0xFE0FU, }, - { 0x2651U, 0xFE0FU, }, - { 0x2652U, 0xFE0FU, }, - { 0x2653U, 0xFE0FU, }, - { 0xD83CDD94U, }, - { 0x269BU, 0xFE0FU, }, - { 0xD83CDE51U, }, - { 0x2622U, 0xFE0FU, }, - { 0x2623U, 0xFE0FU, }, - { 0xD83DDCF4U, }, - { 0xD83DDCF3U, }, - { 0xD83CDE36U, }, - { 0xD83CDE1AU, 0xFE0FU, }, - { 0xD83CDE38U, }, - { 0xD83CDE3AU, }, - { 0xD83CDE37U, }, - { 0x2734U, 0xFE0FU, }, - { 0xD83CDD9AU, }, - { 0xD83DDCAEU, }, - { 0xD83CDE50U, }, - { 0x3299U, 0xFE0FU, }, - { 0x3297U, 0xFE0FU, }, - { 0xD83CDE34U, }, - { 0xD83CDE35U, }, - { 0xD83CDE39U, }, - { 0xD83CDE32U, }, - { 0xD83CDD70U, 0xFE0FU, }, - { 0xD83CDD71U, 0xFE0FU, }, - { 0xD83CDD8EU, }, - { 0xD83CDD91U, }, - { 0xD83CDD7EU, 0xFE0FU, }, - { 0xD83CDD98U, }, - { 0x274CU, }, - { 0x2B55U, 0xFE0FU, }, - { 0xD83DDED1U, }, - { 0x26D4U, 0xFE0FU, }, - { 0xD83DDCDBU, }, - { 0xD83DDEABU, }, - { 0xD83DDCAFU, }, - { 0xD83DDCA2U, }, - { 0x2668U, 0xFE0FU, }, - { 0xD83DDEB7U, }, - { 0xD83DDEAFU, }, - { 0xD83DDEB3U, }, - { 0xD83DDEB1U, }, - { 0xD83DDD1EU, }, - { 0xD83DDCF5U, }, - { 0xD83DDEADU, }, - { 0x2757U, 0xFE0FU, }, - { 0x2755U, }, - { 0x2753U, }, - { 0x2754U, }, - { 0x203CU, 0xFE0FU, }, - { 0x2049U, 0xFE0FU, }, - { 0xD83DDD05U, }, - { 0xD83DDD06U, }, - { 0x303DU, 0xFE0FU, }, - { 0x26A0U, 0xFE0FU, }, - { 0xD83DDEB8U, }, - { 0xD83DDD31U, }, - { 0x269CU, 0xFE0FU, }, - { 0xD83DDD30U, }, - { 0x267BU, 0xFE0FU, }, - { 0x2705U, }, - { 0xD83CDE2FU, 0xFE0FU, }, - { 0xD83DDCB9U, }, - { 0x2747U, 0xFE0FU, }, - { 0x2733U, 0xFE0FU, }, - { 0x274EU, }, - { 0xD83CDF10U, }, - { 0xD83DDCA0U, }, - { 0x24C2U, 0xFE0FU, }, - { 0xD83CDF00U, }, - { 0xD83DDCA4U, }, - { 0xD83CDFE7U, }, - { 0xD83DDEBEU, }, - { 0x267FU, 0xFE0FU, }, - { 0xD83CDD7FU, 0xFE0FU, }, - { 0xD83CDE33U, }, - { 0xD83CDE02U, }, - { 0xD83DDEC2U, }, - { 0xD83DDEC3U, }, - { 0xD83DDEC4U, }, - { 0xD83DDEC5U, }, - { 0xD83DDEB9U, }, - { 0xD83DDEBAU, }, - { 0xD83DDEBCU, }, - { 0xD83DDEBBU, }, - { 0xD83DDEAEU, }, - { 0xD83CDFA6U, }, - { 0xD83DDCF6U, }, - { 0xD83CDE01U, }, - { 0xD83DDD23U, }, - { 0x2139U, 0xFE0FU, }, - { 0xD83DDD24U, }, - { 0xD83DDD21U, }, - { 0xD83DDD20U, }, - { 0xD83CDD96U, }, - { 0xD83CDD97U, }, - { 0xD83CDD99U, }, - { 0xD83CDD92U, }, - { 0xD83CDD95U, }, - { 0xD83CDD93U, }, - { 0x30U, 0xFE0FU, 0x20E3U, }, - { 0x31U, 0xFE0FU, 0x20E3U, }, - { 0x32U, 0xFE0FU, 0x20E3U, }, - { 0x33U, 0xFE0FU, 0x20E3U, }, - { 0x34U, 0xFE0FU, 0x20E3U, }, - { 0x35U, 0xFE0FU, 0x20E3U, }, - { 0x36U, 0xFE0FU, 0x20E3U, }, - { 0x37U, 0xFE0FU, 0x20E3U, }, - { 0x38U, 0xFE0FU, 0x20E3U, }, - { 0x39U, 0xFE0FU, 0x20E3U, }, - { 0xD83DDD1FU, }, - { 0xD83DDD22U, }, - { 0x23U, 0xFE0FU, 0x20E3U, }, - { 0x2AU, 0xFE0FU, 0x20E3U, }, - { 0x25B6U, 0xFE0FU, }, - { 0x23F8U, }, - { 0x23EFU, }, - { 0x23F9U, }, - { 0x23FAU, }, - { 0x23EDU, }, - { 0x23EEU, }, - { 0x23E9U, }, - { 0x23EAU, }, - { 0x23EBU, }, - { 0x23ECU, }, - { 0x25C0U, 0xFE0FU, }, - { 0xD83DDD3CU, }, - { 0xD83DDD3DU, }, - { 0x27A1U, 0xFE0FU, }, - { 0x2B05U, 0xFE0FU, }, - { 0x2B06U, 0xFE0FU, }, - { 0x2B07U, 0xFE0FU, }, - { 0x2197U, 0xFE0FU, }, - { 0x2198U, 0xFE0FU, }, - { 0x2199U, 0xFE0FU, }, - { 0x2196U, 0xFE0FU, }, - { 0x2195U, 0xFE0FU, }, - { 0x2194U, 0xFE0FU, }, - { 0x21AAU, 0xFE0FU, }, - { 0x21A9U, 0xFE0FU, }, - { 0x2934U, 0xFE0FU, }, - { 0x2935U, 0xFE0FU, }, - { 0xD83DDD00U, }, - { 0xD83DDD01U, }, - { 0xD83DDD02U, }, - { 0xD83DDD04U, }, - { 0xD83DDD03U, }, - { 0xD83CDFB5U, }, - { 0xD83CDFB6U, }, - { 0x2795U, }, - { 0x2796U, }, - { 0x2797U, }, - { 0x2716U, 0xFE0FU, }, - { 0xD83DDCB2U, }, - { 0xD83DDCB1U, }, - { 0x2122U, }, - { 0xA9U, }, - { 0xAEU, }, - { 0x3030U, }, - { 0x27B0U, }, - { 0x27BFU, }, - { 0xD83DDD1AU, }, - { 0xD83DDD19U, }, - { 0xD83DDD1BU, }, - { 0xD83DDD1DU, }, - { 0xD83DDD1CU, }, - { 0x2714U, 0xFE0FU, }, - { 0x2611U, 0xFE0FU, }, - { 0xD83DDD18U, }, - { 0x26AAU, 0xFE0FU, }, - { 0x26ABU, 0xFE0FU, }, - { 0xD83DDD34U, }, - { 0xD83DDD35U, }, - { 0xD83DDD3AU, }, - { 0xD83DDD3BU, }, - { 0xD83DDD38U, }, - { 0xD83DDD39U, }, - { 0xD83DDD36U, }, - { 0xD83DDD37U, }, - { 0xD83DDD33U, }, - { 0xD83DDD32U, }, - { 0x25AAU, 0xFE0FU, }, - { 0x25ABU, 0xFE0FU, }, - { 0x25FEU, 0xFE0FU, }, - { 0x25FDU, 0xFE0FU, }, - { 0x25FCU, 0xFE0FU, }, - { 0x25FBU, 0xFE0FU, }, - { 0x2B1BU, 0xFE0FU, }, - { 0x2B1CU, 0xFE0FU, }, - { 0xD83DDD08U, }, - { 0xD83DDD07U, }, - { 0xD83DDD09U, }, - { 0xD83DDD0AU, }, - { 0xD83DDD14U, }, - { 0xD83DDD15U, }, - { 0xD83DDCE3U, }, - { 0xD83DDCE2U, }, - { 0xD83DDC41U, 0x200DU, 0xD83DDDE8U, }, - { 0xD83DDCACU, }, - { 0xD83DDCADU, }, - { 0xD83DDDEFU, }, - { 0x2660U, 0xFE0FU, }, - { 0x2663U, 0xFE0FU, }, - { 0x2665U, 0xFE0FU, }, - { 0x2666U, 0xFE0FU, }, - { 0xD83CDCCFU, }, - { 0xD83CDFB4U, }, - { 0xD83CDC04U, 0xFE0FU, }, - { 0xD83DDD50U, }, - { 0xD83DDD51U, }, - { 0xD83DDD52U, }, - { 0xD83DDD53U, }, - { 0xD83DDD54U, }, - { 0xD83DDD55U, }, - { 0xD83DDD56U, }, - { 0xD83DDD57U, }, - { 0xD83DDD58U, }, - { 0xD83DDD59U, }, - { 0xD83DDD5AU, }, - { 0xD83DDD5BU, }, - { 0xD83DDD5CU, }, - { 0xD83DDD5DU, }, - { 0xD83DDD5EU, }, - { 0xD83DDD5FU, }, - { 0xD83DDD60U, }, - { 0xD83DDD61U, }, - { 0xD83DDD62U, }, - { 0xD83DDD63U, }, - { 0xD83DDD64U, }, - { 0xD83DDD65U, }, - { 0xD83DDD66U, }, - { 0xD83DDD67U, }, - -//}; -// -//InputCategory Category8 = { - - { 0xD83CDFF3U, }, - { 0xD83CDFF4U, }, - { 0xD83CDFC1U, }, - { 0xD83DDEA9U, }, - { 0xD83CDFF3U, 0xFE0FU, 0x200DU, 0xD83CDF08U, }, - { 0xD83CDDE6U, 0xD83CDDEBU, }, - { 0xD83CDDE6U, 0xD83CDDFDU, }, - { 0xD83CDDE6U, 0xD83CDDF1U, }, - { 0xD83CDDE9U, 0xD83CDDFFU, }, - { 0xD83CDDE6U, 0xD83CDDF8U, }, - { 0xD83CDDE6U, 0xD83CDDE9U, }, - { 0xD83CDDE6U, 0xD83CDDF4U, }, - { 0xD83CDDE6U, 0xD83CDDEEU, }, - { 0xD83CDDE6U, 0xD83CDDF6U, }, - { 0xD83CDDE6U, 0xD83CDDECU, }, - { 0xD83CDDE6U, 0xD83CDDF7U, }, - { 0xD83CDDE6U, 0xD83CDDF2U, }, - { 0xD83CDDE6U, 0xD83CDDFCU, }, - { 0xD83CDDE6U, 0xD83CDDFAU, }, - { 0xD83CDDE6U, 0xD83CDDF9U, }, - { 0xD83CDDE6U, 0xD83CDDFFU, }, - { 0xD83CDDE7U, 0xD83CDDF8U, }, - { 0xD83CDDE7U, 0xD83CDDEDU, }, - { 0xD83CDDE7U, 0xD83CDDE9U, }, - { 0xD83CDDE7U, 0xD83CDDE7U, }, - { 0xD83CDDE7U, 0xD83CDDFEU, }, - { 0xD83CDDE7U, 0xD83CDDEAU, }, - { 0xD83CDDE7U, 0xD83CDDFFU, }, - { 0xD83CDDE7U, 0xD83CDDEFU, }, - { 0xD83CDDE7U, 0xD83CDDF2U, }, - { 0xD83CDDE7U, 0xD83CDDF9U, }, - { 0xD83CDDE7U, 0xD83CDDF4U, }, - { 0xD83CDDE7U, 0xD83CDDE6U, }, - { 0xD83CDDE7U, 0xD83CDDFCU, }, - { 0xD83CDDE7U, 0xD83CDDF7U, }, - { 0xD83CDDEEU, 0xD83CDDF4U, }, - { 0xD83CDDFBU, 0xD83CDDECU, }, - { 0xD83CDDE7U, 0xD83CDDF3U, }, - { 0xD83CDDE7U, 0xD83CDDECU, }, - { 0xD83CDDE7U, 0xD83CDDEBU, }, - { 0xD83CDDE7U, 0xD83CDDEEU, }, - { 0xD83CDDF0U, 0xD83CDDEDU, }, - { 0xD83CDDE8U, 0xD83CDDF2U, }, - { 0xD83CDDE8U, 0xD83CDDE6U, }, - { 0xD83CDDEEU, 0xD83CDDE8U, }, - { 0xD83CDDE8U, 0xD83CDDFBU, }, - { 0xD83CDDE7U, 0xD83CDDF6U, }, - { 0xD83CDDF0U, 0xD83CDDFEU, }, - { 0xD83CDDE8U, 0xD83CDDEBU, }, - { 0xD83CDDF9U, 0xD83CDDE9U, }, - { 0xD83CDDE8U, 0xD83CDDF1U, }, - { 0xD83CDDE8U, 0xD83CDDF3U, }, - { 0xD83CDDE8U, 0xD83CDDFDU, }, - { 0xD83CDDE8U, 0xD83CDDE8U, }, - { 0xD83CDDE8U, 0xD83CDDF4U, }, - { 0xD83CDDF0U, 0xD83CDDF2U, }, - { 0xD83CDDE8U, 0xD83CDDECU, }, - { 0xD83CDDE8U, 0xD83CDDE9U, }, - { 0xD83CDDE8U, 0xD83CDDF0U, }, - { 0xD83CDDE8U, 0xD83CDDF7U, }, - { 0xD83CDDE8U, 0xD83CDDEEU, }, - { 0xD83CDDEDU, 0xD83CDDF7U, }, - { 0xD83CDDE8U, 0xD83CDDFAU, }, - { 0xD83CDDE8U, 0xD83CDDFCU, }, - { 0xD83CDDE8U, 0xD83CDDFEU, }, - { 0xD83CDDE8U, 0xD83CDDFFU, }, - { 0xD83CDDE9U, 0xD83CDDF0U, }, - { 0xD83CDDE9U, 0xD83CDDEFU, }, - { 0xD83CDDE9U, 0xD83CDDF2U, }, - { 0xD83CDDE9U, 0xD83CDDF4U, }, - { 0xD83CDDEAU, 0xD83CDDE8U, }, - { 0xD83CDDEAU, 0xD83CDDECU, }, - { 0xD83CDDF8U, 0xD83CDDFBU, }, - { 0xD83CDDECU, 0xD83CDDF6U, }, - { 0xD83CDDEAU, 0xD83CDDF7U, }, - { 0xD83CDDEAU, 0xD83CDDEAU, }, - { 0xD83CDDEAU, 0xD83CDDF9U, }, - { 0xD83CDDEAU, 0xD83CDDFAU, }, - { 0xD83CDDEBU, 0xD83CDDF0U, }, - { 0xD83CDDEBU, 0xD83CDDF4U, }, - { 0xD83CDDEBU, 0xD83CDDEFU, }, - { 0xD83CDDEBU, 0xD83CDDEEU, }, - { 0xD83CDDEBU, 0xD83CDDF7U, }, - { 0xD83CDDECU, 0xD83CDDEBU, }, - { 0xD83CDDF5U, 0xD83CDDEBU, }, - { 0xD83CDDF9U, 0xD83CDDEBU, }, - { 0xD83CDDECU, 0xD83CDDE6U, }, - { 0xD83CDDECU, 0xD83CDDF2U, }, - { 0xD83CDDECU, 0xD83CDDEAU, }, - { 0xD83CDDE9U, 0xD83CDDEAU, }, - { 0xD83CDDECU, 0xD83CDDEDU, }, - { 0xD83CDDECU, 0xD83CDDEEU, }, - { 0xD83CDDECU, 0xD83CDDF7U, }, - { 0xD83CDDECU, 0xD83CDDF1U, }, - { 0xD83CDDECU, 0xD83CDDE9U, }, - { 0xD83CDDECU, 0xD83CDDF5U, }, - { 0xD83CDDECU, 0xD83CDDFAU, }, - { 0xD83CDDECU, 0xD83CDDF9U, }, - { 0xD83CDDECU, 0xD83CDDECU, }, - { 0xD83CDDECU, 0xD83CDDF3U, }, - { 0xD83CDDECU, 0xD83CDDFCU, }, - { 0xD83CDDECU, 0xD83CDDFEU, }, - { 0xD83CDDEDU, 0xD83CDDF9U, }, - { 0xD83CDDEDU, 0xD83CDDF3U, }, - { 0xD83CDDEDU, 0xD83CDDF0U, }, - { 0xD83CDDEDU, 0xD83CDDFAU, }, - { 0xD83CDDEEU, 0xD83CDDF8U, }, - { 0xD83CDDEEU, 0xD83CDDF3U, }, - { 0xD83CDDEEU, 0xD83CDDE9U, }, - { 0xD83CDDEEU, 0xD83CDDF7U, }, - { 0xD83CDDEEU, 0xD83CDDF6U, }, - { 0xD83CDDEEU, 0xD83CDDEAU, }, - { 0xD83CDDEEU, 0xD83CDDF2U, }, - { 0xD83CDDEEU, 0xD83CDDF1U, }, - { 0xD83CDDEEU, 0xD83CDDF9U, }, - { 0xD83CDDEFU, 0xD83CDDF2U, }, - { 0xD83CDDEFU, 0xD83CDDF5U, }, - { 0xD83CDF8CU, }, - { 0xD83CDDEFU, 0xD83CDDEAU, }, - { 0xD83CDDEFU, 0xD83CDDF4U, }, - { 0xD83CDDF0U, 0xD83CDDFFU, }, - { 0xD83CDDF0U, 0xD83CDDEAU, }, - { 0xD83CDDF0U, 0xD83CDDEEU, }, - { 0xD83CDDFDU, 0xD83CDDF0U, }, - { 0xD83CDDF0U, 0xD83CDDFCU, }, - { 0xD83CDDF0U, 0xD83CDDECU, }, - { 0xD83CDDF1U, 0xD83CDDE6U, }, - { 0xD83CDDF1U, 0xD83CDDFBU, }, - { 0xD83CDDF1U, 0xD83CDDE7U, }, - { 0xD83CDDF1U, 0xD83CDDF8U, }, - { 0xD83CDDF1U, 0xD83CDDF7U, }, - { 0xD83CDDF1U, 0xD83CDDFEU, }, - { 0xD83CDDF1U, 0xD83CDDEEU, }, - { 0xD83CDDF1U, 0xD83CDDF9U, }, - { 0xD83CDDF1U, 0xD83CDDFAU, }, - { 0xD83CDDF2U, 0xD83CDDF4U, }, - { 0xD83CDDF2U, 0xD83CDDF0U, }, - { 0xD83CDDF2U, 0xD83CDDECU, }, - { 0xD83CDDF2U, 0xD83CDDFCU, }, - { 0xD83CDDF2U, 0xD83CDDFEU, }, - { 0xD83CDDF2U, 0xD83CDDFBU, }, - { 0xD83CDDF2U, 0xD83CDDF1U, }, - { 0xD83CDDF2U, 0xD83CDDF9U, }, - { 0xD83CDDF2U, 0xD83CDDEDU, }, - { 0xD83CDDF2U, 0xD83CDDF6U, }, - { 0xD83CDDF2U, 0xD83CDDF7U, }, - { 0xD83CDDF2U, 0xD83CDDFAU, }, - { 0xD83CDDFEU, 0xD83CDDF9U, }, - { 0xD83CDDF2U, 0xD83CDDFDU, }, - { 0xD83CDDEBU, 0xD83CDDF2U, }, - { 0xD83CDDF2U, 0xD83CDDE9U, }, - { 0xD83CDDF2U, 0xD83CDDE8U, }, - { 0xD83CDDF2U, 0xD83CDDF3U, }, - { 0xD83CDDF2U, 0xD83CDDEAU, }, - { 0xD83CDDF2U, 0xD83CDDF8U, }, - { 0xD83CDDF2U, 0xD83CDDE6U, }, - { 0xD83CDDF2U, 0xD83CDDFFU, }, - { 0xD83CDDF2U, 0xD83CDDF2U, }, - { 0xD83CDDF3U, 0xD83CDDE6U, }, - { 0xD83CDDF3U, 0xD83CDDF7U, }, - { 0xD83CDDF3U, 0xD83CDDF5U, }, - { 0xD83CDDF3U, 0xD83CDDF1U, }, - { 0xD83CDDF3U, 0xD83CDDE8U, }, - { 0xD83CDDF3U, 0xD83CDDFFU, }, - { 0xD83CDDF3U, 0xD83CDDEEU, }, - { 0xD83CDDF3U, 0xD83CDDEAU, }, - { 0xD83CDDF3U, 0xD83CDDECU, }, - { 0xD83CDDF3U, 0xD83CDDFAU, }, - { 0xD83CDDF3U, 0xD83CDDEBU, }, - { 0xD83CDDF0U, 0xD83CDDF5U, }, - { 0xD83CDDF2U, 0xD83CDDF5U, }, - { 0xD83CDDF3U, 0xD83CDDF4U, }, - { 0xD83CDDF4U, 0xD83CDDF2U, }, - { 0xD83CDDF5U, 0xD83CDDF0U, }, - { 0xD83CDDF5U, 0xD83CDDFCU, }, - { 0xD83CDDF5U, 0xD83CDDF8U, }, - { 0xD83CDDF5U, 0xD83CDDE6U, }, - { 0xD83CDDF5U, 0xD83CDDECU, }, - { 0xD83CDDF5U, 0xD83CDDFEU, }, - { 0xD83CDDF5U, 0xD83CDDEAU, }, - { 0xD83CDDF5U, 0xD83CDDEDU, }, - { 0xD83CDDF5U, 0xD83CDDF3U, }, - { 0xD83CDDF5U, 0xD83CDDF1U, }, - { 0xD83CDDF5U, 0xD83CDDF9U, }, - { 0xD83CDDF5U, 0xD83CDDF7U, }, - { 0xD83CDDF6U, 0xD83CDDE6U, }, - { 0xD83CDDF7U, 0xD83CDDEAU, }, - { 0xD83CDDF7U, 0xD83CDDF4U, }, - { 0xD83CDDF7U, 0xD83CDDFAU, }, - { 0xD83CDDF7U, 0xD83CDDFCU, }, - { 0xD83CDDFCU, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDF2U, }, - { 0xD83CDDF8U, 0xD83CDDF9U, }, - { 0xD83CDDF8U, 0xD83CDDE6U, }, - { 0xD83CDDF8U, 0xD83CDDF3U, }, - { 0xD83CDDF7U, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDE8U, }, - { 0xD83CDDF8U, 0xD83CDDF1U, }, - { 0xD83CDDF8U, 0xD83CDDECU, }, - { 0xD83CDDF8U, 0xD83CDDFDU, }, - { 0xD83CDDF8U, 0xD83CDDF0U, }, - { 0xD83CDDF8U, 0xD83CDDEEU, }, - { 0xD83CDDECU, 0xD83CDDF8U, }, - { 0xD83CDDF8U, 0xD83CDDE7U, }, - { 0xD83CDDF8U, 0xD83CDDF4U, }, - { 0xD83CDDFFU, 0xD83CDDE6U, }, - { 0xD83CDDF0U, 0xD83CDDF7U, }, - { 0xD83CDDF8U, 0xD83CDDF8U, }, - { 0xD83CDDEAU, 0xD83CDDF8U, }, - { 0xD83CDDF1U, 0xD83CDDF0U, }, - { 0xD83CDDE7U, 0xD83CDDF1U, }, - { 0xD83CDDF8U, 0xD83CDDEDU, }, - { 0xD83CDDF0U, 0xD83CDDF3U, }, - { 0xD83CDDF1U, 0xD83CDDE8U, }, - { 0xD83CDDF5U, 0xD83CDDF2U, }, - { 0xD83CDDFBU, 0xD83CDDE8U, }, - { 0xD83CDDF8U, 0xD83CDDE9U, }, - { 0xD83CDDF8U, 0xD83CDDF7U, }, - { 0xD83CDDF8U, 0xD83CDDFFU, }, - { 0xD83CDDF8U, 0xD83CDDEAU, }, - { 0xD83CDDE8U, 0xD83CDDEDU, }, - { 0xD83CDDF8U, 0xD83CDDFEU, }, - { 0xD83CDDF9U, 0xD83CDDFCU, }, - { 0xD83CDDF9U, 0xD83CDDEFU, }, - { 0xD83CDDF9U, 0xD83CDDFFU, }, - { 0xD83CDDF9U, 0xD83CDDEDU, }, - { 0xD83CDDF9U, 0xD83CDDF1U, }, - { 0xD83CDDF9U, 0xD83CDDECU, }, - { 0xD83CDDF9U, 0xD83CDDF0U, }, - { 0xD83CDDF9U, 0xD83CDDF4U, }, - { 0xD83CDDF9U, 0xD83CDDF9U, }, - { 0xD83CDDF9U, 0xD83CDDF3U, }, - { 0xD83CDDF9U, 0xD83CDDF7U, }, - { 0xD83CDDF9U, 0xD83CDDF2U, }, - { 0xD83CDDF9U, 0xD83CDDE8U, }, - { 0xD83CDDF9U, 0xD83CDDFBU, }, - { 0xD83CDDFBU, 0xD83CDDEEU, }, - { 0xD83CDDFAU, 0xD83CDDECU, }, - { 0xD83CDDFAU, 0xD83CDDE6U, }, - { 0xD83CDDE6U, 0xD83CDDEAU, }, - { 0xD83CDDECU, 0xD83CDDE7U, }, - { 0xD83CDDFAU, 0xD83CDDF8U, }, - { 0xD83CDDFAU, 0xD83CDDFEU, }, - { 0xD83CDDFAU, 0xD83CDDFFU, }, - { 0xD83CDDFBU, 0xD83CDDFAU, }, - { 0xD83CDDFBU, 0xD83CDDE6U, }, - { 0xD83CDDFBU, 0xD83CDDEAU, }, - { 0xD83CDDFBU, 0xD83CDDF3U, }, - { 0xD83CDDFCU, 0xD83CDDEBU, }, - { 0xD83CDDEAU, 0xD83CDDEDU, }, - { 0xD83CDDFEU, 0xD83CDDEAU, }, - { 0xD83CDDFFU, 0xD83CDDF2U, }, - { 0xD83CDDFFU, 0xD83CDDFCU, }, -}; - -} // namespace old -} // namespace -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/data.h b/Telegram/SourceFiles/codegen/emoji/data.h deleted file mode 100644 index eb8551f5e..000000000 --- a/Telegram/SourceFiles/codegen/emoji/data.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "codegen/common/logging.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace codegen { -namespace emoji { - -using Id = QString; -struct Emoji { - Id id; - bool postfixed = false; - bool variated = false; - bool colored = false; -}; - -struct Data { - std::vector list; - std::map> map; - std::set postfixRequired; - std::vector> categories; - std::map> replaces; -}; -Data PrepareData(); - -constexpr auto kPostfix = 0xFE0FU; - -common::LogStream logDataError(); - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/generator.cpp b/Telegram/SourceFiles/codegen/emoji/generator.cpp deleted file mode 100644 index 2ef7b6a25..000000000 --- a/Telegram/SourceFiles/codegen/emoji/generator.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -/* -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 "codegen/emoji/generator.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef SUPPORT_IMAGE_GENERATION -Q_IMPORT_PLUGIN(QWebpPlugin) -#ifdef Q_OS_MAC -Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) -#elif defined Q_OS_WIN -Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) -#else // !Q_OS_MAC && !Q_OS_WIN -Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) -#endif // !Q_OS_MAC && !Q_OS_WIN -#endif // SUPPORT_IMAGE_GENERATION - -namespace codegen { -namespace emoji { -namespace { - -constexpr auto kErrorCantWritePath = 851; - -constexpr auto kOriginalBits = 12; -constexpr auto kIdSizeBits = 6; -constexpr auto kColumnBits = 5; -constexpr auto kRowBits = 7; - -common::ProjectInfo Project = { - "codegen_emoji", - "empty", - false, // forceReGenerate -}; - -QRect computeSourceRect(const QImage &image) { - auto size = image.width(); - auto result = QRect(2, 2, size - 4, size - 4); - auto top = 1, bottom = 1, left = 1, right = 1; - auto rgbBits = reinterpret_cast(image.constBits()); - for (auto i = 0; i != size; ++i) { - if (rgbBits[i] > 0 - || rgbBits[(size - 1) * size + i] > 0 - || rgbBits[i * size] > 0 - || rgbBits[i * size + (size - 1)] > 0) { - logDataError() << "Bad border."; - return QRect(); - } - if (rgbBits[1 * size + i] > 0) { - top = -1; - } else if (top > 0 && rgbBits[2 * size + i] > 0) { - top = 0; - } - if (rgbBits[(size - 2) * size + i] > 0) { - bottom = -1; - } else if (bottom > 0 && rgbBits[(size - 3) * size + i] > 0) { - bottom = 0; - } - if (rgbBits[i * size + 1] > 0) { - left = -1; - } else if (left > 0 && rgbBits[i * size + 2] > 0) { - left = 0; - } - if (rgbBits[i * size + (size - 2)] > 0) { - right = -1; - } else if (right > 0 && rgbBits[i * size + (size - 3)] > 0) { - right = 0; - } - } - if (top < 0) { - if (bottom <= 0) { - logDataError() << "Bad vertical :("; - return QRect(); - } else { - result.setY(result.y() + 1); - } - } else if (bottom < 0) { - if (top <= 0) { - logDataError() << "Bad vertical :("; - return QRect(); - } else { - result.setY(result.y() - 1); - } - } - if (left < 0) { - if (right <= 0) { - logDataError() << "Bad horizontal :("; - return QRect(); - } else { - result.setX(result.x() + 1); - } - } else if (right < 0) { - if (left <= 0) { - logDataError() << "Bad horizontal :("; - return QRect(); - } else { - result.setX(result.x() - 1); - } - } - return result; -} - -uint32 Crc32Table[256]; -class Crc32Initializer { -public: - Crc32Initializer() { - uint32 poly = 0x04C11DB7U; - for (auto i = 0; i != 256; ++i) { - Crc32Table[i] = reflect(i, 8) << 24; - for (auto j = 0; j != 8; ++j) { - Crc32Table[i] = (Crc32Table[i] << 1) ^ (Crc32Table[i] & (1 << 31) ? poly : 0); - } - Crc32Table[i] = reflect(Crc32Table[i], 32); - } - } - -private: - uint32 reflect(uint32 val, char ch) { - uint32 result = 0; - for (int i = 1; i < (ch + 1); ++i) { - if (val & 1) { - result |= 1 << (ch - i); - } - val >>= 1; - } - return result; - } - -}; - -uint32 countCrc32(const void *data, std::size_t size) { - static Crc32Initializer InitTable; - - auto buffer = static_cast(data); - auto result = uint32(0xFFFFFFFFU); - for (auto i = std::size_t(0); i != size; ++i) { - result = (result >> 8) ^ Crc32Table[(result & 0xFFU) ^ buffer[i]]; - } - return (result ^ 0xFFFFFFFFU); -} - -} // namespace - -Generator::Generator(const Options &options) : project_(Project) -#ifdef SUPPORT_IMAGE_GENERATION -, writeImages_(options.writeImages) -#endif // SUPPORT_IMAGE_GENERATION -, data_(PrepareData()) -, replaces_(PrepareReplaces(options.replacesPath)) { - QDir dir(options.outputPath); - if (!dir.mkpath(".")) { - common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); - data_ = Data(); - } - if (!CheckAndConvertReplaces(replaces_, data_)) { - replaces_ = Replaces(replaces_.filename); - } - - outputPath_ = dir.absolutePath() + "/emoji"; - spritePath_ = dir.absolutePath() + "/emoji"; - suggestionsPath_ = dir.absolutePath() + "/emoji_suggestions_data"; -} - -int Generator::generate() { - if (data_.list.empty() || replaces_.list.isEmpty()) { - return -1; - } - -#ifdef SUPPORT_IMAGE_GENERATION - if (writeImages_) { - return writeImages() ? 0 : -1; - } -#endif // SUPPORT_IMAGE_GENERATION - - if (!writeSource()) { - return -1; - } - if (!writeHeader()) { - return -1; - } - if (!writeSuggestionsSource()) { - return -1; - } - if (!writeSuggestionsHeader()) { - return -1; - } - - return 0; -} - -constexpr auto kEmojiInRow = 32; -constexpr auto kEmojiRowsInFile = 16; -constexpr auto kEmojiQuality = 99; -constexpr auto kEmojiSize = 72; -constexpr auto kEmojiFontSize = 72; -constexpr auto kEmojiDelta = 67; -constexpr auto kScaleFromLarge = true; - -#ifdef SUPPORT_IMAGE_GENERATION -QImage Generator::generateImage(int imageIndex) { - constexpr auto kLargeEmojiSize = 180; - constexpr auto kLargeEmojiFontSize = 180; - constexpr auto kLargeEmojiDelta = 167; - - auto emojiCount = int(data_.list.size()); - auto columnsCount = kEmojiInRow; - auto fullRowsCount = (emojiCount / columnsCount) + ((emojiCount % columnsCount) ? 1 : 0); - auto imagesCount = (fullRowsCount / kEmojiRowsInFile) + ((fullRowsCount % kEmojiRowsInFile) ? 1 : 0); - - auto sourceSize = kScaleFromLarge ? kLargeEmojiSize : kEmojiSize; - - auto font = QGuiApplication::font(); - font.setFamily(QStringLiteral("Apple Color Emoji")); - font.setPixelSize(kScaleFromLarge ? kLargeEmojiFontSize : kEmojiFontSize); - - auto singleSize = 4 + sourceSize; - const auto inFileShift = (imageIndex * kEmojiInRow * kEmojiRowsInFile); - if (inFileShift >= emojiCount) { - return QImage(); - } - const auto maxInFile = emojiCount - inFileShift; - const auto inFileCount = std::min(maxInFile, kEmojiInRow * kEmojiRowsInFile); - auto rowsCount = (inFileCount / columnsCount) + ((inFileCount % columnsCount) ? 1 : 0); - auto emojiImage = QImage(columnsCount * kEmojiSize, rowsCount * kEmojiSize, QImage::Format_ARGB32); - emojiImage.fill(Qt::transparent); - auto singleImage = QImage(singleSize, singleSize, QImage::Format_ARGB32); - { - QPainter p(&emojiImage); - p.setRenderHint(QPainter::SmoothPixmapTransform); - - auto column = 0; - auto row = 0; - for (auto i = 0; i != inFileCount; ++i) { - auto &emoji = data_.list[inFileShift + i]; - { - singleImage.fill(Qt::transparent); - - QPainter q(&singleImage); - q.setPen(QColor(0, 0, 0, 255)); - q.setFont(font); - const auto delta = kScaleFromLarge ? kLargeEmojiDelta : kEmojiDelta; - q.drawText(2, 2 + delta, emoji.id); - } - auto sourceRect = computeSourceRect(singleImage); - if (sourceRect.isEmpty()) { - return QImage(); - } - auto targetRect = QRect(column * kEmojiSize, row * kEmojiSize, kEmojiSize, kEmojiSize); - if (kScaleFromLarge) { - p.drawImage(targetRect, singleImage.copy(sourceRect).scaled(kEmojiSize, kEmojiSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } else { - p.drawImage(targetRect, singleImage, sourceRect); - } - ++column; - if (column == columnsCount) { - column = 0; - ++row; - } - } - } - return emojiImage; -} - -bool Generator::writeImages() { - auto imageIndex = 0; - while (true) { - auto image = generateImage(imageIndex); - if (image.isNull()) { - break; - } - auto postfix = '_' + QString::number(imageIndex + 1); - auto filename = spritePath_ + postfix + ".webp"; - auto bytes = QByteArray(); - { - QBuffer buffer(&bytes); - if (!image.save(&buffer, "WEBP", kEmojiQuality)) { - logDataError() << "Could not save 'emoji" << postfix.toStdString() << ".webp'."; - return false; - } - } - auto needResave = !QFileInfo(filename).exists(); - if (!needResave) { - QFile file(filename); - if (!file.open(QIODevice::ReadOnly)) { - needResave = true; - } else { - auto already = file.readAll(); - if (already.size() != bytes.size() || memcmp(already.constData(), bytes.constData(), already.size())) { - needResave = true; - } - } - } - if (needResave) { - QFile file(filename); - if (!file.open(QIODevice::WriteOnly)) { - logDataError() << "Could not open 'emoji" << postfix.toStdString() << ".webp'."; - return false; - } else { - if (file.write(bytes) != bytes.size()) { - logDataError() << "Could not write 'emoji" << postfix.toStdString() << ".webp'."; - return false; - } - } - } - ++imageIndex; - } - return true; -} -#endif // SUPPORT_IMAGE_GENERATION - -bool Generator::writeSource() { - source_ = std::make_unique(outputPath_ + ".cpp", project_); - - source_->include("emoji_suggestions_data.h").include("ui/emoji_config.h").newline(); - source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace(); - source_->stream() << "\ -\n\ -std::vector Items;\n\ -\n"; - if (!writeInitCode()) { - return false; - } - if (!writeSections()) { - return false; - } - if (!writeFindReplace()) { - return false; - } - if (!writeFind()) { - return false; - } - source_->popNamespace().newline().pushNamespace("internal"); - source_->stream() << "\ -\n\ -int FullCount() {\n\ - return Items.size();\n\ -}\n\ -\n\ -EmojiPtr ByIndex(int index) {\n\ - return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\ -}\n\ -\n\ -EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\ - auto index = FindReplaceIndex(start, end, outLength);\n\ - return index ? &Items[index - 1] : nullptr;\n\ -}\n\ -\n\ -const std::vector> GetReplacementPairs() {\n\ - return ReplacementPairs;\n\ -}\n\ -\n\ -EmojiPtr Find(const QChar *start, const QChar *end, int *outLength) {\n\ - auto index = FindIndex(start, end, outLength);\n\ - return index ? &Items[index - 1] : nullptr;\n\ -}\n\ -\n\ -void Init() {\n\ - auto id = IdData;\n\ - auto takeString = [&id](int size) {\n\ - auto result = QString::fromRawData(reinterpret_cast(id), size);\n\ - id += size;\n\ - return result;\n\ - };\n\ -\n\ - Items.reserve(base::array_size(Data));\n\ - for (auto &data : Data) {\n\ - Items.emplace_back(\n\ - takeString(data.idSize),\n\ - data.original ? &Items[data.original - 1] : nullptr,\n\ - uint32(Items.size()),\n\ - data.postfixed ? true : false,\n\ - data.variated ? true : false,\n\ - One::CreationTag());\n\ - }\n\ - InitReplacements();\n\ -}\n\ -\n"; - source_->popNamespace(); - - if (!writeGetSections()) { - return false; - } - - return source_->finalize(); -} - -bool Generator::writeHeader() { - auto header = std::make_unique(outputPath_ + ".h", project_); - header->includeFromLibrary("QtCore/QChar"); - header->includeFromLibrary("QtCore/QString"); - header->includeFromLibrary("QtCore/QVector"); - header->newline(); - header->includeFromLibrary("vector"); - header->newline(); - - header->pushNamespace("Ui").pushNamespace("Emoji"); - header->stream() << "class One;\n"; - header->popNamespace().popNamespace().newline(); - - header->stream() << "\ -using EmojiPtr = const Ui::Emoji::One*;\n\ -using EmojiPack = QVector;\n\ -\n"; - - header->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal"); - header->stream() << "\ -\n\ -void Init();\n\ -\n\ -int FullCount();\n\ -EmojiPtr ByIndex(int index);\n\ -\n\ -EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\ -\n\ -const std::vector> GetReplacementPairs();\n\ -EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\ -\n"; - header->popNamespace().stream() << "\ -\n\ -constexpr auto kPostfix = static_cast(0xFE0F);\n\ -\n\ -enum class Section {\n\ - Recent,\n\ - People,\n\ - Nature,\n\ - Food,\n\ - Activity,\n\ - Travel,\n\ - Objects,\n\ - Symbols,\n\ -};\n\ -\n\ -int GetSectionCount(Section section);\n\ -QVector GetSection(Section section);\n\ -\n"; - return header->finalize(); -} - -template -bool Generator::enumerateWholeList(Callback callback) { - auto index = 0; - auto variated = -1; - auto coloredCount = 0; - for (auto &item : data_.list) { - if (!callback(item.id, item.postfixed, item.variated, item.colored, variated)) { - return false; - } - if (coloredCount > 0 && (item.variated || !item.colored)) { - if (!colorsCount_) { - colorsCount_ = coloredCount; - } else if (colorsCount_ != coloredCount) { - logDataError() << "different colored emoji count exist."; - return false; - } - coloredCount = 0; - } - if (item.variated) { - variated = index; - } else if (item.colored) { - if (variated <= 0) { - logDataError() << "wrong order of colored items."; - return false; - } - ++coloredCount; - } else if (variated >= 0) { - variated = -1; - } - ++index; - } - return true; -} - -bool Generator::writeInitCode() { - source_->stream() << "\ -struct DataStruct {\n\ - uint32 original : " << kOriginalBits << ";\n\ - uint32 idSize : " << kIdSizeBits << ";\n\ - uint32 postfixed : 1;\n\ - uint32 variated : 1;\n\ -};\n\ -\n\ -const ushort IdData[] = {"; - startBinary(); - if (!enumerateWholeList([this](Id id, bool isPostfixed, bool isVariated, bool isColored, int original) { - return writeStringBinary(source_.get(), id); - })) { - return false; - } - if (_binaryFullLength >= std::numeric_limits::max()) { - logDataError() << "Too many IdData elements."; - return false; - } - source_->stream() << " };\n\ -\n\ -const DataStruct Data[] = {\n"; - if (!enumerateWholeList([this](Id id, bool isPostfixed, bool isVariated, bool isColored, int original) { - if (original + 1 >= (1 << kOriginalBits)) { - logDataError() << "Too many entries."; - return false; - } - if (id.size() >= (1 << kIdSizeBits)) { - logDataError() << "Too large id."; - return false; - } - source_->stream() << "\ - { uint32(" << (isColored ? (original + 1) : 0) << "), uint32(" << id.size() << "), uint32(" << (isPostfixed ? "1" : "0") << "), uint32(" << (isVariated ? "1" : "0") << ") },\n"; - return true; - })) { - return false; - } - - source_->stream() << "\ -};\n"; - - return true; -} - -bool Generator::writeSections() { - source_->stream() << "\ -const ushort SectionData[] = {"; - startBinary(); - for (auto &category : data_.categories) { - for (auto index : category) { - writeIntBinary(source_.get(), index); - } - } - source_->stream() << " };\n\ -\n\ -EmojiPack FillSection(int offset, int size) {\n\ - auto result = EmojiPack();\n\ - result.reserve(size);\n\ - for (auto index : gsl::make_span(SectionData + offset, size)) {\n\ - result.push_back(&Items[index]);\n\ - }\n\ - return result;\n\ -}\n\n"; - return true; -} - -bool Generator::writeGetSections() { - constexpr const char *sectionNames[] = { - "Section::People", - "Section::Nature", - "Section::Food", - "Section::Activity", - "Section::Travel", - "Section::Objects", - "Section::Symbols", - }; - source_->stream() << "\ -\n\ -int GetSectionCount(Section section) {\n\ - Expects(section != Section::Recent);\n\ -\n\ - switch (section) {\n"; - auto countIndex = 0; - for (auto name : sectionNames) { - if (countIndex >= int(data_.categories.size())) { - logDataError() << "category " << countIndex << " not found."; - return false; - } - source_->stream() << "\ - case " << name << ": return " << data_.categories[countIndex++].size() << ";\n"; - } - source_->stream() << "\ - }\n\ - return 0;\n\ -}\n\ -\n\ -EmojiPack GetSection(Section section) {\n\ - Expects(section != Section::Recent);\n\ -\n\ - switch (section) {\n"; - auto index = 0; - auto offset = 0; - for (auto name : sectionNames) { - if (index >= int(data_.categories.size())) { - logDataError() << "category " << index << " not found."; - return false; - } - auto &category = data_.categories[index++]; - source_->stream() << "\ -\n\ - case " << name << ": {\n\ - static auto result = FillSection(" << offset << ", " << category.size() << ");\n\ - return result;\n\ - } break;\n"; - offset += category.size(); - } - source_->stream() << "\ - }\n\ - return EmojiPack();\n\ -}\n\ -\n"; - return true; -} - -bool Generator::writeFindReplace() { - source_->stream() << "\ -\n\ -const std::vector> ReplacementPairs = {\n"; - for (const auto &[what, index] : data_.replaces) { - source_->stream() << "\ - { \"" << what << "\", " << index << " },\n"; - } - source_->stream() << "\ -};\n\ -\n\ -int FindReplaceIndex(const QChar *start, const QChar *end, int *outLength) {\n\ - auto ch = start;\n\ -\n"; - - if (!writeFindFromDictionary(data_.replaces)) { - return false; - } - - source_->stream() << "\ -}\n"; - - return true; -} - -bool Generator::writeFind() { - source_->stream() << "\ -\n\ -int FindIndex(const QChar *start, const QChar *end, int *outLength) {\n\ - auto ch = start;\n\ -\n"; - - if (!writeFindFromDictionary(data_.map, true, data_.postfixRequired)) { - return false; - } - - source_->stream() << "\ -}\n\ -\n"; - - return true; -} - -bool Generator::writeFindFromDictionary( - const std::map> &dictionary, - bool skipPostfixes, - const std::set &postfixRequired) { - auto tabs = [](int size) { - return QString(size, '\t'); - }; - - std::map uniqueFirstChars; - auto foundMax = 0, foundMin = 65535; - for (auto &item : dictionary) { - auto ch = item.first[0].unicode(); - if (foundMax < ch) foundMax = ch; - if (foundMin > ch) foundMin = ch; - uniqueFirstChars[ch] = 0; - } - - enum class UsedCheckType { - Switch, - If, - }; - auto checkTypes = QVector(); - auto chars = QString(); - auto tabsUsed = 1; - auto lengthsCounted = std::set(); - - auto writeSkipPostfix = [this, &tabs, skipPostfixes](int tabsCount) { - if (skipPostfixes) { - source_->stream() << tabs(tabsCount) << "if (++ch != end && ch->unicode() == kPostfix) ++ch;\n"; - } else { - source_->stream() << tabs(tabsCount) << "++ch;\n"; - } - }; - - // Returns true if at least one check was finished. - auto finishChecksTillKey = [this, &chars, &checkTypes, &tabsUsed, tabs](const QString &key) { - auto result = false; - while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) { - result = true; - - auto wasType = checkTypes.back(); - chars.resize(chars.size() - 1); - checkTypes.pop_back(); - if (wasType == UsedCheckType::Switch || wasType == UsedCheckType::If) { - --tabsUsed; - if (wasType == UsedCheckType::Switch) { - source_->stream() << tabs(tabsUsed) << "break;\n"; - } - if ((!chars.isEmpty() && key.midRef(0, chars.size()) != chars) || key == chars) { - source_->stream() << tabs(tabsUsed) << "}\n"; - } - } - } - return result; - }; - - // Check if we can use "if" for a check on "charIndex" in "it" (otherwise only "switch") - auto canUseIfForCheck = [](auto it, auto end, int charIndex) { - auto key = it->first; - auto i = it; - auto keyStart = key.mid(0, charIndex); - for (++i; i != end; ++i) { - auto nextKey = i->first; - if (nextKey.mid(0, charIndex) != keyStart) { - return true; - } else if (nextKey.size() > charIndex && nextKey[charIndex] != key[charIndex]) { - return false; - } - } - return true; - }; - - for (auto i = dictionary.cbegin(), e = dictionary.cend(); i != e; ++i) { - auto &item = *i; - auto key = item.first; - auto weContinueOldSwitch = finishChecksTillKey(key); - while (chars.size() != key.size()) { - auto checking = chars.size(); - auto partialKey = key.mid(0, checking); - if (dictionary.find(partialKey) != dictionary.cend()) { - if (lengthsCounted.find(partialKey) == end(lengthsCounted)) { - lengthsCounted.emplace(partialKey); - source_->stream() << tabs(tabsUsed) << "if (outLength) *outLength = (ch - start);\n"; - } - } - - auto keyChar = key[checking]; - auto keyCharString = "0x" + QString::number(keyChar.unicode(), 16); - auto usedIfForCheck = !weContinueOldSwitch && canUseIfForCheck(i, e, checking); - if (weContinueOldSwitch) { - weContinueOldSwitch = false; - } else if (!usedIfForCheck) { - source_->stream() << tabs(tabsUsed) << "if (ch != end) switch (ch->unicode()) {\n"; - } - if (usedIfForCheck) { - source_->stream() << tabs(tabsUsed) << "if (ch != end && ch->unicode() == " << keyCharString << ") {\n"; - checkTypes.push_back(UsedCheckType::If); - } else { - source_->stream() << tabs(tabsUsed) << "case " << keyCharString << ":\n"; - checkTypes.push_back(UsedCheckType::Switch); - } - writeSkipPostfix(++tabsUsed); - chars.push_back(keyChar); - } - - if (postfixRequired.find(item.second) != end(postfixRequired)) { - source_->stream() << tabs(tabsUsed) << "if ((ch - 1)->unicode() != kPostfix) {\n"; - source_->stream() << tabs(tabsUsed + 1) << "return 0;\n"; - source_->stream() << tabs(tabsUsed) << "}\n"; - } - if (lengthsCounted.find(key) == end(lengthsCounted)) { - lengthsCounted.emplace(key); - source_->stream() << tabs(tabsUsed) << "if (outLength) *outLength = (ch - start);\n"; - } - - source_->stream() << tabs(tabsUsed) << "return " << (item.second + 1) << ";\n"; - } - finishChecksTillKey(QString()); - - source_->stream() << "\ -\n\ - return 0;\n"; - return true; -} - -bool Generator::writeSuggestionsSource() { - suggestionsSource_ = std::make_unique(suggestionsPath_ + ".cpp", project_); - suggestionsSource_->stream() << "\ -#include \n\ -\n"; - suggestionsSource_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal").pushNamespace(); - suggestionsSource_->stream() << "\ -\n"; - if (!writeReplacements()) { - return false; - } - suggestionsSource_->popNamespace().newline(); - if (!writeGetReplacements()) { - return false; - } - - return suggestionsSource_->finalize(); -} - -bool Generator::writeSuggestionsHeader() { - auto maxLength = 0; - for (auto &replace : replaces_.list) { - if (maxLength < replace.replacement.size()) { - maxLength = replace.replacement.size(); - } - } - auto header = std::make_unique(suggestionsPath_ + ".h", project_); - header->include("emoji_suggestions.h").newline(); - header->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal"); - header->stream() << "\ -\n\ -struct Replacement {\n\ - utf16string emoji;\n\ - utf16string replacement;\n\ - std::vector words;\n\ -};\n\ -\n\ -constexpr auto kReplacementMaxLength = " << maxLength << ";\n\ -\n\ -void InitReplacements();\n\ -const std::vector &GetAllReplacements();\n\ -const std::vector *GetReplacements(utf16char first);\n\ -utf16string GetReplacementEmoji(utf16string replacement);\n\ -\n"; - return header->finalize(); -} - -bool Generator::writeReplacements() { - QMap> byCharIndices; - suggestionsSource_->stream() << "\ -struct ReplacementStruct {\n\ - small emojiSize;\n\ - small replacementSize;\n\ - small wordsCount;\n\ -};\n\ -\n\ -const utf16char ReplacementData[] = {"; - startBinary(); - for (auto i = 0, size = replaces_.list.size(); i != size; ++i) { - auto &replace = replaces_.list[i]; - if (!writeStringBinary(suggestionsSource_.get(), replace.id)) { - return false; - } - if (!writeStringBinary(suggestionsSource_.get(), replace.replacement)) { - return false; - } - for (auto &word : replace.words) { - if (!writeStringBinary(suggestionsSource_.get(), word)) { - return false; - } - auto &index = byCharIndices[word[0]]; - if (index.isEmpty() || index.back() != i) { - index.push_back(i); - } - } - } - suggestionsSource_->stream() << " };\n\ -\n\ -const small ReplacementWordLengths[] = {"; - startBinary(); - for (auto &replace : replaces_.list) { - auto wordLengths = QStringList(); - for (auto &word : replace.words) { - writeIntBinary(suggestionsSource_.get(), word.size()); - } - } - suggestionsSource_->stream() << " };\n\ -\n\ -const ReplacementStruct ReplacementInitData[] = {\n"; - for (auto &replace : replaces_.list) { - suggestionsSource_->stream() << "\ - { small(" << replace.id.size() << "), small(" << replace.replacement.size() << "), small(" << replace.words.size() << ") },\n"; - } - suggestionsSource_->stream() << "};\n\ -\n\ -const medium ReplacementIndices[] = {"; - startBinary(); - for (auto &byCharIndex : byCharIndices) { - for (auto index : byCharIndex) { - writeIntBinary(suggestionsSource_.get(), index); - } - } - suggestionsSource_->stream() << " };\n\ -\n\ -struct ReplacementIndexStruct {\n\ - utf16char ch;\n\ - medium count;\n\ -};\n\ -\n\ -const internal::checksum ReplacementChecksums[] = {\n"; - startBinary(); - for (auto &replace : replaces_.list) { - writeUintBinary(suggestionsSource_.get(), countCrc32(replace.replacement.constData(), replace.replacement.size() * sizeof(QChar))); - } - suggestionsSource_->stream() << " };\n\ -\n\ -const ReplacementIndexStruct ReplacementIndexData[] = {\n"; - startBinary(); - for (auto i = byCharIndices.cbegin(), e = byCharIndices.cend(); i != e; ++i) { - suggestionsSource_->stream() << "\ - { utf16char(" << i.key().unicode() << "), medium(" << i.value().size() << ") },\n"; - } - suggestionsSource_->stream() << "};\n\ -\n\ -std::vector Replacements;\n\ -std::map> ReplacementsMap;\n\ -std::map ReplacementsHash;\n\ -\n"; - return true; -} - -bool Generator::writeGetReplacements() { - suggestionsSource_->stream() << "\ -void InitReplacements() {\n\ - if (!Replacements.empty()) {\n\ - return;\n\ - }\n\ - auto data = ReplacementData;\n\ - auto takeString = [&data](int size) {\n\ - auto result = utf16string(data, size);\n\ - data += size;\n\ - return result;\n\ - };\n\ - auto wordSize = ReplacementWordLengths;\n\ -\n\ - Replacements.reserve(" << replaces_.list.size() << ");\n\ - for (auto item : ReplacementInitData) {\n\ - auto emoji = takeString(item.emojiSize);\n\ - auto replacement = takeString(item.replacementSize);\n\ - auto words = std::vector();\n\ - words.reserve(item.wordsCount);\n\ - for (auto i = 0; i != item.wordsCount; ++i) {\n\ - words.push_back(takeString(*wordSize++));\n\ - }\n\ - Replacements.push_back({ std::move(emoji), std::move(replacement), std::move(words) });\n\ - }\n\ -\n\ - auto indices = ReplacementIndices;\n\ - auto items = &Replacements[0];\n\ - for (auto item : ReplacementIndexData) {\n\ - auto index = std::vector();\n\ - index.reserve(item.count);\n\ - for (auto i = 0; i != item.count; ++i) {\n\ - index.push_back(items + (*indices++));\n\ - }\n\ - ReplacementsMap.emplace(item.ch, std::move(index));\n\ - }\n\ -\n\ - for (auto checksum : ReplacementChecksums) {\n\ - ReplacementsHash.emplace(checksum, items++);\n\ - }\n\ -}\n\ -\n\ -const std::vector *GetReplacements(utf16char first) {\n\ - if (ReplacementsMap.empty()) {\n\ - InitReplacements();\n\ - }\n\ - auto it = ReplacementsMap.find(first);\n\ - return (it == ReplacementsMap.cend()) ? nullptr : &it->second;\n\ -}\n\ -\n\ -const std::vector &GetAllReplacements() {\n\ - return Replacements;\n\ -}\n\ -\n\ -utf16string GetReplacementEmoji(utf16string replacement) {\n\ - auto code = internal::countChecksum(replacement.data(), replacement.size() * sizeof(utf16char));\n\ - auto it = ReplacementsHash.find(code);\n\ - return (it == ReplacementsHash.cend()) ? utf16string() : it->second->emoji;\n\ -}\n\ -\n"; - return true; -} - -void Generator::startBinary() { - _binaryFullLength = _binaryCount = 0; -} - -bool Generator::writeStringBinary(common::CppFile *source, const QString &string) { - if (string.size() >= 256) { - logDataError() << "Too long string: " << string.toStdString(); - return false; - } - for (auto ch : string) { - if (_binaryFullLength > 0) source->stream() << ","; - if (!_binaryCount++) { - source->stream() << "\n"; - } else { - if (_binaryCount == 12) { - _binaryCount = 0; - } - source->stream() << " "; - } - source->stream() << "0x" << QString::number(ch.unicode(), 16); - ++_binaryFullLength; - } - return true; -} - -void Generator::writeIntBinary(common::CppFile *source, int data) { - if (_binaryFullLength > 0) source->stream() << ","; - if (!_binaryCount++) { - source->stream() << "\n"; - } else { - if (_binaryCount == 12) { - _binaryCount = 0; - } - source->stream() << " "; - } - source->stream() << data; - ++_binaryFullLength; -} - -void Generator::writeUintBinary(common::CppFile *source, uint32 data) { - if (_binaryFullLength > 0) source->stream() << ","; - if (!_binaryCount++) { - source->stream() << "\n"; - } else { - if (_binaryCount == 12) { - _binaryCount = 0; - } - source->stream() << " "; - } - source->stream() << "0x" << QString::number(data, 16).toUpper() << "U"; - ++_binaryFullLength; -} - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/generator.h b/Telegram/SourceFiles/codegen/emoji/generator.h deleted file mode 100644 index b8c8541a7..000000000 --- a/Telegram/SourceFiles/codegen/emoji/generator.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/emoji/options.h" -#include "codegen/emoji/data.h" -#include "codegen/emoji/replaces.h" - -namespace codegen { -namespace emoji { - -using uint32 = unsigned int; - -class Generator { -public: - Generator(const Options &options); - Generator(const Generator &other) = delete; - Generator &operator=(const Generator &other) = delete; - - int generate(); - -private: -#ifdef SUPPORT_IMAGE_GENERATION - QImage generateImage(int imageIndex); - bool writeImages(); -#endif // SUPPORT_IMAGE_GENERATION - - bool writeSource(); - bool writeHeader(); - bool writeSuggestionsSource(); - bool writeSuggestionsHeader(); - - template - bool enumerateWholeList(Callback callback); - - bool writeInitCode(); - bool writeSections(); - bool writeReplacements(); - bool writeGetSections(); - bool writeFindReplace(); - bool writeFind(); - bool writeFindFromDictionary( - const std::map> &dictionary, - bool skipPostfixes = false, - const std::set &postfixRequired = {}); - bool writeGetReplacements(); - void startBinary(); - bool writeStringBinary(common::CppFile *source, const QString &string); - void writeIntBinary(common::CppFile *source, int data); - void writeUintBinary(common::CppFile *source, uint32 data); - - const common::ProjectInfo &project_; - int colorsCount_ = 0; -#ifdef SUPPORT_IMAGE_GENERATION - bool writeImages_ = false; -#endif // SUPPORT_IMAGE_GENERATION - QString outputPath_; - QString spritePath_; - std::unique_ptr source_; - Data data_; - - QString suggestionsPath_; - std::unique_ptr suggestionsSource_; - Replaces replaces_; - - int _binaryFullLength = 0; - int _binaryCount = 0; - -}; - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/main.cpp b/Telegram/SourceFiles/codegen/emoji/main.cpp deleted file mode 100644 index 3205055c2..000000000 --- a/Telegram/SourceFiles/codegen/emoji/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* -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 - -#include "codegen/emoji/options.h" -#include "codegen/emoji/generator.h" - -int main(int argc, char *argv[]) { -#ifdef SUPPORT_IMAGE_GENERATION -#ifndef Q_OS_MAC -#error "Image generation is supported only on macOS" -#endif // Q_OS_MAC - QGuiApplication app(argc, argv); -#else // SUPPORT_IMAGE_GENERATION - QCoreApplication app(argc, argv); -#endif // SUPPORT_IMAGE_GENERATION - - auto options = codegen::emoji::parseOptions(); - - codegen::emoji::Generator generator(options); - return generator.generate(); -} diff --git a/Telegram/SourceFiles/codegen/emoji/options.cpp b/Telegram/SourceFiles/codegen/emoji/options.cpp deleted file mode 100644 index 4f11ed637..000000000 --- a/Telegram/SourceFiles/codegen/emoji/options.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 "codegen/emoji/options.h" - -#include -#include -#include "codegen/common/logging.h" - -namespace codegen { -namespace emoji { -namespace { - -constexpr int kErrorOutputPathExpected = 902; -constexpr int kErrorReplacesPathExpected = 903; -constexpr int kErrorOneReplacesPathExpected = 904; - -} // namespace - -using common::logError; - -Options parseOptions() { - Options result; - auto args = QCoreApplication::instance()->arguments(); - for (int i = 1, count = args.size(); i < count; ++i) { // skip first - auto &arg = args.at(i); - - // Output path - if (arg == "-o") { - if (++i == count) { - logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; - return Options(); - } else { - result.outputPath = args.at(i); - } - } else if (arg.startsWith("-o")) { - result.outputPath = arg.mid(2); -#ifdef SUPPORT_IMAGE_GENERATION - } else if (arg == "--images") { - result.writeImages = true; -#endif // SUPPORT_IMAGE_GENERATION - } else if (result.replacesPath.isEmpty()) { - result.replacesPath = arg; - } else { - logError(kErrorOneReplacesPathExpected, "Command Line") << "only one replaces path expected"; - return Options(); - } - } - if (result.outputPath.isEmpty()) { - logError(kErrorOutputPathExpected, "Command Line") << "output path expected"; - return Options(); - } else if (result.replacesPath.isEmpty()) { - logError(kErrorReplacesPathExpected, "Command Line") << "replaces path expected"; - return Options(); - } - return result; -} - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/options.h b/Telegram/SourceFiles/codegen/emoji/options.h deleted file mode 100644 index 6803ce657..000000000 --- a/Telegram/SourceFiles/codegen/emoji/options.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -#ifdef __APPLE__ -#define SUPPORT_IMAGE_GENERATION -#endif // __APPLE__ - -namespace codegen { -namespace emoji { - -struct Options { - QString outputPath = "."; - QString replacesPath; -#ifdef SUPPORT_IMAGE_GENERATION - bool writeImages = false; -#endif // SUPPORT_IMAGE_GENERATION -}; - -// Parsing failed if inputPath is empty in the result. -Options parseOptions(); - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/replaces.cpp b/Telegram/SourceFiles/codegen/emoji/replaces.cpp deleted file mode 100644 index 3b5c237e7..000000000 --- a/Telegram/SourceFiles/codegen/emoji/replaces.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* -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 "codegen/emoji/replaces.h" - -#include "codegen/emoji/data.h" -#include -#include -#include -#include - -namespace codegen { -namespace emoji { -namespace { - -constexpr auto kErrorBadReplaces = 402; - -common::LogStream logReplacesError(const QString &filename) { - return common::logError(kErrorBadReplaces, filename) << "Bad data: "; -} - -auto RegExpCode = QRegularExpression("^:[\\+\\-a-z0-9_]+:$"); -auto RegExpTone = QRegularExpression("_tone[0-9]"); -auto RegExpHex = QRegularExpression("^[0-9a-f]+$"); - -class ReplacementWords { -public: - ReplacementWords(const QString &string); - QVector result() const; - -private: - friend ReplacementWords operator+(const ReplacementWords &a, const ReplacementWords &b); - - QMap wordsWithCounts_; - -}; - -ReplacementWords::ReplacementWords(const QString &string) { - auto feedWord = [this](QString &word) { - if (!word.isEmpty()) { - ++wordsWithCounts_[word]; - word.clear(); - } - }; - // Split by all non-letters-or-numbers. - // Leave '-' and '+' inside a word only if they're followed by a number. - auto word = QString(); - for (auto i = string.cbegin(), e = string.cend(); i != e; ++i) { - if (i->isLetterOrNumber()) { - word.append(*i); - continue; - } else if (*i == '-' || *i == '+') { - if (i + 1 != e && (i + 1)->isNumber()) { - word.append(*i); - continue; - } - } - feedWord(word); - } - feedWord(word); -} - -QVector ReplacementWords::result() const { - auto result = QVector(); - for (auto i = wordsWithCounts_.cbegin(), e = wordsWithCounts_.cend(); i != e; ++i) { - for (auto j = 0, count = i.value(); j != count; ++j) { - result.push_back(i.key()); - } - } - return result; -} - -ReplacementWords operator+(const ReplacementWords &a, const ReplacementWords &b) { - ReplacementWords result = a; - for (auto i = b.wordsWithCounts_.cbegin(), e = b.wordsWithCounts_.cend(); i != e; ++i) { - auto j = result.wordsWithCounts_.constFind(i.key()); - if (j == result.wordsWithCounts_.cend() || j.value() < i.value()) { - result.wordsWithCounts_[i.key()] = i.value(); - } - } - return result; -} - -bool AddReplacement(Replaces &result, const Id &id, const QString &replacement, const QString &name) { - auto replace = Replace(); - replace.id = id; - replace.replacement = replacement; - replace.words = (ReplacementWords(replacement)).result();// + ReplacementWords(name)).result(); - if (replace.words.isEmpty()) { - logReplacesError(result.filename) << "Child '" << replacement.toStdString() << "' has no words."; - return false; - } - result.list.push_back(replace); - return true; -} - -QString ComposeString(const std::initializer_list &chars) { - auto result = QString(); - result.reserve(chars.size()); - for (auto ch : chars) { - result.append(ch); - } - return result; -} - -const auto NotSupported = [] { - auto result = QSet(); - auto insert = [&result](auto... args) { - result.insert(ComposeString({ args... })); - }; - insert(0x0023, 0xFE0F); // :pound_symbol: - insert(0x002A, 0xFE0F); // :asterisk_symbol: - for (auto i = 0; i != 10; ++i) { - insert(0x0030 + i, 0xFE0F); // :digit_zero: ... :digit_nine: - } - for (auto i = 0; i != 5; ++i) { - insert(0xD83C, 0xDFFB + i); // :tone1: ... :tone5: - } - for (auto i = 0; i != 26; ++i) { - insert(0xD83C, 0xDDE6 + i); // :regional_indicator_a: ... :regional_indicator_z: - } - insert(0x2640, 0xFE0F); // :female_sign: - insert(0x2642, 0xFE0F); // :male_sign: - insert(0x2695, 0xFE0F); // :medical_symbol: - - return result; -}(); - -const auto ConvertMap = ([] { - auto result = QMap(); - auto insert = [&result](const std::initializer_list &from, const std::initializer_list &to) { - result.insert(ComposeString(from), ComposeString(to)); - }; - auto insertWithAdd = [&result](const std::initializer_list &from, const QString &added) { - auto code = ComposeString(from); - result.insert(code, code + added); - }; - auto maleModifier = ComposeString({ 0x200D, 0x2642, 0xFE0F }); - auto femaleModifier = ComposeString({ 0x200D, 0x2640, 0xFE0F }); - insertWithAdd({ 0xD83E, 0xDD26 }, maleModifier); - insertWithAdd({ 0xD83E, 0xDD37 }, femaleModifier); - insertWithAdd({ 0xD83E, 0xDD38 }, maleModifier); - insertWithAdd({ 0xD83E, 0xDD39 }, maleModifier); - insertWithAdd({ 0xD83E, 0xDD3C }, maleModifier); - insertWithAdd({ 0xD83E, 0xDD3D }, maleModifier); - insertWithAdd({ 0xD83E, 0xDD3E }, femaleModifier); - - // :kiss_woman_man: - insert({ 0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68 }, { 0xD83D, 0xDC8F }); - - // :family_man_woman_boy: - insert({ 0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69, 0x200D, 0xD83D, 0xDC66 }, { 0xD83D, 0xDC6A }); - - // :couple_with_heart_woman_man: - insert({ 0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }, { 0xD83D, 0xDC91 }); - - auto insertFlag = [insert](char ch1, char ch2, char ch3, char ch4) { - insert({ 0xD83C, 0xDDE6 + (ch1 - 'a'), 0xD83C, 0xDDe6 + (ch2 - 'a') }, { 0xD83C, 0xDDE6 + (ch3 - 'a'), 0xD83C, 0xDDe6 + (ch4 - 'a') }); - }; - insertFlag('a', 'c', 's', 'h'); - insertFlag('b', 'v', 'n', 'o'); - insertFlag('c', 'p', 'f', 'r'); - insertFlag('d', 'g', 'i', 'o'); - insertFlag('e', 'a', 'e', 's'); - insertFlag('h', 'm', 'a', 'u'); - insertFlag('m', 'f', 'f', 'r'); - insertFlag('s', 'j', 'n', 'o'); - insertFlag('t', 'a', 's', 'h'); - insertFlag('u', 'm', 'u', 's'); - - return result; -})(); - -// Empty string result means we should skip this one. -QString ConvertEmojiId(const Id &id, const QString &replacement) { - if (RegExpTone.match(replacement).hasMatch()) { - return QString(); - } - if (NotSupported.contains(id)) { - return QString(); - } - return ConvertMap.value(id, id); -} - -} // namespace - -Replaces PrepareReplaces(const QString &filename) { - auto result = Replaces(filename); - auto content = ([filename] { - QFile f(filename); - return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray(); - })(); - if (content.isEmpty()) { - logReplacesError(filename) << "Could not read data."; - return result; - } - auto error = QJsonParseError(); - auto document = QJsonDocument::fromJson(content, &error); - if (error.error != QJsonParseError::NoError) { - logReplacesError(filename) << "Could not parse data (" << int(error.error) << "): " << error.errorString().toStdString(); - return result; - } - if (!document.isObject()) { - logReplacesError(filename) << "Root object not found."; - return result; - } - auto list = document.object(); - for (auto i = list.constBegin(), e = list.constEnd(); i != e; ++i) { - if (!(*i).isObject()) { - logReplacesError(filename) << "Child object not found."; - return Replaces(filename); - } - auto childKey = i.key(); - auto child = (*i).toObject(); - auto failed = false; - auto getString = [filename, childKey, &child, &failed](const QString &key) { - auto it = child.constFind(key); - if (it == child.constEnd() || !(*it).isString()) { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' field not found: " << key.toStdString(); - failed = true; - return QString(); - } - return (*it).toString(); - }; - auto idParts = getString("output").split('-'); - auto name = getString("name"); - auto replacement = getString("alpha_code"); - auto aliases = getString("aliases").split('|'); - const auto Exceptions = { ":shrug:" }; - for (const auto &exception : Exceptions) { - const auto index = aliases.indexOf(exception); - if (index >= 0) { - aliases.removeAt(index); - } - } - if (aliases.size() == 1 && aliases[0].isEmpty()) { - aliases.clear(); - } - if (failed) { - return Replaces(filename); - } - if (!RegExpCode.match(replacement).hasMatch()) { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' alpha_code invalid: " << replacement.toStdString(); - return Replaces(filename); - } - for (auto &alias : aliases) { - if (!RegExpCode.match(alias).hasMatch()) { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' alias invalid: " << alias.toStdString(); - return Replaces(filename); - } - } - auto id = Id(); - for (auto &idPart : idParts) { - auto ok = true; - auto utf32 = idPart.toInt(&ok, 0x10); - if (!ok || !RegExpHex.match(idPart).hasMatch()) { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString(); - return Replaces(filename); - } - if (utf32 >= 0 && utf32 < 0x10000) { - auto ch = QChar(ushort(utf32)); - if (ch.isLowSurrogate() || ch.isHighSurrogate()) { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString(); - return Replaces(filename); - } - id.append(ch); - } else if (utf32 >= 0x10000 && utf32 <= 0x10FFFF) { - auto hi = ((utf32 - 0x10000) / 0x400) + 0xD800; - auto lo = ((utf32 - 0x10000) % 0x400) + 0xDC00; - id.append(QChar(ushort(hi))); - id.append(QChar(ushort(lo))); - } else { - logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString(); - return Replaces(filename); - } - } - id = ConvertEmojiId(id, replacement); - if (id.isEmpty()) { - continue; - } - if (!AddReplacement(result, id, replacement, name)) { - return Replaces(filename); - } - for (auto &alias : aliases) { - if (!AddReplacement(result, id, alias, name)) { - return Replaces(filename); - } - } - } - if (!AddReplacement(result, ComposeString({ 0xD83D, 0xDC4D }), ":like:", "thumbs up")) { - return Replaces(filename); - } - if (!AddReplacement(result, ComposeString({ 0xD83D, 0xDC4E }), ":dislike:", "thumbs down")) { - return Replaces(filename); - } - if (!AddReplacement(result, ComposeString({ 0xD83E, 0xDD14 }), ":hmm:", "thinking")) { - return Replaces(filename); - } - if (!AddReplacement(result, ComposeString({ 0xD83E, 0xDD73 }), ":party:", "celebration")) { - return Replaces(filename); - } - return result; -} - -bool CheckAndConvertReplaces(Replaces &replaces, const Data &data) { - auto result = Replaces(replaces.filename); - auto sorted = QMap(); - auto findId = [&data](const Id &id) { - return data.map.find(id) != data.map.cend(); - }; - auto findAndSort = [findId, &data, &sorted](Id id, const Replace &replace) { - if (!findId(id)) { - id.replace(QChar(0xFE0F), QString()); - if (!findId(id)) { - return false; - } - } - auto it = data.map.find(id); - id = data.list[it->second].id; - if (data.list[it->second].postfixed) { - id += QChar(kPostfix); - } - auto inserted = sorted.insertMulti(id, replace); - inserted.value().id = id; - return true; - }; - - // Find all replaces in data.map, adjust id if necessary. - // Store all replaces in sorted map to find them fast afterwards. - auto maleModifier = ComposeString({ 0x200D, 0x2642, 0xFE0F }); - auto femaleModifier = ComposeString({ 0x200D, 0x2640, 0xFE0F }); - for (auto &replace : replaces.list) { - if (findAndSort(replace.id, replace)) { - continue; - } - if (replace.id.endsWith(maleModifier)) { - auto defaultId = replace.id.mid(0, replace.id.size() - maleModifier.size()); - if (findAndSort(defaultId, replace)) { - continue; - } - } else if (replace.id.endsWith(femaleModifier)) { - auto defaultId = replace.id.mid(0, replace.id.size() - femaleModifier.size()); - if (findAndSort(defaultId, replace)) { - continue; - } - } else if (findId(replace.id + maleModifier)) { - if (findId(replace.id + femaleModifier)) { - logReplacesError(replaces.filename) << "Replace '" << replace.replacement.toStdString() << "' ambiguous."; - return false; - } else { - findAndSort(replace.id + maleModifier, replace); - continue; - } - } else if (findAndSort(replace.id + femaleModifier, replace)) { - continue; - } - logReplacesError(replaces.filename) << "Replace '" << replace.replacement.toStdString() << "' not found."; - return false; - } - - // Go through all categories and put all replaces in order of emoji in categories. - result.list.reserve(replaces.list.size()); - for (auto &category : data.categories) { - for (auto index : category) { - auto id = data.list[index].id; - if (data.list[index].postfixed) { - id += QChar(kPostfix); - } - for (auto it = sorted.find(id); it != sorted.cend(); sorted.erase(it), it = sorted.find(id)) { - result.list.push_back(it.value()); - } - } - } - if (result.list.size() != replaces.list.size()) { - logReplacesError(replaces.filename) << "Some were not found."; - return false; - } - if (!sorted.isEmpty()) { - logReplacesError(replaces.filename) << "Weird."; - return false; - } - replaces = std::move(result); - return true; -} - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/emoji/replaces.h b/Telegram/SourceFiles/codegen/emoji/replaces.h deleted file mode 100644 index 9156eee8b..000000000 --- a/Telegram/SourceFiles/codegen/emoji/replaces.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "codegen/common/logging.h" -#include "codegen/emoji/data.h" -#include - -namespace codegen { -namespace emoji { - -struct Replace { - Id id; - QString replacement; - QVector words; -}; - -struct Replaces { - Replaces(const QString &filename) : filename(filename) { - } - QString filename; - QVector list; -}; - -Replaces PrepareReplaces(const QString &filename); -bool CheckAndConvertReplaces(Replaces &replaces, const Data &data); - -} // namespace emoji -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/generator.cpp b/Telegram/SourceFiles/codegen/lang/generator.cpp deleted file mode 100644 index 400a5638c..000000000 --- a/Telegram/SourceFiles/codegen/lang/generator.cpp +++ /dev/null @@ -1,593 +0,0 @@ -/* -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 "codegen/lang/generator.h" - -#include -#include -#include -#include -#include -#include - -namespace codegen { -namespace lang { -namespace { - -char hexChar(uchar ch) { - if (ch < 10) { - return '0' + ch; - } else if (ch < 16) { - return 'a' + (ch - 10); - } - return '0'; -} - -char hexSecondChar(char ch) { - return hexChar((*reinterpret_cast(&ch)) & 0x0F); -} - -char hexFirstChar(char ch) { - return hexChar((*reinterpret_cast(&ch)) >> 4); -} - -QString stringToEncodedString(const QString &str) { - QString result, lineBreak = "\\\n"; - result.reserve(str.size() * 8); - bool writingHexEscapedCharacters = false, startOnNewLine = false; - int lastCutSize = 0; - auto utf = str.toUtf8(); - for (auto ch : utf) { - if (result.size() - lastCutSize > 80) { - startOnNewLine = true; - result.append(lineBreak); - lastCutSize = result.size(); - } - if (ch == '\n') { - writingHexEscapedCharacters = false; - result.append("\\n"); - } else if (ch == '\t') { - writingHexEscapedCharacters = false; - result.append("\\t"); - } else if (ch == '"' || ch == '\\') { - writingHexEscapedCharacters = false; - result.append('\\').append(ch); - } else if (ch < 32 || static_cast(ch) > 127) { - writingHexEscapedCharacters = true; - result.append("\\x").append(hexFirstChar(ch)).append(hexSecondChar(ch)); - } else { - if (writingHexEscapedCharacters) { - writingHexEscapedCharacters = false; - result.append("\"\""); - } - result.append(ch); - } - } - return '"' + (startOnNewLine ? lineBreak : QString()) + result + '"'; -} - -QString stringToEncodedString(const std::string &str) { - return stringToEncodedString(QString::fromStdString(str)); -} - -QString stringToBinaryArray(const std::string &str) { - QStringList rows, chars; - chars.reserve(13); - rows.reserve(1 + (str.size() / 13)); - for (uchar ch : str) { - if (chars.size() > 12) { - rows.push_back(chars.join(", ")); - chars.clear(); - } - chars.push_back(QString("0x") + hexFirstChar(ch) + hexSecondChar(ch)); - } - if (!chars.isEmpty()) { - rows.push_back(chars.join(", ")); - } - return QString("{") + ((rows.size() > 1) ? '\n' : ' ') + rows.join(",\n") + " }"; -} - -} // namespace - -Generator::Generator(const LangPack &langpack, const QString &destBasePath, const common::ProjectInfo &project) -: langpack_(langpack) -, basePath_(destBasePath) -, baseName_(QFileInfo(basePath_).baseName()) -, project_(project) { -} - -bool Generator::writeHeader() { - header_ = std::make_unique(basePath_ + ".h", project_); - header_->include("lang/lang_tag.h").include("lang/lang_values.h").newline(); - - writeHeaderForwardDeclarations(); - writeHeaderTagTypes(); - writeHeaderInterface(); - writeHeaderReactiveInterface(); - - return header_->finalize(); -} - -void Generator::writeHeaderForwardDeclarations() { - header_->pushNamespace("Lang").stream() << "\ -\n\ -inline constexpr auto kTagsCount = ushort(" << langpack_.tags.size() << ");\n\ -inline constexpr auto kKeysCount = ushort(" << langpack_.entries.size() << ");\n\ -\n"; - header_->popNamespace().newline(); -} - -void Generator::writeHeaderTagTypes() { - auto index = 0; - for (auto &tag : langpack_.tags) { - if (tag.tag == kPluralTags[0]) { - auto elements = QStringList(); - header_->stream() - << "enum lngtag_" << tag.tag << " : int { "; - for (auto i = 0; i != kPluralTags.size(); ++i) { - elements.push_back("lt_" + kPluralTags[i] + " = " + QString::number(index + i * 1000)); - } - header_->stream() << elements.join(", ") << " };\n"; - ++index; - } else { - header_->stream() << "enum lngtag_" << tag.tag << " : int { lt_" << tag.tag << " = " << index++ << " };\n"; - } - } - header_->newline(); -} - -void Generator::writeHeaderInterface() { - header_->pushNamespace("Lang").stream() << "\ -\n\ -ushort GetTagIndex(QLatin1String tag);\n\ -ushort GetKeyIndex(QLatin1String key);\n\ -bool IsTagReplaced(ushort key, ushort tag);\n\ -QString GetOriginalValue(ushort key);\n\ -\n"; - writeHeaderTagValueLookup(); - header_->popNamespace().newline(); -} - -void Generator::writeHeaderTagValueLookup() { - header_->pushNamespace("details").stream() << "\ -\n\ -template \n\ -struct TagData;\n\ -\n\ -template \n\ -inline constexpr ushort TagValue() {\n\ - return TagData::value;\n\ -}\n\ -\n"; - - for (auto &tag : langpack_.tags) { - header_->stream() << "template <> struct TagData : std::integral_constant {};\n"; - } - - header_->newline().popNamespace(); -} - -void Generator::writeHeaderReactiveInterface() { - header_->pushNamespace("tr"); - - writeHeaderProducersInterface(); - writeHeaderProducersInstances(); - - header_->popNamespace().newline(); -} - -void Generator::writeHeaderProducersInterface() { - header_->pushNamespace("details").stream() << "\ -\n\ -struct Identity {\n\ - QString operator()(const QString &value) const {\n\ - return value;\n\ - }\n\ -};\n\ -\n"; - - header_->popNamespace().newline(); - header_->stream() << "\ -struct now_t {\n\ -};\n\ -\n\ -inline constexpr now_t now{};\n\ -\n\ -inline auto to_count() {\n\ - return rpl::map([](auto value) {\n\ - return float64(value);\n\ - });\n\ -}\n\ -\n\ -template \n\ -using S = std::decay_t()(QString()))>;\n\ -\n\ -template \n\ -struct phrase;\n\ -\n"; - std::set producersDeclared; - for (auto &entry : langpack_.entries) { - const auto isPlural = !entry.keyBase.isEmpty(); - const auto &key = entry.key; - auto tags = QStringList(); - auto producerArgs = QStringList(); - auto currentArgs = QStringList(); - auto values = QStringList(); - values.push_back("base"); - values.push_back("std::move(p)"); - for (auto &tagData : entry.tags) { - const auto &tag = tagData.tag; - const auto isPluralTag = isPlural && (tag == kPluralTags[0]); - tags.push_back("lngtag_" + tag); - const auto type1 = "lngtag_" + tag; - const auto arg1 = type1 + (isPluralTag ? " type" : ""); - const auto producerType2 = (isPluralTag ? "rpl::producer " : "rpl::producer> "); - const auto producerArg2 = producerType2 + tag + "__val"; - const auto currentType2 = (isPluralTag ? "float64 " : "const S

&"); - const auto currentArg2 = currentType2 + tag + "__val"; - producerArgs.push_back(arg1 + ", " + producerArg2); - currentArgs.push_back(arg1 + ", " + currentArg2); - if (isPluralTag) { - values.push_back("type"); - } - values.push_back(tag + "__val"); - } - producerArgs.push_back("P p = P()"); - currentArgs.push_back("P p = P()"); - if (!producersDeclared.emplace(tags.join(',')).second) { - continue; - } - header_->stream() << "\ -template <>\n\ -struct phrase<" << tags.join(", ") << "> {\n\ - template \n\ - rpl::producer> operator()(" << producerArgs.join(", ") << ") const {\n\ - return ::Lang::details::Producer<" << tags.join(", ") << ">::template Combine(" << values.join(", ") << ");\n\ - }\n\ -\n\ - template \n\ - S

operator()(now_t, " << currentArgs.join(", ") << ") const {\n\ - return ::Lang::details::Producer<" << tags.join(", ") << ">::template Current(" << values.join(", ") << ");\n\ - }\n\ -\n\ - ushort base;\n\ -};\n\ -\n"; - } -} - -void Generator::writeHeaderProducersInstances() { - auto index = 0; - for (auto &entry : langpack_.entries) { - const auto isPlural = !entry.keyBase.isEmpty(); - const auto &key = entry.key; - auto tags = QStringList(); - for (auto &tagData : entry.tags) { - const auto &tag = tagData.tag; - tags.push_back("lngtag_" + tag); - } - if (!isPlural || key == ComputePluralKey(entry.keyBase, 0)) { - header_->stream() << "\ -inline constexpr phrase<" << tags.join(", ") << "> " << (isPlural ? entry.keyBase : key) << "{ ushort(" << index << ") };\n"; - } - ++index; - } - header_->newline(); -} - -bool Generator::writeSource() { - source_ = std::make_unique(basePath_ + ".cpp", project_); - - source_->include("lang/lang_keys.h").pushNamespace("Lang").pushNamespace(); - - source_->stream() << "\ -QChar DefaultData[] = {"; - auto count = 0; - auto fulllength = 0; - for (auto &entry : langpack_.entries) { - for (auto ch : entry.value) { - if (fulllength > 0) source_->stream() << ","; - if (!count++) { - source_->stream() << "\n"; - } else { - if (count == 12) { - count = 0; - } - source_->stream() << " "; - } - source_->stream() << "0x" << QString::number(ch.unicode(), 16); - ++fulllength; - } - } - source_->stream() << " };\n\ -\n\ -int Offsets[] = {"; - count = 0; - auto offset = 0; - auto writeOffset = [this, &count, &offset] { - if (offset > 0) source_->stream() << ","; - if (!count++) { - source_->stream() << "\n"; - } else { - if (count == 12) { - count = 0; - } - source_->stream() << " "; - } - source_->stream() << offset; - }; - for (auto &entry : langpack_.entries) { - writeOffset(); - offset += entry.value.size(); - } - writeOffset(); - source_->stream() << " };\n"; - source_->popNamespace().stream() << "\ -\n\ -ushort GetTagIndex(QLatin1String tag) {\n\ - auto size = tag.size();\n\ - auto data = tag.data();\n"; - - auto tagsSet = std::set>(); - for (auto &tag : langpack_.tags) { - tagsSet.insert(tag.tag); - } - - writeSetSearch(tagsSet, [](const QString &tag) { - return "ushort(lt_" + tag + ")"; - }, "kTagsCount"); - - source_->stream() << "\ -}\n\ -\n\ -ushort GetKeyIndex(QLatin1String key) {\n\ - auto size = key.size();\n\ - auto data = key.data();\n"; - - auto index = 0; - auto indices = std::map(); - for (auto &entry : langpack_.entries) { - indices.emplace(getFullKey(entry), QString::number(index++)); - } - const auto indexOfKey = [&](const QString &full) { - const auto i = indices.find(full); - if (i == indices.end()) { - return QString(); - } - return i->second; - }; - - auto taggedKeys = std::map(); - auto keysSet = std::set>(); - for (auto &entry : langpack_.entries) { - if (!entry.keyBase.isEmpty()) { - for (auto i = 0; i != kPluralPartCount; ++i) { - auto keyName = entry.keyBase + '#' + kPluralParts[i]; - taggedKeys.emplace(keyName, ComputePluralKey(entry.keyBase, i)); - keysSet.insert(keyName); - } - } else { - auto full = getFullKey(entry); - if (full != entry.key) { - taggedKeys.emplace(entry.key, full); - } - keysSet.insert(entry.key); - } - } - - writeSetSearch(keysSet, [&](const QString &key) { - auto it = taggedKeys.find(key); - const auto name = (it != taggedKeys.end()) ? it->second : key; - return indexOfKey(name); - }, "kKeysCount"); - header_->popNamespace().newline(); - - source_->stream() << "\ -}\n\ -\n\ -bool IsTagReplaced(ushort key, ushort tag) {\n\ - switch (key) {\n"; - - auto lastWrittenPluralEntry = QString(); - for (auto &entry : langpack_.entries) { - if (entry.tags.empty()) { - continue; - } - if (!entry.keyBase.isEmpty()) { - if (entry.keyBase == lastWrittenPluralEntry) { - continue; - } - lastWrittenPluralEntry = entry.keyBase; - for (auto i = 0; i != kPluralPartCount; ++i) { - source_->stream() << "\ - case " << indexOfKey(ComputePluralKey(entry.keyBase, i)) << ":" << ((i + 1 == kPluralPartCount) ? " {" : "") << "\n"; - } - } else { - source_->stream() << "\ - case " << indexOfKey(getFullKey(entry)) << ": {\n"; - } - source_->stream() << "\ - switch (tag) {\n"; - for (auto &tag : entry.tags) { - source_->stream() << "\ - case lt_" << tag.tag << ":\n"; - } - source_->stream() << "\ - return true;\n\ - }\n\ - } break;\n"; - } - - source_->stream() << "\ - }\ -\n\ - return false;\n\ -}\n\ -\n\ -QString GetOriginalValue(ushort key) {\n\ - Expects(key < kKeysCount);\n\ -\n\ - const auto offset = Offsets[key];\n\ - return QString::fromRawData(DefaultData + offset, Offsets[key + 1] - offset);\n\ -}\n\ -\n"; - - return source_->finalize(); -} - -template -void Generator::writeSetSearch(const std::set> &set, ComputeResult computeResult, const QString &invalidResult) { - auto tabs = [](int size) { - return QString(size, '\t'); - }; - - enum class UsedCheckType { - Switch, - If, - UpcomingIf, - }; - auto checkTypes = QVector(); - auto checkLengthHistory = QVector(); - auto chars = QString(); - auto tabsUsed = 1; - - // Returns true if at least one check was finished. - auto finishChecksTillKey = [this, &chars, &checkTypes, &checkLengthHistory, &tabsUsed, tabs](const QString &key) { - auto result = false; - while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) { - result = true; - - auto wasType = checkTypes.back(); - chars.resize(chars.size() - 1); - checkTypes.pop_back(); - if (wasType == UsedCheckType::Switch || wasType == UsedCheckType::If) { - --tabsUsed; - if (wasType == UsedCheckType::Switch) { - source_->stream() << tabs(tabsUsed) << "break;\n"; - } - if ((!chars.isEmpty() && key.midRef(0, chars.size()) != chars) || key == chars) { - source_->stream() << tabs(tabsUsed) << "}\n"; - checkLengthHistory.pop_back(); - } - } - } - return result; - }; - - // Check if we can use "if" for a check on "charIndex" in "it" (otherwise only "switch") - auto canUseIfForCheck = [](auto it, auto end, int charIndex) { - auto key = *it; - auto i = it; - auto keyStart = key.mid(0, charIndex); - for (++i; i != end; ++i) { - auto nextKey = *i; - if (nextKey.mid(0, charIndex) != keyStart) { - return true; - } else if (nextKey.size() > charIndex && nextKey[charIndex] != key[charIndex]) { - return false; - } - } - return true; - }; - - auto countMinimalLength = [](auto it, auto end, int charIndex) { - auto key = *it; - auto i = it; - auto keyStart = key.mid(0, charIndex); - auto result = key.size(); - for (++i; i != end; ++i) { - auto nextKey = *i; - if (nextKey.mid(0, charIndex) != keyStart) { - break; - } else if (nextKey.size() > charIndex && result > nextKey.size()) { - result = nextKey.size(); - } - } - return result; - }; - - for (auto i = set.begin(), e = set.end(); i != e; ++i) { - // If we use just "auto" here and "name" becomes mutable, - // the operator[] will return QCharRef instead of QChar, - // and "auto ch = name[index]" will behave like "auto &ch =", - // if you assign something to "ch" after that you'll change "name" (!) - const auto name = *i; - - auto weContinueOldSwitch = finishChecksTillKey(name); - while (chars.size() != name.size()) { - auto checking = chars.size(); - auto partialKey = name.mid(0, checking); - - auto keyChar = name[checking]; - auto usedIfForCheckCount = 0; - auto minimalLengthCheck = countMinimalLength(i, e, checking); - for (; checking + usedIfForCheckCount != name.size(); ++usedIfForCheckCount) { - if (!canUseIfForCheck(i, e, checking + usedIfForCheckCount) - || countMinimalLength(i, e, checking + usedIfForCheckCount) != minimalLengthCheck) { - break; - } - } - auto usedIfForCheck = !weContinueOldSwitch && (usedIfForCheckCount > 0); - const auto checkedLength = checkLengthHistory.isEmpty() - ? 0 - : checkLengthHistory.back(); - const auto requiredLength = qMax( - minimalLengthCheck, - checkedLength); - auto checkLengthCondition = QString(); - if (weContinueOldSwitch) { - weContinueOldSwitch = false; - } else { - checkLengthCondition = (requiredLength > checkedLength) ? ("size >= " + QString::number(requiredLength)) : QString(); - if (!usedIfForCheck) { - source_->stream() << tabs(tabsUsed) << (checkLengthCondition.isEmpty() ? QString() : ("if (" + checkLengthCondition + ") ")) << "switch (data[" << checking << "]) {\n"; - checkLengthHistory.push_back(requiredLength); - } - } - if (usedIfForCheck) { - auto conditions = QStringList(); - if (usedIfForCheckCount > 1) { - conditions.push_back("!memcmp(data + " + QString::number(checking) + ", \"" + name.mid(checking, usedIfForCheckCount) + "\", " + QString::number(usedIfForCheckCount) + ")"); - } else { - conditions.push_back("data[" + QString::number(checking) + "] == '" + keyChar + "'"); - } - if (!checkLengthCondition.isEmpty()) { - conditions.push_front(checkLengthCondition); - } - source_->stream() << tabs(tabsUsed) << "if (" << conditions.join(" && ") << ") {\n"; - checkLengthHistory.push_back(requiredLength); - checkTypes.push_back(UsedCheckType::If); - for (auto i = 1; i != usedIfForCheckCount; ++i) { - checkTypes.push_back(UsedCheckType::UpcomingIf); - chars.push_back(keyChar); - keyChar = name[checking + i]; - } - } else { - source_->stream() << tabs(tabsUsed) << "case '" << keyChar << "':\n"; - checkTypes.push_back(UsedCheckType::Switch); - } - ++tabsUsed; - chars.push_back(keyChar); - } - source_->stream() << tabs(tabsUsed) << "return (size == " << chars.size() << ") ? " << computeResult(name) << " : " << invalidResult << ";\n"; - } - finishChecksTillKey(QString()); - - source_->stream() << "\ -\n\ - return " << invalidResult << ";\n"; -} - -QString Generator::getFullKey(const LangPack::Entry &entry) { - if (!entry.keyBase.isEmpty() || entry.tags.empty()) { - return entry.key; - } - return entry.key + "__tagged"; -} - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/generator.h b/Telegram/SourceFiles/codegen/lang/generator.h deleted file mode 100644 index e60e370c2..000000000 --- a/Telegram/SourceFiles/codegen/lang/generator.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/lang/parsed_file.h" - -namespace codegen { -namespace lang { - -class Generator { -public: - Generator(const LangPack &langpack, const QString &destBasePath, const common::ProjectInfo &project); - Generator(const Generator &other) = delete; - Generator &operator=(const Generator &other) = delete; - - bool writeHeader(); - bool writeSource(); - -private: - void writeHeaderForwardDeclarations(); - void writeHeaderTagTypes(); - void writeHeaderInterface(); - void writeHeaderTagValueLookup(); - void writeHeaderReactiveInterface(); - void writeHeaderProducersInterface(); - void writeHeaderProducersInstances(); - - QString getFullKey(const LangPack::Entry &entry); - - template - void writeSetSearch(const std::set> &set, ComputeResult computeResult, const QString &invalidResult); - - const LangPack &langpack_; - QString basePath_, baseName_; - const common::ProjectInfo &project_; - std::unique_ptr source_, header_; - -}; - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/main.cpp b/Telegram/SourceFiles/codegen/lang/main.cpp deleted file mode 100644 index 4ebdf9a59..000000000 --- a/Telegram/SourceFiles/codegen/lang/main.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 - -#include "codegen/lang/options.h" -#include "codegen/lang/processor.h" - -int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); - - auto options = codegen::lang::parseOptions(); - if (options.inputPath.isEmpty()) { - return -1; - } - - codegen::lang::Processor processor(options); - return processor.launch(); -} diff --git a/Telegram/SourceFiles/codegen/lang/options.cpp b/Telegram/SourceFiles/codegen/lang/options.cpp deleted file mode 100644 index 0400acdc2..000000000 --- a/Telegram/SourceFiles/codegen/lang/options.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -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 "codegen/lang/options.h" - -#include -#include -#include -#include "codegen/common/logging.h" - -namespace codegen { -namespace lang { -namespace { - -constexpr int kErrorOutputPathExpected = 902; -constexpr int kErrorInputPathExpected = 903; -constexpr int kErrorSingleInputPathExpected = 904; -constexpr int kErrorWorkingPathExpected = 905; - -} // namespace - -using common::logError; - -Options parseOptions() { - Options result; - auto args = QCoreApplication::instance()->arguments(); - for (int i = 1, count = args.size(); i < count; ++i) { // skip first - auto &arg = args.at(i); - - // Output path - if (arg == "-o") { - if (++i == count) { - logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; - return Options(); - } else { - result.outputPath = args.at(i); - } - } else if (arg.startsWith("-o")) { - result.outputPath = arg.mid(2); - - // Working path - } else if (arg == "-w") { - if (++i == count) { - logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w"; - return Options(); - } else { - common::logSetWorkingPath(args.at(i)); - } - } else if (arg.startsWith("-w")) { - common::logSetWorkingPath(arg.mid(2)); - - // Input path - } else { - if (result.inputPath.isEmpty()) { - result.inputPath = arg; - } else { - logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected"; - return Options(); - } - } - } - if (result.inputPath.isEmpty()) { - logError(kErrorInputPathExpected, "Command Line") << "input path expected"; - return Options(); - } - return result; -} - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/options.h b/Telegram/SourceFiles/codegen/lang/options.h deleted file mode 100644 index 8db15ca10..000000000 --- a/Telegram/SourceFiles/codegen/lang/options.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace codegen { -namespace lang { - -struct Options { - QString outputPath = "."; - QString inputPath; -}; - -// Parsing failed if inputPath is empty in the result. -Options parseOptions(); - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/parsed_file.cpp b/Telegram/SourceFiles/codegen/lang/parsed_file.cpp deleted file mode 100644 index 551c55aef..000000000 --- a/Telegram/SourceFiles/codegen/lang/parsed_file.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* -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 "codegen/lang/parsed_file.h" - -#include -#include -#include -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/common/logging.h" - -namespace codegen { -namespace lang { -namespace { - -using BasicToken = codegen::common::BasicTokenizedFile::Token; -using BasicType = BasicToken::Type; - -constexpr int kErrorBadString = 806; - -bool ValidateAnsiString(const QString &value) { - for (auto ch : value) { - if (ch.unicode() > 127) { - return false; - } - } - return true; -} - -bool ValidateKey(const QString &key) { - static const auto validator = QRegularExpression("^[a-z0-9_.-]+(#(one|other))?$", QRegularExpression::CaseInsensitiveOption); - if (!validator.match(key).hasMatch()) { - return false; - } - if (key.indexOf("__") >= 0) { - return false; - } - return true; -} - -bool ValidateTag(const QString &tag) { - static const auto validator = QRegularExpression("^[a-z0-9_]+$", QRegularExpression::CaseInsensitiveOption); - if (!validator.match(tag).hasMatch()) { - return false; - } - if (tag.indexOf("__") >= 0) { - return false; - } - return true; -} - -QString PrepareCommandString(int index) { - static const QChar TextCommand(0x0010); - static const QChar TextCommandLangTag(0x0020); - auto result = QString(4, TextCommand); - result[1] = TextCommandLangTag; - result[2] = QChar(0x0020 + ushort(index)); - return result; -} - -} // namespace - -const std::array kPluralParts = { { - "zero", - "one", - "two", - "few", - "many", - "other", -} }; - -const std::array kPluralTags = { { - "count", - "count_short", - "count_decimal", -} }; - -QString ComputePluralKey(const QString &base, int index) { - return base + "__plural" + QString::number(index); -} - -ParsedFile::ParsedFile(const Options &options) -: filePath_(options.inputPath) -, file_(filePath_) -, options_(options) { -} - -bool ParsedFile::read() { - if (!file_.read()) { - return false; - } - - do { - if (auto keyToken = file_.getToken(BasicType::String)) { - if (ValidateKey(keyToken.value)) { - if (auto equals = file_.getToken(BasicType::Equals)) { - if (auto valueToken = file_.getToken(BasicType::String)) { - assertNextToken(BasicType::Semicolon); - addEntity(keyToken.value, valueToken.value); - continue; - } else { - logErrorUnexpectedToken() << "string value for '" << keyToken.value.toStdString() << "' key"; - } - } else { - logErrorUnexpectedToken() << "'=' for '" << keyToken.value.toStdString() << "' key"; - } - } else { - logErrorUnexpectedToken() << "string key name (/^[a-z0-9_.-]+(#(one|other))?$/i)"; - } - } - if (file_.atEnd()) { - break; - } - logErrorUnexpectedToken() << "ansi string key name"; - } while (!failed()); - - fillPluralTags(); - - return !failed(); -} - -void ParsedFile::fillPluralTags() { - auto count = result_.entries.size(); - for (auto i = 0; i != count;) { - auto &baseEntry = result_.entries[i]; - if (baseEntry.keyBase.isEmpty()) { - ++i; - continue; - } - logAssert(i + kPluralPartCount < count); - - // Accumulate all tags from all plural variants. - auto tags = std::vector(); - for (auto j = i; j != i + kPluralPartCount; ++j) { - if (tags.empty()) { - tags = result_.entries[j].tags; - } else { - for (auto &tag : result_.entries[j].tags) { - if (std::find(tags.begin(), tags.end(), tag) == tags.end()) { - tags.push_back(tag); - } - } - } - } - logAssert(!tags.empty()); - logAssert(tags.front().tag == kPluralTags[0]); - - // Set this tags list to all plural variants. - for (auto j = i; j != i + kPluralPartCount; ++j) { - result_.entries[j].tags = tags; - } - - i += kPluralPartCount; - } -} - -BasicToken ParsedFile::assertNextToken(BasicToken::Type type) { - auto result = file_.getToken(type); - if (!result) { - logErrorUnexpectedToken() << type; - } - return result; -} - -common::LogStream ParsedFile::logErrorBadString() { - return logError(kErrorBadString); -} - -QString ParsedFile::extractTagsData(const QString &value, LangPack *to) { - auto tagStart = value.indexOf('{'); - if (tagStart < 0) { - return value; - } - - auto tagEnd = 0; - auto finalValue = QString(); - finalValue.reserve(value.size() * 2); - while (tagStart >= 0) { - if (tagStart > tagEnd) { - finalValue.append(value.midRef(tagEnd, tagStart - tagEnd)); - } - ++tagStart; - tagEnd = value.indexOf('}', tagStart); - if (tagEnd < 0) { - logErrorBadString() << "unexpected end of value, end of tag expected."; - return value; - } - finalValue.append(extractTagData(value.mid(tagStart, tagEnd - tagStart), to)); - ++tagEnd; - tagStart = value.indexOf('{', tagEnd); - } - if (tagEnd < value.size()) { - finalValue.append(value.midRef(tagEnd)); - } - return finalValue; -} - -QString ParsedFile::extractTagData(const QString &tagText, LangPack *to) { - auto numericPart = tagText.indexOf(':'); - auto tag = (numericPart > 0) ? tagText.mid(0, numericPart) : tagText; - if (!ValidateTag(tag)) { - logErrorBadString() << "bad tag characters: '" << tagText.toStdString() << "'"; - return QString(); - } - for (auto &previousTag : to->tags) { - if (previousTag.tag == tag) { - logErrorBadString() << "duplicate found for tag '" << tagText.toStdString() << "'"; - return QString(); - } - } - auto index = 0; - auto tagIndex = result_.tags.size(); - for (auto &alreadyTag : result_.tags) { - if (alreadyTag.tag == tag) { - tagIndex = index; - break; - } - ++index; - } - if (tagIndex == result_.tags.size()) { - result_.tags.push_back({ tag }); - } - if (numericPart > 0) { - auto numericParts = tagText.mid(numericPart + 1).split('|'); - if (numericParts.size() != 3) { - logErrorBadString() << "bad option count for plural key part in tag: '" << tagText.toStdString() << "'"; - return QString(); - } - auto index = 0; - for (auto &part : numericParts) { - auto numericPartEntry = LangPack::Entry(); - numericPartEntry.key = tag + QString::number(index++); - if (part.indexOf('#') != part.lastIndexOf('#')) { - logErrorBadString() << "bad option for plural key part in tag: '" << tagText.toStdString() << "', too many '#'."; - return QString(); - } - numericPartEntry.value = part.replace('#', PrepareCommandString(tagIndex)); - to->entries.push_back(numericPartEntry); - } - } - to->tags.push_back({ tag }); - return PrepareCommandString(tagIndex); -} - -void ParsedFile::addEntity(QString key, const QString &value) { - auto pluralPartOffset = key.indexOf('#'); - auto pluralIndex = -1; - if (pluralPartOffset >= 0) { - auto pluralPart = key.mid(pluralPartOffset + 1); - pluralIndex = std::find(kPluralParts.begin(), kPluralParts.end(), pluralPart) - kPluralParts.begin(); - if (pluralIndex < 0 || pluralIndex >= kPluralParts.size()) { - logErrorBadString() << "bad plural part for key '" << key.toStdString() << "': '" << pluralPart.toStdString() << "'"; - return; - } - key = key.mid(0, pluralPartOffset); - } - auto checkKey = [this](const QString &key) { - for (auto &entry : result_.entries) { - if (entry.key == key) { - if (entry.keyBase.isEmpty() || !entry.tags.empty()) { - // Empty tags in plural entry means it was not encountered yet. - logErrorBadString() << "duplicate found for key '" << key.toStdString() << "'"; - return false; - } - } - } - return true; - }; - if (!checkKey(key)) { - return; - } - auto tagsData = LangPack(); - auto entry = LangPack::Entry(); - entry.key = key; - entry.value = extractTagsData(value, &tagsData); - entry.tags = tagsData.tags; - if (pluralIndex >= 0) { - logAssert(tagsData.entries.empty()); - - entry.keyBase = entry.key; - entry.key = ComputePluralKey(entry.keyBase, pluralIndex); - if (!checkKey(entry.key)) { - return; - } - auto baseIndex = -1; - auto alreadyCount = result_.entries.size(); - for (auto i = 0; i != alreadyCount; ++i) { - if (result_.entries[i].keyBase == entry.keyBase) { - // This is not the first appearance of this plural key. - baseIndex = i; - break; - } - } - if (baseIndex < 0) { - baseIndex = result_.entries.size(); - for (auto i = 0; i != kPluralPartCount; ++i) { - auto addingEntry = LangPack::Entry(); - addingEntry.keyBase = entry.keyBase; - addingEntry.key = ComputePluralKey(entry.keyBase, i); - result_.entries.push_back(addingEntry); - } - } - auto entryIndex = baseIndex + pluralIndex; - logAssert(entryIndex < result_.entries.size()); - auto &realEntry = result_.entries[entryIndex]; - logAssert(realEntry.key == entry.key); - realEntry.value = entry.value; - - // Add all new tags to the existing ones. - realEntry.tags = std::vector(1, LangPack::Tag{ kPluralTags[0] }); - - for (auto &tag : entry.tags) { - if (std::find(realEntry.tags.begin(), realEntry.tags.end(), tag) == realEntry.tags.end()) { - realEntry.tags.push_back(tag); - } - } - } else { - result_.entries.push_back(entry); - for (auto &tag : entry.tags) { - const auto plural = std::find(std::begin(kPluralTags), std::end(kPluralTags), tag.tag); - if (plural != std::end(kPluralTags)) { - logErrorBadString() << "plural tag '" << tag.tag.toStdString() << "' used in non-plural key '" << key.toStdString() << "'"; - } - } - for (auto &tagEntry : tagsData.entries) { - auto taggedEntry = LangPack::Entry(); - taggedEntry.key = key + "__" + tagEntry.key; - taggedEntry.value = tagEntry.value; - result_.entries.push_back(taggedEntry); - } - } -} - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/parsed_file.h b/Telegram/SourceFiles/codegen/lang/parsed_file.h deleted file mode 100644 index 948eb247d..000000000 --- a/Telegram/SourceFiles/codegen/lang/parsed_file.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/lang/options.h" - -namespace codegen { -namespace lang { - -constexpr auto kPluralPartCount = 6; -extern const std::array kPluralParts; - -constexpr auto kPluralTagsCount = 3; -extern const std::array kPluralTags; - -QString ComputePluralKey(const QString &base, int index); - -struct LangPack { - struct Tag { - QString tag; - }; - struct Entry { - QString key; - QString value; - QString keyBase; // Empty for not plural entries. - std::vector tags; - }; - std::vector entries; - std::vector tags; - -}; - -inline bool operator==(const LangPack::Tag &a, const LangPack::Tag &b) { - return a.tag == b.tag; -} - -inline bool operator!=(const LangPack::Tag &a, const LangPack::Tag &b) { - return !(a == b); -} - -// Parses an input file to the internal struct. -class ParsedFile { -public: - explicit ParsedFile(const Options &options); - ParsedFile(const ParsedFile &other) = delete; - ParsedFile &operator=(const ParsedFile &other) = delete; - - bool read(); - - LangPack getResult() { - return result_; - } - -private: - bool failed() const { - return failed_ || file_.failed(); - } - - // Log error to std::cerr with 'code' at the current position in file. - common::LogStream logError(int code) { - failed_ = true; - return file_.logError(code); - } - common::LogStream logErrorUnexpectedToken() { - failed_ = true; - return file_.logErrorUnexpectedToken(); - } - common::LogStream logErrorBadString(); - common::LogStream logAssert(bool assertion) { - if (!assertion) { - return logError(common::kErrorInternal) << "internal - "; - } - return common::LogStream(common::LogStream::Null); - } - - // Read next token and fire unexpected token error if it is not of "type". - using BasicToken = common::BasicTokenizedFile::Token; - BasicToken assertNextToken(BasicToken::Type type); - - void addEntity(QString key, const QString &value); - QString extractTagsData(const QString &value, LangPack *to); - QString extractTagData(const QString &tag, LangPack *to); - - void fillPluralTags(); - - QString filePath_; - common::BasicTokenizedFile file_; - Options options_; - bool failed_ = false; - LangPack result_; - -}; - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/processor.cpp b/Telegram/SourceFiles/codegen/lang/processor.cpp deleted file mode 100644 index 6f1d4b2cf..000000000 --- a/Telegram/SourceFiles/codegen/lang/processor.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -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 "codegen/lang/processor.h" - -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/lang/parsed_file.h" -#include "codegen/lang/generator.h" - -namespace codegen { -namespace lang { -namespace { - -constexpr int kErrorCantWritePath = 821; - -} // namespace - -Processor::Processor(const Options &options) -: parser_(std::make_unique(options)) -, options_(options) { -} - -int Processor::launch() { - if (!parser_->read()) { - return -1; - } - - if (!write(parser_->getResult())) { - return -1; - } - - return 0; -} - -bool Processor::write(const LangPack &langpack) const { - bool forceReGenerate = false; - QDir dir(options_.outputPath); - if (!dir.mkpath(".")) { - common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); - return false; - } - - QFileInfo srcFile(options_.inputPath); - QString dstFilePath = dir.absolutePath() + "/lang_auto"; - - common::ProjectInfo project = { - "codegen_style", - srcFile.fileName(), - forceReGenerate - }; - - Generator generator(langpack, dstFilePath, project); - if (!generator.writeHeader()) { - return false; - } - if (!generator.writeSource()) { - return false; - } - return true; -} - -Processor::~Processor() = default; - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/lang/processor.h b/Telegram/SourceFiles/codegen/lang/processor.h deleted file mode 100644 index d5a0d6832..000000000 --- a/Telegram/SourceFiles/codegen/lang/processor.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "codegen/lang/options.h" - -namespace codegen { -namespace lang { -class ParsedFile; -struct LangPack; - -// Walks through a file, parses it and generates the output. -class Processor { -public: - explicit Processor(const Options &options); - Processor(const Processor &other) = delete; - Processor &operator=(const Processor &other) = delete; - - // Returns 0 on success. - int launch(); - - ~Processor(); - -private: - bool write(const LangPack &langpack) const; - - std::unique_ptr parser_; - const Options &options_; - -}; - -} // namespace lang -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/generator.cpp b/Telegram/SourceFiles/codegen/numbers/generator.cpp deleted file mode 100644 index 052b19d59..000000000 --- a/Telegram/SourceFiles/codegen/numbers/generator.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* -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 "codegen/numbers/generator.h" - -#include -#include -#include - -namespace codegen { -namespace numbers { -namespace { - -} // namespace - -Generator::Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project) -: rules_(rules) -, basePath_(destBasePath) -, project_(project) { -} - -bool Generator::writeHeader() { - header_ = std::make_unique(basePath_ + ".h", project_); - - header_->stream() << "QVector phoneNumberParse(const QString &number);\n"; - - return header_->finalize(); -} - -bool Generator::writeSource() { - source_ = std::make_unique(basePath_ + ".cpp", project_); - - source_->stream() << "\ -QVector phoneNumberParse(const QString &number) {\n\ - QVector result;\n\ -\n\ - int32 len = number.size();\n\ - if (len > 0) switch (number.at(0).unicode()) {\n"; - - QString already; - for (auto i = rules_.data.cend(), e = rules_.data.cbegin(); i != e;) { - --i; - QString k = i.key(); - bool onlyLastChanged = true; - while (!already.isEmpty() && (already.size() > k.size() || !already.endsWith(k.at(already.size() - 1)))) { - if (!onlyLastChanged) { - source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n"; - source_->stream() << QString("\t").repeated(already.size()) << "break;\n"; - } - already = already.mid(0, already.size() - 1); - onlyLastChanged = false; - } - if (already == k) { - source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n"; - } else { - bool onlyFirstCheck = true; - while (already.size() < k.size()) { - if (!onlyFirstCheck) source_->stream() << QString("\t").repeated(1 + already.size()) << "if (len > " << already.size() << ") switch (number.at(" << already.size() << ").unicode()) {\n"; - source_->stream() << QString("\t").repeated(1 + already.size()) << "case '" << k.at(already.size()).toLatin1() << "':\n"; - already.push_back(k.at(already.size())); - onlyFirstCheck = false; - } - } - if (i.value().isEmpty()) { - source_->stream() << QString("\t").repeated(1 + already.size()) << "return QVector(1, " << k.size() << ");\n"; - } else { - source_->stream() << QString("\t").repeated(1 + already.size()) << "result.reserve(" << (i.value().size() + 1) << ");\n"; - source_->stream() << QString("\t").repeated(1 + already.size()) << "result.push_back(" << k.size() << ");\n"; - for (int j = 0, l = i.value().size(); j < l; ++j) { - source_->stream() << QString("\t").repeated(1 + already.size()) << "result.push_back(" << i.value().at(j) << ");\n"; - } - source_->stream() << QString("\t").repeated(1 + already.size()) << "return result;\n"; - } - } - bool onlyLastChanged = true; - while (!already.isEmpty()) { - if (!onlyLastChanged) { - source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n"; - } - already = already.mid(0, already.size() - 1); - onlyLastChanged = false; - } - source_->stream() << "\ - }\n\ -\n\ - return result;\n\ -}\n"; - - return source_->finalize(); -} - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/generator.h b/Telegram/SourceFiles/codegen/numbers/generator.h deleted file mode 100644 index f673f1688..000000000 --- a/Telegram/SourceFiles/codegen/numbers/generator.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/numbers/parsed_file.h" - -namespace codegen { -namespace numbers { - -class Generator { -public: - Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project); - Generator(const Generator &other) = delete; - Generator &operator=(const Generator &other) = delete; - - bool writeHeader(); - bool writeSource(); - -private: - const Rules &rules_; - QString basePath_; - const common::ProjectInfo &project_; - std::unique_ptr source_, header_; - -}; - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/main.cpp b/Telegram/SourceFiles/codegen/numbers/main.cpp deleted file mode 100644 index 888c0ec4d..000000000 --- a/Telegram/SourceFiles/codegen/numbers/main.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 - -#include "codegen/numbers/options.h" -#include "codegen/numbers/processor.h" - -int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); - - auto options = codegen::numbers::parseOptions(); - if (options.inputPath.isEmpty()) { - return -1; - } - - codegen::numbers::Processor processor(options); - return processor.launch(); -} diff --git a/Telegram/SourceFiles/codegen/numbers/options.cpp b/Telegram/SourceFiles/codegen/numbers/options.cpp deleted file mode 100644 index 45d8deb54..000000000 --- a/Telegram/SourceFiles/codegen/numbers/options.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -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 "codegen/numbers/options.h" - -#include -#include -#include "codegen/common/logging.h" - -namespace codegen { -namespace numbers { -namespace { - -constexpr int kErrorOutputPathExpected = 902; -constexpr int kErrorInputPathExpected = 903; -constexpr int kErrorSingleInputPathExpected = 904; -constexpr int kErrorWorkingPathExpected = 905; - -} // namespace - -using common::logError; - -Options parseOptions() { - Options result; - auto args = QCoreApplication::instance()->arguments(); - for (auto i = 1, count = args.size(); i < count; ++i) { // skip first - auto &arg = args.at(i); - - // Output path - if (arg == "-o") { - if (++i == count) { - logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; - return Options(); - } else { - result.outputPath = args.at(i); - } - } else if (arg.startsWith("-o")) { - result.outputPath = arg.mid(2); - - // Working path - } else if (arg == "-w") { - if (++i == count) { - logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w"; - return Options(); - } else { - common::logSetWorkingPath(args.at(i)); - } - } else if (arg.startsWith("-w")) { - common::logSetWorkingPath(arg.mid(2)); - - // Input path - } else { - if (result.inputPath.isEmpty()) { - result.inputPath = arg; - } else { - logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected"; - return Options(); - } - } - } - if (result.inputPath.isEmpty()) { - logError(kErrorInputPathExpected, "Command Line") << "input path expected"; - return Options(); - } - return result; -} - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/options.h b/Telegram/SourceFiles/codegen/numbers/options.h deleted file mode 100644 index 8cd549469..000000000 --- a/Telegram/SourceFiles/codegen/numbers/options.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace codegen { -namespace numbers { - -struct Options { - QString outputPath = "."; - QString inputPath; -}; - -// Parsing failed if inputPath is empty in the result. -Options parseOptions(); - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp b/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp deleted file mode 100644 index a554a9ac7..000000000 --- a/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* -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 "codegen/numbers/parsed_file.h" - -#include -#include -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/common/logging.h" -#include "codegen/common/clean_file_reader.h" -#include "codegen/common/checked_utf8_string.h" - -using BasicToken = codegen::common::BasicTokenizedFile::Token; -using BasicType = BasicToken::Type; - -namespace codegen { -namespace numbers { -namespace { - -QByteArray replaceStrings(const QString &filepath) { - common::CleanFileReader reader(filepath); - if (!reader.read()) { - return QByteArray(); - } - common::CheckedUtf8String string(reader.currentPtr(), reader.charsLeft()); - if (!string.isValid()) { - return QByteArray(); - } - - QStringList lines = string.toString().split('\n'); - for (auto &line : lines) { - auto match = QRegularExpression("^(\\d+;[A-Z]+;)([^;]+)(;.*)?$").match(line); - if (match.hasMatch()) { - line = match.captured(1) + '"' + match.captured(2) + '"' + match.captured(3); - } - } - return lines.join('\n').toUtf8(); -} - -} // namespace - -ParsedFile::ParsedFile(const Options &options) -: content_(replaceStrings(options.inputPath)) -, file_(content_, options.inputPath) -, options_(options) { -} - -bool ParsedFile::read() { - if (content_.isEmpty() || !file_.read()) { - return false; - } - - auto filepath = QFileInfo(options_.inputPath).absoluteFilePath(); - do { - if (auto code = file_.getToken(BasicType::Int)) { - if (!file_.getToken(BasicType::Semicolon)) { - logErrorUnexpectedToken() << "';'"; - return false; - } - if (!file_.getToken(BasicType::Name)) { - logErrorUnexpectedToken() << "country code"; - return false; - } - if (!file_.getToken(BasicType::Semicolon)) { - logErrorUnexpectedToken() << "';'"; - return false; - } - if (!file_.getToken(BasicType::String)) { - logErrorUnexpectedToken() << "country name"; - return false; - } - if (file_.getToken(BasicType::Semicolon)) { - if (auto firstPart = file_.getToken(BasicType::Int)) { - if (firstPart.original.toByteArray() != code.original.toByteArray()) { - file_.putBack(); - result_.data.insert(code.original.toStringUnchecked(), Rule()); - continue; - } - - Rule rule; - while (auto part = file_.getToken(BasicType::Name)) { - rule.push_back(part.original.size()); - } - result_.data.insert(code.original.toStringUnchecked(), rule); - if (rule.isEmpty()) { - logErrorUnexpectedToken() << "bad phone pattern"; - return false; - } - - if (!file_.getToken(BasicType::Semicolon)) { - logErrorUnexpectedToken() << "';'"; - return false; - } - if (!file_.getToken(BasicType::Int)) { - logErrorUnexpectedToken() << "country phone len"; - return false; - } - file_.getToken(BasicType::Semicolon); - continue; - } else { - logErrorUnexpectedToken() << "country phone pattern"; - return false; - } - } else if (file_.getToken(BasicType::Int)) { - file_.putBack(); - result_.data.insert(code.original.toStringUnchecked(), Rule()); - continue; - } else { - logErrorUnexpectedToken() << "country phone pattern"; - return false; - } - } - if (file_.atEnd()) { - break; - } - logErrorUnexpectedToken() << "numbers rule"; - } while (!failed()); - - if (failed()) { - result_.data.clear(); - } - return !failed(); -} - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/parsed_file.h b/Telegram/SourceFiles/codegen/numbers/parsed_file.h deleted file mode 100644 index dc2212899..000000000 --- a/Telegram/SourceFiles/codegen/numbers/parsed_file.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/numbers/options.h" - -namespace codegen { -namespace numbers { - -using Rule = QVector; -struct Rules { - QMap data; -}; - -// Parses an input file to the internal struct. -class ParsedFile { -public: - explicit ParsedFile(const Options &options); - ParsedFile(const ParsedFile &other) = delete; - ParsedFile &operator=(const ParsedFile &other) = delete; - - bool read(); - - Rules getResult() { - return result_; - } - -private: - - bool failed() const { - return failed_ || file_.failed(); - } - - // Log error to std::cerr with 'code' at the current position in file. - common::LogStream logError(int code) { - failed_ = true; - return file_.logError(code); - } - common::LogStream logErrorUnexpectedToken() { - failed_ = true; - return file_.logErrorUnexpectedToken(); - } - - QByteArray content_; - common::BasicTokenizedFile file_; - Options options_; - bool failed_ = false; - Rules result_; - -}; - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/processor.cpp b/Telegram/SourceFiles/codegen/numbers/processor.cpp deleted file mode 100644 index 4859ed701..000000000 --- a/Telegram/SourceFiles/codegen/numbers/processor.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -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 "codegen/numbers/processor.h" - -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/numbers/parsed_file.h" -#include "codegen/numbers/generator.h" - -namespace codegen { -namespace numbers { -namespace { - -constexpr int kErrorCantWritePath = 851; - -} // namespace - -Processor::Processor(const Options &options) -: parser_(std::make_unique(options)) -, options_(options) { -} - -int Processor::launch() { - if (!parser_->read()) { - return -1; - } - - auto result = parser_->getResult(); - if (!write(result)) { - return -1; - } - - return 0; -} - -bool Processor::write(const Rules &rules) const { - QDir dir(options_.outputPath); - if (!dir.mkpath(".")) { - common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); - return false; - } - - QFileInfo srcFile(options_.inputPath); - QString dstFilePath = dir.absolutePath() + "/numbers"; - - common::ProjectInfo project = { - "codegen_style", - srcFile.fileName(), - false, // forceReGenerate - }; - - Generator generator(rules, dstFilePath, project); - if (!generator.writeHeader()) { - return false; - } - if (!generator.writeSource()) { - return false; - } - - return true; -} - -Processor::~Processor() = default; - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/processor.h b/Telegram/SourceFiles/codegen/numbers/processor.h deleted file mode 100644 index 762185f62..000000000 --- a/Telegram/SourceFiles/codegen/numbers/processor.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "codegen/numbers/options.h" - -namespace codegen { -namespace numbers { -class ParsedFile; -struct Rules; - -// Walks through a file, parses it and generates number formatter. -class Processor { -public: - explicit Processor(const Options &options); - Processor(const Processor &other) = delete; - Processor &operator=(const Processor &other) = delete; - - // Returns 0 on success. - int launch(); - - ~Processor(); - -private: - bool write(const Rules &rules) const; - - std::unique_ptr parser_; - const Options &options_; - -}; - -} // namespace numbers -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp deleted file mode 100644 index 385cc1140..000000000 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ /dev/null @@ -1,1343 +0,0 @@ -/* -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 "codegen/style/generator.h" - -#include "base/crc32hash.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include "codegen/style/parsed_file.h" - -using Module = codegen::style::structure::Module; -using Struct = codegen::style::structure::Struct; -using Variable = codegen::style::structure::Variable; -using Tag = codegen::style::structure::TypeTag; - -namespace codegen { -namespace style { -namespace { - -constexpr int kErrorBadIconSize = 861; - -const auto kMustBeContrast = std::map{ - { "dialogsMenuIconFg", "dialogsBg" }, - { "windowBoldFg", "windowBg" }, -}; - -char hexChar(uchar ch) { - if (ch < 10) { - return '0' + ch; - } else if (ch < 16) { - return 'a' + (ch - 10); - } - return '0'; -} - -char hexSecondChar(char ch) { - return hexChar((*reinterpret_cast(&ch)) & 0x0F); -} - -char hexFirstChar(char ch) { - return hexChar((*reinterpret_cast(&ch)) >> 4); -} - -QString stringToEncodedString(const QString &str) { - QString result, lineBreak = "\\\n"; - result.reserve(str.size() * 8); - bool writingHexEscapedCharacters = false, startOnNewLine = false; - int lastCutSize = 0; - auto utf = str.toUtf8(); - for (auto ch : utf) { - if (result.size() - lastCutSize > 80) { - startOnNewLine = true; - result.append(lineBreak); - lastCutSize = result.size(); - } - if (ch == '\n') { - writingHexEscapedCharacters = false; - result.append("\\n"); - } else if (ch == '\t') { - writingHexEscapedCharacters = false; - result.append("\\t"); - } else if (ch == '"' || ch == '\\') { - writingHexEscapedCharacters = false; - result.append('\\').append(ch); - } else if (ch < 32 || static_cast(ch) > 127) { - writingHexEscapedCharacters = true; - result.append("\\x").append(hexFirstChar(ch)).append(hexSecondChar(ch)); - } else { - if (writingHexEscapedCharacters) { - writingHexEscapedCharacters = false; - result.append("\"\""); - } - result.append(ch); - } - } - return '"' + (startOnNewLine ? lineBreak : QString()) + result + '"'; -} - -QString stringToEncodedString(const std::string &str) { - return stringToEncodedString(QString::fromStdString(str)); -} - -QString stringToBinaryArray(const std::string &str) { - QStringList rows, chars; - chars.reserve(13); - rows.reserve(1 + (str.size() / 13)); - for (uchar ch : str) { - if (chars.size() > 12) { - rows.push_back(chars.join(", ")); - chars.clear(); - } - chars.push_back(QString("0x") + hexFirstChar(ch) + hexSecondChar(ch)); - } - if (!chars.isEmpty()) { - rows.push_back(chars.join(", ")); - } - return QString("{") + ((rows.size() > 1) ? '\n' : ' ') + rows.join(",\n") + " }"; -} - -QString pxValueName(int value) { - QString result = "px"; - if (value < 0) { - value = -value; - result += 'm'; - } - return result + QString::number(value); -} - -QString moduleBaseName(const structure::Module &module) { - auto moduleInfo = QFileInfo(module.filepath()); - auto moduleIsPalette = (moduleInfo.suffix() == "palette"); - return moduleIsPalette ? "palette" : "style_" + moduleInfo.baseName(); -} - -QString colorFallbackName(structure::Value value) { - auto copy = value.copyOf(); - if (!copy.isEmpty()) { - return copy.back(); - } - return value.Color().fallback; -} - -QChar paletteColorPart(uchar part) { - part = (part & 0x0F); - if (part >= 10) { - return 'a' + (part - 10); - } - return '0' + part; -} - -QString paletteColorComponent(uchar value) { - return QString() + paletteColorPart(value >> 4) + paletteColorPart(value); -} - -QString paletteColorValue(const structure::data::color &value) { - auto result = paletteColorComponent(value.red) + paletteColorComponent(value.green) + paletteColorComponent(value.blue); - if (value.alpha != 255) result += paletteColorComponent(value.alpha); - return result; -} - -} // namespace - -Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette) -: module_(module) -, basePath_(destBasePath) -, baseName_(QFileInfo(basePath_).baseName()) -, project_(project) -, isPalette_(isPalette) { -} - -bool Generator::writeHeader() { - header_ = std::make_unique(basePath_ + ".h", project_); - - header_->include("ui/style/style_core.h").newline(); - - if (!writeHeaderRequiredIncludes()) { - return false; - } - if (!writeHeaderStyleNamespace()) { - return false; - } - if (!writeRefsDeclarations()) { - return false; - } - - return header_->finalize(); -} - -bool Generator::writeSource() { - source_ = std::make_unique(basePath_ + ".cpp", project_); - - writeIncludesInSource(); - - if (module_.hasVariables()) { - source_->pushNamespace().newline(); - source_->stream() << "\ -bool inited = false;\n\ -\n\ -class Module_" << baseName_ << " : public style::internal::ModuleBase {\n\ -public:\n\ - Module_" << baseName_ << "() { style::internal::registerModule(this); }\n\ -\n\ - void start(int scale) override {\n\ - style::internal::init_" << baseName_ << "(scale);\n\ - }\n\ -};\n\ -Module_" << baseName_ << " registrator;\n"; - if (isPalette_) { - source_->newline(); - source_->stream() << "style::palette _palette;\n"; - } else { - if (!writeVariableDefinitions()) { - return false; - } - } - source_->newline().popNamespace(); - - source_->newline().pushNamespace("st"); - if (!writeRefsDefinition()) { - return false; - } - - source_->popNamespace().newline().pushNamespace("style"); - if (isPalette_) { - writeSetPaletteColor(); - } - source_->pushNamespace("internal").newline(); - if (!writeVariableInit()) { - return false; - } - } - - return source_->finalize(); -} - -// Empty result means an error. -QString Generator::typeToString(structure::Type type) const { - switch (type.tag) { - case Tag::Invalid: return QString(); - case Tag::Int: return "int"; - case Tag::Double: return "double"; - case Tag::Pixels: return "int"; - case Tag::String: return "QString"; - case Tag::Color: return "style::color"; - case Tag::Point: return "style::point"; - case Tag::Size: return "style::size"; - case Tag::Align: return "style::align"; - case Tag::Margins: return "style::margins"; - case Tag::Font: return "style::font"; - case Tag::Icon: return "style::icon"; - case Tag::Struct: return "style::" + type.name.back(); - } - return QString(); -} - -// Empty result means an error. -QString Generator::typeToDefaultValue(structure::Type type) const { - switch (type.tag) { - case Tag::Invalid: return QString(); - case Tag::Int: return "0"; - case Tag::Double: return "0."; - case Tag::Pixels: return "0"; - case Tag::String: return "QString()"; - case Tag::Color: return "{ Qt::Uninitialized }"; - case Tag::Point: return "{ 0, 0 }"; - case Tag::Size: return "{ 0, 0 }"; - case Tag::Align: return "style::al_topleft"; - case Tag::Margins: return "{ 0, 0, 0, 0 }"; - case Tag::Font: return "{ Qt::Uninitialized }"; - case Tag::Icon: return "{ Qt::Uninitialized }"; - case Tag::Struct: { - if (auto realType = module_.findStruct(type.name)) { - QStringList fields; - for (auto field : realType->fields) { - fields.push_back(typeToDefaultValue(field.type)); - } - return "{ " + fields.join(", ") + " }"; - } - return QString(); - } break; - } - return QString(); -} - -// Empty result means an error. -QString Generator::valueAssignmentCode(structure::Value value) const { - auto copy = value.copyOf(); - if (!copy.isEmpty()) { - return "st::" + copy.back(); - } - - switch (value.type().tag) { - case Tag::Invalid: return QString(); - case Tag::Int: return QString("%1").arg(value.Int()); - case Tag::Double: return QString("%1").arg(value.Double()); - case Tag::Pixels: return pxValueName(value.Int()); - case Tag::String: return QString("QString::fromUtf8(%1)").arg(stringToEncodedString(value.String())); - case Tag::Color: { - auto v(value.Color()); - if (v.red == v.green && v.red == v.blue && v.red == 0 && v.alpha == 255) { - return QString("st::windowFg"); - } else if (v.red == v.green && v.red == v.blue && v.red == 255 && v.alpha == 0) { - return QString("st::transparent"); - } else { - common::logError(common::kErrorInternal, "") << "bad color value"; - return QString(); - } - } break; - case Tag::Point: { - auto v(value.Point()); - return QString("{ %1, %2 }").arg(pxValueName(v.x)).arg(pxValueName(v.y)); - } break; - case Tag::Size: { - auto v(value.Size()); - return QString("{ %1, %2 }").arg(pxValueName(v.width)).arg(pxValueName(v.height)); - } break; - case Tag::Align: return QString("style::al_%1").arg(value.String().c_str()); - case Tag::Margins: { - auto v(value.Margins()); - return QString("{ %1, %2, %3, %4 }").arg(pxValueName(v.left)).arg(pxValueName(v.top)).arg(pxValueName(v.right)).arg(pxValueName(v.bottom)); - } break; - case Tag::Font: { - auto v(value.Font()); - QString family = "0"; - if (!v.family.empty()) { - auto familyIndex = fontFamilies_.value(v.family, -1); - if (familyIndex < 0) { - return QString(); - } - family = QString("font%1index").arg(familyIndex); - } - return QString("{ %1, %2, %3 }").arg(pxValueName(v.size)).arg(v.flags).arg(family); - } break; - case Tag::Icon: { - auto v(value.Icon()); - if (v.parts.empty()) return QString("{}"); - - QStringList parts; - for (const auto &part : v.parts) { - auto maskIndex = iconMasks_.value(part.filename, -1); - if (maskIndex < 0) { - return QString(); - } - auto color = valueAssignmentCode(part.color); - auto offset = valueAssignmentCode(part.offset); - parts.push_back(QString("MonoIcon{ &iconMask%1, %2, %3 }").arg(maskIndex).arg(color).arg(offset)); - } - return QString("{ %1 }").arg(parts.join(", ")); - } break; - case Tag::Struct: { - if (!value.Fields()) return QString(); - - QStringList fields; - for (auto field : *value.Fields()) { - fields.push_back(valueAssignmentCode(field.variable.value)); - } - return "{ " + fields.join(", ") + " }"; - } break; - } - return QString(); -} - -bool Generator::writeHeaderRequiredIncludes() { - std::function findInIncludes = [&](const Module &module, const structure::FullName &name) { - auto result = QString(); - module.enumIncludes([&](const Module &included) { - if (Module::findStructInModule(name, included)) { - result = moduleBaseName(included); - return false; - } - result = findInIncludes(included, name); - return true; - }); - return result; - }; - - auto includes = QStringList(); - const auto written = module_.enumStructs([&](const Struct &value) -> bool { - for (const auto &field : value.fields) { - if (field.type.tag == structure::TypeTag::Struct) { - const auto name = field.type.name; - if (!module_.findStructInModule(name, module_)) { - const auto base = findInIncludes(module_, name); - if (base.isEmpty()) { - return false; - } - if (!includes.contains(base)) { - includes.push_back(base); - } - } - } - } - return true; - }); - if (!written) { - return false; - } else if (includes.isEmpty()) { - return true; - } - for (const auto base : includes) { - header_->include(base + ".h"); - } - header_->newline(); - return true; -} - -bool Generator::writeHeaderStyleNamespace() { - if (!module_.hasStructs() && !module_.hasVariables()) { - return true; - } - header_->pushNamespace("style"); - - if (module_.hasVariables()) { - header_->pushNamespace("internal").newline(); - header_->stream() << "void init_" << baseName_ << "(int scale);\n\n"; - header_->popNamespace(); - } - bool wroteForwardDeclarations = writeStructsForwardDeclarations(); - if (module_.hasStructs()) { - if (!wroteForwardDeclarations) { - header_->newline(); - } - if (!writeStructsDefinitions()) { - return false; - } - } else if (isPalette_) { - if (!wroteForwardDeclarations) { - header_->newline(); - } - if (!writePaletteDefinition()) { - return false; - } - } - - header_->popNamespace().newline(); - return true; -} - -bool Generator::writePaletteDefinition() { - header_->stream() << "\ -class palette {\n\ -public:\n\ - palette() = default;\n\ - palette(const palette &other) = delete;\n\ -\n\ - QByteArray save() const;\n\ - bool load(const QByteArray &cache);\n\ -\n\ - enum class SetResult {\n\ - Ok,\n\ - KeyNotFound,\n\ - ValueNotFound,\n\ - Duplicate,\n\ - };\n\ - SetResult setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ - SetResult setColor(QLatin1String name, QLatin1String from);\n\ - void reset() {\n\ - clear();\n\ - finalize();\n\ - }\n\ -\n\ - // Created not inited, should be finalized before usage.\n\ - void finalize();\n\ -\n\ - int indexOfColor(color c) const;\n\ - color colorAtIndex(int index) const;\n\ -\n\ - inline const color &get_transparent() const { return _colors[0]; }; // special color\n"; - - int indexInPalette = 1; - if (!module_.enumVariables([&](const Variable &variable) -> bool { - auto name = variable.name.back(); - if (variable.value.type().tag != structure::TypeTag::Color) { - return false; - } - - auto index = (indexInPalette++); - header_->stream() << "\tinline const color &get_" << name << "() const { return _colors[" << index << "]; };\n"; - return true; - })) return false; - - auto count = indexInPalette; - header_->stream() << "\ -\n\ - palette &operator=(const palette &other) {\n\ - auto wasReady = _ready;\n\ - for (int i = 0; i != kCount; ++i) {\n\ - if (other._status[i] == Status::Loaded) {\n\ - if (_status[i] == Status::Initial) {\n\ - new (data(i)) internal::ColorData(*other.data(i));\n\ - } else {\n\ - *data(i) = *other.data(i);\n\ - }\n\ - } else if (_status[i] != Status::Initial) {\n\ - data(i)->~ColorData();\n\ - _status[i] = Status::Initial;\n\ - _ready = false;\n\ - }\n\ - }\n\ - if (wasReady && !_ready) {\n\ - finalize();\n\ - }\n\ - return *this;\n\ - }\n\ -\n\ - static int32 Checksum();\n\ -\n\ - ~palette() {\n\ - clear();\n\ - }\n\ -\n\ -private:\n\ - static constexpr auto kCount = " << count << ";\n\ -\n\ - void clear() {\n\ - for (int i = 0; i != kCount; ++i) {\n\ - if (_status[i] != Status::Initial) {\n\ - data(i)->~ColorData();\n\ - _status[i] = Status::Initial;\n\ - _ready = false;\n\ - }\n\ - }\n\ - }\n\ -\n\ - struct TempColorData { uchar r, g, b, a; };\n\ - void compute(int index, int fallbackIndex, TempColorData value) {\n\ - if (_status[index] == Status::Initial) {\n\ - if (fallbackIndex >= 0 && _status[fallbackIndex] == Status::Loaded) {\n\ - _status[index] = Status::Loaded;\n\ - new (data(index)) internal::ColorData(*data(fallbackIndex));\n\ - } else {\n\ - _status[index] = Status::Created;\n\ - new (data(index)) internal::ColorData(value.r, value.g, value.b, value.a);\n\ - }\n\ - }\n\ - }\n\ -\n\ - internal::ColorData *data(int index) {\n\ - return reinterpret_cast(_data) + index;\n\ - }\n\ -\n\ - const internal::ColorData *data(int index) const {\n\ - return reinterpret_cast(_data) + index;\n\ - }\n\ -\n\ - void setData(int index, const internal::ColorData &value) {\n\ - if (_status[index] == Status::Initial) {\n\ - new (data(index)) internal::ColorData(value);\n\ - } else {\n\ - *data(index) = value;\n\ - }\n\ - _status[index] = Status::Loaded;\n\ - }\n\ -\n\ - enum class Status {\n\ - Initial,\n\ - Created,\n\ - Loaded,\n\ - };\n\ -\n\ - alignas(alignof(internal::ColorData)) char _data[sizeof(internal::ColorData) * kCount];\n\ -\n\ - color _colors[kCount] = {\n"; - for (int i = 0; i != count; ++i) { - header_->stream() << "\t\tdata(" << i << "),\n"; - } - header_->stream() << "\ - };\n\ - Status _status[kCount] = { Status::Initial };\n\ - bool _ready = false;\n\ -\n\ -};\n\ -\n\ -namespace main_palette {\n\ -\n\ -QByteArray save();\n\ -bool load(const QByteArray &cache);\n\ -palette::SetResult setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ -palette::SetResult setColor(QLatin1String name, QLatin1String from);\n\ -void apply(const palette &other);\n\ -void reset();\n\ -int indexOfColor(color c);\n\ -\n\ -struct row {\n\ -\tQLatin1String name;\n\ -\tQLatin1String value;\n\ -\tQLatin1String fallback;\n\ -\tQLatin1String description;\n\ -};\n\ -QList data();\n\ -\n\ -} // namespace main_palette\n"; - - return true; -} - -bool Generator::writeStructsForwardDeclarations() { - bool hasNoExternalStructs = module_.enumVariables([&](const Variable &value) -> bool { - if (value.value.type().tag == structure::TypeTag::Struct) { - if (!module_.findStructInModule(value.value.type().name, module_)) { - return false; - } - } - return true; - }); - if (hasNoExternalStructs) { - return false; - } - - header_->newline(); - std::set alreadyDeclaredTypes; - bool result = module_.enumVariables([&](const Variable &value) -> bool { - if (value.value.type().tag == structure::TypeTag::Struct) { - if (!module_.findStructInModule(value.value.type().name, module_)) { - if (alreadyDeclaredTypes.find(value.value.type().name.back()) == alreadyDeclaredTypes.end()) { - header_->stream() << "struct " << value.value.type().name.back() << ";\n"; - alreadyDeclaredTypes.emplace(value.value.type().name.back()); - } - } - } - return true; - }); - header_->newline(); - return result; -} - -bool Generator::writeStructsDefinitions() { - if (!module_.hasStructs()) { - return true; - } - - bool result = module_.enumStructs([&](const Struct &value) -> bool { - header_->stream() << "\ -struct " << value.name.back() << " {\n"; - for (auto &field : value.fields) { - auto type = typeToString(field.type); - if (type.isEmpty()) { - return false; - } - header_->stream() << "\t" << type << " " << field.name.back() << ";\n"; - } - header_->stream() << "\ -};\n\n"; - return true; - }); - - return result; -} - -bool Generator::writeRefsDeclarations() { - if (!module_.hasVariables()) { - return true; - } - - header_->pushNamespace("st"); - - if (isPalette_) { - header_->stream() << "extern const style::color &transparent; // special color\n"; - } - bool result = module_.enumVariables([&](const Variable &value) -> bool { - auto name = value.name.back(); - auto type = typeToString(value.value.type()); - if (type.isEmpty()) { - return false; - } - - header_->stream() << "extern const " << type << " &" << name << ";\n"; - return true; - }); - - header_->popNamespace(); - - return result; -} - -bool Generator::writeIncludesInSource() { - if (!module_.hasIncludes()) { - return true; - } - - auto includes = QStringList(); - std::function collector = [&](const Module &module) { - module.enumIncludes(collector); - auto base = moduleBaseName(module); - if (!includes.contains(base)) { - includes.push_back(base); - } - return true; - }; - auto result = module_.enumIncludes(collector); - for (auto base : includes) { - source_->include(base + ".h"); - } - source_->newline(); - return result; -} - -bool Generator::writeVariableDefinitions() { - if (!module_.hasVariables()) { - return true; - } - - source_->newline(); - bool result = module_.enumVariables([&](const Variable &variable) -> bool { - auto name = variable.name.back(); - auto type = typeToString(variable.value.type()); - if (type.isEmpty()) { - return false; - } - source_->stream() << type << " _" << name << " = " << typeToDefaultValue(variable.value.type()) << ";\n"; - return true; - }); - return result; -} - -bool Generator::writeRefsDefinition() { - if (!module_.hasVariables()) { - return true; - } - - if (isPalette_) { - source_->stream() << "const style::color &transparent(_palette.get_transparent()); // special color\n"; - } - bool result = module_.enumVariables([&](const Variable &variable) -> bool { - auto name = variable.name.back(); - auto type = typeToString(variable.value.type()); - if (type.isEmpty()) { - return false; - } - source_->stream() << "const " << type << " &" << name << "("; - if (isPalette_) { - source_->stream() << "_palette.get_" << name << "()"; - } else { - source_->stream() << "_" << name; - } - source_->stream() << ");\n"; - return true; - }); - return result; -} - -bool Generator::writeSetPaletteColor() { - source_->newline(); - source_->stream() << "\n\ -int palette::indexOfColor(style::color c) const {\n\ - auto start = data(0);\n\ - if (c._data >= start && c._data < start + kCount) {\n\ - return static_cast(c._data - start);\n\ - }\n\ - return -1;\n\ -}\n\ -\n\ -color palette::colorAtIndex(int index) const {\n\ - Assert(_ready);\n\ - Assert(index >= 0 && index < kCount);\n\ - return _colors[index];\n\ -}\n\ -\n\ -void palette::finalize() {\n\ - if (_ready) return;\n\ - _ready = true;\n\ -\n\ - compute(0, -1, { 255, 255, 255, 0}); // special color\n"; - - QList names; - module_.enumVariables([&](const Variable &variable) -> bool { - names.push_back(variable.name); - return true; - }); - - QString dataRows; - int indexInPalette = 1; - QByteArray checksumString; - checksumString.append("&transparent:{ 255, 255, 255, 0 }"); - auto result = module_.enumVariables([&](const Variable &variable) -> bool { - auto name = variable.name.back(); - auto index = indexInPalette++; - paletteIndices_.emplace(name, index); - if (variable.value.type().tag != structure::TypeTag::Color) { - return false; - } - auto color = variable.value.Color(); - auto fallbackIterator = paletteIndices_.find(colorFallbackName(variable.value)); - auto fallbackIndex = (fallbackIterator == paletteIndices_.end()) ? -1 : fallbackIterator->second; - auto assignment = QString("{ %1, %2, %3, %4 }").arg(color.red).arg(color.green).arg(color.blue).arg(color.alpha); - source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", " << assignment << ");\n"; - checksumString.append('&' + name + ':' + assignment); - - auto isCopy = !variable.value.copyOf().isEmpty(); - auto colorString = paletteColorValue(color); - auto fallbackName = QString(); - if (fallbackIndex > 0) { - auto fallbackVariable = module_.findVariableInModule(names[fallbackIndex - 1], module_); - if (fallbackVariable && fallbackVariable->value.type().tag == structure::TypeTag::Color) { - fallbackName = fallbackVariable->name.back(); - } - } - auto value = isCopy ? fallbackName : '#' + colorString; - if (value.isEmpty()) { - return false; - } - - dataRows.append("\tresult.push_back({ qstr(\"" + name + "\"), qstr(\"" + value + "\"), qstr(\"" + (isCopy ? QString() : fallbackName) + "\"), qstr(" + stringToEncodedString(variable.description.toStdString()) + ") });\n"); - return true; - }); - if (!result) { - return false; - } - auto count = indexInPalette; - auto checksum = base::crc32(checksumString.constData(), checksumString.size()); - - source_->stream() << "\n\n"; - for (const auto &[over, under] : kMustBeContrast) { - const auto overIndex = paletteIndices_.find(over); - const auto underIndex = paletteIndices_.find(under); - if (overIndex == paletteIndices_.end() || underIndex == paletteIndices_.end()) { - return false; - } - source_->stream() << "\tinternal::EnsureContrast(*data(" << overIndex->second << "), *data(" << underIndex->second << "));\n"; - } - source_->stream() << "\ -}\n\ -\n\ -int32 palette::Checksum() {\n\ - return " << checksum << ";\n\ -}\n"; - - source_->newline().pushNamespace().newline(); - source_->stream() << "\ -int getPaletteIndex(QLatin1String name) {\n\ - auto size = name.size();\n\ - auto data = name.data();\n"; - - auto tabs = [](int size) { - return QString(size, '\t'); - }; - - enum class UsedCheckType { - Switch, - If, - UpcomingIf, - }; - auto checkTypes = QVector(); - auto checkLengthHistory = QVector(1, 0); - auto chars = QString(); - auto tabsUsed = 1; - - // Returns true if at least one check was finished. - auto finishChecksTillKey = [&](const QString &key) { - auto result = false; - while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) { - result = true; - - auto wasType = checkTypes.back(); - chars.resize(chars.size() - 1); - checkTypes.pop_back(); - checkLengthHistory.pop_back(); - if (wasType == UsedCheckType::Switch || wasType == UsedCheckType::If) { - --tabsUsed; - if (wasType == UsedCheckType::Switch) { - source_->stream() << tabs(tabsUsed) << "break;\n"; - } - if ((!chars.isEmpty() && key.midRef(0, chars.size()) != chars) || key == chars) { - source_->stream() << tabs(tabsUsed) << "}\n"; - } - } - } - return result; - }; - - // Check if we can use "if" for a check on "charIndex" in "it" (otherwise only "switch") - auto canUseIfForCheck = [](auto it, auto end, int charIndex) { - auto key = it->first; - auto i = it; - auto keyStart = key.mid(0, charIndex); - for (++i; i != end; ++i) { - auto nextKey = i->first; - if (nextKey.mid(0, charIndex) != keyStart) { - return true; - } else if (nextKey.size() > charIndex && nextKey[charIndex] != key[charIndex]) { - return false; - } - } - return true; - }; - - auto countMinimalLength = [](auto it, auto end, int charIndex) { - auto key = it->first; - auto i = it; - auto keyStart = key.mid(0, charIndex); - auto result = key.size(); - for (++i; i != end; ++i) { - auto nextKey = i->first; - if (nextKey.mid(0, charIndex) != keyStart) { - break; - } else if (nextKey.size() > charIndex && result > nextKey.size()) { - result = nextKey.size(); - } - } - return result; - }; - - for (auto i = paletteIndices_.begin(), e = paletteIndices_.end(); i != e; ++i) { - auto name = i->first; - auto index = i->second; - - auto weContinueOldSwitch = finishChecksTillKey(name); - while (chars.size() != name.size()) { - auto checking = chars.size(); - auto partialKey = name.mid(0, checking); - - auto keyChar = name[checking]; - auto usedIfForCheckCount = 0; - auto minimalLengthCheck = countMinimalLength(i, e, checking); - for (; checking + usedIfForCheckCount != name.size(); ++usedIfForCheckCount) { - if (!canUseIfForCheck(i, e, checking + usedIfForCheckCount) - || countMinimalLength(i, e, checking + usedIfForCheckCount) != minimalLengthCheck) { - break; - } - } - auto usedIfForCheck = !weContinueOldSwitch && (usedIfForCheckCount > 0); - auto checkLengthCondition = QString(); - if (weContinueOldSwitch) { - weContinueOldSwitch = false; - } else { - checkLengthCondition = (minimalLengthCheck > checkLengthHistory.back()) ? ("size >= " + QString::number(minimalLengthCheck)) : QString(); - if (!usedIfForCheck) { - source_->stream() << tabs(tabsUsed) << (checkLengthCondition.isEmpty() ? QString() : ("if (" + checkLengthCondition + ") ")) << "switch (data[" << checking << "]) {\n"; - } - } - if (usedIfForCheck) { - auto conditions = QStringList(); - if (usedIfForCheckCount > 1) { - conditions.push_back("!memcmp(data + " + QString::number(checking) + ", \"" + name.mid(checking, usedIfForCheckCount) + "\", " + QString::number(usedIfForCheckCount) + ")"); - } else { - conditions.push_back("data[" + QString::number(checking) + "] == '" + keyChar + "'"); - } - if (!checkLengthCondition.isEmpty()) { - conditions.push_front(checkLengthCondition); - } - source_->stream() << tabs(tabsUsed) << "if (" << conditions.join(" && ") << ") {\n"; - checkTypes.push_back(UsedCheckType::If); - for (auto i = 1; i != usedIfForCheckCount; ++i) { - checkTypes.push_back(UsedCheckType::UpcomingIf); - chars.push_back(keyChar); - checkLengthHistory.push_back(qMax(minimalLengthCheck, checkLengthHistory.back())); - keyChar = name[checking + i]; - } - } else { - source_->stream() << tabs(tabsUsed) << "case '" << keyChar << "':\n"; - checkTypes.push_back(UsedCheckType::Switch); - } - ++tabsUsed; - chars.push_back(keyChar); - checkLengthHistory.push_back(qMax(minimalLengthCheck, checkLengthHistory.back())); - } - source_->stream() << tabs(tabsUsed) << "return (size == " << chars.size() << ") ? " << index << " : -1;\n"; - } - finishChecksTillKey(QString()); - - source_->stream() << "\ -\n\ - return -1;\n\ -}\n"; - - source_->newline().popNamespace().newline(); - source_->stream() << "\ -QByteArray palette::save() const {\n\ - if (!_ready) const_cast(this)->finalize();\n\ -\n\ - auto result = QByteArray(" << (count * 4) << ", Qt::Uninitialized);\n\ - for (auto i = 0, index = 0; i != " << count << "; ++i) {\n\ - result[index++] = static_cast(data(i)->c.red());\n\ - result[index++] = static_cast(data(i)->c.green());\n\ - result[index++] = static_cast(data(i)->c.blue());\n\ - result[index++] = static_cast(data(i)->c.alpha());\n\ - }\n\ - return result;\n\ -}\n\ -\n\ -bool palette::load(const QByteArray &cache) {\n\ - if (cache.size() != " << (count * 4) << ") return false;\n\ -\n\ - auto p = reinterpret_cast(cache.constData());\n\ - for (auto i = 0; i != " << count << "; ++i) {\n\ - setData(i, { p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3] });\n\ - }\n\ - return true;\n\ -}\n\ -\n\ -palette::SetResult palette::setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ - auto nameIndex = getPaletteIndex(name);\n\ - if (nameIndex < 0) return SetResult::KeyNotFound;\n\ - auto duplicate = (_status[nameIndex] != Status::Initial);\n\ -\n\ - setData(nameIndex, { r, g, b, a });\n\ - return duplicate ? SetResult::Duplicate : SetResult::Ok;\n\ -}\n\ -\n\ -palette::SetResult palette::setColor(QLatin1String name, QLatin1String from) {\n\ - auto nameIndex = getPaletteIndex(name);\n\ - if (nameIndex < 0) return SetResult::KeyNotFound;\n\ - auto duplicate = (_status[nameIndex] != Status::Initial);\n\ -\n\ - auto fromIndex = getPaletteIndex(from);\n\ - if (fromIndex < 0 || _status[fromIndex] != Status::Loaded) return SetResult::ValueNotFound;\n\ -\n\ - setData(nameIndex, *data(fromIndex));\n\ - return duplicate ? SetResult::Duplicate : SetResult::Ok;\n\ -}\n\ -\n\ -namespace main_palette {\n\ -\n\ -QByteArray save() {\n\ - return _palette.save();\n\ -}\n\ -\n\ -bool load(const QByteArray &cache) {\n\ - if (_palette.load(cache)) {\n\ - style::internal::resetIcons();\n\ - return true;\n\ - }\n\ - return false;\n\ -}\n\ -\n\ -palette::SetResult setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ - return _palette.setColor(name, r, g, b, a);\n\ -}\n\ -\n\ -palette::SetResult setColor(QLatin1String name, QLatin1String from) {\n\ - return _palette.setColor(name, from);\n\ -}\n\ -\n\ -void apply(const palette &other) {\n\ - _palette = other;\n\ - style::internal::resetIcons();\n\ -}\n\ -\n\ -void reset() {\n\ - _palette.reset();\n\ - style::internal::resetIcons();\n\ -}\n\ -\n\ -int indexOfColor(color c) {\n\ - return _palette.indexOfColor(c);\n\ -}\n\ -\n\ -QList data() {\n\ - auto result = QList();\n\ - result.reserve(" << count << ");\n\ -\n\ -" << dataRows << "\n\ - return result;\n\ -}\n\ -\n\ -} // namespace main_palette\n\ -\n"; - - return result; -} - -bool Generator::writeVariableInit() { - if (!module_.hasVariables()) { - return true; - } - - if (!collectUniqueValues()) { - return false; - } - bool hasUniqueValues = (!pxValues_.isEmpty() || !fontFamilies_.isEmpty() || !iconMasks_.isEmpty()); - if (hasUniqueValues) { - source_->pushNamespace(); - if (!writePxValuesInit()) { - return false; - } - if (!writeFontFamiliesInit()) { - return false; - } - if (!writeIconValues()) { - return false; - } - source_->popNamespace().newline(); - } - - source_->stream() << "\ -void init_" << baseName_ << "(int scale) {\n\ - if (inited) return;\n\ - inited = true;\n\n"; - - if (module_.hasIncludes()) { - bool writtenAtLeastOne = false; - bool result = module_.enumIncludes([&](const Module &module) -> bool { - if (module.hasVariables()) { - source_->stream() << "\tinit_" + moduleBaseName(module) + "(scale);\n"; - writtenAtLeastOne = true; - } - return true; - }); - if (!result) { - return false; - } - if (writtenAtLeastOne) { - source_->newline(); - } - } - - if (!pxValues_.isEmpty() || !fontFamilies_.isEmpty()) { - if (!pxValues_.isEmpty()) { - source_->stream() << "\tinitPxValues(scale);\n"; - } - if (!fontFamilies_.isEmpty()) { - source_->stream() << "\tinitFontFamilies();\n"; - } - source_->newline(); - } - - if (isPalette_) { - source_->stream() << "\t_palette.finalize();\n"; - } else if (!module_.enumVariables([&](const Variable &variable) -> bool { - auto name = variable.name.back(); - auto value = valueAssignmentCode(variable.value); - if (value.isEmpty()) { - return false; - } - source_->stream() << "\t_" << name << " = " << value << ";\n"; - return true; - })) { - return false; - } - source_->stream() << "\ -}\n\n"; - return true; -} - -bool Generator::writePxValuesInit() { - if (pxValues_.isEmpty()) { - return true; - } - - for (auto i = pxValues_.cbegin(), e = pxValues_.cend(); i != e; ++i) { - source_->stream() << "int " << pxValueName(i.key()) << " = " << i.key() << ";\n"; - } - source_->stream() << "\ -void initPxValues(int scale) {\n"; - for (auto it = pxValues_.cbegin(), e = pxValues_.cend(); it != e; ++it) { - auto value = it.key(); - source_->stream() << "\t" << pxValueName(value) << " = ConvertScale(" << value << ", scale);\n"; - } - source_->stream() << "\ -}\n\n"; - return true; -} - -bool Generator::writeFontFamiliesInit() { - if (fontFamilies_.isEmpty()) { - return true; - } - - for (auto familyIndex : fontFamilies_) { - source_->stream() << "int font" << familyIndex << "index;\n"; - } - source_->stream() << "void initFontFamilies() {\n"; - for (auto i = fontFamilies_.cbegin(), e = fontFamilies_.cend(); i != e; ++i) { - auto family = stringToEncodedString(i.key()); - source_->stream() << "\tfont" << i.value() << "index = style::internal::registerFontFamily(" << family << ");\n"; - } - source_->stream() << "}\n\n"; - return true; -} - -namespace { - -QByteArray iconMaskValueSize(int width, int height) { - QByteArray result; - QLatin1String generateTag("GENERATE:"); - result.append(generateTag.data(), generateTag.size()); - QLatin1String sizeTag("SIZE:"); - result.append(sizeTag.data(), sizeTag.size()); - { - QDataStream stream(&result, QIODevice::Append); - stream.setVersion(QDataStream::Qt_5_1); - stream << qint32(width) << qint32(height); - } - return result; -} - -QByteArray iconMaskValuePng(QString filepath) { - QByteArray result; - - QFileInfo fileInfo(filepath); - auto directory = fileInfo.dir(); - auto nameAndModifiers = fileInfo.fileName().split('-'); - filepath = directory.filePath(nameAndModifiers[0]); - auto modifiers = nameAndModifiers.mid(1); - - const auto readImage = [&](const QString &postfix) { - const auto path = filepath + postfix + ".png"; - auto result = QImage(path); - if (result.isNull()) { - common::logError(common::kErrorFileNotOpened, path) << "could not open icon file"; - return QImage(); - } else if (result.format() != QImage::Format_RGB32) { - result = std::move(result).convertToFormat(QImage::Format_RGB32); - } - result.setDevicePixelRatio(1.); - return result; - }; - auto png1x = readImage(""); - auto png2x = readImage("@2x"); - auto png3x = readImage("@3x"); - if (png1x.isNull() || png2x.isNull() || png3x.isNull()) { - return result; - } - if (png1x.width() * 2 != png2x.width() - || png1x.height() * 2 != png2x.height() - || png1x.width() * 3 != png3x.width() - || png1x.height() * 3 != png3x.height()) { - common::logError(kErrorBadIconSize, filepath + ".png") - << "bad icons size, 1x: " - << png1x.width() << "x" << png1x.height() - << ", 2x: " - << png2x.width() << "x" << png2x.height() - << ", 3x: " - << png3x.width() << "x" << png3x.height(); - return result; - } - for (const auto modifierName : modifiers) { - if (const auto modifier = GetModifier(modifierName)) { - modifier(png1x); - modifier(png2x); - modifier(png3x); - } else { - common::logError(common::kErrorInternal, filepath) << "modifier should be valid here, name: " << modifierName.toStdString(); - return result; - } - } - QImage composed(png3x.width(), png3x.height() + png2x.height(), QImage::Format_RGB32); - composed.fill(Qt::black); - { - QPainter p(&composed); - p.drawImage(0, 0, png1x); - p.drawImage(png1x.width(), 0, png2x); - p.drawImage(0, png2x.height(), png3x); - } - { - QBuffer buffer(&result); - composed.save(&buffer, "PNG"); - } - return result; -} - -} // namespace - -bool Generator::writeIconValues() { - if (iconMasks_.isEmpty()) { - return true; - } - - for (auto i = iconMasks_.cbegin(), e = iconMasks_.cend(); i != e; ++i) { - QString filePath = i.key(); - QByteArray maskData; - QImage png100x, png200x; - if (filePath.startsWith("size://")) { - QStringList dimensions = filePath.mid(7).split(','); - if (dimensions.size() < 2 || dimensions.at(0).toInt() <= 0 || dimensions.at(1).toInt() <= 0) { - common::logError(common::kErrorFileNotOpened, filePath) << "bad dimensions"; - return false; - } - maskData = iconMaskValueSize(dimensions.at(0).toInt(), dimensions.at(1).toInt()); - } else { - maskData = iconMaskValuePng(filePath); - } - if (maskData.isEmpty()) { - return false; - } - source_->stream() << "const uchar iconMask" << i.value() << "Data[] = " << stringToBinaryArray(std::string(maskData.constData(), maskData.size())) << ";\n"; - source_->stream() << "IconMask iconMask" << i.value() << "(iconMask" << i.value() << "Data);\n\n"; - } - return true; -} - -bool Generator::collectUniqueValues() { - int fontFamilyIndex = 0; - int iconMaskIndex = 0; - std::function collector = [this, &collector, &fontFamilyIndex, &iconMaskIndex](const Variable &variable) { - auto value = variable.value; - if (!value.copyOf().isEmpty()) { - return true; - } - - switch (value.type().tag) { - case Tag::Invalid: - case Tag::Int: - case Tag::Double: - case Tag::String: - case Tag::Color: - case Tag::Align: break; - case Tag::Pixels: pxValues_.insert(value.Int(), true); break; - case Tag::Point: { - auto v(value.Point()); - pxValues_.insert(v.x, true); - pxValues_.insert(v.y, true); - } break; - case Tag::Size: { - auto v(value.Size()); - pxValues_.insert(v.width, true); - pxValues_.insert(v.height, true); - } break; - case Tag::Margins: { - auto v(value.Margins()); - pxValues_.insert(v.left, true); - pxValues_.insert(v.top, true); - pxValues_.insert(v.right, true); - pxValues_.insert(v.bottom, true); - } break; - case Tag::Font: { - auto v(value.Font()); - pxValues_.insert(v.size, true); - if (!v.family.empty() && !fontFamilies_.contains(v.family)) { - fontFamilies_.insert(v.family, ++fontFamilyIndex); - } - } break; - case Tag::Icon: { - auto v(value.Icon()); - for (auto &part : v.parts) { - pxValues_.insert(part.offset.Point().x, true); - pxValues_.insert(part.offset.Point().y, true); - if (!iconMasks_.contains(part.filename)) { - iconMasks_.insert(part.filename, ++iconMaskIndex); - } - } - } break; - case Tag::Struct: { - auto fields = variable.value.Fields(); - if (!fields) { - return false; - } - - for (auto field : *fields) { - if (!collector(field.variable)) { - return false; - } - } - } break; - } - return true; - }; - return module_.enumVariables(collector); -} - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.h b/Telegram/SourceFiles/codegen/style/generator.h deleted file mode 100644 index 062c0cbc8..000000000 --- a/Telegram/SourceFiles/codegen/style/generator.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/style/structure_types.h" - -namespace codegen { -namespace style { -namespace structure { -class Module; -} // namespace structure - -class Generator { -public: - Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette); - Generator(const Generator &other) = delete; - Generator &operator=(const Generator &other) = delete; - - bool writeHeader(); - bool writeSource(); - -private: - QString typeToString(structure::Type type) const; - QString typeToDefaultValue(structure::Type type) const; - QString valueAssignmentCode(structure::Value value) const; - - bool writeHeaderRequiredIncludes(); - bool writeHeaderStyleNamespace(); - bool writeStructsForwardDeclarations(); - bool writeStructsDefinitions(); - bool writePaletteDefinition(); - bool writeRefsDeclarations(); - - bool writeIncludesInSource(); - bool writeVariableDefinitions(); - bool writeRefsDefinition(); - bool writeSetPaletteColor(); - bool writeVariableInit(); - bool writePxValuesInit(); - bool writeFontFamiliesInit(); - bool writeIconValues(); - bool writeIconsInit(); - - bool collectUniqueValues(); - - const structure::Module &module_; - QString basePath_, baseName_; - const common::ProjectInfo &project_; - std::unique_ptr source_, header_; - bool isPalette_ = false; - - QMap pxValues_; - QMap fontFamilies_; - QMap iconMasks_; // icon file -> index - std::map> paletteIndices_; - -}; - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/main.cpp b/Telegram/SourceFiles/codegen/style/main.cpp deleted file mode 100644 index 27b0cc78c..000000000 --- a/Telegram/SourceFiles/codegen/style/main.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 - -#include "codegen/style/options.h" -#include "codegen/style/processor.h" - -int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); - - auto options = codegen::style::parseOptions(); - if (options.inputPath.isEmpty()) { - return -1; - } - - codegen::style::Processor processor(options); - return processor.launch(); -} diff --git a/Telegram/SourceFiles/codegen/style/module.cpp b/Telegram/SourceFiles/codegen/style/module.cpp deleted file mode 100644 index 120a8a4f4..000000000 --- a/Telegram/SourceFiles/codegen/style/module.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* -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 "codegen/style/module.h" - -namespace codegen { -namespace style { -namespace structure { -namespace { - -QString fullNameKey(const FullName &name) { - return name.join('.'); -} - -} // namespace - -Module::Module(const QString &fullpath) : fullpath_(fullpath) { -} - -void Module::addIncluded(std::unique_ptr &&value) { - included_.push_back(std::move(value)); -} - -bool Module::addStruct(const Struct &value) { - if (findStruct(value.name)) { - return false; - } - structsByName_.insert(fullNameKey(value.name), structs_.size()); - structs_.push_back(value); - return true; -} - -const Struct *Module::findStruct(const FullName &name) const { - if (auto result = findStructInModule(name, *this)) { - return result; - } - for (const auto &module : included_) { - if (auto result = module->findStruct(name)) { - return result; - } - } - return nullptr; -} - -bool Module::addVariable(const Variable &value) { - if (findVariable(value.name)) { - return false; - } - variablesByName_.insert(fullNameKey(value.name), variables_.size()); - variables_.push_back(value); - return true; -} - -const Variable *Module::findVariable(const FullName &name, bool *outFromThisModule) const { - if (auto result = findVariableInModule(name, *this)) { - if (outFromThisModule) *outFromThisModule = true; - return result; - } - for (const auto &module : included_) { - if (auto result = module->findVariable(name)) { - if (outFromThisModule) *outFromThisModule = false; - return result; - } - } - return nullptr; -} - -const Struct *Module::findStructInModule(const FullName &name, const Module &module) { - auto index = module.structsByName_.value(fullNameKey(name), -1); - if (index < 0) { - return nullptr; - } - return &module.structs_.at(index); -} - -const Variable *Module::findVariableInModule(const FullName &name, const Module &module) { - auto index = module.variablesByName_.value(fullNameKey(name), -1); - if (index < 0) { - return nullptr; - } - return &module.variables_.at(index); -} - -} // namespace structure -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/module.h b/Telegram/SourceFiles/codegen/style/module.h deleted file mode 100644 index b97916526..000000000 --- a/Telegram/SourceFiles/codegen/style/module.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include "codegen/style/structure_types.h" - -namespace codegen { -namespace style { -namespace structure { - -class Module { -public: - - explicit Module(const QString &fullpath); - - QString filepath() const { - return fullpath_; - } - - void addIncluded(std::unique_ptr &&value); - - bool hasIncludes() const { - return !included_.empty(); - } - template - bool enumIncludes(F functor) const { - for (const auto &module : included_) { - if (!functor(*module)) { - return false; - } - } - return true; - } - - // Returns false if there is a struct with such name already. - bool addStruct(const Struct &value); - // Returns nullptr if there is no such struct in result_ or any of included modules. - const Struct *findStruct(const FullName &name) const; - bool hasStructs() const { - return !structs_.isEmpty(); - } - - template - bool enumStructs(F functor) const { - for (const auto &value : structs_) { - if (!functor(value)) { - return false; - } - } - return true; - } - - // Returns false if there is a variable with such name already. - bool addVariable(const Variable &value); - // Returns nullptr if there is no such variable in result_ or any of included modules. - const Variable *findVariable(const FullName &name, bool *outFromThisModule = nullptr) const; - bool hasVariables() const { - return !variables_.isEmpty(); - } - - template - bool enumVariables(F functor) const { - for (const auto &value : variables_) { - if (!functor(value)) { - return false; - } - } - return true; - } - - explicit operator bool() const { - return !fullpath_.isEmpty(); - } - - static const Struct *findStructInModule(const FullName &name, const Module &module); - static const Variable *findVariableInModule(const FullName &name, const Module &module); - -private: - QString fullpath_; - std::vector> included_; - QList structs_; - QList variables_; - QMap structsByName_; - QMap variablesByName_; - -}; - -} // namespace structure -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/options.cpp b/Telegram/SourceFiles/codegen/style/options.cpp deleted file mode 100644 index 434a6f4d4..000000000 --- a/Telegram/SourceFiles/codegen/style/options.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* -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 "codegen/style/options.h" - -#include -#include -#include -#include "codegen/common/logging.h" - -namespace codegen { -namespace style { -namespace { - -constexpr int kErrorIncludePathExpected = 901; -constexpr int kErrorOutputPathExpected = 902; -constexpr int kErrorInputPathExpected = 903; -constexpr int kErrorSingleInputPathExpected = 904; -constexpr int kErrorWorkingPathExpected = 905; - -} // namespace - -using common::logError; - -Options parseOptions() { - Options result; - auto args = QCoreApplication::instance()->arguments(); - for (int i = 1, count = args.size(); i < count; ++i) { // skip first - auto &arg = args.at(i); - - // Include paths - if (arg == "-I") { - if (++i == count) { - logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I"; - return Options(); - } else { - result.includePaths.push_back(args.at(i)); - } - } else if (arg.startsWith("-I")) { - result.includePaths.push_back(arg.mid(2)); - - // Output path - } else if (arg == "-o") { - if (++i == count) { - logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; - return Options(); - } else { - result.outputPath = args.at(i); - } - } else if (arg.startsWith("-o")) { - result.outputPath = arg.mid(2); - - // Working path - } else if (arg == "-w") { - if (++i == count) { - logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w"; - return Options(); - } else { - common::logSetWorkingPath(args.at(i)); - } - } else if (arg.startsWith("-w")) { - common::logSetWorkingPath(arg.mid(2)); - - // Input path - } else { - if (result.inputPath.isEmpty()) { - result.inputPath = arg; - } else { - logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected"; - return Options(); - } - } - } - if (result.inputPath.isEmpty()) { - logError(kErrorInputPathExpected, "Command Line") << "input path expected"; - return Options(); - } - result.isPalette = (QFileInfo(result.inputPath).suffix() == "palette"); - return result; -} - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/options.h b/Telegram/SourceFiles/codegen/style/options.h deleted file mode 100644 index b25a23c30..000000000 --- a/Telegram/SourceFiles/codegen/style/options.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include - -namespace codegen { -namespace style { - -struct Options { - QStringList includePaths = { "." }; - QString outputPath = "."; - QString inputPath; - bool isPalette = false; -}; - -// Parsing failed if inputPath is empty in the result. -Options parseOptions(); - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp deleted file mode 100644 index 3a9c29934..000000000 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ /dev/null @@ -1,840 +0,0 @@ -/* -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 "codegen/style/parsed_file.h" - -#include -#include -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/common/logging.h" - -using BasicToken = codegen::common::BasicTokenizedFile::Token; -using BasicType = BasicToken::Type; - -namespace codegen { -namespace style { - -using structure::logFullName; - -namespace { - -constexpr int kErrorInIncluded = 801; -constexpr int kErrorTypeMismatch = 802; -constexpr int kErrorUnknownField = 803; -constexpr int kErrorIdentifierNotFound = 804; -constexpr int kErrorAlreadyDefined = 805; -constexpr int kErrorBadString = 806; -constexpr int kErrorIconDuplicate = 807; -constexpr int kErrorBadIconModifier = 808; -constexpr int kErrorCyclicDependency = 809; - -QString findInputFile(const Options &options) { - for (const auto &dir : options.includePaths) { - QString tryPath = QDir(dir).absolutePath() + '/' + options.inputPath; - if (QFileInfo(tryPath).exists()) { - return tryPath; - } - } - return options.inputPath; -} - -QString tokenValue(const BasicToken &token) { - if (token.type == BasicType::String) { - return token.value; - } - return token.original.toStringUnchecked(); -} - -bool isValidColor(const QString &str) { - auto len = str.size(); - if (len != 6 && len != 8) { - return false; - } - - for (auto ch : str) { - auto code = ch.unicode(); - if ((code < '0' || code > '9') && (code < 'a' || code > 'f')) { - return false; - } - } - return true; -} - -uchar toGray(uchar r, uchar g, uchar b) { - return qMax(qMin(int(0.21 * r + 0.72 * g + 0.07 * b), 255), 0); -} - -uchar readHexUchar(QChar ch) { - auto code = ch.unicode(); - return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code + 10 - 'a') & 0xFF); -} - -uchar readHexUchar(QChar char1, QChar char2) { - return ((readHexUchar(char1) & 0x0F) << 4) | (readHexUchar(char2) & 0x0F); -} - -structure::data::color convertWebColor(const QString &str, const QString &fallback = QString()) { - uchar r = 0, g = 0, b = 0, a = 255; - if (isValidColor(str)) { - r = readHexUchar(str.at(0), str.at(1)); - g = readHexUchar(str.at(2), str.at(3)); - b = readHexUchar(str.at(4), str.at(5)); - if (str.size() == 8) { - a = readHexUchar(str.at(6), str.at(7)); - } - } - return { r, g, b, a, fallback }; -} - -structure::data::color convertIntColor(int r, int g, int b, int a) { - return { uchar(r & 0xFF), uchar(g & 0xFF), uchar(b & 0xFF), uchar(a & 0xFF) }; -} - -std::string logType(const structure::Type &type) { - if (type.tag == structure::TypeTag::Struct) { - return "struct " + logFullName(type.name); - } - static auto builtInTypes = new QMap { - { structure::TypeTag::Int , "int" }, - { structure::TypeTag::Double , "double" }, - { structure::TypeTag::Pixels , "pixels" }, - { structure::TypeTag::String , "string" }, - { structure::TypeTag::Color , "color" }, - { structure::TypeTag::Point , "point" }, - { structure::TypeTag::Size , "size" }, - { structure::TypeTag::Align , "align" }, - { structure::TypeTag::Margins , "margins" }, - { structure::TypeTag::Font , "font" }, - }; - return builtInTypes->value(type.tag, "invalid"); -} - -bool validateAnsiString(const QString &value) { - for (auto ch : value) { - if (ch.unicode() > 127) { - return false; - } - } - return true; -} - -bool validateAlignString(const QString &value) { - return QRegularExpression("^[a-z_]+$").match(value).hasMatch(); -} - -} // namespace - -Modifier GetModifier(const QString &name) { - static QMap modifiers; - if (modifiers.empty()) { - modifiers.insert("invert", [](QImage &image) { - image.invertPixels(); - }); - modifiers.insert("flip_horizontal", [](QImage &image) { - image = image.mirrored(true, false); - }); - modifiers.insert("flip_vertical", [](QImage &image) { - image = image.mirrored(false, true); - }); - } - return modifiers.value(name); -} - -ParsedFile::ParsedFile( - const Options &options, - std::vector includeStack) -: filePath_(findInputFile(options)) -, file_(filePath_) -, options_(options) -, includeStack_(includeStack) { -} - -bool ParsedFile::read() { - if (std::find(begin(includeStack_), end(includeStack_), filePath_) - != end(includeStack_)) { - logError(kErrorCyclicDependency) << "include cycle detected."; - return false; - } else if (!file_.read()) { - return false; - } - - auto absolutePath = QFileInfo(filePath_).absoluteFilePath(); - module_ = std::make_unique(absolutePath); - do { - if (auto startToken = file_.getToken(BasicType::Name)) { - if (tokenValue(startToken) == "using") { - if (auto includedResult = readIncluded()) { - module_->addIncluded(std::move(includedResult)); - continue; - } - } else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) { - if (auto structResult = readStruct(tokenValue(startToken))) { - if (module_->addStruct(structResult)) { - continue; - } - logError(kErrorAlreadyDefined) << "struct '" << logFullName(structResult.name) << "' already defined"; - break; - } - } else if (auto colonToken = file_.getToken(BasicType::Colon)) { - if (auto variableResult = readVariable(tokenValue(startToken))) { - if (module_->addVariable(variableResult)) { - continue; - } - logError(kErrorAlreadyDefined) << "variable '" << logFullName(variableResult.name) << "' already defined"; - break; - } - } - } - if (file_.atEnd()) { - break; - } - logErrorUnexpectedToken() << "using keyword, or struct definition, or variable definition"; - } while (!failed()); - - if (failed()) { - module_ = nullptr; - } - return !failed(); -} - -common::LogStream ParsedFile::logErrorTypeMismatch() { - return logError(kErrorTypeMismatch) << "type mismatch: "; -} - -ParsedFile::ModulePtr ParsedFile::readIncluded() { - if (auto usingFile = assertNextToken(BasicType::String)) { - if (assertNextToken(BasicType::Semicolon)) { - auto includeStack = includeStack_; - includeStack.push_back(filePath_); - ParsedFile included( - includedOptions(tokenValue(usingFile)), - includeStack); - if (included.read()) { - return included.getResult(); - } else { - logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'"; - } - } - } - return nullptr; -} - -structure::Struct ParsedFile::readStruct(const QString &name) { - if (options_.isPalette) { - logErrorUnexpectedToken() << "unique color variable for the palette"; - return {}; - } - - structure::Struct result = { composeFullName(name) }; - do { - if (auto fieldName = file_.getToken(BasicType::Name)) { - if (auto field = readStructField(tokenValue(fieldName))) { - result.fields.push_back(field); - } - } else if (assertNextToken(BasicType::RightBrace)) { - if (result.fields.isEmpty()) { - logErrorUnexpectedToken() << "at least one field in struct"; - } - break; - } - } while (!failed()); - return result; -} - -structure::Variable ParsedFile::readVariable(const QString &name) { - structure::Variable result = { composeFullName(name) }; - if (auto value = readValue()) { - result.value = value; - if (options_.isPalette && value.type().tag != structure::TypeTag::Color) { - logErrorUnexpectedToken() << "unique color variable for the palette"; - return {}; - } - if (value.type().tag != structure::TypeTag::Struct || !value.copyOf().empty()) { - assertNextToken(BasicType::Semicolon); - result.description = file_.getCurrentLineComment(); - } - } - return result; -} - -structure::StructField ParsedFile::readStructField(const QString &name) { - structure::StructField result = { composeFullName(name) }; - if (auto colonToken = assertNextToken(BasicType::Colon)) { - if (auto type = readType()) { - result.type = type; - assertNextToken(BasicType::Semicolon); - } - } - return result; -} - -structure::Type ParsedFile::readType() { - structure::Type result; - if (auto nameToken = assertNextToken(BasicType::Name)) { - auto name = tokenValue(nameToken); - if (auto builtInType = typeNames_.value(name.toStdString())) { - result = builtInType; - } else { - auto fullName = composeFullName(name); - if (module_->findStruct(fullName)) { - result.tag = structure::TypeTag::Struct; - result.name = fullName; - } else { - logError(kErrorIdentifierNotFound) << "type name '" << logFullName(fullName) << "' not found"; - } - } - } - return result; -} - -structure::Value ParsedFile::readValue() { - if (auto colorValue = readColorValue()) { - return colorValue; - } else if (auto pointValue = readPointValue()) { - return pointValue; - } else if (auto sizeValue = readSizeValue()) { - return sizeValue; - } else if (auto alignValue = readAlignValue()) { - return alignValue; - } else if (auto marginsValue = readMarginsValue()) { - return marginsValue; - } else if (auto fontValue = readFontValue()) { - return fontValue; - } else if (auto iconValue = readIconValue()) { - return iconValue; - } else if (auto numericValue = readNumericValue()) { - return numericValue; - } else if (auto stringValue = readStringValue()) { - return stringValue; - } else if (auto structValue = readStructValue()) { - return structValue; - } else if (auto copyValue = readCopyValue()) { - return copyValue; - } else { - logErrorUnexpectedToken() << "variable value"; - } - return {}; -} - -structure::Value ParsedFile::readStructValue() { - if (auto structName = file_.getToken(BasicType::Name)) { - if (auto result = defaultConstructedStruct(composeFullName(tokenValue(structName)))) { - if (file_.getToken(BasicType::LeftParenthesis)) { - if (!readStructParents(result)) { - return {}; - } - } - if (assertNextToken(BasicType::LeftBrace)) { - readStructValueInner(result); - } - return result; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName &structName) { - if (auto pattern = module_->findStruct(structName)) { - QList fields; - fields.reserve(pattern->fields.size()); - for (const auto &fieldType : pattern->fields) { - fields.push_back({ - { // variable - fieldType.name, - { fieldType.type, Qt::Uninitialized }, // value - }, - structure::data::field::Status::Uninitialized, // status - }); - } - return { structName, fields }; - } - return {}; -} - -void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) { - bool fromTheSameModule = false; - if (auto parent = module_->findVariable(parentName, &fromTheSameModule)) { - if (parent->value.type() != result.type()) { - logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(parent->value.type()) << "' while child value has type " << logType(result.type()); - return; - } - - const auto *srcFields(parent->value.Fields()); - auto *dstFields(result.Fields()); - if (!srcFields || !dstFields) { - logAssert(false) << "struct data check failed"; - return; - } - - logAssert(srcFields->size() == dstFields->size()) << "struct size check failed"; - for (int i = 0, s = srcFields->size(); i != s; ++i) { - const auto &srcField(srcFields->at(i)); - auto &dstField((*dstFields)[i]); - using Status = structure::data::field::Status; - if (srcField.status == Status::Explicit || - dstField.status == Status::Uninitialized) { - const auto &srcValue(srcField.variable.value); - auto &dstValue(dstField.variable.value); - logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed"; - - // Optimization: don't let the style files to contain unnamed inherited - // icons from the other (included) style files, because they will - // duplicate the binary data across different style c++ source files. - // - // Example: - // a.style has "A: Struct { icon: icon { ..file.. } };" and - // b.style has "B: Struct(A) { .. };" with non-overriden icon field. - // Then both style_a.cpp and style_b.cpp will contain binary data of "file". - if (!fromTheSameModule - && srcValue.type().tag == structure::TypeTag::Icon - && !srcValue.Icon().parts.empty() - && srcValue.copyOf().isEmpty()) { - logError(kErrorIconDuplicate) << "an unnamed icon field '" << logFullName(srcField.variable.name) << "' is inherited from parent '" << logFullName(parentName) << "'"; - return; - } - dstValue = srcValue; - dstField.status = Status::Implicit; - } - } - } else { - logError(kErrorIdentifierNotFound) << "parent '" << logFullName(parentName) << "' not found"; - } -} - -bool ParsedFile::readStructValueInner(structure::Value &result) { - do { - if (auto fieldName = file_.getToken(BasicType::Name)) { - if (!assertNextToken(BasicType::Colon)) { - return false; - } - - if (auto field = readVariable(tokenValue(fieldName))) { - if (!assignStructField(result, field)) { - return false; - } - } - } else if (assertNextToken(BasicType::RightBrace)) { - return true; - } - } while (!failed()); - return false; -} - -bool ParsedFile::assignStructField(structure::Value &result, const structure::Variable &field) { - auto *fields = result.Fields(); - if (!fields) { - logAssert(false) << "struct data check failed"; - return false; - } - for (auto &already : *fields) { - if (already.variable.name == field.name) { - if (already.variable.value.type() == field.value.type()) { - already.variable.value = field.value; - already.status = structure::data::field::Status::Explicit; - return true; - } else { - logErrorTypeMismatch() << "field '" << logFullName(already.variable.name) << "' has type '" << logType(already.variable.value.type()) << "' while value has type '" << logType(field.value.type()) << "'"; - return false; - } - } - } - logError(kErrorUnknownField) << "field '" << logFullName(field.name) << "' was not found in struct of type '" << logType(result.type()) << "'"; - return false; -} - -bool ParsedFile::readStructParents(structure::Value &result) { - do { - if (auto parentName = assertNextToken(BasicType::Name)) { - applyStructParent(result, composeFullName(tokenValue(parentName))); - if (file_.getToken(BasicType::RightParenthesis)) { - return true; - } else { - assertNextToken(BasicType::Comma); - } - } else { - logErrorUnexpectedToken() << "struct variable parent"; - } - } while (!failed()); - return false; -} - -structure::Value ParsedFile::readPositiveValue() { - auto numericToken = file_.getAnyToken(); - if (numericToken.type == BasicType::Int) { - return { structure::TypeTag::Int, tokenValue(numericToken).toInt() }; - } else if (numericToken.type == BasicType::Double) { - return { structure::TypeTag::Double, tokenValue(numericToken).toDouble() }; - } else if (numericToken.type == BasicType::Name) { - auto value = tokenValue(numericToken); - auto match = QRegularExpression("^\\d+px$").match(value); - if (match.hasMatch()) { - return { structure::TypeTag::Pixels, value.mid(0, value.size() - 2).toInt() }; - } - } - file_.putBack(); - return {}; -} - -structure::Value ParsedFile::readNumericValue() { - if (auto value = readPositiveValue()) { - return value; - } else if (auto minusToken = file_.getToken(BasicType::Minus)) { - if (auto positiveValue = readNumericValue()) { - return { positiveValue.type().tag, -positiveValue.Int() }; - } - logErrorUnexpectedToken() << "numeric value"; - } - return {}; -} - -structure::Value ParsedFile::readStringValue() { - if (auto stringToken = file_.getToken(BasicType::String)) { - auto value = tokenValue(stringToken); - if (validateAnsiString(value)) { - return { structure::TypeTag::String, stringToken.value.toStdString() }; - } - logError(kErrorBadString) << "unicode symbols are not supported"; - } - return {}; -} - -structure::Value ParsedFile::readColorValue() { - if (auto numberSign = file_.getToken(BasicType::Number)) { - if (options_.isPalette) { - auto color = file_.getAnyToken(); - if (color.type == BasicType::Int || color.type == BasicType::Name) { - auto chars = tokenValue(color).toLower(); - if (isValidColor(chars)) { - if (auto fallbackSeparator = file_.getToken(BasicType::Or)) { - if (options_.isPalette) { - if (auto fallbackName = file_.getToken(BasicType::Name)) { - structure::FullName name = { tokenValue(fallbackName) }; - if (auto variable = module_->findVariableInModule(name, *module_)) { - return { convertWebColor(chars, tokenValue(fallbackName)) }; - } else { - logError(kErrorIdentifierNotFound) << "fallback color name"; - } - } else { - logErrorUnexpectedToken() << "fallback color name"; - } - } else { - logErrorUnexpectedToken() << "';', color fallbacks are only allowed in palette module"; - } - } else { - return { convertWebColor(chars) }; - } - } - } else { - logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format"; - } - } else { - logErrorUnexpectedToken() << "color value alias, unique color values are only allowed in palette module"; - } - } else if (auto transparentName = file_.getToken(BasicType::Name)) { - if (tokenValue(transparentName) == "transparent") { - return { structure::data::color { 255, 255, 255, 0 } }; - } - file_.putBack(); - } - - return {}; -} - -structure::Value ParsedFile::readPointValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "point") { - assertNextToken(BasicType::LeftParenthesis); - - auto x = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma); - auto y = readNumericOrNumericCopyValue(); - if (x.type().tag != structure::TypeTag::Pixels || - y.type().tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "expected two px values for the point"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { structure::data::point { x.Int(), y.Int() } }; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readSizeValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "size") { - assertNextToken(BasicType::LeftParenthesis); - - auto w = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma); - auto h = readNumericOrNumericCopyValue(); - if (w.type().tag != structure::TypeTag::Pixels || - h.type().tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "expected two px values for the size"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { structure::data::size { w.Int(), h.Int() } }; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readAlignValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "align") { - assertNextToken(BasicType::LeftParenthesis); - - auto align = tokenValue(assertNextToken(BasicType::Name)); - - assertNextToken(BasicType::RightParenthesis); - - if (validateAlignString(align)) { - return { structure::TypeTag::Align, align.toStdString() }; - } else { - logError(kErrorBadString) << "bad align string"; - } - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readMarginsValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "margins") { - assertNextToken(BasicType::LeftParenthesis); - - auto l = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma); - auto t = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma); - auto r = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma); - auto b = readNumericOrNumericCopyValue(); - if (l.type().tag != structure::TypeTag::Pixels || - t.type().tag != structure::TypeTag::Pixels || - r.type().tag != structure::TypeTag::Pixels || - b.type().tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "expected four px values for the margins"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { structure::data::margins { l.Int(), t.Int(), r.Int(), b.Int() } }; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readFontValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "font") { - assertNextToken(BasicType::LeftParenthesis); - - int flags = 0; - structure::Value family, size; - do { - if (auto formatToken = file_.getToken(BasicType::Name)) { - if (tokenValue(formatToken) == "bold") { - flags |= structure::data::font::Bold; - } else if (tokenValue(formatToken) == "italic") { - flags |= structure::data::font::Italic; - } else if (tokenValue(formatToken) == "underline") { - flags |= structure::data::font::Underline; - } else { - file_.putBack(); - } - } - if (auto familyValue = readStringOrStringCopyValue()) { - family = familyValue; - } else if (auto sizeValue = readNumericOrNumericCopyValue()) { - size = sizeValue; - } else if (file_.getToken(BasicType::RightParenthesis)) { - break; - } else { - logErrorUnexpectedToken() << "font family, font size or ')'"; - } - } while (!failed()); - - if (size.type().tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "px value for the font size expected"; - } - return { structure::data::font { family.String(), size.Int(), flags } }; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readIconValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "icon") { - std::vector parts; - if (file_.getToken(BasicType::LeftBrace)) { // complex icon - do { - if (file_.getToken(BasicType::RightBrace)) { - break; - } else if (file_.getToken(BasicType::LeftBrace)) { - if (auto part = readMonoIconFields()) { - assertNextToken(BasicType::RightBrace); - parts.push_back(part); - file_.getToken(BasicType::Comma); - continue; - } - return {}; - } else { - logErrorUnexpectedToken() << "icon part or '}'"; - return {}; - } - } while (true); - - } else if (file_.getToken(BasicType::LeftParenthesis)) { // short icon - if (auto theOnlyPart = readMonoIconFields()) { - assertNextToken(BasicType::RightParenthesis); - parts.push_back(theOnlyPart); - } - } - - return { structure::data::icon { parts } }; - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readCopyValue() { - if (auto copyName = file_.getToken(BasicType::Name)) { - structure::FullName name = { tokenValue(copyName) }; - if (auto variable = module_->findVariable(name)) { - return variable->value.makeCopy(variable->name); - } - file_.putBack(); - } - return {}; -} - -structure::Value ParsedFile::readNumericOrNumericCopyValue() { - if (auto result = readNumericValue()) { - return result; - } else if (auto copy = readCopyValue()) { - auto type = copy.type().tag; - if (type == structure::TypeTag::Int - || type == structure::TypeTag::Double - || type == structure::TypeTag::Pixels) { - return copy; - } else { - file_.putBack(); - } - } - return {}; -} - -structure::Value ParsedFile::readStringOrStringCopyValue() { - if (auto result = readStringValue()) { - return result; - } else if (auto copy = readCopyValue()) { - auto type = copy.type().tag; - if (type == structure::TypeTag::String) { - return copy; - } else { - file_.putBack(); - } - } - return {}; -} - -structure::data::monoicon ParsedFile::readMonoIconFields() { - structure::data::monoicon result; - result.filename = readMonoIconFilename(); - if (!result.filename.isEmpty() && file_.getToken(BasicType::Comma)) { - if (auto color = readValue()) { - if (color.type().tag == structure::TypeTag::Color) { - result.color = color; - if (file_.getToken(BasicType::Comma)) { - if (auto offset = readValue()) { - if (offset.type().tag == structure::TypeTag::Point) { - result.offset = offset; - } else { - logErrorUnexpectedToken() << "icon offset"; - } - } else { - logErrorUnexpectedToken() << "icon offset"; - } - } else { - result.offset = { structure::data::point { 0, 0 } }; - } - } else { - logErrorUnexpectedToken() << "icon color"; - } - } else { - logErrorUnexpectedToken() << "icon color"; - } - } - return result; -} - -QString ParsedFile::readMonoIconFilename() { - if (auto filename = readValue()) { - if (filename.type().tag == structure::TypeTag::String) { - auto fullpath = QString::fromStdString(filename.String()); - auto pathAndModifiers = fullpath.split('-'); - auto filepath = pathAndModifiers[0]; - auto modifiers = pathAndModifiers.mid(1); - for (auto modifierName : modifiers) { - if (!GetModifier(modifierName)) { - logError(kErrorBadIconModifier) << "unknown modifier: " << modifierName.toStdString(); - return QString(); - } - } - for (auto &path : options_.includePaths) { - QFileInfo fileinfo(path + '/' + filepath + ".png"); - if (fileinfo.exists()) { - return path + '/' + fullpath; - } - } - for (auto &path : options_.includePaths) { - QFileInfo fileinfo(path + "/icons/" + filepath + ".png"); - if (fileinfo.exists()) { - return path + "/icons/" + fullpath; - } - } - logError(common::kErrorFileNotFound) << "could not open icon file '" << filename.String() << "'"; - } else if (filename.type().tag == structure::TypeTag::Size) { - return QString("size://%1,%2").arg(filename.Size().width).arg(filename.Size().height); - } - } - logErrorUnexpectedToken() << "icon filename or rect size"; - return QString(); -} - -BasicToken ParsedFile::assertNextToken(BasicToken::Type type) { - auto result = file_.getToken(type); - if (!result) { - logErrorUnexpectedToken() << type; - } - return result; -} - -Options ParsedFile::includedOptions(const QString &filepath) { - auto result = options_; - result.inputPath = filepath; - result.includePaths[0] = QFileInfo(filePath_).dir().absolutePath(); - result.isPalette = (QFileInfo(filepath).suffix() == "palette"); - return result; -} - -// Compose context-dependent full name. -structure::FullName ParsedFile::composeFullName(const QString &name) { - return { name }; -} - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h deleted file mode 100644 index 6b95d1ffb..000000000 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include "codegen/common/basic_tokenized_file.h" -#include "codegen/style/options.h" -#include "codegen/style/module.h" - -namespace codegen { -namespace style { - -using Modifier = std::function; -Modifier GetModifier(const QString &name); - -// Parses an input file to the internal struct. -class ParsedFile { -public: - explicit ParsedFile( - const Options &options, - std::vector includeStack = {}); - ParsedFile(const ParsedFile &other) = delete; - ParsedFile &operator=(const ParsedFile &other) = delete; - - bool read(); - - using ModulePtr = std::unique_ptr; - ModulePtr getResult() { - return std::move(module_); - } - -private: - bool failed() const { - return failed_ || file_.failed(); - } - - // Log error to std::cerr with 'code' at the current position in file. - common::LogStream logError(int code) { - failed_ = true; - return file_.logError(code); - } - common::LogStream logErrorUnexpectedToken() { - failed_ = true; - return file_.logErrorUnexpectedToken(); - } - common::LogStream logErrorTypeMismatch(); - common::LogStream logAssert(bool assertion) { - if (!assertion) { - return logError(common::kErrorInternal) << "internal - "; - } - return common::LogStream(common::LogStream::Null); - } - - // Helper methods for context-dependent reading. - ModulePtr readIncluded(); - structure::Struct readStruct(const QString &name); - structure::Variable readVariable(const QString &name); - - structure::StructField readStructField(const QString &name); - structure::Type readType(); - structure::Value readValue(); - - structure::Value readStructValue(); - structure::Value defaultConstructedStruct(const structure::FullName &name); - void applyStructParent(structure::Value &result, const structure::FullName &parentName); - bool readStructValueInner(structure::Value &result); - bool assignStructField(structure::Value &result, const structure::Variable &field); - bool readStructParents(structure::Value &result); - - // Simple methods for reading value types. - structure::Value readPositiveValue(); - structure::Value readNumericValue(); - structure::Value readStringValue(); - structure::Value readColorValue(); - structure::Value readPointValue(); - structure::Value readSizeValue(); - structure::Value readAlignValue(); - structure::Value readMarginsValue(); - structure::Value readFontValue(); - structure::Value readIconValue(); - structure::Value readCopyValue(); - - structure::Value readNumericOrNumericCopyValue(); - structure::Value readStringOrStringCopyValue(); - - structure::data::monoicon readMonoIconFields(); - QString readMonoIconFilename(); - - // Read next token and fire unexpected token error if it is not of "type". - using BasicToken = common::BasicTokenizedFile::Token; - BasicToken assertNextToken(BasicToken::Type type); - - // Look through include directories in options_ and find absolute include path. - Options includedOptions(const QString &filepath); - - // Compose context-dependent full name. - structure::FullName composeFullName(const QString &name); - - QString filePath_; - common::BasicTokenizedFile file_; - Options options_; - bool failed_ = false; - ModulePtr module_; - - std::vector includeStack_; - - QMap typeNames_ = { - { "int" , { structure::TypeTag::Int } }, - { "double" , { structure::TypeTag::Double } }, - { "pixels" , { structure::TypeTag::Pixels } }, - { "string" , { structure::TypeTag::String } }, - { "color" , { structure::TypeTag::Color } }, - { "point" , { structure::TypeTag::Point } }, - { "size" , { structure::TypeTag::Size } }, - { "align" , { structure::TypeTag::Align } }, - { "margins" , { structure::TypeTag::Margins } }, - { "font" , { structure::TypeTag::Font } }, - { "icon" , { structure::TypeTag::Icon } }, - }; - -}; - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/processor.cpp b/Telegram/SourceFiles/codegen/style/processor.cpp deleted file mode 100644 index 2c1bc1df8..000000000 --- a/Telegram/SourceFiles/codegen/style/processor.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* -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 "codegen/style/processor.h" - -#include -#include -#include "codegen/common/cpp_file.h" -#include "codegen/style/parsed_file.h" -#include "codegen/style/generator.h" - -namespace codegen { -namespace style { -namespace { - -constexpr int kErrorCantWritePath = 821; - -QString destFileBaseName(const structure::Module &module) { - return "style_" + QFileInfo(module.filepath()).baseName(); -} - -} // namespace - -Processor::Processor(const Options &options) -: parser_(std::make_unique(options)) -, options_(options) { -} - -int Processor::launch() { - if (!parser_->read()) { - return -1; - } - - auto module = parser_->getResult(); - if (!write(*module)) { - return -1; - } - - return 0; -} - -bool Processor::write(const structure::Module &module) const { - bool forceReGenerate = false; - QDir dir(options_.outputPath); - if (!dir.mkpath(".")) { - common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); - return false; - } - - QFileInfo srcFile(module.filepath()); - QString dstFilePath = dir.absolutePath() + '/' + (options_.isPalette ? "palette" : destFileBaseName(module)); - - common::ProjectInfo project = { - "codegen_style", - srcFile.fileName(), - forceReGenerate - }; - - Generator generator(module, dstFilePath, project, options_.isPalette); - if (!generator.writeHeader()) { - return false; - } - if (!generator.writeSource()) { - return false; - } - return true; -} - -Processor::~Processor() = default; - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/processor.h b/Telegram/SourceFiles/codegen/style/processor.h deleted file mode 100644 index 8d4d35b97..000000000 --- a/Telegram/SourceFiles/codegen/style/processor.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include "codegen/style/options.h" - -namespace codegen { -namespace style { -namespace structure { -class Module; -} // namespace structure -class ParsedFile; - -// Walks through a file, parses it and parses dependency files if necessary. -// Uses Generator class to produce the final output. -class Processor { -public: - explicit Processor(const Options &options); - Processor(const Processor &other) = delete; - Processor &operator=(const Processor &other) = delete; - - // Returns 0 on success. - int launch(); - - ~Processor(); - -private: - bool write(const structure::Module &module) const; - - std::unique_ptr parser_; - const Options &options_; - -}; - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure_types.cpp b/Telegram/SourceFiles/codegen/style/structure_types.cpp deleted file mode 100644 index 9ce705408..000000000 --- a/Telegram/SourceFiles/codegen/style/structure_types.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* -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 "codegen/style/structure_types.h" - -namespace codegen { -namespace style { -namespace structure { - -struct Value::DataTypes { - class TInt : public DataBase { - public: - TInt(int value) : value_(value) { - } - int Int() const override { return value_; } - - private: - int value_; - - }; - - class TDouble : public DataBase { - public: - TDouble(double value) : value_(value) { - } - double Double() const override { return value_; } - - private: - double value_; - - }; - - class TString : public DataBase { - public: - TString(std::string value) : value_(value) { - } - std::string String() const override { return value_; } - - private: - std::string value_; - - }; - - class TPoint : public DataBase { - public: - TPoint(data::point value) : value_(value) { - } - data::point Point() const override { return value_; } - - private: - data::point value_; - - }; - - class TSize : public DataBase { - public: - TSize(data::size value) : value_(value) { - } - data::size Size() const override { return value_; } - - private: - data::size value_; - - }; - - class TColor : public DataBase { - public: - TColor(data::color value) : value_(value) { - } - data::color Color() const override { return value_; } - - private: - data::color value_; - - }; - - class TMargins : public DataBase { - public: - TMargins(data::margins value) : value_(value) { - } - data::margins Margins() const override { return value_; } - - private: - data::margins value_; - - }; - - class TFont : public DataBase { - public: - TFont(data::font value) : value_(value) { - } - data::font Font() const override { return value_; } - - private: - data::font value_; - - }; - - class TIcon : public DataBase { - public: - TIcon(data::icon value) : value_(value) { - } - data::icon Icon() const override { return value_; } - - private: - data::icon value_; - - }; - - class TFields : public DataBase { - public: - TFields(data::fields value) : value_(value) { - } - const data::fields *Fields() const override { return &value_; } - data::fields *Fields() override { return &value_; } - - private: - data::fields value_; - - }; -}; - -Value::Value() : Value(TypeTag::Invalid, std::make_shared()) { -} - -Value::Value(data::point value) : Value(TypeTag::Point, std::make_shared(value)) { -} - -Value::Value(data::size value) : Value(TypeTag::Size, std::make_shared(value)) { -} - -Value::Value(data::color value) : Value(TypeTag::Color, std::make_shared(value)) { -} - -Value::Value(data::margins value) : Value(TypeTag::Margins, std::make_shared(value)) { -} - -Value::Value(data::font value) : Value(TypeTag::Font, std::make_shared(value)) { -} - -Value::Value(data::icon value) : Value(TypeTag::Icon, std::make_shared(value)) { -} - -Value::Value(const FullName &type, data::fields value) -: type_ { TypeTag::Struct, type } -, data_(std::make_shared(value)) { -} - -Value::Value(TypeTag type, double value) : Value(type, std::make_shared(value)) { - if (type_.tag != TypeTag::Double) { - type_.tag = TypeTag::Invalid; - data_ = std::make_shared(); - } -} - -Value::Value(TypeTag type, int value) : Value(type, std::make_shared(value)) { - if (type_.tag != TypeTag::Int && type_.tag != TypeTag::Pixels) { - type_.tag = TypeTag::Invalid; - data_ = std::make_shared(); - } -} - -Value::Value(TypeTag type, std::string value) : Value(type, std::make_shared(value)) { - if (type_.tag != TypeTag::String && - type_.tag != TypeTag::Align) { - type_.tag = TypeTag::Invalid; - data_ = std::make_shared(); - } -} - -Value::Value(Type type, Qt::Initialization) : type_(type) { - switch (type_.tag) { - case TypeTag::Invalid: data_ = std::make_shared(); break; - case TypeTag::Int: data_ = std::make_shared(0); break; - case TypeTag::Double: data_ = std::make_shared(0.); break; - case TypeTag::Pixels: data_ = std::make_shared(0); break; - case TypeTag::String: data_ = std::make_shared(""); break; - case TypeTag::Color: data_ = std::make_shared(data::color { 0, 0, 0, 255 }); break; - case TypeTag::Point: data_ = std::make_shared(data::point { 0, 0 }); break; - case TypeTag::Size: data_ = std::make_shared(data::size { 0, 0 }); break; - case TypeTag::Align: data_ = std::make_shared("topleft"); break; - case TypeTag::Margins: data_ = std::make_shared(data::margins { 0, 0, 0, 0 }); break; - case TypeTag::Font: data_ = std::make_shared(data::font { "", 13, 0 }); break; - case TypeTag::Icon: data_ = std::make_shared(data::icon {}); break; - case TypeTag::Struct: data_ = std::make_shared(data::fields {}); break; - } -} - -Value::Value(TypeTag type, std::shared_ptr &&data) : type_ { type }, data_(std::move(data)) { -} - -} // namespace structure -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h deleted file mode 100644 index de6392c06..000000000 --- a/Telegram/SourceFiles/codegen/style/structure_types.h +++ /dev/null @@ -1,230 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include -#include -#include -#include - -namespace codegen { -namespace style { -namespace structure { - -// List of names, like overview.document.bg -using FullName = QStringList; -inline std::string logFullName(const FullName &name) { - return name.join('.').toStdString(); -} - -struct Variable; -class Value; - -enum class TypeTag { - Invalid, - Int, - Double, - Pixels, - String, - Color, - Point, - Size, - Align, - Margins, - Font, - Icon, - Struct, -}; - -struct Type { - TypeTag tag; - FullName name; // only for type == ClassType::Struct - - explicit operator bool() const { - return (tag != TypeTag::Invalid); - } -}; -inline bool operator==(const Type &a, const Type &b) { - return (a.tag == b.tag) && (a.name == b.name); -} -inline bool operator!=(const Type &a, const Type &b) { - return !(a == b); -} - -namespace data { - -struct point { - int x, y; -}; - -struct size { - int width, height; -}; - -struct color { - uchar red, green, blue, alpha; - QString fallback; -}; - -struct margins { - int left, top, right, bottom; -}; - -struct font { - enum Flag { - Bold = 0x01, - Italic = 0x02, - Underline = 0x04, - }; - std::string family; - int size; - int flags; -}; - -struct monoicon; -struct icon { - std::vector parts; -}; - -struct field; // defined after Variable is defined -using fields = QList; - -} // namespace data - -class Value { -public: - Value(); - Value(data::point value); - Value(data::size value); - Value(data::color value); - Value(data::margins value); - Value(data::font value); - Value(data::icon value); - Value(const FullName &type, data::fields value); - - // Can be only double. - Value(TypeTag type, double value); - - // Can be int / pixels. - Value(TypeTag type, int value); - - // Can be string / align. - Value(TypeTag type, std::string value); - - // Default constructed value (uninitialized). - Value(Type type, Qt::Initialization); - - Type type() const { return type_; } - int Int() const { return data_->Int(); } - double Double() const { return data_->Double(); } - std::string String() const { return data_->String(); } - data::point Point() const { return data_->Point(); } - data::size Size() const { return data_->Size(); }; - data::color Color() const { return data_->Color(); }; - data::margins Margins() const { return data_->Margins(); }; - data::font Font() const { return data_->Font(); }; - data::icon Icon() const { return data_->Icon(); }; - const data::fields *Fields() const { return data_->Fields(); }; - data::fields *Fields() { return data_->Fields(); }; - - explicit operator bool() const { - return type_.tag != TypeTag::Invalid; - } - - Value makeCopy(const FullName ©Of) const { - Value result(*this); - result.copyOf_ = copyOf; - return result; - } - - const FullName ©Of() const { - return copyOf_; - } - -private: - class DataBase { - public: - virtual int Int() const { return 0; } - virtual double Double() const { return 0.; } - virtual std::string String() const { return std::string(); } - virtual data::point Point() const { return {}; }; - virtual data::size Size() const { return {}; }; - virtual data::color Color() const { return {}; }; - virtual data::margins Margins() const { return {}; }; - virtual data::font Font() const { return {}; }; - virtual data::icon Icon() const { return {}; }; - virtual const data::fields *Fields() const { return nullptr; }; - virtual data::fields *Fields() { return nullptr; }; - virtual ~DataBase() { - } - }; - struct DataTypes; - - Value(TypeTag type, std::shared_ptr &&data); - - Type type_; - std::shared_ptr data_; - - FullName copyOf_; // for copies of existing named values - -}; - -struct Variable { - FullName name; - Value value; - QString description; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -namespace data { -struct field { - enum class Status { - Uninitialized, - Implicit, - Explicit - }; - Variable variable; - Status status; -}; - -struct monoicon { - QString filename; - Value color; - Value offset; - - explicit operator bool() const { - return !filename.isEmpty(); - } -}; -} // namespace data - -struct StructField { - FullName name; - Type type; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -struct Struct { - FullName name; - QList fields; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -} // namespace structure -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/core/crash_reports.cpp b/Telegram/SourceFiles/core/crash_reports.cpp index ce938a3fa..cd8da1d1d 100644 --- a/Telegram/SourceFiles/core/crash_reports.cpp +++ b/Telegram/SourceFiles/core/crash_reports.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "platform/platform_specific.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "core/launcher.h" #include diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 3ba28309b..883c985a7 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_launcher.h" #include "platform/platform_specific.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "ui/main_queue_processor.h" #include "core/crash_reports.h" #include "core/update_checker.h" diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 9da3cc41e..6f255a94c 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "core/sandbox.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "mainwidget.h" #include "mainwindow.h" #include "storage/localstorage.h" diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp index 645ce116c..2f6107b45 100644 --- a/Telegram/SourceFiles/core/shortcuts.cpp +++ b/Telegram/SourceFiles/core/shortcuts.cpp @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "core/application.h" #include "media/player/media_player_instance.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "base/parse_helper.h" #include "facades.h" diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp index 338d1f114..efe892b81 100644 --- a/Telegram/SourceFiles/core/update_checker.cpp +++ b/Telegram/SourceFiles/core/update_checker.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "core/update_checker.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "base/timer.h" #include "base/bytes.h" #include "base/unixtime.h" diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 17ffef451..1d1c27f06 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -31,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_loader.h" // unique_ptr #include "media/streaming/media_streaming_reader.h" // make_shared #include "boxes/abstract_box.h" -#include "platform/platform_info.h" #include "passport/passport_form_controller.h" #include "window/themes/window_theme.h" #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name @@ -48,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "data/data_scheduled_messages.h" #include "data/data_cloud_themes.h" +#include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "facades.h" #include "app.h" diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 88d4e2fc0..e42995f19 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style index 90c641750..4059cc3b1 100644 --- a/Telegram/SourceFiles/export/view/export.style +++ b/Telegram/SourceFiles/export/view/export.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "boxes/boxes.style"; diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index f5b2103bf..d40907a8a 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -16,9 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "storage/localstorage.h" #include "core/file_utilities.h" -#include "platform/platform_info.h" #include "main/main_session.h" #include "data/data_session.h" +#include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "facades.h" #include "styles/style_export.h" diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 6c26bb8eb..a1b1e6fcf 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/clip/media_clip_reader.h" #include "window/window_session_controller.h" #include "history/history_item_components.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "data/data_peer.h" #include "data/data_user.h" #include "observer_peer.h" diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 36f8d7a71..183a3e0e5 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "chat_helpers/message_field.h" #include "boxes/sticker_set_box.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "mainwindow.h" #include "mainwidget.h" #include "core/application.h" diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 1f5d6feef..eebecd866 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 305f9c71d..dc4812a5f 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" #include "chat_helpers/stickers.h" #include "history/history_widget.h" +#include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "mainwindow.h" #include "mainwidget.h" @@ -43,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "core/application.h" #include "apiwrap.h" -#include "platform/platform_info.h" #include "lang/lang_keys.h" #include "data/data_session.h" #include "data/data_media_types.h" diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 76726733f..e00b3c9b8 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_file_origin.h" #include "core/file_utilities.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "window/window_peer_menu.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 0b7ef4ea3..fdee4ebae 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index fdbd4f5fe..8e63a2869 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -29,7 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "styles/style_overview.h" #include "styles/style_info.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "media/player/media_player_instance.h" #include "boxes/peer_list_controllers.h" #include "boxes/confirm_box.h" diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 3754dd367..be68680a9 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; countryRipple: defaultRippleAnimation; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 51aeeb4c6..a64998fd0 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_slide_animation.h" #include "window/window_connecting_widget.h" #include "window/window_lock_widgets.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "data/data_user.h" #include "window/themes/window_theme.h" #include "lang/lang_cloud_manager.h" diff --git a/Telegram/SourceFiles/lang/lang_instance.cpp b/Telegram/SourceFiles/lang/lang_instance.cpp index f6177f0fd..8d03bce4a 100644 --- a/Telegram/SourceFiles/lang/lang_instance.cpp +++ b/Telegram/SourceFiles/lang/lang_instance.cpp @@ -10,9 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "storage/serialize_common.h" #include "storage/localstorage.h" -#include "platform/platform_info.h" #include "boxes/confirm_box.h" #include "lang/lang_file_parser.h" +#include "base/platform/base_platform_info.h" #include "base/qthelp_regex.h" namespace Lang { diff --git a/Telegram/SourceFiles/lang/lang_translator.cpp b/Telegram/SourceFiles/lang/lang_translator.cpp index 926489c1a..90918947e 100644 --- a/Telegram/SourceFiles/lang/lang_translator.cpp +++ b/Telegram/SourceFiles/lang/lang_translator.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_translator.h" #include "lang/lang_keys.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" namespace Lang { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 40b80f9fd..0f89d2468 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "settings/settings_intro.h" #include "platform/platform_notifications_manager.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "window/layer_widget.h" #include "window/notifications_manager.h" #include "window/themes/window_theme.h" diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index a0e636dab..493675cd6 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -6,7 +6,7 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "overview/overview.style"; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index ad3109ef1..210cc88da 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/file_utilities.h" #include "core/mime_type.h" -#include "platform/platform_info.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "ui/image/image.h" @@ -42,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/window_controller.h" #include "main/main_account.h" // Account::sessionValue. +#include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "observer_peer.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 205cb6679..0f0a2c8cf 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -6,7 +6,7 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; mediaviewOverDuration: 150; diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 03dcade11..bfb160e49 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "history/history.style"; using "ui/widgets/widgets.style"; using "media/view/mediaview.style"; diff --git a/Telegram/SourceFiles/passport/passport.style b/Telegram/SourceFiles/passport/passport.style index e89471071..dd02dccd3 100644 --- a/Telegram/SourceFiles/passport/passport.style +++ b/Telegram/SourceFiles/passport/passport.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "boxes/boxes.style"; diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp index 658595612..2a42af852 100644 --- a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_controller.h" #include "lang/lang_keys.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" diff --git a/Telegram/SourceFiles/platform/linux/info_linux.cpp b/Telegram/SourceFiles/platform/linux/info_linux.cpp deleted file mode 100644 index 8d5e66764..000000000 --- a/Telegram/SourceFiles/platform/linux/info_linux.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -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 "platform/linux/info_linux.h" - -#include - -namespace Platform { - -QString DeviceModelPretty() { -#ifdef Q_OS_LINUX64 - return "PC 64bit"; -#else // Q_OS_LINUX64 - return "PC 32bit"; -#endif // Q_OS_LINUX64 -} - -QString SystemVersionPretty() { - const auto result = getenv("XDG_CURRENT_DESKTOP"); - const auto value = result ? QString::fromLatin1(result) : QString(); - const auto list = value.split(':', QString::SkipEmptyParts); - return list.isEmpty() ? "Linux" : "Linux " + list[0]; -} - -QString SystemCountry() { - return QLocale::system().name().split('_').last(); -} - -QString SystemLanguage() { - const auto system = QLocale::system(); - const auto languages = system.uiLanguages(); - return languages.isEmpty() - ? system.name().split('_').first() - : languages.front(); -} - -QDate WhenSystemBecomesOutdated() { - return QDate(); -} - -int AutoUpdateVersion() { - return 2; -} - -QString AutoUpdateKey() { - if (IsLinux32Bit()) { - return "linux32"; - } else if (IsLinux64Bit()) { - return "linux"; - } else { - Unexpected("Platform in AutoUpdateKey."); - } -} - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/info_linux.h b/Telegram/SourceFiles/platform/linux/info_linux.h deleted file mode 100644 index f909003a3..000000000 --- a/Telegram/SourceFiles/platform/linux/info_linux.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "platform/platform_info.h" - -namespace Platform { - -inline constexpr bool IsLinux() { - return true; -} - -inline constexpr bool IsLinux32Bit() { -#ifdef Q_OS_LINUX32 - return true; -#else // Q_OS_LINUX32 - return false; -#endif // Q_OS_LINUX32 -} - -inline constexpr bool IsLinux64Bit() { -#ifdef Q_OS_LINUX64 - return true; -#else // Q_OS_LINUX64 - return false; -#endif // Q_OS_LINUX64 -} - -inline constexpr bool IsWindows() { return false; } -inline constexpr bool IsWindowsStoreBuild() { return false; } -inline bool IsWindowsXPOrGreater() { return false; } -inline bool IsWindowsVistaOrGreater() { return false; } -inline bool IsWindows7OrGreater() { return false; } -inline bool IsWindows8OrGreater() { return false; } -inline bool IsWindows8Point1OrGreater() { return false; } -inline bool IsWindows10OrGreater() { return false; } -inline constexpr bool IsMac() { return false; } -inline constexpr bool IsMacOldBuild() { return false; } -inline constexpr bool IsMacStoreBuild() { return false; } -inline bool IsMac10_6OrGreater() { return false; } -inline bool IsMac10_7OrGreater() { return false; } -inline bool IsMac10_8OrGreater() { return false; } -inline bool IsMac10_9OrGreater() { return false; } -inline bool IsMac10_10OrGreater() { return false; } -inline bool IsMac10_11OrGreater() { return false; } -inline bool IsMac10_12OrGreater() { return false; } -inline bool IsMac10_13OrGreater() { return false; } -inline bool IsMac10_14OrGreater() { return false; } - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp index 2722ffbff..679f4864b 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/launcher_linux.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "core/crash_reports.h" #include "core/update_checker.h" diff --git a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm index b9b371883..885e141b8 100644 --- a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm +++ b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/file_utilities_mac.h" -#include "platform/mac/mac_utilities.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "lang/lang_keys.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/platform/mac/info_mac.cpp b/Telegram/SourceFiles/platform/mac/info_mac.cpp deleted file mode 100644 index 789939892..000000000 --- a/Telegram/SourceFiles/platform/mac/info_mac.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/* -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 "platform/mac/info_mac.h" - diff --git a/Telegram/SourceFiles/platform/mac/info_mac.h b/Telegram/SourceFiles/platform/mac/info_mac.h deleted file mode 100644 index befd1b4d7..000000000 --- a/Telegram/SourceFiles/platform/mac/info_mac.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "platform/platform_info.h" - -namespace Platform { - -inline constexpr bool IsMac() { - return true; -} - -inline constexpr bool IsMacOldBuild() { -#ifdef OS_MAC_OLD - return true; -#else // OS_MAC_OLD - return false; -#endif // OS_MAC_OLD -} - -inline constexpr bool IsMacStoreBuild() { -#ifdef OS_MAC_STORE - return true; -#else // OS_MAC_STORE - return false; -#endif // OS_MAC_STORE -} - -inline constexpr bool IsWindows() { return false; } -inline constexpr bool IsWindowsStoreBuild() { return false; } -inline bool IsWindowsXPOrGreater() { return false; } -inline bool IsWindowsVistaOrGreater() { return false; } -inline bool IsWindows7OrGreater() { return false; } -inline bool IsWindows8OrGreater() { return false; } -inline bool IsWindows8Point1OrGreater() { return false; } -inline bool IsWindows10OrGreater() { return false; } -inline constexpr bool IsLinux() { return false; } -inline constexpr bool IsLinux32Bit() { return false; } -inline constexpr bool IsLinux64Bit() { return false; } - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/info_mac.mm b/Telegram/SourceFiles/platform/mac/info_mac.mm deleted file mode 100644 index 7a34eca46..000000000 --- a/Telegram/SourceFiles/platform/mac/info_mac.mm +++ /dev/null @@ -1,178 +0,0 @@ -/* -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 "platform/mac/info_mac.h" - -#include "platform/platform_info.h" -#include "platform/mac/mac_utilities.h" - -#include -#include - -namespace Platform { -namespace { - -QString FromIdentifier(const QString &model) { - if (model.isEmpty() || model.toLower().indexOf("mac") < 0) { - return QString(); - } - QStringList words; - QString word; - for (const QChar ch : model) { - if (!ch.isLetter()) { - continue; - } - if (ch.isUpper()) { - if (!word.isEmpty()) { - words.push_back(word); - word = QString(); - } - } - word.append(ch); - } - if (!word.isEmpty()) { - words.push_back(word); - } - QString result; - for (const QString word : words) { - if (!result.isEmpty() - && word != "Mac" - && word != "Book") { - result.append(' '); - } - result.append(word); - } - return result; -} - -int MinorVersion() { - static const int version = QSysInfo::macVersion(); - constexpr int kShift = 2; - if (version == QSysInfo::MV_Unknown -#ifndef OS_MAC_OLD - || version == QSysInfo::MV_None -#endif // OS_MAC_OLD - || version < kShift + 6) { - return 0; - } - return version - kShift; -} - -template -bool IsMacThatOrGreater() { - static const auto result = (MinorVersion() >= Minor); - return result; -} - -} // namespace - -QString DeviceModelPretty() { - size_t length = 0; - sysctlbyname("hw.model", nullptr, &length, nullptr, 0); - if (length > 0) { - QByteArray bytes(length, Qt::Uninitialized); - sysctlbyname("hw.model", bytes.data(), &length, nullptr, 0); - const QString parsed = FromIdentifier(QString::fromUtf8(bytes)); - if (!parsed.isEmpty()) { - return parsed; - } - } - return "Mac"; -} - -QString SystemVersionPretty() { - const auto version = MinorVersion(); - if (!version) { - return "OS X"; - } else if (version < 12) { - return QString("OS X 10.%1").arg(version); - } - return QString("macOS 10.%1").arg(version); -} - -QString SystemCountry() { - NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale. - NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - return countryCode ? NS2QString(countryCode) : QString(); -} - -QString SystemLanguage() { - if (auto currentLocale = [NSLocale currentLocale]) { // get the current locale. - if (NSString *collator = [currentLocale objectForKey:NSLocaleCollatorIdentifier]) { - return NS2QString(collator); - } - if (NSString *identifier = [currentLocale objectForKey:NSLocaleIdentifier]) { - return NS2QString(identifier); - } - if (NSString *language = [currentLocale objectForKey:NSLocaleLanguageCode]) { - return NS2QString(language); - } - } - return QString(); -} - -QDate WhenSystemBecomesOutdated() { - if (!IsMac10_10OrGreater()) { - return QDate(2019, 9, 1); - } - return QDate(); -} - -int AutoUpdateVersion() { - if (!IsMac10_10OrGreater()) { - return 1; - } - return 2; -} - -QString AutoUpdateKey() { - if (IsMacOldBuild()) { - return "mac32"; - } else if (!IsMac10_12OrGreater()) { - return "osx"; - } else { - return "mac"; - } -} - -bool IsMac10_6OrGreater() { - return IsMacThatOrGreater<6>(); -} - -bool IsMac10_7OrGreater() { - return IsMacThatOrGreater<7>(); -} - -bool IsMac10_8OrGreater() { - return IsMacThatOrGreater<8>(); -} - -bool IsMac10_9OrGreater() { - return IsMacThatOrGreater<9>(); -} - -bool IsMac10_10OrGreater() { - return IsMacThatOrGreater<10>(); -} - -bool IsMac10_11OrGreater() { - return IsMacThatOrGreater<11>(); -} - -bool IsMac10_12OrGreater() { - return IsMacThatOrGreater<12>(); -} - -bool IsMac10_13OrGreater() { - return IsMacThatOrGreater<13>(); -} - -bool IsMac10_14OrGreater() { - return IsMacThatOrGreater<14>(); -} - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/launcher_mac.mm b/Telegram/SourceFiles/platform/mac/launcher_mac.mm index 23d4dadca..69d421024 100644 --- a/Telegram/SourceFiles/platform/mac/launcher_mac.mm +++ b/Telegram/SourceFiles/platform/mac/launcher_mac.mm @@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" -#include "platform/mac/mac_utilities.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "platform/platform_specific.h" #include diff --git a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm index 8467d04a0..796af5936 100644 --- a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm +++ b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm @@ -28,7 +28,7 @@ #include "mainwidget.h" #include "mainwindow.h" #include "observer_peer.h" -#include "platform/mac/mac_utilities.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "stickers.h" #include "styles/style_dialogs.h" #include "styles/style_media_player.h" diff --git a/Telegram/SourceFiles/platform/mac/mac_utilities.h b/Telegram/SourceFiles/platform/mac/mac_utilities.h deleted file mode 100644 index 319d247c0..000000000 --- a/Telegram/SourceFiles/platform/mac/mac_utilities.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace Platform { - -inline NSString *Q2NSString(const QString &str) { - return [NSString stringWithUTF8String:str.toUtf8().constData()]; -} - -inline QString NS2QString(NSString *str) { - return QString::fromUtf8([str cStringUsingEncoding:NSUTF8StringEncoding]); -} - -template -inline QString MakeFromLetters(const uint32 (&letters)[Size]) { - QString result; - result.reserve(Size); - for (int32 i = 0; i < Size; ++i) { - auto code = letters[i]; - auto salt1 = (code >> 8) & 0xFFU; - auto salt2 = (code >> 24) & 0xFFU; - auto part1 = ((code & 0xFFU) ^ (salt1 ^ salt2)) & 0xFFU; - auto part2 = (((code >> 16) & 0xFFU) ^ (salt1 ^ ~salt2)) & 0xFFU; - result.push_back(QChar((part2 << 8) | part1)); - } - return result; -} - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/mac_utilities.mm b/Telegram/SourceFiles/platform/mac/mac_utilities.mm deleted file mode 100644 index 6099bcbce..000000000 --- a/Telegram/SourceFiles/platform/mac/mac_utilities.mm +++ /dev/null @@ -1,8 +0,0 @@ -/* -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 "platform/mac/mac_utilities.h" diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 83ff560bf..c949052d5 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -26,11 +26,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" #include "platform/mac/mac_touchbar.h" #include "platform/platform_notifications_manager.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "boxes/peer_list_controllers.h" #include "boxes/about_box.h" #include "lang/lang_keys.h" -#include "platform/mac/mac_utilities.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "ui/widgets/input_fields.h" #include "facades.h" #include "app.h" diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 25727ac17..8d89dcdc4 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -7,9 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/notifications_manager_mac.h" +#include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" -#include "platform/platform_info.h" -#include "platform/mac/mac_utilities.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "history/history.h" #include "ui/empty_userpic.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index 2f2d3d67b..d95d3a116 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "mainwindow.h" #include "history/history_location_manager.h" -#include "platform/mac/mac_utilities.h" +#include "base/platform/mac/base_platform_mac_utilities.h" #include "facades.h" #include diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 76f3a2792..ef6540016 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -16,8 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" #include "platform/mac/mac_touchbar.h" -#include "platform/mac/mac_utilities.h" -#include "platform/platform_info.h" +#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/base_platform_info.h" #include "lang/lang_keys.h" #include "base/timer.h" #include "facades.h" diff --git a/Telegram/SourceFiles/platform/platform_info.h b/Telegram/SourceFiles/platform/platform_info.h deleted file mode 100644 index 3fe92ea28..000000000 --- a/Telegram/SourceFiles/platform/platform_info.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace Platform { - -[[nodiscard]] QString DeviceModelPretty(); -[[nodiscard]] QString SystemVersionPretty(); -[[nodiscard]] QString SystemCountry(); -[[nodiscard]] QString SystemLanguage(); -[[nodiscard]] QDate WhenSystemBecomesOutdated(); -[[nodiscard]] int AutoUpdateVersion(); -[[nodiscard]] QString AutoUpdateKey(); - -[[nodiscard]] constexpr bool IsWindows(); -[[nodiscard]] constexpr bool IsWindowsStoreBuild(); -[[nodiscard]] bool IsWindowsXPOrGreater(); -[[nodiscard]] bool IsWindowsVistaOrGreater(); -[[nodiscard]] bool IsWindows7OrGreater(); -[[nodiscard]] bool IsWindows8OrGreater(); -[[nodiscard]] bool IsWindows8Point1OrGreater(); -[[nodiscard]] bool IsWindows10OrGreater(); - -[[nodiscard]] constexpr bool IsMac(); -[[nodiscard]] constexpr bool IsMacOldBuild(); -[[nodiscard]] constexpr bool IsMacStoreBuild(); -[[nodiscard]] bool IsMac10_6OrGreater(); -[[nodiscard]] bool IsMac10_7OrGreater(); -[[nodiscard]] bool IsMac10_8OrGreater(); -[[nodiscard]] bool IsMac10_9OrGreater(); -[[nodiscard]] bool IsMac10_10OrGreater(); -[[nodiscard]] bool IsMac10_11OrGreater(); -[[nodiscard]] bool IsMac10_12OrGreater(); -[[nodiscard]] bool IsMac10_13OrGreater(); -[[nodiscard]] bool IsMac10_14OrGreater(); - -[[nodiscard]] constexpr bool IsLinux(); -[[nodiscard]] constexpr bool IsLinux32Bit(); -[[nodiscard]] constexpr bool IsLinux64Bit(); - -} // namespace Platform - -#ifdef Q_OS_MAC -#include "platform/mac/info_mac.h" -#elif defined Q_OS_LINUX // Q_OS_MAC -#include "platform/linux/info_linux.h" -#elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX -#include "platform/win/info_win.h" -#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WIN diff --git a/Telegram/SourceFiles/platform/win/info_win.cpp b/Telegram/SourceFiles/platform/win/info_win.cpp deleted file mode 100644 index 6a5a45fe1..000000000 --- a/Telegram/SourceFiles/platform/win/info_win.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/* -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 "platform/win/info_win.h" - -#include "platform/platform_info.h" -#include "platform/win/wrapper_windows_h.h" - -#include - -namespace Platform { -namespace { - -QString GetLangCodeById(unsigned int lngId) { - const auto primary = (lngId & 0xFFU); - switch (primary) { - case 0x36: return qsl("af"); - case 0x1C: return qsl("sq"); - case 0x5E: return qsl("am"); - case 0x01: return qsl("ar"); - case 0x2B: return qsl("hy"); - case 0x4D: return qsl("as"); - case 0x2C: return qsl("az"); - case 0x45: return qsl("bn"); - case 0x6D: return qsl("ba"); - case 0x2D: return qsl("eu"); - case 0x23: return qsl("be"); - case 0x1A: - if (lngId == LANG_CROATIAN) { - return qsl("hr"); - } else if (lngId == LANG_BOSNIAN_NEUTRAL || lngId == LANG_BOSNIAN) { - return qsl("bs"); - } - return qsl("sr"); - break; - case 0x7E: return qsl("br"); - case 0x02: return qsl("bg"); - case 0x92: return qsl("ku"); - case 0x03: return qsl("ca"); - case 0x04: return qsl("zh"); - case 0x83: return qsl("co"); - case 0x05: return qsl("cs"); - case 0x06: return qsl("da"); - case 0x65: return qsl("dv"); - case 0x13: return qsl("nl"); - case 0x09: return qsl("en"); - case 0x25: return qsl("et"); - case 0x38: return qsl("fo"); - case 0x0B: return qsl("fi"); - case 0x0c: return qsl("fr"); - case 0x62: return qsl("fy"); - case 0x56: return qsl("gl"); - case 0x37: return qsl("ka"); - case 0x07: return qsl("de"); - case 0x08: return qsl("el"); - case 0x6F: return qsl("kl"); - case 0x47: return qsl("gu"); - case 0x68: return qsl("ha"); - case 0x0D: return qsl("he"); - case 0x39: return qsl("hi"); - case 0x0E: return qsl("hu"); - case 0x0F: return qsl("is"); - case 0x70: return qsl("ig"); - case 0x21: return qsl("id"); - case 0x5D: return qsl("iu"); - case 0x3C: return qsl("ga"); - case 0x34: return qsl("xh"); - case 0x35: return qsl("zu"); - case 0x10: return qsl("it"); - case 0x11: return qsl("ja"); - case 0x4B: return qsl("kn"); - case 0x3F: return qsl("kk"); - case 0x53: return qsl("kh"); - case 0x87: return qsl("rw"); - case 0x12: return qsl("ko"); - case 0x40: return qsl("ky"); - case 0x54: return qsl("lo"); - case 0x26: return qsl("lv"); - case 0x27: return qsl("lt"); - case 0x6E: return qsl("lb"); - case 0x2F: return qsl("mk"); - case 0x3E: return qsl("ms"); - case 0x4C: return qsl("ml"); - case 0x3A: return qsl("mt"); - case 0x81: return qsl("mi"); - case 0x4E: return qsl("mr"); - case 0x50: return qsl("mn"); - case 0x61: return qsl("ne"); - case 0x14: return qsl("no"); - case 0x82: return qsl("oc"); - case 0x48: return qsl("or"); - case 0x63: return qsl("ps"); - case 0x29: return qsl("fa"); - case 0x15: return qsl("pl"); - case 0x16: return qsl("pt"); - case 0x67: return qsl("ff"); - case 0x46: return qsl("pa"); - case 0x18: return qsl("ro"); - case 0x17: return qsl("rm"); - case 0x19: return qsl("ru"); - case 0x3B: return qsl("se"); - case 0x4F: return qsl("sa"); - case 0x32: return qsl("tn"); - case 0x59: return qsl("sd"); - case 0x5B: return qsl("si"); - case 0x1B: return qsl("sk"); - case 0x24: return qsl("sl"); - case 0x0A: return qsl("es"); - case 0x41: return qsl("sw"); - case 0x1D: return qsl("sv"); - case 0x28: return qsl("tg"); - case 0x49: return qsl("ta"); - case 0x44: return qsl("tt"); - case 0x4A: return qsl("te"); - case 0x1E: return qsl("th"); - case 0x51: return qsl("bo"); - case 0x73: return qsl("ti"); - case 0x1F: return qsl("tr"); - case 0x42: return qsl("tk"); - case 0x22: return qsl("uk"); - case 0x20: return qsl("ur"); - case 0x80: return qsl("ug"); - case 0x43: return qsl("uz"); - case 0x2A: return qsl("vi"); - case 0x52: return qsl("cy"); - case 0x88: return qsl("wo"); - case 0x78: return qsl("ii"); - case 0x6A: return qsl("yo"); - } - return QString(); -} - -} // namespace - -QString DeviceModelPretty() { - return "PC"; -} - -QString SystemVersionPretty() { - if (IsWindows10OrGreater()) { - return "Windows 10"; - } else if (IsWindows8Point1OrGreater()) { - return "Windows 8.1"; - } else if (IsWindows8OrGreater()) { - return "Windows 8"; - } else if (IsWindows7OrGreater()) { - return "Windows 7"; - } else if (IsWindowsVistaOrGreater()) { - return "Windows Vista"; - } else if (IsWindowsXPOrGreater()) { - return "Windows XP"; - } else { - return QSysInfo::prettyProductName(); - } -} - -QString SystemCountry() { - int chCount = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, 0, 0); - if (chCount && chCount < 128) { - WCHAR wstrCountry[128]; - int len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, wstrCountry, chCount); - if (len) { - return QString::fromStdWString(std::wstring(wstrCountry)); - } - } - return QString(); -} - -QString SystemLanguage() { - constexpr auto kMaxLanguageLength = 128; - - auto uiLanguageId = GetUserDefaultUILanguage(); - auto uiLanguageLength = GetLocaleInfo(uiLanguageId, LOCALE_SNAME, nullptr, 0); - if (uiLanguageLength > 0 && uiLanguageLength < kMaxLanguageLength) { - WCHAR uiLanguageWideString[kMaxLanguageLength] = { 0 }; - uiLanguageLength = GetLocaleInfo(uiLanguageId, LOCALE_SNAME, uiLanguageWideString, uiLanguageLength); - if (uiLanguageLength <= 0) { - return QString(); - } - return QString::fromWCharArray(uiLanguageWideString); - } - auto uiLanguageCodeLength = GetLocaleInfo(uiLanguageId, LOCALE_ILANGUAGE, nullptr, 0); - if (uiLanguageCodeLength > 0 && uiLanguageCodeLength < kMaxLanguageLength) { - WCHAR uiLanguageCodeWideString[kMaxLanguageLength] = { 0 }; - uiLanguageCodeLength = GetLocaleInfo(uiLanguageId, LOCALE_ILANGUAGE, uiLanguageCodeWideString, uiLanguageCodeLength); - if (uiLanguageCodeLength <= 0) { - return QString(); - } - - auto languageCode = 0U; - for (auto i = 0; i != uiLanguageCodeLength; ++i) { - auto ch = uiLanguageCodeWideString[i]; - if (!ch) { - break; - } - languageCode *= 0x10U; - if (ch >= WCHAR('0') && ch <= WCHAR('9')) { - languageCode += static_cast(int(ch) - int(WCHAR('0'))); - } else if (ch >= WCHAR('A') && ch <= WCHAR('F')) { - languageCode += static_cast(0x0A + int(ch) - int(WCHAR('A'))); - } else { - return QString(); - } - } - return GetLangCodeById(languageCode); - } - return QString(); -} - -QDate WhenSystemBecomesOutdated() { - if (!IsWindows7OrGreater()) { - return QDate(2019, 9, 1); - } - return QDate(); -} - -int AutoUpdateVersion() { - if (!IsWindows7OrGreater()) { - return 1; - } - return 2; -} - -QString AutoUpdateKey() { - return "win"; -} - -bool IsWindowsXPOrGreater() { - static const auto result = ::IsWindowsXPOrGreater(); - return result; -} - -bool IsWindowsVistaOrGreater() { - static const auto result = ::IsWindowsVistaOrGreater(); - return result; -} - -bool IsWindows7OrGreater() { - static const auto result = ::IsWindows7OrGreater(); - return result; -} - -bool IsWindows8OrGreater() { - static const auto result = ::IsWindows8OrGreater(); - return result; -} - -bool IsWindows8Point1OrGreater() { - static const auto result = ::IsWindows8Point1OrGreater(); - return result; -} - -bool IsWindows10OrGreater() { - static const auto result = ::IsWindows10OrGreater(); - return result; -} - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/info_win.h b/Telegram/SourceFiles/platform/win/info_win.h deleted file mode 100644 index a399684b9..000000000 --- a/Telegram/SourceFiles/platform/win/info_win.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "platform/platform_info.h" - -namespace Platform { - -inline constexpr bool IsWindows() { - return true; -} - -inline constexpr bool IsWindowsStoreBuild() { -#ifdef OS_WIN_STORE - return true; -#else // OS_WIN_STORE - return false; -#endif // OS_WIN_STORE -} - -inline constexpr bool IsMac() { return false; } -inline constexpr bool IsMacOldBuild() { return false; } -inline constexpr bool IsMacStoreBuild() { return false; } -inline bool IsMac10_6OrGreater() { return false; } -inline bool IsMac10_7OrGreater() { return false; } -inline bool IsMac10_8OrGreater() { return false; } -inline bool IsMac10_9OrGreater() { return false; } -inline bool IsMac10_10OrGreater() { return false; } -inline bool IsMac10_11OrGreater() { return false; } -inline bool IsMac10_12OrGreater() { return false; } -inline bool IsMac10_13OrGreater() { return false; } -inline bool IsMac10_14OrGreater() { return false; } -inline constexpr bool IsLinux() { return false; } -inline constexpr bool IsLinux32Bit() { return false; } -inline constexpr bool IsLinux64Bit() { return false; } - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/launcher_win.cpp b/Telegram/SourceFiles/platform/win/launcher_win.cpp index 5eda272fb..8dde12456 100644 --- a/Telegram/SourceFiles/platform/win/launcher_win.cpp +++ b/Telegram/SourceFiles/platform/win/launcher_win.cpp @@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" -#include "platform/platform_info.h" -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/base_platform_info.h" +#include "base/platform/win/base_platform_windows_h.h" #include #include diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 87d3422ee..ef5efd377 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_main_window.h" -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" #include "base/flags.h" #include diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 56a692ff5..6be74a4e2 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_specific.h" -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" namespace Data { class LocationPoint; diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h index a2a1952a1..160693a8c 100644 --- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h +++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" namespace Platform { namespace AppUserModelId { diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.h b/Telegram/SourceFiles/platform/win/windows_dlls.h index 2d72bf742..ee9b266a3 100644 --- a/Telegram/SourceFiles/platform/win/windows_dlls.h +++ b/Telegram/SourceFiles/platform/win/windows_dlls.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" #include #include diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.h b/Telegram/SourceFiles/platform/win/windows_event_filter.h index 594e20edf..920d31caf 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.h +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" #include diff --git a/Telegram/SourceFiles/platform/win/wrapper_windows_h.h b/Telegram/SourceFiles/platform/win/wrapper_windows_h.h deleted file mode 100644 index e06f131cc..000000000 --- a/Telegram/SourceFiles/platform/win/wrapper_windows_h.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -#ifdef small -#undef small -#endif // small diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 46ad1668b..0869bbc6a 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "info/info.style"; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index fc9e71d41..8192b7d91 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -5,7 +5,7 @@ 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 */ -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "info/info.style"; using "boxes/boxes.style"; diff --git a/Telegram/SourceFiles/settings/settings_advanced.cpp b/Telegram/SourceFiles/settings/settings_advanced.cpp index 0da98cd4f..36720cc6b 100644 --- a/Telegram/SourceFiles/settings/settings_advanced.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "info/profile/info_profile_button.h" #include "platform/platform_specific.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" #include "core/update_checker.h" diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index ca0fe6e28..01e6b1702 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -41,7 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_cloud_themes.h" #include "chat_helpers/emoji_sets_manager.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "support/support_common.h" #include "support/support_templates.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 82e6b4349..d373256f5 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "lang/lang_keys.h" +#include "boxes/abstract_box.h" #include "app.h" #include "styles/style_settings.h" #include "styles/style_boxes.h" diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index ea7c4a8fc..a42bcedba 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "platform/platform_notifications_manager.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "mainwindow.h" #include "core/application.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp index c353339b7..00a73f0a0 100644 --- a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp +++ b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "storage/storage_clear_legacy.h" -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" namespace Storage { namespace details { diff --git a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp index f4957af69..e06667c38 100644 --- a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp +++ b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_file_lock.h" #include "platform/win/windows_dlls.h" -#include "platform/win/wrapper_windows_h.h" +#include "base/platform/win/base_platform_windows_h.h" #include #include diff --git a/Telegram/SourceFiles/ui/abstract_button.cpp b/Telegram/SourceFiles/ui/abstract_button.cpp deleted file mode 100644 index 799972b8f..000000000 --- a/Telegram/SourceFiles/ui/abstract_button.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* -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 "ui/abstract_button.h" - -#include "ui/ui_utility.h" -#include "ui/ui_integration.h" - -#include - -#include -#include - -namespace Ui { - -AbstractButton::AbstractButton(QWidget *parent) : RpWidget(parent) { - setMouseTracking(true); - - using namespace rpl::mappers; - shownValue() - | rpl::filter(_1 == false) - | rpl::start_with_next([this] { clearState(); }, lifetime()); -} - -void AbstractButton::leaveEventHook(QEvent *e) { - if (_state & StateFlag::Down) return; - - setOver(false, StateChangeSource::ByHover); - return TWidget::leaveEventHook(e); -} - -void AbstractButton::enterEventHook(QEvent *e) { - checkIfOver(mapFromGlobal(QCursor::pos())); - return TWidget::enterEventHook(e); -} - -void AbstractButton::setAcceptBoth(bool acceptBoth) { - _acceptBoth = acceptBoth; -} - -void AbstractButton::checkIfOver(QPoint localPos) { - auto over = rect().marginsRemoved(getMargins()).contains(localPos); - setOver(over, StateChangeSource::ByHover); -} - -void AbstractButton::mousePressEvent(QMouseEvent *e) { - checkIfOver(e->pos()); - if (_acceptBoth || (e->buttons() & Qt::LeftButton)) { - if ((_state & StateFlag::Over) && !(_state & StateFlag::Down)) { - auto was = _state; - _state |= StateFlag::Down; - onStateChanged(was, StateChangeSource::ByPress); - - e->accept(); - } - } -} - -void AbstractButton::mouseMoveEvent(QMouseEvent *e) { - if (rect().marginsRemoved(getMargins()).contains(e->pos())) { - setOver(true, StateChangeSource::ByHover); - } else { - setOver(false, StateChangeSource::ByHover); - } -} - -void AbstractButton::mouseReleaseEvent(QMouseEvent *e) { - if (_state & StateFlag::Down) { - const auto was = _state; - _state &= ~State(StateFlag::Down); - - const auto weak = MakeWeak(this); - onStateChanged(was, StateChangeSource::ByPress); - if (!weak) { - return; - } - - if (was & StateFlag::Over) { - clicked(e->modifiers(), e->button()); - } else { - setOver(false, StateChangeSource::ByHover); - } - } -} - -void AbstractButton::clicked( - Qt::KeyboardModifiers modifiers, - Qt::MouseButton button) { - _modifiers = modifiers; - const auto weak = MakeWeak(this); - if (const auto callback = _clickedCallback) { - callback(); - } - if (weak) { - _clicks.fire_copy(button); - } -} - -void AbstractButton::setPointerCursor(bool enablePointerCursor) { - if (_enablePointerCursor != enablePointerCursor) { - _enablePointerCursor = enablePointerCursor; - updateCursor(); - } -} - -void AbstractButton::setOver(bool over, StateChangeSource source) { - if (over && !(_state & StateFlag::Over)) { - auto was = _state; - _state |= StateFlag::Over; - Integration::Instance().registerLeaveSubscription(this); - onStateChanged(was, source); - } else if (!over && (_state & StateFlag::Over)) { - auto was = _state; - _state &= ~State(StateFlag::Over); - Integration::Instance().unregisterLeaveSubscription(this); - onStateChanged(was, source); - } - updateCursor(); -} - -void AbstractButton::updateCursor() { - auto pointerCursor = _enablePointerCursor && (_state & StateFlag::Over); - setCursor(pointerCursor ? style::cur_pointer : style::cur_default); -} - -void AbstractButton::setDisabled(bool disabled) { - auto was = _state; - if (disabled && !(_state & StateFlag::Disabled)) { - _state |= StateFlag::Disabled; - onStateChanged(was, StateChangeSource::ByUser); - } else if (!disabled && (_state & StateFlag::Disabled)) { - _state &= ~State(StateFlag::Disabled); - onStateChanged(was, StateChangeSource::ByUser); - } -} - -void AbstractButton::clearState() { - auto was = _state; - _state = StateFlag::None; - onStateChanged(was, StateChangeSource::ByUser); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/abstract_button.h b/Telegram/SourceFiles/ui/abstract_button.h deleted file mode 100644 index 0bc7236d7..000000000 --- a/Telegram/SourceFiles/ui/abstract_button.h +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 -*/ -#pragma once - -#include -#include "ui/rp_widget.h" -#include "base/flags.h" - -namespace Ui { - -class AbstractButton : public RpWidget { -public: - AbstractButton(QWidget *parent); - - Qt::KeyboardModifiers clickModifiers() const { - return _modifiers; - } - - void setDisabled(bool disabled = true); - virtual void clearState(); - bool isOver() const { - return _state & StateFlag::Over; - } - bool isDown() const { - return _state & StateFlag::Down; - } - bool isDisabled() const { - return _state & StateFlag::Disabled; - } - - void setPointerCursor(bool enablePointerCursor); - - void setAcceptBoth(bool acceptBoth = true); - - void setClickedCallback(Fn callback) { - _clickedCallback = std::move(callback); - } - - rpl::producer clicks() const { - return _clicks.events(); - } - template - void addClickHandler(Handler &&handler) { - clicks( - ) | rpl::start_with_next( - std::forward(handler), - lifetime()); - } - -protected: - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - -protected: - enum class StateFlag { - None = 0, - Over = (1 << 0), - Down = (1 << 1), - Disabled = (1 << 2), - }; - friend constexpr bool is_flag_type(StateFlag) { return true; }; - using State = base::flags; - - State state() const { - return _state; - } - - enum class StateChangeSource { - ByUser = 0x00, - ByPress = 0x01, - ByHover = 0x02, - }; - void setOver(bool over, StateChangeSource source = StateChangeSource::ByUser); - - virtual void onStateChanged(State was, StateChangeSource source) { - } - - void clicked(Qt::KeyboardModifiers modifiers, Qt::MouseButton button); - -private: - void updateCursor(); - void checkIfOver(QPoint localPos); - - State _state = StateFlag::None; - - bool _acceptBoth = false; - Qt::KeyboardModifiers _modifiers; - bool _enablePointerCursor = true; - - Fn _clickedCallback; - - rpl::event_stream _clicks; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/basic_click_handlers.cpp b/Telegram/SourceFiles/ui/basic_click_handlers.cpp deleted file mode 100644 index b679ca58a..000000000 --- a/Telegram/SourceFiles/ui/basic_click_handlers.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* -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 "ui/basic_click_handlers.h" - -#include "ui/widgets/tooltip.h" -#include "ui/text/text_entity.h" -#include "ui/ui_integration.h" -#include "base/qthelp_url.h" - -#include -#include -#include -#include - -UrlClickHandler::UrlClickHandler(const QString &url, bool fullDisplayed) -: TextClickHandler(fullDisplayed) -, _originalUrl(url) { - if (isEmail()) { - _readable = _originalUrl; - } else { - const auto original = QUrl(_originalUrl); - const auto good = QUrl(original.isValid() - ? original.toEncoded() - : QString()); - _readable = good.isValid() ? good.toDisplayString() : _originalUrl; - } -} - -QString UrlClickHandler::copyToClipboardContextItemText() const { - return isEmail() - ? Ui::Integration::Instance().phraseContextCopyEmail() - : Ui::Integration::Instance().phraseContextCopyLink(); -} - -QString UrlClickHandler::url() const { - if (isEmail()) { - return _originalUrl; - } - - QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString()); - QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl); - - if (!result.isEmpty() - && !QRegularExpression( - QStringLiteral("^[a-zA-Z]+:")).match(result).hasMatch()) { - // No protocol. - return QStringLiteral("http://") + result; - } - return result; -} - -void UrlClickHandler::Open(QString url, QVariant context) { - Ui::Tooltip::Hide(); - if (!Ui::Integration::Instance().handleUrlClick(url, context) - && !url.isEmpty()) { - QDesktopServices::openUrl(url); - } -} - -auto UrlClickHandler::getTextEntity() const -> TextEntity { - const auto type = isEmail() ? EntityType::Email : EntityType::Url; - return { type, _originalUrl }; -} diff --git a/Telegram/SourceFiles/ui/basic_click_handlers.h b/Telegram/SourceFiles/ui/basic_click_handlers.h deleted file mode 100644 index a25a7ea78..000000000 --- a/Telegram/SourceFiles/ui/basic_click_handlers.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/algorithm.h" -#include "ui/click_handler.h" - -class TextClickHandler : public ClickHandler { -public: - TextClickHandler(bool fullDisplayed = true) - : _fullDisplayed(fullDisplayed) { - } - - QString copyToClipboardText() const override { - return url(); - } - - QString tooltip() const override { - return _fullDisplayed ? QString() : readable(); - } - - void setFullDisplayed(bool full) { - _fullDisplayed = full; - } - -protected: - virtual QString url() const = 0; - virtual QString readable() const { - return url(); - } - - bool _fullDisplayed; - -}; - -class UrlClickHandler : public TextClickHandler { -public: - UrlClickHandler(const QString &url, bool fullDisplayed = true); - - QString copyToClipboardContextItemText() const override; - - QString dragText() const override { - return url(); - } - - TextEntity getTextEntity() const override; - - static void Open(QString url, QVariant context = {}); - void onClick(ClickContext context) const override { - const auto button = context.button; - if (button == Qt::LeftButton || button == Qt::MiddleButton) { - Open(url(), context.other); - } - } - - [[nodiscard]] static bool IsEmail(const QString &url) { - const auto at = url.indexOf('@'), slash = url.indexOf('/'); - return ((at > 0) && (slash < 0 || slash > at)); - } - -protected: - QString url() const override; - QString readable() const override { - return _readable; - } - -private: - [[nodiscard]] bool isEmail() const { - return IsEmail(_originalUrl); - } - - QString _originalUrl, _readable; - -}; diff --git a/Telegram/SourceFiles/ui/click_handler.cpp b/Telegram/SourceFiles/ui/click_handler.cpp deleted file mode 100644 index f201ae06b..000000000 --- a/Telegram/SourceFiles/ui/click_handler.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* -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 "ui/click_handler.h" - -#include "base/algorithm.h" -#include "ui/text/text_entity.h" - -#include - -namespace { - -ClickHandlerPtr &ClickHandlerActive() { - static auto result = ClickHandlerPtr(); - return result; -} - -ClickHandlerPtr &ClickHandlerPressed() { - static auto result = ClickHandlerPtr(); - return result; -} - -} // namespace - -ClickHandlerHost *ClickHandler::_activeHost = nullptr; -ClickHandlerHost *ClickHandler::_pressedHost = nullptr; - -ClickHandlerHost::~ClickHandlerHost() { - ClickHandler::hostDestroyed(this); -} - -bool ClickHandler::setActive( - const ClickHandlerPtr &p, - ClickHandlerHost *host) { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - if (active == p) { - return false; - } - - // emit clickHandlerActiveChanged only when there is no - // other pressed click handler currently, if there is - // this method will be called when it is unpressed - if (active) { - const auto emitClickHandlerActiveChanged = false - || !pressed - || (pressed == active); - const auto wasactive = base::take(active); - if (_activeHost) { - if (emitClickHandlerActiveChanged) { - _activeHost->clickHandlerActiveChanged(wasactive, false); - } - _activeHost = nullptr; - } - } - if (p) { - active = p; - if ((_activeHost = host)) { - bool emitClickHandlerActiveChanged = (!pressed || pressed == active); - if (emitClickHandlerActiveChanged) { - _activeHost->clickHandlerActiveChanged(active, true); - } - } - } - return true; -} - -bool ClickHandler::clearActive(ClickHandlerHost *host) { - if (host && _activeHost != host) { - return false; - } - return setActive(ClickHandlerPtr(), host); -} - -void ClickHandler::pressed() { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - unpressed(); - if (!active) { - return; - } - pressed = active; - if ((_pressedHost = _activeHost)) { - _pressedHost->clickHandlerPressedChanged(pressed, true); - } -} - -ClickHandlerPtr ClickHandler::unpressed() { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - if (pressed) { - const auto activated = (active == pressed); - const auto waspressed = base::take(pressed); - if (_pressedHost) { - _pressedHost->clickHandlerPressedChanged(waspressed, false); - _pressedHost = nullptr; - } - - if (activated) { - return active; - } else if (active && _activeHost) { - // emit clickHandlerActiveChanged for current active - // click handler, which we didn't emit while we has - // a pressed click handler - _activeHost->clickHandlerActiveChanged(active, true); - } - } - return ClickHandlerPtr(); -} - -ClickHandlerPtr ClickHandler::getActive() { - return ClickHandlerActive(); -} - -ClickHandlerPtr ClickHandler::getPressed() { - return ClickHandlerPressed(); -} - -bool ClickHandler::showAsActive(const ClickHandlerPtr &p) { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - return p && (p == active) && (!pressed || (p == pressed)); -} - -bool ClickHandler::showAsPressed(const ClickHandlerPtr &p) { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - return p && (p == active) && (p == pressed); -} - -void ClickHandler::hostDestroyed(ClickHandlerHost *host) { - auto &active = ClickHandlerActive(); - auto &pressed = ClickHandlerPressed(); - - if (_activeHost == host) { - active = nullptr; - _activeHost = nullptr; - } - if (_pressedHost == host) { - pressed = nullptr; - _pressedHost = nullptr; - } -} - -auto ClickHandler::getTextEntity() const -> TextEntity { - return { EntityType::Invalid }; -} - -void ActivateClickHandler( - not_null guard, - ClickHandlerPtr handler, - ClickContext context) { - crl::on_main(guard, [=] { - handler->onClick(context); - }); -} - -void ActivateClickHandler( - not_null guard, - ClickHandlerPtr handler, - Qt::MouseButton button) { - ActivateClickHandler(guard, handler, ClickContext{ button }); -} diff --git a/Telegram/SourceFiles/ui/click_handler.h b/Telegram/SourceFiles/ui/click_handler.h deleted file mode 100644 index 6c158dfb7..000000000 --- a/Telegram/SourceFiles/ui/click_handler.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" - -#include - -class ClickHandler; -using ClickHandlerPtr = std::shared_ptr; - -struct ClickContext { - Qt::MouseButton button = Qt::LeftButton; - QVariant other; -}; - -class ClickHandlerHost { -protected: - virtual void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { - } - virtual void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) { - } - virtual ~ClickHandlerHost() = 0; - friend class ClickHandler; - -}; - -enum class EntityType; -class ClickHandler { -public: - virtual ~ClickHandler() { - } - - virtual void onClick(ClickContext context) const = 0; - - // What text to show in a tooltip when mouse is over that click handler as a link in Text. - virtual QString tooltip() const { - return QString(); - } - - // What to drop in the input fields when dragging that click handler as a link from Text. - virtual QString dragText() const { - return QString(); - } - - // Copy to clipboard support. - virtual QString copyToClipboardText() const { - return QString(); - } - virtual QString copyToClipboardContextItemText() const { - return QString(); - } - - // Entities in text support. - struct TextEntity { - EntityType type = EntityType(); - QString data; - }; - virtual TextEntity getTextEntity() const; - - // This method should be called on mouse over a click handler. - // It returns true if the active handler was changed or false otherwise. - static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr); - - // This method should be called when mouse leaves the host. - // It returns true if the active handler was changed or false otherwise. - static bool clearActive(ClickHandlerHost *host = nullptr); - - // This method should be called on mouse press event. - static void pressed(); - - // This method should be called on mouse release event. - // The activated click handler (if any) is returned. - static ClickHandlerPtr unpressed(); - - static ClickHandlerPtr getActive(); - static ClickHandlerPtr getPressed(); - - static bool showAsActive(const ClickHandlerPtr &p); - static bool showAsPressed(const ClickHandlerPtr &p); - static void hostDestroyed(ClickHandlerHost *host); - -private: - static ClickHandlerHost *_activeHost; - static ClickHandlerHost *_pressedHost; - -}; - -class LeftButtonClickHandler : public ClickHandler { -public: - void onClick(ClickContext context) const override final { - if (context.button == Qt::LeftButton) { - onClickImpl(); - } - } - -protected: - virtual void onClickImpl() const = 0; - -}; - -class LambdaClickHandler : public ClickHandler { -public: - LambdaClickHandler(Fn handler) : _handler(std::move(handler)) { - } - void onClick(ClickContext context) const override final { - if (context.button == Qt::LeftButton && _handler) { - _handler(); - } - } - -private: - Fn _handler; - -}; - -void ActivateClickHandler( - not_null guard, - ClickHandlerPtr handler, - ClickContext context); -void ActivateClickHandler( - not_null guard, - ClickHandlerPtr handler, - Qt::MouseButton button); diff --git a/Telegram/SourceFiles/ui/delayed_activation.cpp b/Telegram/SourceFiles/ui/delayed_activation.cpp deleted file mode 100644 index d039223ba..000000000 --- a/Telegram/SourceFiles/ui/delayed_activation.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* -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 "ui/delayed_activation.h" - -#include "ui/ui_utility.h" - -#include - -namespace Ui { -namespace { - -auto Paused = false; -auto Window = QPointer(); - -} // namespace - -void ActivateWindowDelayed(not_null widget) { - if (Paused) { - return; - } else if (std::exchange(Window, widget.get())) { - return; - } - crl::on_main(Window, [=] { - if (const auto widget = base::take(Window)) { - if (!widget->isHidden()) { - widget->activateWindow(); - } - } - }); -} - -void PreventDelayedActivation() { - Window = nullptr; - Paused = true; - PostponeCall([] { - Paused = false; - }); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/delayed_activation.h b/Telegram/SourceFiles/ui/delayed_activation.h deleted file mode 100644 index 60eeaefb2..000000000 --- a/Telegram/SourceFiles/ui/delayed_activation.h +++ /dev/null @@ -1,15 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace Ui { - -void ActivateWindowDelayed(not_null widget); -void PreventDelayedActivation(); - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/animation_value.cpp b/Telegram/SourceFiles/ui/effects/animation_value.cpp deleted file mode 100644 index aa45cc0a8..000000000 --- a/Telegram/SourceFiles/ui/effects/animation_value.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 "ui/effects/animation_value.h" - -#include "ui/painter.h" - -#include // M_PI - -namespace anim { -namespace { - -bool AnimationsDisabled = false; - -} // namespace - -transition linear = [](const float64 &delta, const float64 &dt) { - return delta * dt; -}; - -transition sineInOut = [](const float64 &delta, const float64 &dt) { - return -(delta / 2) * (cos(M_PI * dt) - 1); -}; - -transition halfSine = [](const float64 &delta, const float64 &dt) { - return delta * sin(M_PI * dt / 2); -}; - -transition easeOutBack = [](const float64 &delta, const float64 &dt) { - static constexpr auto s = 1.70158; - - const float64 t = dt - 1; - return delta * (t * t * ((s + 1) * t + s) + 1); -}; - -transition easeInCirc = [](const float64 &delta, const float64 &dt) { - return -delta * (sqrt(1 - dt * dt) - 1); -}; - -transition easeOutCirc = [](const float64 &delta, const float64 &dt) { - const float64 t = dt - 1; - return delta * sqrt(1 - t * t); -}; - -transition easeInCubic = [](const float64 &delta, const float64 &dt) { - return delta * dt * dt * dt; -}; - -transition easeOutCubic = [](const float64 &delta, const float64 &dt) { - const float64 t = dt - 1; - return delta * (t * t * t + 1); -}; - -transition easeInQuint = [](const float64 &delta, const float64 &dt) { - const float64 t2 = dt * dt; - return delta * t2 * t2 * dt; -}; - -transition easeOutQuint = [](const float64 &delta, const float64 &dt) { - const float64 t = dt - 1, t2 = t * t; - return delta * (t2 * t2 * t + 1); -}; - -bool Disabled() { - return AnimationsDisabled; -} - -void SetDisabled(bool disabled) { - AnimationsDisabled = disabled; -} - -void DrawStaticLoading( - QPainter &p, - QRectF rect, - int stroke, - QPen pen, - QBrush brush) { - PainterHighQualityEnabler hq(p); - - p.setBrush(brush); - pen.setWidthF(stroke); - pen.setCapStyle(Qt::RoundCap); - pen.setJoinStyle(Qt::RoundJoin); - p.setPen(pen); - p.drawEllipse(rect); - - const auto center = rect.center(); - const auto first = QPointF(center.x(), rect.y() + 1.5 * stroke); - const auto delta = center.y() - first.y(); - const auto second = QPointF(center.x() + delta * 2 / 3., center.y()); - if (delta > 0) { - QPainterPath path; - path.moveTo(first); - path.lineTo(center); - path.lineTo(second); - p.drawPath(path); - } -} - -} // anim diff --git a/Telegram/SourceFiles/ui/effects/animation_value.h b/Telegram/SourceFiles/ui/effects/animation_value.h deleted file mode 100644 index 2d822eb33..000000000 --- a/Telegram/SourceFiles/ui/effects/animation_value.h +++ /dev/null @@ -1,356 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" - -#include "ui/style/style_core.h" - -namespace anim { - -enum class type { - normal, - instant, -}; - -enum class activation { - normal, - background, -}; - -using transition = Fn; - -extern transition linear; -extern transition sineInOut; -extern transition halfSine; -extern transition easeOutBack; -extern transition easeInCirc; -extern transition easeOutCirc; -extern transition easeInCubic; -extern transition easeOutCubic; -extern transition easeInQuint; -extern transition easeOutQuint; - -inline transition bumpy(float64 bump) { - auto dt0 = (bump - sqrt(bump * (bump - 1.))); - auto k = (1 / (2 * dt0 - 1)); - return [bump, dt0, k](float64 delta, float64 dt) { - return delta * (bump - k * (dt - dt0) * (dt - dt0)); - }; -} - -// Basic animated value. -class value { -public: - using ValueType = float64; - - value() = default; - value(float64 from) : _cur(from), _from(from) { - } - value(float64 from, float64 to) : _cur(from), _from(from), _delta(to - from) { - } - void start(float64 to) { - _from = _cur; - _delta = to - _from; - } - void restart() { - _delta = _from + _delta - _cur; - _from = _cur; - } - - float64 from() const { - return _from; - } - float64 current() const { - return _cur; - } - float64 to() const { - return _from + _delta; - } - void add(float64 delta) { - _from += delta; - _cur += delta; - } - value &update(float64 dt, transition func) { - _cur = _from + func(_delta, dt); - return *this; - } - void finish() { - _cur = _from + _delta; - _from = _cur; - _delta = 0; - } - -private: - float64 _cur = 0.; - float64 _from = 0.; - float64 _delta = 0.; - -}; - -TG_FORCE_INLINE int interpolate(int a, int b, float64 b_ratio) { - return qRound(a + float64(b - a) * b_ratio); -} - -#ifdef ARCH_CPU_32_BITS -#define SHIFTED_USE_32BIT -#endif // ARCH_CPU_32_BITS - -#ifdef SHIFTED_USE_32BIT - -using ShiftedMultiplier = uint32; - -struct Shifted { - Shifted() = default; - Shifted(uint32 low, uint32 high) : low(low), high(high) { - } - uint32 low = 0; - uint32 high = 0; -}; - -TG_FORCE_INLINE Shifted operator+(Shifted a, Shifted b) { - return Shifted(a.low + b.low, a.high + b.high); -} - -TG_FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) { - return Shifted(shifted.low * multiplier, shifted.high * multiplier); -} - -TG_FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) { - return Shifted(shifted.low * multiplier, shifted.high * multiplier); -} - -TG_FORCE_INLINE Shifted shifted(uint32 components) { - return Shifted( - (components & 0x000000FFU) | ((components & 0x0000FF00U) << 8), - ((components & 0x00FF0000U) >> 16) | ((components & 0xFF000000U) >> 8)); -} - -TG_FORCE_INLINE uint32 unshifted(Shifted components) { - return ((components.low & 0x0000FF00U) >> 8) - | ((components.low & 0xFF000000U) >> 16) - | ((components.high & 0x0000FF00U) << 8) - | (components.high & 0xFF000000U); -} - -TG_FORCE_INLINE Shifted reshifted(Shifted components) { - return Shifted((components.low >> 8) & 0x00FF00FFU, (components.high >> 8) & 0x00FF00FFU); -} - -TG_FORCE_INLINE Shifted shifted(QColor color) { - // Make it premultiplied. - auto alpha = static_cast((color.alpha() & 0xFF) + 1); - auto components = Shifted(static_cast(color.blue() & 0xFF) | (static_cast(color.green() & 0xFF) << 16), - static_cast(color.red() & 0xFF) | (static_cast(255) << 16)); - return reshifted(components * alpha); -} - -TG_FORCE_INLINE uint32 getPremultiplied(QColor color) { - // Make it premultiplied. - auto alpha = static_cast((color.alpha() & 0xFF) + 1); - auto components = Shifted(static_cast(color.blue() & 0xFF) | (static_cast(color.green() & 0xFF) << 16), - static_cast(color.red() & 0xFF) | (static_cast(255) << 16)); - return unshifted(components * alpha); -} - -TG_FORCE_INLINE uint32 getAlpha(Shifted components) { - return (components.high & 0x00FF0000U) >> 16; -} - -TG_FORCE_INLINE Shifted non_premultiplied(QColor color) { - return Shifted(static_cast(color.blue() & 0xFF) | (static_cast(color.green() & 0xFF) << 16), - static_cast(color.red() & 0xFF) | (static_cast(color.alpha() & 0xFF) << 16)); -} - -TG_FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) { - auto bOpacity = std::clamp(interpolate(0, 255, b_ratio), 0, 255) + 1; - auto aOpacity = (256 - bOpacity); - auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity); - return { - static_cast((components.high >> 8) & 0xFF), - static_cast((components.low >> 24) & 0xFF), - static_cast((components.low >> 8) & 0xFF), - static_cast((components.high >> 24) & 0xFF), - }; -} - -#else // SHIFTED_USE_32BIT - -using ShiftedMultiplier = uint64; - -struct Shifted { - Shifted() = default; - Shifted(uint32 value) : value(value) { - } - Shifted(uint64 value) : value(value) { - } - uint64 value = 0; -}; - -TG_FORCE_INLINE Shifted operator+(Shifted a, Shifted b) { - return Shifted(a.value + b.value); -} - -TG_FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) { - return Shifted(shifted.value * multiplier); -} - -TG_FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) { - return Shifted(shifted.value * multiplier); -} - -TG_FORCE_INLINE Shifted shifted(uint32 components) { - auto wide = static_cast(components); - return (wide & 0x00000000000000FFULL) - | ((wide & 0x000000000000FF00ULL) << 8) - | ((wide & 0x0000000000FF0000ULL) << 16) - | ((wide & 0x00000000FF000000ULL) << 24); -} - -TG_FORCE_INLINE uint32 unshifted(Shifted components) { - return static_cast((components.value & 0x000000000000FF00ULL) >> 8) - | static_cast((components.value & 0x00000000FF000000ULL) >> 16) - | static_cast((components.value & 0x0000FF0000000000ULL) >> 24) - | static_cast((components.value & 0xFF00000000000000ULL) >> 32); -} - -TG_FORCE_INLINE Shifted reshifted(Shifted components) { - return (components.value >> 8) & 0x00FF00FF00FF00FFULL; -} - -TG_FORCE_INLINE Shifted shifted(QColor color) { - // Make it premultiplied. - auto alpha = static_cast((color.alpha() & 0xFF) + 1); - auto components = static_cast(color.blue() & 0xFF) - | (static_cast(color.green() & 0xFF) << 16) - | (static_cast(color.red() & 0xFF) << 32) - | (static_cast(255) << 48); - return reshifted(components * alpha); -} - -TG_FORCE_INLINE uint32 getPremultiplied(QColor color) { - // Make it premultiplied. - auto alpha = static_cast((color.alpha() & 0xFF) + 1); - auto components = static_cast(color.blue() & 0xFF) - | (static_cast(color.green() & 0xFF) << 16) - | (static_cast(color.red() & 0xFF) << 32) - | (static_cast(255) << 48); - return unshifted(components * alpha); -} - -TG_FORCE_INLINE uint32 getAlpha(Shifted components) { - return (components.value & 0x00FF000000000000ULL) >> 48; -} - -TG_FORCE_INLINE Shifted non_premultiplied(QColor color) { - return static_cast(color.blue() & 0xFF) - | (static_cast(color.green() & 0xFF) << 16) - | (static_cast(color.red() & 0xFF) << 32) - | (static_cast(color.alpha() & 0xFF) << 48); -} - -TG_FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) { - auto bOpacity = std::clamp(interpolate(0, 255, b_ratio), 0, 255) + 1; - auto aOpacity = (256 - bOpacity); - auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity); - return { - static_cast((components.value >> 40) & 0xFF), - static_cast((components.value >> 24) & 0xFF), - static_cast((components.value >> 8) & 0xFF), - static_cast((components.value >> 56) & 0xFF), - }; -} - -#endif // SHIFTED_USE_32BIT - -TG_FORCE_INLINE QColor color(style::color a, QColor b, float64 b_ratio) { - return color(a->c, b, b_ratio); -} - -TG_FORCE_INLINE QColor color(QColor a, style::color b, float64 b_ratio) { - return color(a, b->c, b_ratio); -} - -TG_FORCE_INLINE QColor color(style::color a, style::color b, float64 b_ratio) { - return color(a->c, b->c, b_ratio); -} - -TG_FORCE_INLINE QPen pen(QColor a, QColor b, float64 b_ratio) { - return color(a, b, b_ratio); -} - -TG_FORCE_INLINE QPen pen(style::color a, QColor b, float64 b_ratio) { - return (b_ratio > 0) ? pen(a->c, b, b_ratio) : a; -} - -TG_FORCE_INLINE QPen pen(QColor a, style::color b, float64 b_ratio) { - return (b_ratio < 1) ? pen(a, b->c, b_ratio) : b; -} - -TG_FORCE_INLINE QPen pen(style::color a, style::color b, float64 b_ratio) { - return (b_ratio > 0) ? ((b_ratio < 1) ? pen(a->c, b->c, b_ratio) : b) : a; -} - -TG_FORCE_INLINE QBrush brush(QColor a, QColor b, float64 b_ratio) { - return color(a, b, b_ratio); -} - -TG_FORCE_INLINE QBrush brush(style::color a, QColor b, float64 b_ratio) { - return (b_ratio > 0) ? brush(a->c, b, b_ratio) : a; -} - -TG_FORCE_INLINE QBrush brush(QColor a, style::color b, float64 b_ratio) { - return (b_ratio < 1) ? brush(a, b->c, b_ratio) : b; -} - -TG_FORCE_INLINE QBrush brush(style::color a, style::color b, float64 b_ratio) { - return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a; -} - -template -QPainterPath interpolate(QPointF (&from)[N], QPointF (&to)[N], float64 k) { - static_assert(N > 1, "Wrong points count in path!"); - - auto from_coef = 1. - k, to_coef = k; - QPainterPath result; - auto x = from[0].x() * from_coef + to[0].x() * to_coef; - auto y = from[0].y() * from_coef + to[0].y() * to_coef; - result.moveTo(x, y); - for (int i = 1; i != N; ++i) { - result.lineTo(from[i].x() * from_coef + to[i].x() * to_coef, from[i].y() * from_coef + to[i].y() * to_coef); - } - result.lineTo(x, y); - return result; -} - -template -QPainterPath path(QPointF (&from)[N]) { - static_assert(N > 1, "Wrong points count in path!"); - - QPainterPath result; - auto x = from[0].x(); - auto y = from[0].y(); - result.moveTo(x, y); - for (int i = 1; i != N; ++i) { - result.lineTo(from[i].x(), from[i].y()); - } - result.lineTo(x, y); - return result; -} - -bool Disabled(); -void SetDisabled(bool disabled); - -void DrawStaticLoading( - QPainter &p, - QRectF rect, - int stroke, - QPen pen, - QBrush brush = Qt::NoBrush); - -}; diff --git a/Telegram/SourceFiles/ui/effects/animations.cpp b/Telegram/SourceFiles/ui/effects/animations.cpp deleted file mode 100644 index 7b575f1b6..000000000 --- a/Telegram/SourceFiles/ui/effects/animations.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* -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 "ui/effects/animations.h" - -#include "ui/ui_utility.h" -#include "base/invoke_queued.h" - -#include - -#include -#include -#include -#include -#include -#include - -namespace Ui { -namespace Animations { -namespace { - -constexpr auto kAnimationTick = crl::time(1000) / 120; -constexpr auto kIgnoreUpdatesTimeout = crl::time(4); - -Manager *ManagerInstance = nullptr; - -} // namespace - -void Basic::start() { - Expects(ManagerInstance != nullptr); - - if (animating()) { - restart(); - } else { - ManagerInstance->start(this); - } -} - -void Basic::stop() { - Expects(ManagerInstance != nullptr); - - if (animating()) { - ManagerInstance->stop(this); - } -} - -void Basic::restart() { - Expects(_started >= 0); - - _started = crl::now(); - - Ensures(_started >= 0); -} - -void Basic::markStarted() { - Expects(_started < 0); - - _started = crl::now(); - - Ensures(_started >= 0); -} - -void Basic::markStopped() { - Expects(_started >= 0); - - _started = -1; -} - -Manager::Manager() { - Expects(ManagerInstance == nullptr); - - ManagerInstance = this; - - crl::on_main_update_requests( - ) | rpl::filter([=] { - return (_lastUpdateTime + kIgnoreUpdatesTimeout < crl::now()); - }) | rpl::start_with_next([=] { - update(); - }, _lifetime); -} - -Manager::~Manager() { - Expects(ManagerInstance == this); - Expects(_active.empty()); - Expects(_starting.empty()); - - ManagerInstance = nullptr; -} - -void Manager::start(not_null animation) { - _forceImmediateUpdate = true; - if (_updating) { - _starting.emplace_back(animation.get()); - } else { - schedule(); - _active.emplace_back(animation.get()); - } -} - -void Manager::stop(not_null animation) { - if (empty(_active) && empty(_starting)) { - return; - } - const auto value = animation.get(); - const auto proj = &ActiveBasicPointer::get; - auto &list = _updating ? _starting : _active; - list.erase(ranges::remove(list, value, proj), end(list)); - - if (_updating) { - const auto i = ranges::find(_active, value, proj); - if (i != end(_active)) { - *i = nullptr; - } - } else if (empty(_active)) { - stopTimer(); - } -} - -void Manager::update() { - if (_active.empty() || _updating || _scheduled) { - return; - } - const auto now = crl::now(); - if (_forceImmediateUpdate) { - _forceImmediateUpdate = false; - } - schedule(); - - _updating = true; - const auto guard = gsl::finally([&] { _updating = false; }); - - _lastUpdateTime = now; - const auto isFinished = [&](const ActiveBasicPointer &element) { - return !element.call(now); - }; - _active.erase(ranges::remove_if(_active, isFinished), end(_active)); - - if (!empty(_starting)) { - _active.insert( - end(_active), - std::make_move_iterator(begin(_starting)), - std::make_move_iterator(end(_starting))); - _starting.clear(); - } -} - -void Manager::updateQueued() { - Expects(_timerId == 0); - - _timerId = -1; - InvokeQueued(delayedCallGuard(), [=] { - Expects(_timerId < 0); - - _timerId = 0; - update(); - }); -} - -void Manager::schedule() { - if (_scheduled || _timerId < 0) { - return; - } - stopTimer(); - - _scheduled = true; - PostponeCall(delayedCallGuard(), [=] { - _scheduled = false; - if (_forceImmediateUpdate) { - _forceImmediateUpdate = false; - updateQueued(); - } else { - const auto next = _lastUpdateTime + kAnimationTick; - const auto now = crl::now(); - if (now < next) { - _timerId = startTimer(next - now, Qt::PreciseTimer); - } else { - updateQueued(); - } - } - }); -} - -not_null Manager::delayedCallGuard() const { - return static_cast(this); -} - -void Manager::stopTimer() { - if (_timerId > 0) { - killTimer(base::take(_timerId)); - } -} - -void Manager::timerEvent(QTimerEvent *e) { - update(); -} - -} // namespace Animations -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/animations.h b/Telegram/SourceFiles/ui/effects/animations.h deleted file mode 100644 index 6c9b16336..000000000 --- a/Telegram/SourceFiles/ui/effects/animations.h +++ /dev/null @@ -1,419 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/effects/animation_value.h" - -#include -#include - -namespace Ui { -namespace Animations { - -class Manager; - -class Basic final { -public: - Basic() = default; - Basic(const Basic &other) = delete; - Basic &operator=(const Basic &other) = delete; - - template - explicit Basic(Callback &&callback); - - template - void init(Callback &&callback); - - void start(); - void stop(); - - [[nodiscard]] crl::time started() const; - [[nodiscard]] bool animating() const; - - ~Basic(); - -private: - friend class Manager; - - template - [[nodiscard]] static Fn Prepare(Callback &&callback); - - [[nodiscard]] bool call(crl::time now) const; - void restart(); - - void markStarted(); - void markStopped(); - - crl::time _started = -1; - Fn _callback; - -}; - -class Simple final { -public: - template - void start( - Callback &&callback, - float64 from, - float64 to, - crl::time duration, - anim::transition transition = anim::linear); - void change( - float64 to, - crl::time duration, - anim::transition transition = anim::linear); - void stop(); - [[nodiscard]] bool animating() const; - [[nodiscard]] float64 value(float64 final) const; - -private: - class ShortTracker { - public: - ShortTracker() { - restart(); - } - ShortTracker(const ShortTracker &other) = delete; - ShortTracker &operator=(const ShortTracker &other) = delete; - ~ShortTracker() { - release(); - } - void restart() { - if (!std::exchange(_paused, true)) { - style::internal::StartShortAnimation(); - } - } - void release() { - if (std::exchange(_paused, false)) { - style::internal::StopShortAnimation(); - } - } - - private: - bool _paused = false; - - }; - - struct Data { - explicit Data(float64 initial) : value(initial) { - } - ~Data() { - if (markOnDelete) { - *markOnDelete = true; - } - } - - Basic animation; - anim::transition transition; - float64 from = 0.; - float64 delta = 0.; - float64 value = 0.; - float64 duration = 0.; - bool *markOnDelete = nullptr; - ShortTracker tracker; - }; - - template - [[nodiscard]] static decltype(auto) Prepare(Callback &&callback); - - void prepare(float64 from, crl::time duration); - void startPrepared( - float64 to, - crl::time duration, - anim::transition transition); - - static constexpr auto kLongAnimationDuration = crl::time(1000); - - mutable std::unique_ptr _data; - -}; - -class Manager final : private QObject { -public: - Manager(); - ~Manager(); - - void update(); - -private: - class ActiveBasicPointer { - public: - ActiveBasicPointer(Basic *value = nullptr) : _value(value) { - if (_value) { - _value->markStarted(); - } - } - ActiveBasicPointer(ActiveBasicPointer &&other) - : _value(base::take(other._value)) { - } - ActiveBasicPointer &operator=(ActiveBasicPointer &&other) { - if (_value != other._value) { - if (_value) { - _value->markStopped(); - } - _value = base::take(other._value); - } - return *this; - } - ~ActiveBasicPointer() { - if (_value) { - _value->markStopped(); - } - } - - [[nodiscard]] bool call(crl::time now) const { - return _value && _value->call(now); - } - - friend inline bool operator==( - const ActiveBasicPointer &a, - const ActiveBasicPointer &b) { - return a._value == b._value; - } - - Basic *get() const { - return _value; - } - - private: - Basic *_value = nullptr; - - }; - - friend class Basic; - - void timerEvent(QTimerEvent *e) override; - - void start(not_null animation); - void stop(not_null animation); - - void schedule(); - void updateQueued(); - void stopTimer(); - not_null delayedCallGuard() const; - - crl::time _lastUpdateTime = 0; - int _timerId = 0; - bool _updating = false; - bool _scheduled = false; - bool _forceImmediateUpdate = false; - std::vector _active; - std::vector _starting; - rpl::lifetime _lifetime; - -}; - -template -Fn Basic__PrepareCrlTime(Callback &&callback) { - using Return = decltype(callback(crl::time(0))); - if constexpr (std::is_convertible_v) { - return std::forward(callback); - } else if constexpr (std::is_same_v) { - return [callback = std::forward(callback)]( - crl::time time) { - callback(time); - return true; - }; - } else { - static_assert(false_t(callback), "Expected void or bool."); - } -} - -template -Fn Basic__PreparePlain(Callback &&callback) { - using Return = decltype(callback()); - if constexpr (std::is_convertible_v) { - return [callback = std::forward(callback)](crl::time) { - return callback(); - }; - } else if constexpr (std::is_same_v) { - return [callback = std::forward(callback)](crl::time) { - callback(); - return true; - }; - } else { - static_assert(false_t(callback), "Expected void or bool."); - } -} - -template -inline Fn Basic::Prepare(Callback &&callback) { - if constexpr (rpl::details::is_callable_plain_v) { - return Basic__PrepareCrlTime(std::forward(callback)); - } else if constexpr (rpl::details::is_callable_plain_v) { - return Basic__PreparePlain(std::forward(callback)); - } else { - static_assert(false_t(callback), "Expected crl::time or no args."); - } -} - -template -inline Basic::Basic(Callback &&callback) -: _callback(Prepare(std::forward(callback))) { -} - -template -inline void Basic::init(Callback &&callback) { - _callback = Prepare(std::forward(callback)); -} - -TG_FORCE_INLINE crl::time Basic::started() const { - return _started; -} - -TG_FORCE_INLINE bool Basic::animating() const { - return (_started >= 0); -} - -TG_FORCE_INLINE bool Basic::call(crl::time now) const { - Expects(_started >= 0); - - // _started may be greater than now if we called restart while iterating. - const auto onstack = _callback; - return onstack(std::max(_started, now)); -} - -inline Basic::~Basic() { - stop(); -} - -template -decltype(auto) Simple__PrepareFloat64(Callback &&callback) { - using Return = decltype(callback(float64(0.))); - if constexpr (std::is_convertible_v) { - return std::forward(callback); - } else if constexpr (std::is_same_v) { - return [callback = std::forward(callback)]( - float64 value) { - callback(value); - return true; - }; - } else { - static_assert(false_t(callback), "Expected void or float64."); - } -} - -template -decltype(auto) Simple__PreparePlain(Callback &&callback) { - using Return = decltype(callback()); - if constexpr (std::is_convertible_v) { - return [callback = std::forward(callback)](float64) { - return callback(); - }; - } else if constexpr (std::is_same_v) { - return [callback = std::forward(callback)](float64) { - callback(); - return true; - }; - } else { - static_assert(false_t(callback), "Expected void or bool."); - } -} - -template -decltype(auto) Simple::Prepare(Callback &&callback) { - if constexpr (rpl::details::is_callable_plain_v) { - return Simple__PrepareFloat64(std::forward(callback)); - } else if constexpr (rpl::details::is_callable_plain_v) { - return Simple__PreparePlain(std::forward(callback)); - } else { - static_assert(false_t(callback), "Expected float64 or no args."); - } -} - -template -inline void Simple::start( - Callback &&callback, - float64 from, - float64 to, - crl::time duration, - anim::transition transition) { - prepare(from, duration); - _data->animation.init([ - that = _data.get(), - callback = Prepare(std::forward(callback)) - ](crl::time now) { - const auto time = anim::Disabled() - ? that->duration - : (now - that->animation.started()); - const auto finished = (time >= that->duration); - const auto progress = finished - ? that->delta - : that->transition(that->delta, time / that->duration); - that->value = that->from + progress; - - if (finished) { - that->animation.stop(); - } - - auto deleted = false; - that->markOnDelete = &deleted; - const auto result = callback(that->value) && !finished; - if (!deleted) { - that->markOnDelete = nullptr; - if (!result) { - that->tracker.release(); - } - } - return result; - }); - startPrepared(to, duration, transition); -} - -inline void Simple::change( - float64 to, - crl::time duration, - anim::transition transition) { - Expects(_data != nullptr); - - prepare(0. /* ignored */, duration); - startPrepared(to, duration, transition); -} - -inline void Simple::prepare(float64 from, crl::time duration) { - const auto isLong = (duration > kLongAnimationDuration); - if (!_data) { - _data = std::make_unique(from); - } else if (!isLong) { - _data->tracker.restart(); - } - if (isLong) { - _data->tracker.release(); - } -} - -inline void Simple::stop() { - _data = nullptr; -} - -inline bool Simple::animating() const { - if (!_data) { - return false; - } else if (!_data->animation.animating()) { - _data = nullptr; - return false; - } - return true; -} - -TG_FORCE_INLINE float64 Simple::value(float64 final) const { - return animating() ? _data->value : final; -} - -inline void Simple::startPrepared( - float64 to, - crl::time duration, - anim::transition transition) { - _data->from = _data->value; - _data->delta = to - _data->from; - _data->duration = duration; - _data->transition = transition; - _data->animation.start(); -} - -} // namespace Animations -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/cross_animation.cpp b/Telegram/SourceFiles/ui/effects/cross_animation.cpp deleted file mode 100644 index 8b523ff3d..000000000 --- a/Telegram/SourceFiles/ui/effects/cross_animation.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -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 "ui/effects/cross_animation.h" - -#include "ui/effects/animation_value.h" -#include "ui/painter.h" - -#include - -namespace Ui { -namespace { - -constexpr auto kPointCount = 12; -constexpr auto kStaticLoadingValue = float64(-666); -constexpr auto kFullArcLength = 360 * 16; - - -// -// 1 3 -// X X X X -// X X X X -// 0 X X 4 -// X X X X -// X 2 X -// X X -// X X -// 11 5 -// X X -// X X -// X 8 X -// X X X X -// 10 X X 6 -// X X X X -// X X X X -// 9 7 -// - -void transformLoadingCross(float64 loading, std::array &points, int &paintPointsCount) { - auto moveTo = [](QPointF &point, QPointF &to, float64 ratio) { - point = point * (1. - ratio) + to * ratio; - }; - auto moveFrom = [](QPointF &point, QPointF &from, float64 ratio) { - point = from * (1. - ratio) + point * ratio; - }; - auto paintPoints = [&points, &paintPointsCount](std::initializer_list &&indices) { - auto index = 0; - for (auto paintIndex : indices) { - points[index++] = points[paintIndex]; - } - paintPointsCount = indices.size(); - }; - - if (loading < 0.125) { - auto ratio = loading / 0.125; - moveTo(points[6], points[5], ratio); - moveTo(points[7], points[8], ratio); - } else if (loading < 0.25) { - auto ratio = (loading - 0.125) / 0.125; - moveTo(points[9], points[8], ratio); - moveTo(points[10], points[11], ratio); - paintPoints({ 0, 1, 2, 3, 4, 9, 10, 11 }); - } else if (loading < 0.375) { - auto ratio = (loading - 0.25) / 0.125; - moveTo(points[0], points[11], ratio); - moveTo(points[1], points[2], ratio); - paintPoints({ 0, 1, 2, 3, 4, 8 }); - } else if (loading < 0.5) { - auto ratio = (loading - 0.375) / 0.125; - moveTo(points[8], points[4], ratio); - moveTo(points[11], points[3], ratio); - paintPoints({ 3, 4, 8, 11 }); - } else if (loading < 0.625) { - auto ratio = (loading - 0.5) / 0.125; - moveFrom(points[8], points[4], ratio); - moveFrom(points[11], points[3], ratio); - paintPoints({ 3, 4, 8, 11 }); - } else if (loading < 0.75) { - auto ratio = (loading - 0.625) / 0.125; - moveFrom(points[6], points[5], ratio); - moveFrom(points[7], points[8], ratio); - paintPoints({ 3, 4, 5, 6, 7, 11 }); - } else if (loading < 0.875) { - auto ratio = (loading - 0.75) / 0.125; - moveFrom(points[9], points[8], ratio); - moveFrom(points[10], points[11], ratio); - paintPoints({ 3, 4, 5, 6, 7, 8, 9, 10 }); - } else { - auto ratio = (loading - 0.875) / 0.125; - moveFrom(points[0], points[11], ratio); - moveFrom(points[1], points[2], ratio); - } -} - -} // namespace - -void CrossAnimation::paintStaticLoading( - Painter &p, - const style::CrossAnimation &st, - style::color color, - int x, - int y, - int outerWidth, - float64 shown) { - paint(p, st, color, x, y, outerWidth, shown, kStaticLoadingValue); -} - -void CrossAnimation::paint( - Painter &p, - const style::CrossAnimation &st, - style::color color, - int x, - int y, - int outerWidth, - float64 shown, - float64 loading) { - PainterHighQualityEnabler hq(p); - - auto sqrt2 = sqrt(2.); - auto deleteScale = shown + st.minScale * (1. - shown); - auto deleteSkip = (deleteScale * st.skip) + (1. - deleteScale) * (st.size / 2); - auto deleteLeft = style::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; - std::array 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.) }, - } }; - auto pathDeleteSize = kPointCount; - - const auto staticLoading = (loading == kStaticLoadingValue); - auto loadingArcLength = staticLoading ? kFullArcLength : 0; - if (loading > 0.) { - transformLoadingCross(loading, pathDelete, pathDeleteSize); - - auto loadingArc = (loading >= 0.5) ? (loading - 1.) : loading; - loadingArcLength = qRound(-loadingArc * 2 * kFullArcLength); - } - - if (!staticLoading) { - 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 != pathDeleteSize; ++i) { - path.lineTo(pathDelete[i]); - } - path.lineTo(pathDelete[0]); - p.fillPath(path, color); - } - if (loadingArcLength != 0) { - auto roundSkip = (st.size * (1 - sqrt2) + 2 * sqrt2 * deleteSkip + st.stroke) / 2; - auto roundPart = QRectF(x + roundSkip, y + roundSkip, st.size - 2 * roundSkip, st.size - 2 * roundSkip); - if (staticLoading) { - anim::DrawStaticLoading(p, roundPart, st.stroke, color); - } else { - auto loadingArcStart = kFullArcLength / 8; - if (shown < 1.) { - loadingArcStart -= qRound(-(shown - 1.) * kFullArcLength / 4.); - } - if (loadingArcLength < 0) { - loadingArcStart += loadingArcLength; - loadingArcLength = -loadingArcLength; - } - - p.setBrush(Qt::NoBrush); - auto pen = color->p; - pen.setWidthF(st.stroke); - pen.setCapStyle(Qt::RoundCap); - p.setPen(pen); - p.drawArc(roundPart, loadingArcStart, loadingArcLength); - } - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/cross_animation.h b/Telegram/SourceFiles/ui/effects/cross_animation.h deleted file mode 100644 index b2b5eb069..000000000 --- a/Telegram/SourceFiles/ui/effects/cross_animation.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" - -class Painter; - -namespace Ui { - -class CrossAnimation { -public: - static void paint( - Painter &p, - const style::CrossAnimation &st, - style::color color, - int x, - int y, - int outerWidth, - float64 shown, - float64 loading = 0.); - static void paintStaticLoading( - Painter &p, - const style::CrossAnimation &st, - style::color color, - int x, - int y, - int outerWidth, - float64 shown); - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/fade_animation.cpp b/Telegram/SourceFiles/ui/effects/fade_animation.cpp deleted file mode 100644 index 105ef111c..000000000 --- a/Telegram/SourceFiles/ui/effects/fade_animation.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* -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 "ui/effects/fade_animation.h" - -#include "ui/ui_utility.h" -#include "ui/painter.h" -#include "app.h" - -namespace Ui { -namespace { - -constexpr int kWideScale = 5; - -} // namespace - -FadeAnimation::FadeAnimation(TWidget *widget, float64 scale) -: _widget(widget) -, _scale(scale) { -} - -bool FadeAnimation::paint(QPainter &p) { - if (_cache.isNull()) return false; - - const auto cache = _cache; - auto opacity = _animation.value(_visible ? 1. : 0.); - p.setOpacity(opacity); - if (_scale < 1.) { - PainterHighQualityEnabler hq(p); - auto targetRect = QRect( - (1 - kWideScale) / 2 * _size.width(), - (1 - kWideScale) / 2 * _size.height(), - kWideScale * _size.width(), - kWideScale * _size.height()); - auto scale = opacity + (1. - opacity) * _scale; - auto shownWidth = anim::interpolate( - (1 - kWideScale) / 2 * _size.width(), - 0, - scale); - auto shownHeight = anim::interpolate( - (1 - kWideScale) / 2 * _size.height(), - 0, - scale); - auto margins = QMargins( - shownWidth, - shownHeight, - shownWidth, - shownHeight); - p.drawPixmap(targetRect.marginsAdded(margins), cache); - } else { - p.drawPixmap(0, 0, cache); - } - return true; -} - -void FadeAnimation::refreshCache() { - if (!_cache.isNull()) { - _cache = QPixmap(); - _cache = grabContent(); - Assert(!_cache.isNull()); - } -} - -QPixmap FadeAnimation::grabContent() { - SendPendingMoveResizeEvents(_widget); - _size = _widget->size(); - if (_size.isEmpty()) { - auto image = QImage( - cIntRetinaFactor(), - cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - return App::pixmapFromImageInPlace(std::move(image)); - } - auto widgetContent = GrabWidget(_widget); - if (_scale < 1.) { - auto result = QImage(kWideScale * _size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); - result.fill(Qt::transparent); - { - Painter p(&result); - p.drawPixmap((kWideScale - 1) / 2 * _size.width(), (kWideScale - 1) / 2 * _size.height(), widgetContent); - } - return App::pixmapFromImageInPlace(std::move(result)); - } - return widgetContent; -} - -void FadeAnimation::setFinishedCallback(FinishedCallback &&callback) { - _finishedCallback = std::move(callback); -} - -void FadeAnimation::setUpdatedCallback(UpdatedCallback &&callback) { - _updatedCallback = std::move(callback); -} - -void FadeAnimation::show() { - _visible = true; - stopAnimation(); -} - -void FadeAnimation::hide() { - _visible = false; - stopAnimation(); -} - -void FadeAnimation::stopAnimation() { - _animation.stop(); - if (!_cache.isNull()) { - _cache = QPixmap(); - if (_finishedCallback) { - _finishedCallback(); - } - } - if (_visible == _widget->isHidden()) { - _widget->setVisible(_visible); - } -} - -void FadeAnimation::fadeIn(int duration) { - if (_visible) return; - - _visible = true; - startAnimation(duration); -} - -void FadeAnimation::fadeOut(int duration) { - if (!_visible) return; - - _visible = false; - startAnimation(duration); -} - -void FadeAnimation::startAnimation(int duration) { - if (_cache.isNull()) { - _cache = grabContent(); - Assert(!_cache.isNull()); - } - auto from = _visible ? 0. : 1.; - auto to = _visible ? 1. : 0.; - _animation.start([this]() { updateCallback(); }, from, to, duration); - updateCallback(); - if (_widget->isHidden()) { - _widget->show(); - } -} - -void FadeAnimation::updateCallback() { - _widget->update(); - if (_updatedCallback) { - _updatedCallback(_animation.value(_visible ? 1. : 0.)); - } - if (!_animation.animating()) { - stopAnimation(); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/fade_animation.h b/Telegram/SourceFiles/ui/effects/fade_animation.h deleted file mode 100644 index c110f56cf..000000000 --- a/Telegram/SourceFiles/ui/effects/fade_animation.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/effects/animations.h" -#include "styles/style_widgets.h" - -namespace Ui { - -class FadeAnimation { -public: - FadeAnimation(TWidget *widget, float64 scale = 1.); - - bool paint(QPainter &p); - void refreshCache(); - - using FinishedCallback = Fn; - void setFinishedCallback(FinishedCallback &&callback); - - using UpdatedCallback = Fn; - void setUpdatedCallback(UpdatedCallback &&callback); - - void show(); - void hide(); - - void fadeIn(int duration); - void fadeOut(int duration); - - void finish() { - stopAnimation(); - } - - bool animating() const { - return _animation.animating(); - } - bool visible() const { - return _visible; - } - -private: - void startAnimation(int duration); - void stopAnimation(); - - void updateCallback(); - QPixmap grabContent(); - - TWidget *_widget = nullptr; - float64 _scale = 1.; - - Ui::Animations::Simple _animation; - QSize _size; - QPixmap _cache; - bool _visible = false; - - FinishedCallback _finishedCallback; - UpdatedCallback _updatedCallback; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/numbers_animation.cpp b/Telegram/SourceFiles/ui/effects/numbers_animation.cpp deleted file mode 100644 index 953625f8e..000000000 --- a/Telegram/SourceFiles/ui/effects/numbers_animation.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* -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 "ui/effects/numbers_animation.h" - -#include "ui/painter.h" -#include "styles/style_widgets.h" - -#include - -namespace Ui { - -NumbersAnimation::NumbersAnimation( - const style::font &font, - Fn animationCallback) -: _font(font) -, _animationCallback(std::move(animationCallback)) { - for (auto ch = '0'; ch != '9'; ++ch) { - accumulate_max(_digitWidth, _font->m.width(ch)); - } -} - -void NumbersAnimation::setText(const QString &text, int value) { - if (_a_ready.animating()) { - _delayedText = text; - _delayedValue = value; - } else { - realSetText(text, value); - } -} - -void NumbersAnimation::animationCallback() { - if (_animationCallback) { - _animationCallback(); - } - if (_widthChangedCallback) { - _widthChangedCallback(); - } - if (!_a_ready.animating()) { - if (!_delayedText.isEmpty()) { - setText(_delayedText, _delayedValue); - } - } -} - -void NumbersAnimation::realSetText(QString text, int value) { - _delayedText = QString(); - _delayedValue = 0; - - _growing = (value > _value); - _value = value; - - auto newSize = text.size(); - while (_digits.size() < newSize) { - _digits.push_front(Digit()); - } - while (_digits.size() > newSize && !_digits.front().to.unicode()) { - _digits.pop_front(); - } - auto oldSize = _digits.size(); - auto animating = false; - for (auto i = 0, size = _digits.size(); i != size; ++i) { - auto &digit = _digits[i]; - digit.from = digit.to; - digit.fromWidth = digit.toWidth; - digit.to = (newSize + i < size) ? QChar(0) : text[newSize + i - size]; - digit.toWidth = digit.to.unicode() ? _font->m.width(digit.to) : 0; - if (digit.from != digit.to) { - animating = true; - } - if (!digit.from.unicode()) { - --oldSize; - } - } - _fromWidth = oldSize * _digitWidth; - _toWidth = newSize * _digitWidth; - if (animating) { - _a_ready.start( - [this] { animationCallback(); }, - 0., - 1., - st::slideWrapDuration); - } -} - -int NumbersAnimation::countWidth() const { - return anim::interpolate( - _fromWidth, - _toWidth, - anim::easeOutCirc(1., _a_ready.value(1.))); -} - -int NumbersAnimation::maxWidth() const { - return std::max(_fromWidth, _toWidth); -} - -void NumbersAnimation::finishAnimating() { - auto width = countWidth(); - _a_ready.stop(); - if (_widthChangedCallback && countWidth() != width) { - _widthChangedCallback(); - } - if (!_delayedText.isEmpty()) { - setText(_delayedText, _delayedValue); - } -} - -void NumbersAnimation::paint(QPainter &p, int x, int y, int outerWidth) { - auto digitsCount = _digits.size(); - if (!digitsCount) return; - - auto progress = anim::easeOutCirc(1., _a_ready.value(1.)); - auto width = anim::interpolate(_fromWidth, _toWidth, progress); - - QString singleChar('0'); - if (style::RightToLeft()) x = outerWidth - x - width; - x += width - _digits.size() * _digitWidth; - auto fromTop = anim::interpolate(0, _font->height, progress) * (_growing ? 1 : -1); - auto toTop = anim::interpolate(_font->height, 0, progress) * (_growing ? -1 : 1); - for (auto i = 0; i != digitsCount; ++i) { - auto &digit = _digits[i]; - auto from = digit.from; - auto to = digit.to; - if (from == to) { - p.setOpacity(1.); - singleChar[0] = from; - p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + _font->ascent, singleChar); - } else { - if (from.unicode()) { - p.setOpacity(1. - progress); - singleChar[0] = from; - p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + fromTop + _font->ascent, singleChar); - } - if (to.unicode()) { - p.setOpacity(progress); - singleChar[0] = to; - p.drawText(x + (_digitWidth - digit.toWidth) / 2, y + toTop + _font->ascent, singleChar); - } - } - x += _digitWidth; - } - p.setOpacity(1.); -} - -LabelWithNumbers::LabelWithNumbers( - QWidget *parent, - const style::FlatLabel &st, - int textTop, - const StringWithNumbers &value) -: RpWidget(parent) -, _st(st) -, _textTop(textTop) -, _before(GetBefore(value)) -, _after(GetAfter(value)) -, _numbers(_st.style.font, [=] { update(); }) -, _beforeWidth(_st.style.font->width(_before)) -, _afterWidth(st.style.font->width(_after)) { - Expects((value.offset < 0) == (value.length == 0)); - - const auto numbers = GetNumbers(value); - _numbers.setText(numbers, numbers.toInt()); - _numbers.finishAnimating(); -} - -QString LabelWithNumbers::GetBefore(const StringWithNumbers &value) { - return value.text.mid(0, value.offset); -} - -QString LabelWithNumbers::GetAfter(const StringWithNumbers &value) { - return (value.offset >= 0) - ? value.text.mid(value.offset + value.length) - : QString(); -} - -QString LabelWithNumbers::GetNumbers(const StringWithNumbers &value) { - return (value.offset >= 0) - ? value.text.mid(value.offset, value.length) - : QString(); -} - -void LabelWithNumbers::setValue(const StringWithNumbers &value) { - _before = GetBefore(value); - _after = GetAfter(value); - const auto numbers = GetNumbers(value); - _numbers.setText(numbers, numbers.toInt()); - - const auto oldBeforeWidth = std::exchange( - _beforeWidth, - _st.style.font->width(_before)); - _beforeWidthAnimation.start( - [this] { update(); }, - oldBeforeWidth, - _beforeWidth, - st::slideWrapDuration, - anim::easeOutCirc); - - _afterWidth = _st.style.font->width(_after); -} - -void LabelWithNumbers::finishAnimating() { - _beforeWidthAnimation.stop(); - _numbers.finishAnimating(); - update(); -} - -void LabelWithNumbers::paintEvent(QPaintEvent *e) { - Painter p(this); - - const auto beforeWidth = _beforeWidthAnimation.value(_beforeWidth); - - p.setFont(_st.style.font); - p.setBrush(Qt::NoBrush); - p.setPen(_st.textFg); - auto left = 0; - const auto outerWidth = width(); - - p.setClipRect(0, 0, left + beforeWidth, height()); - p.drawTextLeft(left, _textTop, outerWidth, _before, _beforeWidth); - left += beforeWidth; - p.setClipping(false); - - _numbers.paint(p, left, _textTop, outerWidth); - left += _numbers.countWidth(); - - const auto availableWidth = outerWidth - left; - const auto text = (availableWidth < _afterWidth) - ? _st.style.font->elided(_after, availableWidth) - : _after; - const auto textWidth = (availableWidth < _afterWidth) ? -1 : _afterWidth; - p.drawTextLeft(left, _textTop, outerWidth, text, textWidth); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/numbers_animation.h b/Telegram/SourceFiles/ui/effects/numbers_animation.h deleted file mode 100644 index a863c8c72..000000000 --- a/Telegram/SourceFiles/ui/effects/numbers_animation.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/effects/animations.h" - -namespace style { -struct FlatLabel; -} // namespace style - -namespace Ui { - -class NumbersAnimation { -public: - NumbersAnimation( - const style::font &font, - Fn animationCallback); - - void setWidthChangedCallback(Fn callback) { - _widthChangedCallback = std::move(callback); - } - void setText(const QString &text, int value); - void finishAnimating(); - - void paint(QPainter &p, int x, int y, int outerWidth); - int countWidth() const; - int maxWidth() const; - -private: - struct Digit { - QChar from = 0; - QChar to = 0; - int fromWidth = 0; - int toWidth = 0; - }; - - void animationCallback(); - void realSetText(QString text, int value); - - const style::font &_font; - - QList _digits; - int _digitWidth = 0; - - int _fromWidth = 0; - int _toWidth = 0; - - Ui::Animations::Simple _a_ready; - QString _delayedText; - int _delayedValue = 0; - - int _value = 0; - bool _growing = false; - - Fn _animationCallback; - Fn _widthChangedCallback; - -}; - -struct StringWithNumbers { - static StringWithNumbers FromString(const QString &text) { - return { text }; - } - - QString text; - int offset = -1; - int length = 0; -}; - -class LabelWithNumbers : public Ui::RpWidget { -public: - LabelWithNumbers( - QWidget *parent, - const style::FlatLabel &st, - int textTop, - const StringWithNumbers &value); - - void setValue(const StringWithNumbers &value); - void finishAnimating(); - - int naturalWidth() const override { - return _beforeWidth + _numbers.maxWidth() + _afterWidth; - } - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - static QString GetBefore(const StringWithNumbers &value); - static QString GetAfter(const StringWithNumbers &value); - static QString GetNumbers(const StringWithNumbers &value); - - const style::FlatLabel &_st; - int _textTop; - QString _before; - QString _after; - NumbersAnimation _numbers; - int _beforeWidth = 0; - int _afterWidth = 0; - Ui::Animations::Simple _beforeWidthAnimation; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp deleted file mode 100644 index 651e65729..000000000 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/* -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 "ui/effects/panel_animation.h" - -#include "ui/effects/animation_value.h" -#include "ui/ui_utility.h" - -#include - -namespace Ui { - -void RoundShadowAnimation::start(int frameWidth, int frameHeight, float64 devicePixelRatio) { - Expects(!started()); - - _frameWidth = frameWidth; - _frameHeight = frameHeight; - _frame = QImage(_frameWidth, _frameHeight, QImage::Format_ARGB32_Premultiplied); - _frame.setDevicePixelRatio(devicePixelRatio); - _frameIntsPerLine = (_frame.bytesPerLine() >> 2); - _frameInts = reinterpret_cast(_frame.bits()); - _frameIntsPerLineAdded = _frameIntsPerLine - _frameWidth; - Assert(_frame.depth() == static_cast(sizeof(uint32) << 3)); - Assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2)); - Assert(_frameIntsPerLineAdded >= 0); -} - -void RoundShadowAnimation::setShadow(const style::Shadow &st) { - _shadow.extend = st.extend * style::DevicePixelRatio(); - _shadow.left = cloneImage(st.left); - if (_shadow.valid()) { - _shadow.topLeft = cloneImage(st.topLeft); - _shadow.top = cloneImage(st.top); - _shadow.topRight = cloneImage(st.topRight); - _shadow.right = cloneImage(st.right); - _shadow.bottomRight = cloneImage(st.bottomRight); - _shadow.bottom = cloneImage(st.bottom); - _shadow.bottomLeft = cloneImage(st.bottomLeft); - Assert(!_shadow.topLeft.isNull() - && !_shadow.top.isNull() - && !_shadow.topRight.isNull() - && !_shadow.right.isNull() - && !_shadow.bottomRight.isNull() - && !_shadow.bottom.isNull() - && !_shadow.bottomLeft.isNull()); - } else { - _shadow.topLeft = - _shadow.top = - _shadow.topRight = - _shadow.right = - _shadow.bottomRight = - _shadow.bottom = - _shadow.bottomLeft = QImage(); - } -} - -void RoundShadowAnimation::setCornerMasks( - const std::array &corners) { - setCornerMask(_topLeft, corners[0]); - setCornerMask(_topRight, corners[1]); - setCornerMask(_bottomLeft, corners[2]); - setCornerMask(_bottomRight, corners[3]); -} - -void RoundShadowAnimation::setCornerMask(Corner &corner, const QImage &image) { - Expects(!started()); - - corner.image = image; - if (corner.valid()) { - corner.width = corner.image.width(); - corner.height = corner.image.height(); - corner.bytes = corner.image.constBits(); - corner.bytesPerPixel = (corner.image.depth() >> 3); - corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel; - Assert(corner.image.depth() == (corner.bytesPerPixel << 3)); - Assert(corner.bytesPerLineAdded >= 0); - } else { - corner.width = corner.height = 0; - } -} - -QImage RoundShadowAnimation::cloneImage(const style::icon &source) { - if (source.empty()) return QImage(); - - auto result = QImage( - source.size() * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(style::DevicePixelRatio()); - result.fill(Qt::transparent); - { - QPainter p(&result); - source.paint(p, 0, 0, source.width()); - } - return result; -} - -void RoundShadowAnimation::paintCorner(Corner &corner, int left, int top) { - auto mask = corner.bytes; - auto bytesPerPixel = corner.bytesPerPixel; - auto bytesPerLineAdded = corner.bytesPerLineAdded; - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width; - for (auto y = 0; y != corner.height; ++y) { - for (auto x = 0; x != corner.width; ++x) { - auto alpha = static_cast(*mask) + 1; - *frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha); - ++frameInts; - mask += bytesPerPixel; - } - frameInts += frameIntsPerLineAdd; - mask += bytesPerLineAdded; - } -} - -void RoundShadowAnimation::paintShadow(int left, int top, int right, int bottom) { - paintShadowCorner(left, top, _shadow.topLeft); - paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight); - paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight); - paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft); - paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left); - paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right); - paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top); - paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom); -} - -void RoundShadowAnimation::paintShadowCorner(int left, int top, const QImage &image) { - auto imageWidth = image.width(); - auto imageHeight = image.height(); - auto imageInts = reinterpret_cast(image.constBits()); - auto imageIntsPerLine = (image.bytesPerLine() >> 2); - auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth; - if (left < 0) { - auto shift = -base::take(left); - imageWidth -= shift; - imageInts += shift; - } - if (top < 0) { - auto shift = -base::take(top); - imageHeight -= shift; - imageInts += shift * imageIntsPerLine; - } - if (left + imageWidth > _frameWidth) { - imageWidth = _frameWidth - left; - } - if (top + imageHeight > _frameHeight) { - imageHeight = _frameHeight - top; - } - if (imageWidth < 0 || imageHeight < 0) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; - for (auto y = 0; y != imageHeight; ++y) { - for (auto x = 0; x != imageWidth; ++x) { - auto source = *frameInts; - auto shadowAlpha = qMax(_frameAlpha - int(source >> 24), 0); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); - ++frameInts; - ++imageInts; - } - frameInts += frameIntsPerLineAdd; - imageInts += imageIntsPerLineAdded; - } -} - -void RoundShadowAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) { - auto imageWidth = image.width(); - auto imageInts = reinterpret_cast(image.constBits()); - if (left < 0) { - auto shift = -base::take(left); - imageWidth -= shift; - imageInts += shift; - } - if (top < 0) top = 0; - accumulate_min(bottom, _frameHeight); - accumulate_min(imageWidth, _frameWidth - left); - if (imageWidth < 0 || bottom <= top) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; - for (auto y = top; y != bottom; ++y) { - for (auto x = 0; x != imageWidth; ++x) { - auto source = *frameInts; - auto shadowAlpha = _frameAlpha - (source >> 24); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); - ++frameInts; - ++imageInts; - } - frameInts += frameIntsPerLineAdd; - imageInts -= imageWidth; - } -} - -void RoundShadowAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) { - auto imageHeight = image.height(); - auto imageInts = reinterpret_cast(image.constBits()); - auto imageIntsPerLine = (image.bytesPerLine() >> 2); - if (top < 0) { - auto shift = -base::take(top); - imageHeight -= shift; - imageInts += shift * imageIntsPerLine; - } - if (left < 0) left = 0; - accumulate_min(right, _frameWidth); - accumulate_min(imageHeight, _frameHeight - top); - if (imageHeight < 0 || right <= left) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left); - for (auto y = 0; y != imageHeight; ++y) { - auto imagePattern = anim::shifted(*imageInts); - for (auto x = left; x != right; ++x) { - auto source = *frameInts; - auto shadowAlpha = _frameAlpha - (source >> 24); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha); - ++frameInts; - } - frameInts += frameIntsPerLineAdd; - imageInts += imageIntsPerLine; - } -} - -void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { - Expects(!started()); - - const auto pixelRatio = style::DevicePixelRatio(); - _finalImage = PixmapFromImage( - std::move(finalImage).convertToFormat( - QImage::Format_ARGB32_Premultiplied)); - - Assert(!_finalImage.isNull()); - _finalWidth = _finalImage.width(); - _finalHeight = _finalImage.height(); - Assert(!(_finalWidth % pixelRatio)); - Assert(!(_finalHeight % pixelRatio)); - _finalInnerLeft = inner.x(); - _finalInnerTop = inner.y(); - _finalInnerWidth = inner.width(); - _finalInnerHeight = inner.height(); - Assert(!(_finalInnerLeft % pixelRatio)); - Assert(!(_finalInnerTop % pixelRatio)); - Assert(!(_finalInnerWidth % pixelRatio)); - Assert(!(_finalInnerHeight % pixelRatio)); - _finalInnerRight = _finalInnerLeft + _finalInnerWidth; - _finalInnerBottom = _finalInnerTop + _finalInnerHeight; - Assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); - - setStartWidth(); - setStartHeight(); - setStartAlpha(); - setStartFadeTop(); - createFadeMask(); - setWidthDuration(); - setHeightDuration(); - setAlphaDuration(); - if (!_skipShadow) { - setShadow(_st.shadow); - } - - auto checkCorner = [this, inner](Corner &corner) { - if (!corner.valid()) return; - if ((_startWidth >= 0 && _startWidth < _finalWidth) - || (_startHeight >= 0 && _startHeight < _finalHeight)) { - Assert(corner.width <= inner.width()); - Assert(corner.height <= inner.height()); - } - }; - checkCorner(_topLeft); - checkCorner(_topRight); - checkCorner(_bottomLeft); - checkCorner(_bottomRight); -} - -void PanelAnimation::setStartWidth() { - _startWidth = qRound(_st.startWidth * _finalInnerWidth); - if (_startWidth >= 0) Assert(_startWidth <= _finalInnerWidth); -} - -void PanelAnimation::setStartHeight() { - _startHeight = qRound(_st.startHeight * _finalInnerHeight); - if (_startHeight >= 0) Assert(_startHeight <= _finalInnerHeight); -} - -void PanelAnimation::setStartAlpha() { - _startAlpha = qRound(_st.startOpacity * 255); - Assert(_startAlpha >= 0 && _startAlpha < 256); -} - -void PanelAnimation::setStartFadeTop() { - _startFadeTop = qRound(_st.startFadeTop * _finalInnerHeight); -} - -void PanelAnimation::createFadeMask() { - auto resultHeight = qRound(_finalImage.height() * _st.fadeHeight); - if (auto remove = (resultHeight % style::DevicePixelRatio())) { - resultHeight -= remove; - } - auto finalAlpha = qRound(_st.fadeOpacity * 255); - Assert(finalAlpha >= 0 && finalAlpha < 256); - auto result = QImage(style::DevicePixelRatio(), resultHeight, QImage::Format_ARGB32_Premultiplied); - auto ints = reinterpret_cast(result.bits()); - auto intsPerLineAdded = (result.bytesPerLine() >> 2) - style::DevicePixelRatio(); - auto up = (_origin == PanelAnimation::Origin::BottomLeft || _origin == PanelAnimation::Origin::BottomRight); - auto from = up ? resultHeight : 0, to = resultHeight - from, delta = up ? -1 : 1; - auto fadeFirstAlpha = up ? (finalAlpha + 1) : 1; - auto fadeLastAlpha = up ? 1 : (finalAlpha + 1); - _fadeFirst = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeFirstAlpha) >> 8)); - _fadeLast = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeLastAlpha) >> 8)); - for (auto y = from; y != to; y += delta) { - auto alpha = static_cast(finalAlpha * y) / resultHeight; - auto value = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha; - for (auto x = 0; x != style::DevicePixelRatio(); ++x) { - *ints++ = value; - } - ints += intsPerLineAdded; - } - _fadeMask = PixmapFromImage(style::colorizeImage(result, _st.fadeBg)); - _fadeHeight = _fadeMask.height(); -} - -void PanelAnimation::setSkipShadow(bool skipShadow) { - Assert(!started()); - _skipShadow = skipShadow; -} - -void PanelAnimation::setWidthDuration() { - _widthDuration = _st.widthDuration; - Assert(_widthDuration >= 0.); - Assert(_widthDuration <= 1.); -} - -void PanelAnimation::setHeightDuration() { - Assert(!started()); - _heightDuration = _st.heightDuration; - Assert(_heightDuration >= 0.); - Assert(_heightDuration <= 1.); -} - -void PanelAnimation::setAlphaDuration() { - Assert(!started()); - _alphaDuration = _st.opacityDuration; - Assert(_alphaDuration >= 0.); - Assert(_alphaDuration <= 1.); -} - -void PanelAnimation::start() { - Assert(!_finalImage.isNull()); - RoundShadowAnimation::start(_finalWidth, _finalHeight, _finalImage.devicePixelRatio()); - auto checkCorner = [this](const Corner &corner) { - if (!corner.valid()) return; - if (_startWidth >= 0) Assert(corner.width <= _startWidth); - if (_startHeight >= 0) Assert(corner.height <= _startHeight); - Assert(corner.width <= _finalInnerWidth); - Assert(corner.height <= _finalInnerHeight); - }; - checkCorner(_topLeft); - checkCorner(_topRight); - checkCorner(_bottomLeft); - checkCorner(_bottomRight); -} - -void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity) { - Assert(started()); - Assert(dt >= 0.); - - const auto pixelRatio = style::DevicePixelRatio(); - - auto &transition = anim::easeOutCirc; - if (dt < _alphaDuration) opacity *= transition(1., dt / _alphaDuration); - _frameAlpha = anim::interpolate(1, 256, opacity); - - auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration)); - auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration)); - if (auto decrease = (frameWidth % pixelRatio)) { - frameWidth -= decrease; - } - if (auto decrease = (frameHeight % pixelRatio)) { - frameHeight -= decrease; - } - auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth); - auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight); - auto frameRight = frameLeft + frameWidth; - auto frameBottom = frameTop + frameHeight; - - auto fadeTop = (_fadeHeight > 0) ? std::clamp(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight; - if (auto decrease = (fadeTop % pixelRatio)) { - fadeTop -= decrease; - } - auto fadeBottom = (fadeTop < frameHeight) ? std::min(fadeTop + _fadeHeight, frameHeight) : frameHeight; - auto fadeSkipLines = 0; - if (_origin == Origin::BottomLeft || _origin == Origin::BottomRight) { - fadeTop = frameHeight - fadeTop; - fadeBottom = frameHeight - fadeBottom; - qSwap(fadeTop, fadeBottom); - fadeSkipLines = fadeTop + _fadeHeight - fadeBottom; - } - fadeTop += frameTop; - fadeBottom += frameTop; - - if (opacity < 1.) { - _frame.fill(Qt::transparent); - } - { - QPainter p(&_frame); - p.setOpacity(opacity); - auto painterFrameLeft = frameLeft / pixelRatio; - auto painterFrameTop = frameTop / pixelRatio; - auto painterFadeBottom = fadeBottom / pixelRatio; - p.drawPixmap(painterFrameLeft, painterFrameTop, _finalImage, frameLeft, frameTop, frameWidth, frameHeight); - if (_fadeHeight) { - if (frameTop != fadeTop) { - p.fillRect(painterFrameLeft, painterFrameTop, frameWidth, fadeTop - frameTop, _fadeFirst); - } - if (fadeTop != fadeBottom) { - auto painterFadeTop = fadeTop / pixelRatio; - auto painterFrameWidth = frameWidth / pixelRatio; - auto painterFrameHeight = frameHeight / pixelRatio; - p.drawPixmap(painterFrameLeft, painterFadeTop, painterFrameWidth, painterFadeBottom - painterFadeTop, _fadeMask, 0, fadeSkipLines, pixelRatio, fadeBottom - fadeTop); - } - if (fadeBottom != frameBottom) { - p.fillRect(painterFrameLeft, painterFadeBottom, frameWidth, frameBottom - fadeBottom, _fadeLast); - } - } - } - auto frameInts = _frameInts + frameLeft + frameTop * _frameIntsPerLine; - auto frameIntsPerLineAdd = (_finalWidth - frameWidth) + _frameIntsPerLineAdded; - - // Draw corners - paintCorner(_topLeft, frameLeft, frameTop); - paintCorner(_topRight, frameRight - _topRight.width, frameTop); - paintCorner(_bottomLeft, frameLeft, frameBottom - _bottomLeft.height); - paintCorner(_bottomRight, frameRight - _bottomRight.width, frameBottom - _bottomRight.height); - - // Draw shadow upon the transparent - auto outerLeft = frameLeft; - auto outerTop = frameTop; - auto outerRight = frameRight; - auto outerBottom = frameBottom; - if (_shadow.valid()) { - outerLeft -= _shadow.extend.left(); - outerTop -= _shadow.extend.top(); - outerRight += _shadow.extend.right(); - outerBottom += _shadow.extend.bottom(); - } - if (pixelRatio > 1) { - if (auto skipLeft = (outerLeft % pixelRatio)) { - outerLeft -= skipLeft; - } - if (auto skipTop = (outerTop % pixelRatio)) { - outerTop -= skipTop; - } - if (auto skipRight = (outerRight % pixelRatio)) { - outerRight += (pixelRatio - skipRight); - } - if (auto skipBottom = (outerBottom % pixelRatio)) { - outerBottom += (pixelRatio - skipBottom); - } - } - - if (opacity == 1.) { - // Fill above the frame top with transparent. - auto fillTopInts = (_frameInts + outerTop * _frameIntsPerLine + outerLeft); - auto fillWidth = (outerRight - outerLeft) * sizeof(uint32); - for (auto fillTop = frameTop - outerTop; fillTop != 0; --fillTop) { - memset(fillTopInts, 0, fillWidth); - fillTopInts += _frameIntsPerLine; - } - - // Fill to the left and to the right of the frame with transparent. - auto fillLeft = (frameLeft - outerLeft) * sizeof(uint32); - auto fillRight = (outerRight - frameRight) * sizeof(uint32); - if (fillLeft || fillRight) { - auto fillInts = _frameInts + frameTop * _frameIntsPerLine; - for (auto y = frameTop; y != frameBottom; ++y) { - memset(fillInts + outerLeft, 0, fillLeft); - memset(fillInts + frameRight, 0, fillRight); - fillInts += _frameIntsPerLine; - } - } - - // Fill below the frame bottom with transparent. - auto fillBottomInts = (_frameInts + frameBottom * _frameIntsPerLine + outerLeft); - for (auto fillBottom = outerBottom - frameBottom; fillBottom != 0; --fillBottom) { - memset(fillBottomInts, 0, fillWidth); - fillBottomInts += _frameIntsPerLine; - } - } - - if (_shadow.valid()) { - paintShadow(outerLeft, outerTop, outerRight, outerBottom); - } - - // Debug - //frameInts = _frameInts; - //auto pattern = anim::shifted((static_cast(0xFF) << 24) | (static_cast(0xFF) << 16) | (static_cast(0xFF) << 8) | static_cast(0xFF)); - //for (auto y = 0; y != _finalHeight; ++y) { - // for (auto x = 0; x != _finalWidth; ++x) { - // auto source = *frameInts; - // auto sourceAlpha = (source >> 24); - // *frameInts = anim::unshifted(anim::shifted(source) * 256 + pattern * (256 - sourceAlpha)); - // ++frameInts; - // } - // frameInts += _frameIntsPerLineAdded; - //} - - p.drawImage(style::rtlpoint(x + (outerLeft / pixelRatio), y + (outerTop / pixelRatio), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop)); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.h b/Telegram/SourceFiles/ui/effects/panel_animation.h deleted file mode 100644 index 73c91e096..000000000 --- a/Telegram/SourceFiles/ui/effects/panel_animation.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" - -namespace Ui { - -class RoundShadowAnimation { -public: - void setCornerMasks(const std::array &corners); - -protected: - void start(int frameWidth, int frameHeight, float64 devicePixelRatio); - void setShadow(const style::Shadow &st); - - bool started() const { - return !_frame.isNull(); - } - - struct Corner { - QImage image; - int width = 0; - int height = 0; - const uchar *bytes = nullptr; - int bytesPerPixel = 0; - int bytesPerLineAdded = 0; - - bool valid() const { - return !image.isNull(); - } - }; - void setCornerMask(Corner &corner, const QImage &image); - void paintCorner(Corner &corner, int left, int top); - - struct Shadow { - style::margins extend; - QImage left, topLeft, top, topRight, right, bottomRight, bottom, bottomLeft; - - bool valid() const { - return !left.isNull(); - } - }; - QImage cloneImage(const style::icon &source); - void paintShadow(int left, int top, int right, int bottom); - void paintShadowCorner(int left, int top, const QImage &image); - void paintShadowVertical(int left, int top, int bottom, const QImage &image); - void paintShadowHorizontal(int left, int right, int top, const QImage &image); - - Shadow _shadow; - - Corner _topLeft; - Corner _topRight; - Corner _bottomLeft; - Corner _bottomRight; - - QImage _frame; - uint32 *_frameInts = nullptr; - int _frameWidth = 0; - int _frameHeight = 0; - int _frameAlpha = 0; // recounted each getFrame() - int _frameIntsPerLine = 0; - int _frameIntsPerLineAdded = 0; - -}; - -class PanelAnimation : public RoundShadowAnimation { -public: - enum class Origin { - TopLeft, - TopRight, - BottomLeft, - BottomRight, - }; - PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) { - } - - void setFinalImage(QImage &&finalImage, QRect inner); - void setSkipShadow(bool skipShadow); - - void start(); - void paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity); - -private: - void setStartWidth(); - void setStartHeight(); - void setStartAlpha(); - void setStartFadeTop(); - void createFadeMask(); - void setWidthDuration(); - void setHeightDuration(); - void setAlphaDuration(); - - const style::PanelAnimation &_st; - Origin _origin = Origin::TopLeft; - - QPixmap _finalImage; - int _finalWidth = 0; - int _finalHeight = 0; - int _finalInnerLeft = 0; - int _finalInnerTop = 0; - int _finalInnerRight = 0; - int _finalInnerBottom = 0; - int _finalInnerWidth = 0; - int _finalInnerHeight = 0; - - bool _skipShadow = false; - int _startWidth = -1; - int _startHeight = -1; - int _startAlpha = 0; - - int _startFadeTop = 0; - QPixmap _fadeMask; - int _fadeHeight = 0; - QBrush _fadeFirst, _fadeLast; - - float64 _widthDuration = 1.; - float64 _heightDuration = 1.; - float64 _alphaDuration = 1.; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp deleted file mode 100644 index d823a5a53..000000000 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* -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 "ui/effects/ripple_animation.h" - -#include "ui/effects/animations.h" -#include "ui/painter.h" -#include "ui/ui_utility.h" - -namespace Ui { - -class RippleAnimation::Ripple { -public: - Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn update); - Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn update); - - void paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride); - - void stop(); - void unstop(); - void finish(); - void clearCache(); - bool finished() const { - return _hiding && !_hide.animating(); - } - -private: - const style::RippleAnimation &_st; - Fn _update; - - QPoint _origin; - int _radiusFrom = 0; - int _radiusTo = 0; - - bool _hiding = false; - Ui::Animations::Simple _show; - Ui::Animations::Simple _hide; - QPixmap _cache; - QImage _frame; - -}; - -RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn update) -: _st(st) -, _update(update) -, _origin(origin) -, _radiusFrom(startRadius) -, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) { - _frame.setDevicePixelRatio(mask.devicePixelRatio()); - - const auto pixelRatio = style::DevicePixelRatio(); - QPoint points[] = { - { 0, 0 }, - { _frame.width() / pixelRatio, 0 }, - { _frame.width() / pixelRatio, _frame.height() / pixelRatio }, - { 0, _frame.height() / pixelRatio }, - }; - for (auto point : points) { - accumulate_max(_radiusTo, style::point::dotProduct(_origin - point, _origin - point)); - } - _radiusTo = qRound(sqrt(_radiusTo)); - - _show.start(_update, 0., 1., _st.showDuration, anim::easeOutQuint); -} - -RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn update) -: _st(st) -, _update(update) -, _origin( - mask.width() / (2 * style::DevicePixelRatio()), - mask.height() / (2 * style::DevicePixelRatio())) -, _radiusFrom(mask.width() + mask.height()) -, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) { - _frame.setDevicePixelRatio(mask.devicePixelRatio()); - _radiusTo = _radiusFrom; - _hide.start(_update, 0., 1., _st.hideDuration); -} - -void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride) { - auto opacity = _hide.value(_hiding ? 0. : 1.); - if (opacity == 0.) { - return; - } - - if (_cache.isNull() || colorOverride != nullptr) { - auto radius = anim::interpolate(_radiusFrom, _radiusTo, _show.value(1.)); - _frame.fill(Qt::transparent); - { - QPainter p(&_frame); - p.setPen(Qt::NoPen); - if (colorOverride) { - p.setBrush(*colorOverride); - } else { - p.setBrush(_st.color); - } - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(_origin, radius, radius); - } - p.setCompositionMode(QPainter::CompositionMode_DestinationIn); - p.drawPixmap(0, 0, mask); - } - if (radius == _radiusTo && colorOverride == nullptr) { - _cache = PixmapFromImage(std::move(_frame)); - } - } - auto saved = p.opacity(); - if (opacity != 1.) p.setOpacity(saved * opacity); - if (_cache.isNull()) { - p.drawImage(0, 0, _frame); - } else { - p.drawPixmap(0, 0, _cache); - } - if (opacity != 1.) p.setOpacity(saved); -} - -void RippleAnimation::Ripple::stop() { - _hiding = true; - _hide.start(_update, 1., 0., _st.hideDuration); -} - -void RippleAnimation::Ripple::unstop() { - if (_hiding) { - if (_hide.animating()) { - _hide.start(_update, 0., 1., _st.hideDuration); - } - _hiding = false; - } -} - -void RippleAnimation::Ripple::finish() { - if (_update) { - _update(); - } - _show.stop(); - _hide.stop(); -} - -void RippleAnimation::Ripple::clearCache() { - _cache = QPixmap(); -} - -RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn callback) -: _st(st) -, _mask(PixmapFromImage(std::move(mask))) -, _update(callback) { -} - - -void RippleAnimation::add(QPoint origin, int startRadius) { - lastStop(); - _ripples.push_back(std::make_unique(_st, origin, startRadius, _mask, _update)); -} - -void RippleAnimation::addFading() { - lastStop(); - _ripples.push_back(std::make_unique(_st, _mask, _update)); -} - -void RippleAnimation::lastStop() { - if (!_ripples.empty()) { - _ripples.back()->stop(); - } -} - -void RippleAnimation::lastUnstop() { - if (!_ripples.empty()) { - _ripples.back()->unstop(); - } -} - -void RippleAnimation::lastFinish() { - if (!_ripples.empty()) { - _ripples.back()->finish(); - } -} - -void RippleAnimation::forceRepaint() { - for (const auto &ripple : _ripples) { - ripple->clearCache(); - } - if (_update) { - _update(); - } -} - -void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride) { - if (_ripples.empty()) { - return; - } - - if (style::RightToLeft()) { - x = outerWidth - x - (_mask.width() / style::DevicePixelRatio()); - } - p.translate(x, y); - for (const auto &ripple : _ripples) { - ripple->paint(p, _mask, colorOverride); - } - p.translate(-x, -y); - clearFinished(); -} - -QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn drawer) { - auto result = QImage(size * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(style::DevicePixelRatio()); - result.fill(filled ? QColor(255, 255, 255) : Qt::transparent); - if (drawer) { - Painter p(&result); - PainterHighQualityEnabler hq(p); - - p.setPen(Qt::NoPen); - p.setBrush(QColor(255, 255, 255)); - drawer(p); - } - return result; -} - -QImage RippleAnimation::rectMask(QSize size) { - return maskByDrawer(size, true, Fn()); -} - -QImage RippleAnimation::roundRectMask(QSize size, int radius) { - return maskByDrawer(size, false, [size, radius](QPainter &p) { - p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius); - }); -} - -QImage RippleAnimation::ellipseMask(QSize size) { - return maskByDrawer(size, false, [size](QPainter &p) { - p.drawEllipse(0, 0, size.width(), size.height()); - }); -} - -void RippleAnimation::clearFinished() { - while (!_ripples.empty() && _ripples.front()->finished()) { - _ripples.pop_front(); - } -} - -void RippleAnimation::clear() { - _ripples.clear(); -} - -RippleAnimation::~RippleAnimation() = default; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.h b/Telegram/SourceFiles/ui/effects/ripple_animation.h deleted file mode 100644 index f7e0403e2..000000000 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" - -#include - -namespace Ui { - -class RippleAnimation { -public: - // White upon transparent mask, like colorizeImage(black-white-mask, white). - RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn update); - - void add(QPoint origin, int startRadius = 0); - void addFading(); - void lastStop(); - void lastUnstop(); - void lastFinish(); - void forceRepaint(); - - void paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride = nullptr); - - bool empty() const { - return _ripples.empty(); - } - - static QImage maskByDrawer(QSize size, bool filled, Fn drawer); - static QImage rectMask(QSize size); - static QImage roundRectMask(QSize size, int radius); - static QImage ellipseMask(QSize size); - - ~RippleAnimation(); - -private: - void clear(); - void clearFinished(); - - const style::RippleAnimation &_st; - QPixmap _mask; - Fn _update; - - class Ripple; - std::deque> _ripples; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/emoji_config.cpp b/Telegram/SourceFiles/ui/emoji_config.cpp deleted file mode 100644 index 0252669e3..000000000 --- a/Telegram/SourceFiles/ui/emoji_config.cpp +++ /dev/null @@ -1,873 +0,0 @@ -/* -WARNING! All changes made in this file will be lost! -Created from 'empty' by 'codegen_emoji' - -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 "emoji_config.h" - -#include "chat_helpers/emoji_suggestions_helper.h" -#include "base/bytes.h" -#include "base/openssl_help.h" -#include "base/parse_helper.h" -#include "ui/style/style_core.h" -#include "ui/painter.h" -#include "ui/ui_utility.h" -#include "ui/ui_log.h" -#include "styles/style_basic.h" - -#include -#include -#include -#include - -#include - -namespace Ui { -namespace Emoji { -namespace { - -constexpr auto kUniversalSize = 72; -constexpr auto kImagesPerRow = 32; -constexpr auto kImageRowsPerSprite = 16; - -constexpr auto kSetVersion = uint32(1); -constexpr auto kCacheVersion = uint32(3); -constexpr auto kMaxId = uint32(1 << 8); - -constexpr auto kScaleForTouchBar = 150; - -const auto kSets = { - Set{ 0, 0, 0, "Mac", ":/gui/emoji/set0_preview.webp" }, - Set{ 1, 246, 7'336'383, "Android", ":/gui/emoji/set1_preview.webp" }, - Set{ 2, 206, 5'038'738, "Twemoji", ":/gui/emoji/set2_preview.webp" }, - Set{ 3, 238, 6'992'260, "EmojiOne", ":/gui/emoji/set3_preview.webp" }, -}; - -// Right now we can't allow users of Ui::Emoji to create custom sizes. -// Any Instance::Instance() can invalidate Universal.id() and sprites. -// So all Instance::Instance() should happen before async generations. -class Instance { -public: - explicit Instance(int size); - - bool cached() const; - void draw(QPainter &p, EmojiPtr emoji, int x, int y); - -private: - void readCache(); - void generateCache(); - void checkUniversalImages(); - void pushSprite(QImage &&data); - - int _id = 0; - int _size = 0; - std::vector _sprites; - base::binary_guard _generating; - -}; - -auto SizeNormal = -1; -auto SizeLarge = -1; -auto SpritesCount = -1; - -auto InstanceNormal = std::unique_ptr(); -auto InstanceLarge = std::unique_ptr(); -auto Universal = std::shared_ptr(); -auto CanClearUniversal = false; -auto Updates = rpl::event_stream<>(); - -#if defined Q_OS_MAC && !defined OS_MAC_OLD -auto TouchbarSize = -1; -auto TouchbarInstance = std::unique_ptr(); -auto TouchbarEmoji = (Instance*)nullptr; -#endif - -auto MainEmojiMap = std::map(); -auto OtherEmojiMap = base::flat_map>(); - -int RowsCount(int index) { - if (index + 1 < SpritesCount) { - return kImageRowsPerSprite; - } - const auto count = internal::FullCount() - - (index * kImagesPerRow * kImageRowsPerSprite); - return (count / kImagesPerRow) - + ((count % kImagesPerRow) ? 1 : 0); -} - -QString CacheFileNameMask(int size) { - return "cache_" + QString::number(size) + '_'; -} - -QString CacheFilePath(int size, int index) { - return internal::CacheFileFolder() - + '/' - + CacheFileNameMask(size) - + QString::number(index); -} - -QString CurrentSettingPath() { - return internal::CacheFileFolder() + "/current"; -} - -bool IsValidSetId(int id) { - return (id == 0) || (id > 0 && id < kMaxId); -} - -uint32 ComputeVersion(int id) { - Expects(IsValidSetId(id)); - - static_assert(kCacheVersion > 0 && kCacheVersion < (1 << 16)); - static_assert(kSetVersion > 0 && kSetVersion < (1 << 8)); - - auto result = uint32(kCacheVersion); - if (!id) { - return result; - } - result |= (uint32(id) << 24) | (uint32(kSetVersion) << 16); - return result; -} - -int ReadCurrentSetId() { - const auto path = CurrentSettingPath(); - auto file = QFile(path); - if (!file.open(QIODevice::ReadOnly)) { - return 0; - } - auto stream = QDataStream(&file); - stream.setVersion(QDataStream::Qt_5_1); - auto id = qint32(0); - stream >> id; - return (stream.status() == QDataStream::Ok && IsValidSetId(id)) - ? id - : 0; -} - -void SwitchToSetPrepared(int id, std::shared_ptr images) { - auto setting = QFile(CurrentSettingPath()); - if (!id) { - setting.remove(); - } else if (setting.open(QIODevice::WriteOnly)) { - auto stream = QDataStream(&setting); - stream.setVersion(QDataStream::Qt_5_1); - stream << qint32(id); - } - Universal = std::move(images); - CanClearUniversal = false; - MainEmojiMap.clear(); - OtherEmojiMap.clear(); - Updates.fire({}); -} - -void ClearCurrentSetIdSync() { - Expects(Universal != nullptr); - - const auto id = Universal->id(); - if (!id) { - return; - } - QDir(internal::SetDataPath(id)).removeRecursively(); - - const auto newId = 0; - auto universal = std::make_shared(newId); - universal->ensureLoaded(); - SwitchToSetPrepared(newId, std::move(universal)); -} - -void SaveToFile(int id, const QImage &image, int size, int index) { - Expects(image.bytesPerLine() == image.width() * 4); - - QFile f(CacheFilePath(size, index)); - if (!f.open(QIODevice::WriteOnly)) { - if (!QDir::current().mkpath(internal::CacheFileFolder()) - || !f.open(QIODevice::WriteOnly)) { - UI_LOG(("App Error: Could not open emoji cache '%1' for size %2_%3" - ).arg(f.fileName() - ).arg(size - ).arg(index)); - return; - } - } - const auto write = [&](bytes::const_span data) { - return f.write( - reinterpret_cast(data.data()), - data.size() - ) == data.size(); - }; - const uint32 header[] = { - uint32(ComputeVersion(id)), - uint32(size), - uint32(image.width()), - uint32(image.height()), - }; - const auto data = bytes::const_span( - reinterpret_cast(image.bits()), - image.width() * image.height() * 4); - if (!write(bytes::make_span(header)) - || !write(data) - || !write(openssl::Sha256(bytes::make_span(header), data)) - || false) { - UI_LOG(("App Error: Could not write emoji cache '%1' for size %2" - ).arg(f.fileName() - ).arg(size)); - } -} - -QImage LoadFromFile(int id, int size, int index) { - const auto rows = RowsCount(index); - const auto width = kImagesPerRow * size; - const auto height = rows * size; - const auto fileSize = 4 * sizeof(uint32) - + (width * height * 4) - + openssl::kSha256Size; - QFile f(CacheFilePath(size, index)); - if (!f.exists() - || f.size() != fileSize - || !f.open(QIODevice::ReadOnly)) { - return QImage(); - } - const auto read = [&](bytes::span data) { - return f.read( - reinterpret_cast(data.data()), - data.size() - ) == data.size(); - }; - uint32 header[4] = { 0 }; - if (!read(bytes::make_span(header)) - || header[0] != ComputeVersion(id) - || header[1] != size - || header[2] != width - || header[3] != height) { - return QImage(); - } - auto result = QImage( - width, - height, - QImage::Format_ARGB32_Premultiplied); - Assert(result.bytesPerLine() == width * 4); - const auto data = bytes::make_span( - reinterpret_cast(result.bits()), - width * height * 4); - auto signature = bytes::vector(openssl::kSha256Size); - if (!read(data) - || !read(signature) - //|| (bytes::compare( - // signature, - // openssl::Sha256(bytes::make_span(header), data)) != 0) - || false) { - return QImage(); - } - crl::async([=, signature = std::move(signature)] { - // This should not happen (invalid signature), - // so we delay this check and fix only the next launch. - const auto data = bytes::make_span( - reinterpret_cast(result.bits()), - width * height * 4); - const auto result = bytes::compare( - signature, - openssl::Sha256(bytes::make_span(header), data)); - if (result != 0) { - QFile(CacheFilePath(size, index)).remove(); - } - }); - return result; -} - -std::vector LoadSprites(int id) { - Expects(IsValidSetId(id)); - Expects(SpritesCount > 0); - - auto result = std::vector(); - const auto folder = (id != 0) - ? internal::SetDataPath(id) + '/' - : QStringLiteral(":/gui/emoji/"); - const auto base = folder + "emoji_"; - return ranges::view::ints( - 0, - SpritesCount - ) | ranges::view::transform([&](int index) { - return base + QString::number(index + 1) + ".webp"; - }) | ranges::view::transform([](const QString &path) { - return QImage(path, "WEBP").convertToFormat( - QImage::Format_ARGB32_Premultiplied); - }) | ranges::to_vector; -} - -bool ValidateConfig(int id) { - Expects(IsValidSetId(id)); - - if (!id) { - return true; - } - constexpr auto kSizeLimit = 65536; - auto config = QFile(internal::SetDataPath(id) + "/config.json"); - if (!config.open(QIODevice::ReadOnly) || config.size() > kSizeLimit) { - return false; - } - auto error = QJsonParseError{ 0, QJsonParseError::NoError }; - const auto document = QJsonDocument::fromJson( - base::parse::stripComments(config.readAll()), - &error); - config.close(); - if (error.error != QJsonParseError::NoError) { - return false; - } - if (document.object()["id"].toInt() != id - || document.object()["version"].toInt() != kSetVersion) { - return false; - } - return true; -} - -std::vector LoadAndValidateSprites(int id) { - Expects(IsValidSetId(id)); - Expects(SpritesCount > 0); - - if (!ValidateConfig(id)) { - return {}; - } - auto result = LoadSprites(id); - const auto sizes = ranges::view::ints( - 0, - SpritesCount - ) | ranges::view::transform([](int index) { - return QSize( - kImagesPerRow * kUniversalSize, - RowsCount(index) * kUniversalSize); - }); - const auto good = ranges::view::zip_with( - [](const QImage &data, QSize size) { return data.size() == size; }, - result, - sizes); - if (ranges::find(good, false) != end(good)) { - return {}; - } - return result; -} - -void ClearUniversalChecked() { - Expects(InstanceNormal != nullptr && InstanceLarge != nullptr); - - if (CanClearUniversal - && Universal - && InstanceNormal->cached() - && InstanceLarge->cached()) { - Universal->clear(); - } -} - -} // namespace - -namespace internal { - -QString CacheFileFolder() { - return Integration::Instance().emojiCacheFolder(); -} - -QString SetDataPath(int id) { - Expects(IsValidSetId(id) && id != 0); - - return CacheFileFolder() + "/set" + QString::number(id); -} - -} // namespace internal - -UniversalImages::UniversalImages(int id) : _id(id) { - Expects(IsValidSetId(id)); -} - -int UniversalImages::id() const { - return _id; -} - -bool UniversalImages::ensureLoaded() { - Expects(SpritesCount > 0); - - if (!_sprites.empty()) { - return true; - } - _sprites = LoadAndValidateSprites(_id); - return !_sprites.empty(); -} - -void UniversalImages::clear() { - _sprites.clear(); -} - -void UniversalImages::draw( - QPainter &p, - EmojiPtr emoji, - int size, - int x, - int y) const { - Expects(emoji->sprite() < _sprites.size()); - - const auto large = kUniversalSize; - const auto &original = _sprites[emoji->sprite()]; - const auto data = original.bits(); - const auto stride = original.bytesPerLine(); - const auto format = original.format(); - const auto row = emoji->row(); - const auto column = emoji->column(); - auto single = QImage( - data + (row * kImagesPerRow * large + column) * large * 4, - large, - large, - stride, - format - ).scaled( - size, - size, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - single.setDevicePixelRatio(p.device()->devicePixelRatio()); - p.drawImage(x, y, single); -} - -QImage UniversalImages::generate(int size, int index) const { - Expects(size > 0); - Expects(index < _sprites.size()); - - const auto rows = RowsCount(index); - const auto large = kUniversalSize; - const auto &original = _sprites[index]; - const auto data = original.bits(); - const auto stride = original.bytesPerLine(); - const auto format = original.format(); - auto result = QImage( - size * kImagesPerRow, - size * rows, - QImage::Format_ARGB32_Premultiplied); - result.fill(Qt::transparent); - { - QPainter p(&result); - for (auto y = 0; y != rows; ++y) { - for (auto x = 0; x != kImagesPerRow; ++x) { - const auto single = QImage( - data + (y * kImagesPerRow * large + x) * large * 4, - large, - large, - stride, - format - ).scaled( - size, - size, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - p.drawImage( - x * size, - y * size, - single); - } - } - } - SaveToFile(_id, result, size, index); - return result; -} - -void Init() { - internal::Init(); - - const auto count = internal::FullCount(); - const auto persprite = kImagesPerRow * kImageRowsPerSprite; - SpritesCount = (count / persprite) + ((count % persprite) ? 1 : 0); - - SizeNormal = style::ConvertScale(18, style::Scale() * style::DevicePixelRatio()); - SizeLarge = int(style::ConvertScale(18 * 4 / 3., style::Scale() * style::DevicePixelRatio())); - Universal = std::make_shared(ReadCurrentSetId()); - CanClearUniversal = false; - - InstanceNormal = std::make_unique(SizeNormal); - InstanceLarge = std::make_unique(SizeLarge); - -#if defined Q_OS_MAC && !defined OS_MAC_OLD - if (style::Scale() != kScaleForTouchBar) { - TouchbarSize = int(style::ConvertScale(18 * 4 / 3., - kScaleForTouchBar * style::DevicePixelRatio())); - TouchbarInstance = std::make_unique(TouchbarSize); - TouchbarEmoji = TouchbarInstance.get(); - } else { - TouchbarEmoji = InstanceLarge.get(); - } -#endif -} - -void Clear() { - MainEmojiMap.clear(); - OtherEmojiMap.clear(); - - InstanceNormal = nullptr; - InstanceLarge = nullptr; -#if defined Q_OS_MAC && !defined OS_MAC_OLD - TouchbarInstance = nullptr; - TouchbarEmoji = nullptr; -#endif -} - -void ClearIrrelevantCache() { - Expects(SizeNormal > 0); - Expects(SizeLarge > 0); - - crl::async([] { - const auto folder = internal::CacheFileFolder(); - const auto list = QDir(folder).entryList(QDir::Files); - const auto good1 = CacheFileNameMask(SizeNormal); - const auto good2 = CacheFileNameMask(SizeLarge); - const auto good3full = CurrentSettingPath(); - for (const auto &name : list) { - if (!name.startsWith(good1) && !name.startsWith(good2)) { - const auto full = folder + '/' + name; - if (full != good3full) { - QFile(full).remove(); - } - } - } - }); -} - -std::vector Sets() { - return kSets | ranges::to_vector; -} - -int CurrentSetId() { - Expects(Universal != nullptr); - - return Universal->id(); -} - -void SwitchToSet(int id, Fn callback) { - Expects(IsValidSetId(id)); - - if (Universal && Universal->id() == id) { - callback(true); - return; - } - crl::async([=] { - auto universal = std::make_shared(id); - if (!universal->ensureLoaded()) { - crl::on_main([=] { - callback(false); - }); - } else { - crl::on_main([=, universal = std::move(universal)]() mutable { - SwitchToSetPrepared(id, std::move(universal)); - callback(true); - }); - } - }); -} - -bool SetIsReady(int id) { - Expects(IsValidSetId(id)); - - if (!id) { - return true; - } - const auto folder = internal::SetDataPath(id) + '/'; - auto names = ranges::view::ints( - 0, - SpritesCount + 1 - ) | ranges::view::transform([](int index) { - return index - ? "emoji_" + QString::number(index) + ".webp" - : QString("config.json"); - }); - const auto bad = ranges::find_if(names, [&](const QString &name) { - return !QFile(folder + name).exists(); - }); - return (bad == names.end()); -} - -rpl::producer<> Updated() { - return Updates.events(); -} - -int GetSizeNormal() { - Expects(SizeNormal > 0); - - return SizeNormal; -} - -int GetSizeLarge() { - Expects(SizeLarge > 0); - - return SizeLarge; -} - -#if defined Q_OS_MAC && !defined OS_MAC_OLD -int GetSizeTouchbar() { - return (style::Scale() == kScaleForTouchBar) - ? GetSizeLarge() - : TouchbarSize; -} -#endif - -int One::variantsCount() const { - return hasVariants() ? 5 : 0; -} - -int One::variantIndex(EmojiPtr variant) const { - return (variant - original()); -} - -EmojiPtr One::variant(int index) const { - return (index >= 0 && index <= variantsCount()) ? (original() + index) : this; -} - -QString IdFromOldKey(uint64 oldKey) { - auto code = uint32(oldKey >> 32); - auto code2 = uint32(oldKey & 0xFFFFFFFFLLU); - if (!code && code2) { - code = base::take(code2); - } - if ((code & 0xFFFF0000U) != 0xFFFF0000U) { // code and code2 contain the whole id - auto result = QString(); - result.reserve(4); - auto addCode = [&result](uint32 code) { - if (auto high = (code >> 16)) { - result.append(QChar(static_cast(high & 0xFFFFU))); - } - result.append(QChar(static_cast(code & 0xFFFFU))); - }; - addCode(code); - if (code2) addCode(code2); - return result; - } - - // old sequence - auto sequenceIndex = int(code & 0xFFFFU); - switch (sequenceIndex) { - case 0: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 1: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 2: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa6\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 3: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 4: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 5: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 6: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 7: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa6\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 8: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 9: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 10: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 11: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 12: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa6\xe2\x80\x8d\xf0\x9f\x91\xa6"); - case 13: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa7"); - case 14: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xe2\x9d\xa4\xef\xb8\x8f\xe2\x80\x8d\xf0\x9f\x91\xa9"); - case 15: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xe2\x9d\xa4\xef\xb8\x8f\xe2\x80\x8d\xf0\x9f\x91\xa8"); - case 16: return QString::fromUtf8("\xf0\x9f\x91\xa9\xe2\x80\x8d\xe2\x9d\xa4\xef\xb8\x8f\xe2\x80\x8d\xf0\x9f\x92\x8b\xe2\x80\x8d\xf0\x9f\x91\xa9"); - case 17: return QString::fromUtf8("\xf0\x9f\x91\xa8\xe2\x80\x8d\xe2\x9d\xa4\xef\xb8\x8f\xe2\x80\x8d\xf0\x9f\x92\x8b\xe2\x80\x8d\xf0\x9f\x91\xa8"); - case 18: return QString::fromUtf8("\xf0\x9f\x91\x81\xe2\x80\x8d\xf0\x9f\x97\xa8"); - } - return QString(); -} - -QVector GetDefaultRecent() { - const auto defaultRecent = { - 0xD83DDE02LLU, - 0xD83DDE18LLU, - 0x2764LLU, - 0xD83DDE0DLLU, - 0xD83DDE0ALLU, - 0xD83DDE01LLU, - 0xD83DDC4DLLU, - 0x263ALLU, - 0xD83DDE14LLU, - 0xD83DDE04LLU, - 0xD83DDE2DLLU, - 0xD83DDC8BLLU, - 0xD83DDE12LLU, - 0xD83DDE33LLU, - 0xD83DDE1CLLU, - 0xD83DDE48LLU, - 0xD83DDE09LLU, - 0xD83DDE03LLU, - 0xD83DDE22LLU, - 0xD83DDE1DLLU, - 0xD83DDE31LLU, - 0xD83DDE21LLU, - 0xD83DDE0FLLU, - 0xD83DDE1ELLU, - 0xD83DDE05LLU, - 0xD83DDE1ALLU, - 0xD83DDE4ALLU, - 0xD83DDE0CLLU, - 0xD83DDE00LLU, - 0xD83DDE0BLLU, - 0xD83DDE06LLU, - 0xD83DDC4CLLU, - 0xD83DDE10LLU, - 0xD83DDE15LLU, - }; - auto result = QVector(); - for (const auto oldKey : defaultRecent) { - if (const auto emoji = FromOldKey(oldKey)) { - result.push_back(emoji); - } - } - return result; -} - -const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) { - auto &map = (fontHeight == st::normalFont->height * style::DevicePixelRatio()) - ? MainEmojiMap - : OtherEmojiMap[fontHeight]; - auto i = map.find(emoji->index()); - if (i != end(map)) { - return i->second; - } - auto image = QImage( - SizeNormal + st::emojiPadding * 2, - fontHeight, - QImage::Format_ARGB32_Premultiplied); - image.setDevicePixelRatio(style::DevicePixelRatio()); - image.fill(Qt::transparent); - { - QPainter p(&image); - PainterHighQualityEnabler hq(p); - Draw( - p, - emoji, - SizeNormal, - st::emojiPadding * style::DevicePixelRatio(), - (fontHeight - SizeNormal) / 2); - } - return map.emplace( - emoji->index(), - PixmapFromImage(std::move(image)) - ).first->second; -} - -void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) { -#if defined Q_OS_MAC && !defined OS_MAC_OLD - const auto s = (style::Scale() == kScaleForTouchBar) - ? SizeLarge - : TouchbarSize; - if (size == s) { - TouchbarEmoji->draw(p, emoji, x, y); - return; - } -#endif - if (size == SizeNormal) { - InstanceNormal->draw(p, emoji, x, y); - } else if (size == SizeLarge) { - InstanceLarge->draw(p, emoji, x, y); - } else { - Unexpected("Size in Ui::Emoji::Draw."); - } -} - -Instance::Instance(int size) : _id(Universal->id()), _size(size) { - Expects(Universal != nullptr); - - readCache(); - if (!cached()) { - generateCache(); - } -} - -bool Instance::cached() const { - Expects(Universal != nullptr); - - return (Universal->id() == _id) && (_sprites.size() == SpritesCount); -} - -void Instance::draw(QPainter &p, EmojiPtr emoji, int x, int y) { - if (Universal && Universal->id() != _id) { - generateCache(); - } - const auto sprite = emoji->sprite(); - if (sprite >= _sprites.size()) { - Assert(Universal != nullptr); - Universal->draw(p, emoji, _size, x, y); - return; - } - p.drawPixmap( - QPoint(x, y), - _sprites[sprite], - QRect(emoji->column() * _size, emoji->row() * _size, _size, _size)); -} - -void Instance::readCache() { - for (auto i = 0; i != SpritesCount; ++i) { - auto image = LoadFromFile(_id, _size, i); - if (image.isNull()) { - return; - } - pushSprite(std::move(image)); - } -} - -void Instance::checkUniversalImages() { - Expects(Universal != nullptr); - - if (_id != Universal->id()) { - _id = Universal->id(); - _generating = nullptr; - _sprites.clear(); - } - if (!Universal->ensureLoaded() && Universal->id() != 0) { - ClearCurrentSetIdSync(); - } -} - -void Instance::generateCache() { - checkUniversalImages(); - - const auto cachePath = internal::CacheFileFolder(); - if (cachePath.isEmpty()) { - return; - } - const auto size = _size; - const auto index = _sprites.size(); - crl::async([ - =, - universal = Universal, - guard = _generating.make_guard() - ]() mutable { - crl::on_main(std::move(guard), [ - =, - image = universal->generate(size, index) - ]() mutable { - if (universal != Universal) { - return; - } - pushSprite(std::move(image)); - if (cached()) { - ClearUniversalChecked(); - } else { - generateCache(); - } - }); - }); -} - -void Instance::pushSprite(QImage &&data) { - _sprites.push_back(PixmapFromImage(std::move(data))); - _sprites.back().setDevicePixelRatio(style::DevicePixelRatio()); -} - -const std::shared_ptr &SourceImages() { - return Universal; -} - -void ClearSourceImages(const std::shared_ptr &images) { - if (images == Universal) { - CanClearUniversal = true; - ClearUniversalChecked(); - } -} - -void ReplaceSourceImages(std::shared_ptr images) { - Expects(images != nullptr); - - if (Universal->id() == images->id()) { - Universal = std::move(images); - } -} - -} // namespace Emoji -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/emoji_config.h b/Telegram/SourceFiles/ui/emoji_config.h deleted file mode 100644 index f6fe3bcbb..000000000 --- a/Telegram/SourceFiles/ui/emoji_config.h +++ /dev/null @@ -1,192 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" -#include "base/binary_guard.h" -#include "emoji.h" - -#include -#include - -#include - -namespace Ui { -namespace Emoji { -namespace internal { - -[[nodiscard]] QString CacheFileFolder(); -[[nodiscard]] QString SetDataPath(int id); - -} // namespace internal - -void Init(); -void Clear(); - -void ClearIrrelevantCache(); - -struct Set { - int id = 0; - int postId = 0; - int size = 0; - QString name; - QString previewPath; -}; - -// Thread safe, callback is called on main thread. -void SwitchToSet(int id, Fn callback); - -std::vector Sets(); -int CurrentSetId(); -bool SetIsReady(int id); -rpl::producer<> Updated(); - -int GetSizeNormal(); -int GetSizeLarge(); -#if defined Q_OS_MAC && !defined OS_MAC_OLD -int GetSizeTouchbar(); -#endif - -class One { - struct CreationTag { - }; - -public: - One(One &&other) = default; - One(const QString &id, EmojiPtr original, uint32 index, bool hasPostfix, bool colorizable, const CreationTag &) - : _id(id) - , _original(original) - , _index(index) - , _hasPostfix(hasPostfix) - , _colorizable(colorizable) { - Expects(!_colorizable || !colored()); - } - - QString id() const { - return _id; - } - QString text() const { - return hasPostfix() ? (_id + QChar(kPostfix)) : _id; - } - - bool colored() const { - return (_original != nullptr); - } - EmojiPtr original() const { - return _original ? _original : this; - } - QString nonColoredId() const { - return original()->id(); - } - - bool hasPostfix() const { - return _hasPostfix; - } - - bool hasVariants() const { - return _colorizable || colored(); - } - int variantsCount() const; - int variantIndex(EmojiPtr variant) const; - EmojiPtr variant(int index) const; - - int index() const { - return _index; - } - int sprite() const { - return int(_index >> 9); - } - int row() const { - return int((_index >> 5) & 0x0FU); - } - int column() const { - return int(_index & 0x1FU); - } - - QString toUrl() const { - return "emoji://e." + QString::number(index()); - } - -private: - const QString _id; - const EmojiPtr _original = nullptr; - const uint32 _index = 0; - const bool _hasPostfix = false; - const bool _colorizable = false; - - friend void internal::Init(); - -}; - -inline EmojiPtr FromUrl(const QString &url) { - auto start = qstr("emoji://e."); - if (url.startsWith(start)) { - return internal::ByIndex(url.midRef(start.size()).toInt()); // skip emoji://e. - } - return nullptr; -} - -inline EmojiPtr Find(const QChar *start, const QChar *end, int *outLength = nullptr) { - return internal::Find(start, end, outLength); -} - -inline EmojiPtr Find(const QString &text, int *outLength = nullptr) { - return Find(text.constBegin(), text.constEnd(), outLength); -} - -QString IdFromOldKey(uint64 oldKey); - -inline EmojiPtr FromOldKey(uint64 oldKey) { - return Find(IdFromOldKey(oldKey)); -} - -inline int ColorIndexFromCode(uint32 code) { - switch (code) { - case 0xD83CDFFBU: return 1; - case 0xD83CDFFCU: return 2; - case 0xD83CDFFDU: return 3; - case 0xD83CDFFEU: return 4; - case 0xD83CDFFFU: return 5; - } - return 0; -} - -inline int ColorIndexFromOldKey(uint64 oldKey) { - return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU)); -} - -QVector GetDefaultRecent(); - -const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight); -void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y); - -class UniversalImages { -public: - explicit UniversalImages(int id); - - int id() const; - bool ensureLoaded(); - void clear(); - - void draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) const; - - // This method must be thread safe and so it is called after - // the _id value is fixed and all _sprites are loaded. - QImage generate(int size, int index) const; - -private: - const int _id = 0; - std::vector _sprites; - -}; - -const std::shared_ptr &SourceImages(); -void ClearSourceImages(const std::shared_ptr &images); - -} // namespace Emoji -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/focus_persister.h b/Telegram/SourceFiles/ui/focus_persister.h deleted file mode 100644 index e74987eb0..000000000 --- a/Telegram/SourceFiles/ui/focus_persister.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace Ui { - -class FocusPersister { -public: - FocusPersister(QWidget *parent, QWidget *steal = nullptr) - : _weak(GrabFocused(parent)) { - if (steal) { - steal->setFocus(); - } - } - - ~FocusPersister() { - if (auto strong = _weak.data()) { - if (auto window = strong->window()) { - if (window->focusWidget() != strong) { - strong->setFocus(); - } - } - } - } - -private: - static QWidget *GrabFocused(QWidget *parent) { - if (auto window = parent ? parent->window() : nullptr) { - return window->focusWidget(); - } - return nullptr; - } - QPointer _weak; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/image/image_prepare.cpp b/Telegram/SourceFiles/ui/image/image_prepare.cpp deleted file mode 100644 index 52631dd93..000000000 --- a/Telegram/SourceFiles/ui/image/image_prepare.cpp +++ /dev/null @@ -1,639 +0,0 @@ -/* -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 "ui/image/image_prepare.h" - -#include "ui/effects/animation_value.h" -#include "ui/style/style_core.h" -#include "ui/painter.h" -#include "base/flat_map.h" -#include "styles/palette.h" -#include "styles/style_basic.h" - -namespace Images { -namespace { - -TG_FORCE_INLINE uint64 blurGetColors(const uchar *p) { - return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48); -} - -const QImage &circleMask(QSize size) { - uint64 key = (uint64(uint32(size.width())) << 32) - | uint64(uint32(size.height())); - - static auto masks = base::flat_map(); - const auto i = masks.find(key); - if (i != end(masks)) { - return i->second; - } - auto mask = QImage( - size, - QImage::Format_ARGB32_Premultiplied); - mask.fill(Qt::transparent); - { - QPainter p(&mask); - PainterHighQualityEnabler hq(p); - p.setBrush(Qt::white); - p.setPen(Qt::NoPen); - p.drawEllipse(QRect(QPoint(), size)); - } - return masks.emplace(key, std::move(mask)).first->second; -} - -std::array PrepareCornersMask(int radius) { - auto result = std::array(); - const auto side = radius * style::DevicePixelRatio(); - auto full = QImage( - QSize(side, side) * 3, - QImage::Format_ARGB32_Premultiplied); - full.fill(Qt::transparent); - { - QPainter p(&full); - PainterHighQualityEnabler hq(p); - - p.setPen(Qt::NoPen); - p.setBrush(Qt::white); - p.drawRoundedRect(0, 0, side * 3, side * 3, side, side); - } - result[0] = full.copy(0, 0, side, side); - result[1] = full.copy(side * 2, 0, side, side); - result[2] = full.copy(0, side * 2, side, side); - result[3] = full.copy(side * 2, side * 2, side, side); - for (auto &image : result) { - image.setDevicePixelRatio(style::DevicePixelRatio()); - } - return result; -} - -} // namespace - -QPixmap PixmapFast(QImage &&image) { - Expects(image.format() == QImage::Format_ARGB32_Premultiplied - || image.format() == QImage::Format_RGB32); - - return QPixmap::fromImage(std::move(image), Qt::NoFormatConversion); -} - -const std::array &CornersMask(ImageRoundRadius radius) { - if (radius == ImageRoundRadius::Large) { - static auto Mask = PrepareCornersMask(st::roundRadiusLarge); - return Mask; - } else { - static auto Mask = PrepareCornersMask(st::roundRadiusSmall); - return Mask; - } -} - -std::array PrepareCorners( - ImageRoundRadius radius, - const style::color &color) { - auto result = CornersMask(radius); - for (auto &image : result) { - style::colorizeImage(image, color->c, &image); - } - return result; -} - -QImage prepareBlur(QImage img) { - if (img.isNull()) { - return img; - } - const auto ratio = img.devicePixelRatio(); - const auto fmt = img.format(); - if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { - img = std::move(img).convertToFormat(QImage::Format_ARGB32_Premultiplied); - img.setDevicePixelRatio(ratio); - } - - uchar *pix = img.bits(); - if (pix) { - int w = img.width(), h = img.height(), wold = w, hold = h; - const int radius = 3; - const int r1 = radius + 1; - const int div = radius * 2 + 1; - const int stride = w * 4; - if (radius < 16 && div < w && div < h && stride <= w * 4) { - bool withalpha = img.hasAlphaChannel(); - if (withalpha) { - QImage imgsmall(w, h, img.format()); - { - QPainter p(&imgsmall); - PainterHighQualityEnabler hq(p); - - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, w, h, Qt::transparent); - p.drawImage(QRect(radius, radius, w - 2 * radius, h - 2 * radius), img, QRect(0, 0, w, h)); - } - imgsmall.setDevicePixelRatio(ratio); - auto was = img; - img = std::move(imgsmall); - imgsmall = QImage(); - Assert(!img.isNull()); - - pix = img.bits(); - if (!pix) return was; - } - uint64 *rgb = new uint64[w * h]; - - int x, y, i; - - int yw = 0; - const int we = w - r1; - for (y = 0; y < h; y++) { - uint64 cur = blurGetColors(&pix[yw]); - uint64 rgballsum = -radius * cur; - uint64 rgbsum = cur * ((r1 * (r1 + 1)) >> 1); - - for (i = 1; i <= radius; i++) { - uint64 cur = blurGetColors(&pix[yw + i * 4]); - rgbsum += cur * (r1 - i); - rgballsum += cur; - } - - x = 0; - -#define update(start, middle, end) \ -rgb[y * w + x] = (rgbsum >> 4) & 0x00FF00FF00FF00FFLL; \ -rgballsum += blurGetColors(&pix[yw + (start) * 4]) - 2 * blurGetColors(&pix[yw + (middle) * 4]) + blurGetColors(&pix[yw + (end) * 4]); \ -rgbsum += rgballsum; \ -x++; - - while (x < r1) { - update(0, x, x + r1); - } - while (x < we) { - update(x - r1, x, x + r1); - } - while (x < w) { - update(x - r1, x, w - 1); - } - -#undef update - - yw += stride; - } - - const int he = h - r1; - for (x = 0; x < w; x++) { - uint64 rgballsum = -radius * rgb[x]; - uint64 rgbsum = rgb[x] * ((r1 * (r1 + 1)) >> 1); - for (i = 1; i <= radius; i++) { - rgbsum += rgb[i * w + x] * (r1 - i); - rgballsum += rgb[i * w + x]; - } - - y = 0; - int yi = x * 4; - -#define update(start, middle, end) \ -uint64 res = rgbsum >> 4; \ -pix[yi] = res & 0xFF; \ -pix[yi + 1] = (res >> 16) & 0xFF; \ -pix[yi + 2] = (res >> 32) & 0xFF; \ -pix[yi + 3] = (res >> 48) & 0xFF; \ -rgballsum += rgb[x + (start) * w] - 2 * rgb[x + (middle) * w] + rgb[x + (end) * w]; \ -rgbsum += rgballsum; \ -y++; \ -yi += stride; - - while (y < r1) { - update(0, y, y + r1); - } - while (y < he) { - update(y - r1, y, y + r1); - } - while (y < h) { - update(y - r1, y, h - 1); - } - -#undef update - } - - delete[] rgb; - } - } - return img; -} - -QImage BlurLargeImage(QImage image, int radius) { - const auto width = image.width(); - const auto height = image.height(); - if (width <= radius || height <= radius || radius < 1) { - return image; - } - - if (image.format() != QImage::Format_RGB32 - && image.format() != QImage::Format_ARGB32_Premultiplied) { - image = std::move(image).convertToFormat( - QImage::Format_ARGB32_Premultiplied); - } - const auto pixels = image.bits(); - - const auto width_m1 = width - 1; - const auto height_m1 = height - 1; - const auto widthxheight = width * height; - const auto div = 2 * radius + 1; - const auto radius_p1 = radius + 1; - const auto divsum = radius_p1 * radius_p1; - - const auto dvcount = 256 * divsum; - const auto buffers = (div * 3) // stack - + std::max(width, height) // vmin - + widthxheight * 3 // rgb - + dvcount; // dv - auto storage = std::vector(buffers); - auto taken = 0; - const auto take = [&](int size) { - const auto result = gsl::make_span(storage).subspan(taken, size); - taken += size; - return result; - }; - - // Small buffers - const auto stack = take(div * 3).data(); - const auto vmin = take(std::max(width, height)).data(); - - // Large buffers - const auto rgb = take(widthxheight * 3).data(); - const auto dvs = take(dvcount); - - auto &&ints = ranges::view::ints; - for (auto &&[value, index] : ranges::view::zip(dvs, ints(0, ranges::unreachable))) { - value = (index / divsum); - } - const auto dv = dvs.data(); - - // Variables - auto stackpointer = 0; - for (const auto x : ints(0, width)) { - vmin[x] = std::min(x + radius_p1, width_m1); - } - for (const auto y : ints(0, height)) { - auto rinsum = 0; - auto ginsum = 0; - auto binsum = 0; - auto routsum = 0; - auto goutsum = 0; - auto boutsum = 0; - auto rsum = 0; - auto gsum = 0; - auto bsum = 0; - - const auto y_width = y * width; - for (const auto i : ints(-radius, radius + 1)) { - const auto sir = &stack[(i + radius) * 3]; - const auto x = std::clamp(i, 0, width_m1); - const auto offset = (y_width + x) * 4; - sir[0] = pixels[offset]; - sir[1] = pixels[offset + 1]; - sir[2] = pixels[offset + 2]; - - const auto rbs = radius_p1 - std::abs(i); - rsum += sir[0] * rbs; - gsum += sir[1] * rbs; - bsum += sir[2] * rbs; - - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - } - stackpointer = radius; - - for (const auto x : ints(0, width)) { - const auto position = (y_width + x) * 3; - rgb[position] = dv[rsum]; - rgb[position + 1] = dv[gsum]; - rgb[position + 2] = dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - const auto stackstart = (stackpointer - radius + div) % div; - const auto sir = &stack[stackstart * 3]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - const auto offset = (y_width + vmin[x]) * 4; - sir[0] = pixels[offset]; - sir[1] = pixels[offset + 1]; - sir[2] = pixels[offset + 2]; - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - { - stackpointer = (stackpointer + 1) % div; - const auto sir = &stack[stackpointer * 3]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - } - } - } - - for (const auto y : ints(0, height)) { - vmin[y] = std::min(y + radius_p1, height_m1) * width; - } - for (const auto x : ints(0, width)) { - auto rinsum = 0; - auto ginsum = 0; - auto binsum = 0; - auto routsum = 0; - auto goutsum = 0; - auto boutsum = 0; - auto rsum = 0; - auto gsum = 0; - auto bsum = 0; - for (const auto i : ints(-radius, radius + 1)) { - const auto y = std::clamp(i, 0, height_m1); - const auto position = (y * width + x) * 3; - const auto sir = &stack[(i + radius) * 3]; - - sir[0] = rgb[position]; - sir[1] = rgb[position + 1]; - sir[2] = rgb[position + 2]; - - const auto rbs = radius_p1 - std::abs(i); - rsum += sir[0] * rbs; - gsum += sir[1] * rbs; - bsum += sir[2] * rbs; - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - } - stackpointer = radius; - for (const auto y : ints(0, height)) { - const auto offset = (y * width + x) * 4; - pixels[offset] = dv[rsum]; - pixels[offset + 1] = dv[gsum]; - pixels[offset + 2] = dv[bsum]; - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - const auto stackstart = (stackpointer - radius + div) % div; - const auto sir = &stack[stackstart * 3]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - const auto position = (vmin[y] + x) * 3; - sir[0] = rgb[position]; - sir[1] = rgb[position + 1]; - sir[2] = rgb[position + 2]; - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - { - stackpointer = (stackpointer + 1) % div; - const auto sir = &stack[stackpointer * 3]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - } - } - } - return image; -} - -void prepareCircle(QImage &img) { - Assert(!img.isNull()); - - img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); - Assert(!img.isNull()); - - QPainter p(&img); - p.setCompositionMode(QPainter::CompositionMode_DestinationIn); - p.drawImage( - QRect(QPoint(), img.size() / img.devicePixelRatio()), - circleMask(img.size())); -} - -void prepareRound( - QImage &image, - QImage *cornerMasks, - RectParts corners, - QRect target) { - if (target.isNull()) { - target = QRect(QPoint(), image.size()); - } else { - Assert(QRect(QPoint(), image.size()).contains(target)); - } - auto cornerWidth = cornerMasks[0].width(); - auto cornerHeight = cornerMasks[0].height(); - auto imageWidth = image.width(); - auto imageHeight = image.height(); - if (imageWidth < 2 * cornerWidth || imageHeight < 2 * cornerHeight) { - return; - } - constexpr auto imageIntsPerPixel = 1; - auto imageIntsPerLine = (image.bytesPerLine() >> 2); - Assert(image.depth() == static_cast((imageIntsPerPixel * sizeof(uint32)) << 3)); - Assert(image.bytesPerLine() == (imageIntsPerLine << 2)); - - auto ints = reinterpret_cast(image.bits()); - auto intsTopLeft = ints + target.x() + target.y() * imageWidth; - auto intsTopRight = ints + target.x() + target.width() - cornerWidth + target.y() * imageWidth; - auto intsBottomLeft = ints + target.x() + (target.y() + target.height() - cornerHeight) * imageWidth; - auto intsBottomRight = ints + target.x() + target.width() - cornerWidth + (target.y() + target.height() - cornerHeight) * imageWidth; - auto maskCorner = [&](uint32 *imageInts, const QImage &mask) { - auto maskWidth = mask.width(); - auto maskHeight = mask.height(); - auto maskBytesPerPixel = (mask.depth() >> 3); - auto maskBytesPerLine = mask.bytesPerLine(); - auto maskBytesAdded = maskBytesPerLine - maskWidth * maskBytesPerPixel; - auto maskBytes = mask.constBits(); - Assert(maskBytesAdded >= 0); - Assert(mask.depth() == (maskBytesPerPixel << 3)); - auto imageIntsAdded = imageIntsPerLine - maskWidth * imageIntsPerPixel; - Assert(imageIntsAdded >= 0); - for (auto y = 0; y != maskHeight; ++y) { - for (auto x = 0; x != maskWidth; ++x) { - auto opacity = static_cast(*maskBytes) + 1; - *imageInts = anim::unshifted(anim::shifted(*imageInts) * opacity); - maskBytes += maskBytesPerPixel; - imageInts += imageIntsPerPixel; - } - maskBytes += maskBytesAdded; - imageInts += imageIntsAdded; - } - }; - if (corners & RectPart::TopLeft) maskCorner(intsTopLeft, cornerMasks[0]); - if (corners & RectPart::TopRight) maskCorner(intsTopRight, cornerMasks[1]); - if (corners & RectPart::BottomLeft) maskCorner(intsBottomLeft, cornerMasks[2]); - if (corners & RectPart::BottomRight) maskCorner(intsBottomRight, cornerMasks[3]); -} - -void prepareRound( - QImage &image, - ImageRoundRadius radius, - RectParts corners, - QRect target) { - if (!static_cast(corners)) { - return; - } else if (radius == ImageRoundRadius::Ellipse) { - Assert((corners & RectPart::AllCorners) == RectPart::AllCorners); - Assert(target.isNull()); - prepareCircle(image); - return; - } - Assert(!image.isNull()); - - image.setDevicePixelRatio(style::DevicePixelRatio()); - image = std::move(image).convertToFormat( - QImage::Format_ARGB32_Premultiplied); - Assert(!image.isNull()); - - auto masks = CornersMask(radius); - prepareRound(image, masks.data(), corners, target); -} - -QImage prepareColored(style::color add, QImage image) { - return prepareColored(add->c, std::move(image)); -} - -QImage prepareColored(QColor add, QImage image) { - const auto format = image.format(); - if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32_Premultiplied) { - image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); - } - - if (const auto pix = image.bits()) { - const auto ca = int(add.alphaF() * 0xFF); - const auto cr = int(add.redF() * 0xFF); - const auto cg = int(add.greenF() * 0xFF); - const auto cb = int(add .blueF() * 0xFF); - const auto w = image.width(); - const auto h = image.height(); - const auto size = w * h * 4; - for (auto i = index_type(); i != size; i += 4) { - int b = pix[i], g = pix[i + 1], r = pix[i + 2], a = pix[i + 3], aca = a * ca; - pix[i + 0] = uchar(b + ((aca * (cb - b)) >> 16)); - pix[i + 1] = uchar(g + ((aca * (cg - g)) >> 16)); - pix[i + 2] = uchar(r + ((aca * (cr - r)) >> 16)); - pix[i + 3] = uchar(a + ((aca * (0xFF - a)) >> 16)); - } - } - return image; -} - -QImage prepareOpaque(QImage image) { - if (image.hasAlphaChannel()) { - image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); - auto ints = reinterpret_cast(image.bits()); - auto bg = anim::shifted(st::imageBgTransparent->c); - auto width = image.width(); - auto height = image.height(); - auto addPerLine = (image.bytesPerLine() / sizeof(uint32)) - width; - for (auto y = 0; y != height; ++y) { - for (auto x = 0; x != width; ++x) { - auto components = anim::shifted(*ints); - *ints++ = anim::unshifted(components * 256 + bg * (256 - anim::getAlpha(components))); - } - ints += addPerLine; - } - } - return image; -} - -QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, int outerh, const style::color *colored) { - Assert(!img.isNull()); - if (options & Images::Option::Blurred) { - img = prepareBlur(std::move(img)); - Assert(!img.isNull()); - } - if (w <= 0 || (w == img.width() && (h <= 0 || h == img.height()))) { - } else if (h <= 0) { - img = img.scaledToWidth(w, (options & Images::Option::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); - Assert(!img.isNull()); - } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, (options & Images::Option::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); - Assert(!img.isNull()); - } - if (outerw > 0 && outerh > 0) { - const auto pixelRatio = style::DevicePixelRatio(); - outerw *= pixelRatio; - outerh *= pixelRatio; - if (outerw != w || outerh != h) { - img.setDevicePixelRatio(pixelRatio); - auto result = QImage(outerw, outerh, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(pixelRatio); - if (options & Images::Option::TransparentBackground) { - result.fill(Qt::transparent); - } - { - QPainter p(&result); - if (!(options & Images::Option::TransparentBackground)) { - if (w < outerw || h < outerh) { - p.fillRect(0, 0, result.width(), result.height(), st::imageBg); - } - } - p.drawImage((result.width() - img.width()) / (2 * pixelRatio), (result.height() - img.height()) / (2 * pixelRatio), img); - } - img = result; - Assert(!img.isNull()); - } - } - auto corners = [](Images::Options options) { - return ((options & Images::Option::RoundedTopLeft) ? RectPart::TopLeft : RectPart::None) - | ((options & Images::Option::RoundedTopRight) ? RectPart::TopRight : RectPart::None) - | ((options & Images::Option::RoundedBottomLeft) ? RectPart::BottomLeft : RectPart::None) - | ((options & Images::Option::RoundedBottomRight) ? RectPart::BottomRight : RectPart::None); - }; - if (options & Images::Option::Circled) { - prepareCircle(img); - Assert(!img.isNull()); - } else if (options & Images::Option::RoundedLarge) { - prepareRound(img, ImageRoundRadius::Large, corners(options)); - Assert(!img.isNull()); - } else if (options & Images::Option::RoundedSmall) { - prepareRound(img, ImageRoundRadius::Small, corners(options)); - Assert(!img.isNull()); - } - if (options & Images::Option::Colored) { - Assert(colored != nullptr); - img = prepareColored(*colored, std::move(img)); - } - img.setDevicePixelRatio(style::DevicePixelRatio()); - return img; -} - -} // namespace Images diff --git a/Telegram/SourceFiles/ui/image/image_prepare.h b/Telegram/SourceFiles/ui/image/image_prepare.h deleted file mode 100644 index 9838549d1..000000000 --- a/Telegram/SourceFiles/ui/image/image_prepare.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/flags.h" -#include "ui/rect_part.h" -#include "ui/style/style_core.h" - -namespace Storage { -namespace Cache { -struct Key; -} // namespace Cache -} // namespace Storage - -enum class ImageRoundRadius { - None, - Large, - Small, - Ellipse, -}; - -namespace Images { - -[[nodiscard]] QPixmap PixmapFast(QImage &&image); -[[nodiscard]] QImage BlurLargeImage(QImage image, int radius); -[[nodiscard]] const std::array &CornersMask( - ImageRoundRadius radius); -[[nodiscard]] std::array PrepareCorners( - ImageRoundRadius radius, - const style::color &color); - -QImage prepareBlur(QImage image); -void prepareRound( - QImage &image, - ImageRoundRadius radius, - RectParts corners = RectPart::AllCorners, - QRect target = QRect()); -void prepareRound( - QImage &image, - QImage *cornerMasks, - RectParts corners = RectPart::AllCorners, - QRect target = QRect()); -void prepareCircle(QImage &image); -QImage prepareColored(style::color add, QImage image); -QImage prepareColored(QColor add, QImage image); -QImage prepareOpaque(QImage image); - -enum class Option { - None = 0, - Smooth = (1 << 0), - Blurred = (1 << 1), - Circled = (1 << 2), - RoundedLarge = (1 << 3), - RoundedSmall = (1 << 4), - RoundedTopLeft = (1 << 5), - RoundedTopRight = (1 << 6), - RoundedBottomLeft = (1 << 7), - RoundedBottomRight = (1 << 8), - RoundedAll = (None - | RoundedTopLeft - | RoundedTopRight - | RoundedBottomLeft - | RoundedBottomRight), - Colored = (1 << 9), - TransparentBackground = (1 << 10), -}; -using Options = base::flags

(this, _st.menu)); - init(); -} - -// Not ready with submenus yet. -//DropdownMenu::DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap) -//, _st(st) { -// _menu = setOwnedWidget(object_ptr(this, menu, _st.menu)); -// init(); -// -// for (auto action : actions()) { -// if (auto submenu = action->menu()) { -// auto it = _submenus.insert(action, new DropdownMenu(submenu, st)); -// it.value()->deleteOnHide(false); -// } -// } -//} - -void DropdownMenu::init() { - InnerDropdown::setHiddenCallback([this] { hideFinish(); }); - - _menu->setResizedCallback([this] { resizeToContent(); }); - _menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) { - handleActivated(action, actionTop, source); - }); - _menu->setTriggeredCallback([this](QAction *action, int actionTop, TriggeredSource source) { - handleTriggered(action, actionTop, source); - }); - _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); - _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); - _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); - _menu->setMouseReleaseDelegate([this](QPoint globalPosition) { handleMouseRelease(globalPosition); }); - - setMouseTracking(true); - - hide(); -} - -not_null DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { - return _menu->addAction(text, receiver, member, icon, iconOver); -} - -not_null DropdownMenu::addAction(const QString &text, Fn callback, const style::icon *icon, const style::icon *iconOver) { - return _menu->addAction(text, std::move(callback), icon, iconOver); -} - -not_null DropdownMenu::addSeparator() { - return _menu->addSeparator(); -} - -void DropdownMenu::clearActions() { - //for (auto submenu : base::take(_submenus)) { - // delete submenu; - //} - return _menu->clearActions(); -} - -const std::vector> &DropdownMenu::actions() const { - return _menu->actions(); -} - -void DropdownMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) { - if (source == TriggeredSource::Mouse) { - if (!popupSubmenuFromAction(action, actionTop, source)) { - if (auto currentSubmenu = base::take(_activeSubmenu)) { - currentSubmenu->hideMenu(true); - } - } - } -} - -void DropdownMenu::handleTriggered(QAction *action, int actionTop, TriggeredSource source) { - if (!popupSubmenuFromAction(action, actionTop, source)) { - hideMenu(); - _triggering = true; - emit action->trigger(); - _triggering = false; - if (_deleteLater) { - _deleteLater = false; - deleteLater(); - } - } -} - -// Not ready with submenus yet. -bool DropdownMenu::popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source) { - //if (auto submenu = _submenus.value(action)) { - // if (_activeSubmenu == submenu) { - // submenu->hideMenu(true); - // } else { - // popupSubmenu(submenu, actionTop, source); - // } - // return true; - //} - return false; -} - -//void DropdownMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) { -// if (auto currentSubmenu = base::take(_activeSubmenu)) { -// currentSubmenu->hideMenu(true); -// } -// if (submenu) { -// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0))); -// auto menuBottomRight = mapFromGlobal(_menu->mapToGlobal(QPoint(_menu->width(), _menu->height()))); -// QPoint p(menuTopLeft.x() + (rtl() ? (width() - menuBottomRight.x()) : menuBottomRight.x()), menuTopLeft.y() + actionTop); -// _activeSubmenu = submenu; -// _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); -// -// _menu->setChildShown(true); -// } else { -// _menu->setChildShown(false); -// } -//} - -void DropdownMenu::forwardKeyPress(int key) { - if (!handleKeyPress(key)) { - _menu->handleKeyPress(key); - } -} - -bool DropdownMenu::handleKeyPress(int key) { - if (_activeSubmenu) { - _activeSubmenu->handleKeyPress(key); - return true; - } else if (key == Qt::Key_Escape) { - hideMenu(_parent ? true : false); - return true; - } else if (key == (style::RightToLeft() ? Qt::Key_Right : Qt::Key_Left)) { - if (_parent) { - hideMenu(true); - return true; - } - } - return false; -} - -void DropdownMenu::handleMouseMove(QPoint globalPosition) { - if (_parent) { - _parent->forwardMouseMove(globalPosition); - } -} - -void DropdownMenu::handleMousePress(QPoint globalPosition) { - if (_parent) { - _parent->forwardMousePress(globalPosition); - } else { - hideMenu(); - } -} - -void DropdownMenu::handleMouseRelease(QPoint globalPosition) { - if (_parent) { - _parent->forwardMouseRelease(globalPosition); - } else { - hideMenu(); - } -} - -void DropdownMenu::focusOutEvent(QFocusEvent *e) { - hideMenu(); -} - -void DropdownMenu::hideEvent(QHideEvent *e) { - if (_deleteOnHide) { - if (_triggering) { - _deleteLater = true; - } else { - deleteLater(); - } - } -} - -void DropdownMenu::keyPressEvent(QKeyEvent *e) { - forwardKeyPress(e->key()); -} - -void DropdownMenu::mouseMoveEvent(QMouseEvent *e) { - forwardMouseMove(e->globalPos()); -} - -void DropdownMenu::mousePressEvent(QMouseEvent *e) { - forwardMousePress(e->globalPos()); -} - -void DropdownMenu::hideMenu(bool fast) { - if (isHidden()) return; - if (_parent && !isHiding()) { - _parent->childHiding(this); - } - if (fast) { - hideFast(); - } else { - hideAnimated(); - if (_parent) { - _parent->hideMenu(); - } - } - if (_activeSubmenu) { - _activeSubmenu->hideMenu(fast); - } -} - -void DropdownMenu::childHiding(DropdownMenu *child) { - if (_activeSubmenu && _activeSubmenu == child) { - _activeSubmenu = SubmenuPointer(); - } -} - -void DropdownMenu::hideFinish() { - _menu->clearSelection(); - if (_hiddenCallback) { - _hiddenCallback(); - } -} - -// Not ready with submenus yet. -//void DropdownMenu::deleteOnHide(bool del) { -// _deleteOnHide = del; -//} - -//void DropdownMenu::popup(const QPoint &p) { -// showMenu(p, nullptr, TriggeredSource::Mouse); -//} -// -//void DropdownMenu::showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source) { -// _parent = parent; -// -// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0))); -// auto w = p - QPoint(0, menuTopLeft.y()); -// auto r = QApplication::desktop()->screenGeometry(p); -// if (rtl()) { -// if (w.x() - width() < r.x() - _padding.left()) { -// if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { -// w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); -// } else { -// w.setX(r.x() - _padding.left()); -// } -// } else { -// w.setX(w.x() - width()); -// } -// } else { -// if (w.x() + width() - _padding.right() > r.x() + r.width()) { -// if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { -// w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); -// } else { -// w.setX(r.x() + r.width() - width() + _padding.right()); -// } -// } -// } -// if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { -// if (_parent) { -// w.setY(r.y() + r.height() - height() + _padding.bottom()); -// } else { -// w.setY(p.y() - height() + _padding.bottom()); -// } -// } -// if (w.y() < r.y()) { -// w.setY(r.y()); -// } -// move(w); -// -// _menu->setShowSource(source); -//} - -DropdownMenu::~DropdownMenu() { - clearActions(); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h deleted file mode 100644 index 8caf9ef4d..000000000 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" -#include "ui/widgets/inner_dropdown.h" -#include "ui/widgets/menu.h" - -namespace Ui { - -class DropdownMenu : public InnerDropdown { - Q_OBJECT - -public: - DropdownMenu(QWidget *parent, const style::DropdownMenu &st = st::defaultDropdownMenu); - - not_null addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addAction(const QString &text, Fn callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addSeparator(); - void clearActions(); - - void setHiddenCallback(Fn callback) { - _hiddenCallback = std::move(callback); - } - - const std::vector> &actions() const; - - ~DropdownMenu(); - -protected: - void focusOutEvent(QFocusEvent *e) override; - void hideEvent(QHideEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - -private slots: - void onHidden() { - hideFinish(); - } - -private: - // Not ready with submenus yet. - DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st = st::defaultDropdownMenu); - void deleteOnHide(bool del); - void popup(const QPoint &p); - void hideMenu(bool fast = false); - - void childHiding(DropdownMenu *child); - - void init(); - void hideFinish(); - - using TriggeredSource = Menu::TriggeredSource; - void handleActivated(QAction *action, int actionTop, TriggeredSource source); - void handleTriggered(QAction *action, int actionTop, TriggeredSource source); - void forwardKeyPress(int key); - bool handleKeyPress(int key); - void forwardMouseMove(QPoint globalPosition) { - _menu->handleMouseMove(globalPosition); - } - void handleMouseMove(QPoint globalPosition); - void forwardMousePress(QPoint globalPosition) { - _menu->handleMousePress(globalPosition); - } - void handleMousePress(QPoint globalPosition); - void forwardMouseRelease(QPoint globalPosition) { - _menu->handleMouseRelease(globalPosition); - } - void handleMouseRelease(QPoint globalPosition); - - using SubmenuPointer = QPointer; - bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); - void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source); - void showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source); - - const style::DropdownMenu &_st; - Fn _hiddenCallback; - - QPointer _menu; - - // Not ready with submenus yet. - //using Submenus = QMap; - //Submenus _submenus; - - DropdownMenu *_parent = nullptr; - - SubmenuPointer _activeSubmenu; - - bool _deleteOnHide = false; - bool _triggering = false; - bool _deleteLater = false; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp deleted file mode 100644 index 66b7a62fa..000000000 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* -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 "ui/widgets/inner_dropdown.h" - -#include "ui/widgets/scroll_area.h" -#include "ui/widgets/shadow.h" -#include "ui/effects/panel_animation.h" -#include "ui/image/image_prepare.h" -#include "ui/ui_utility.h" - -namespace { - -constexpr float64 kFadeHeight = 1. / 3; -constexpr int kFadeAlphaMax = 160; - -} // namespace - -namespace Ui { - -InnerDropdown::InnerDropdown( - QWidget *parent, - const style::InnerDropdown &st) -: RpWidget(parent) -, _st(st) -, _roundRect(ImageRoundRadius::Small, _st.bg) -, _scroll(this, _st.scroll) { - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated())); - - connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - - hide(); - - shownValue( - ) | rpl::filter([](bool shown) { - return shown; - }) | rpl::take(1) | rpl::map([=] { - // We can't invoke this before the window is created. - // So instead we start handling them on the first show(). - return macWindowDeactivateEvents(); - }) | rpl::flatten_latest( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=] { - leaveEvent(nullptr); - }, lifetime()); -} - -QPointer InnerDropdown::doSetOwnedWidget( - object_ptr widget) { - auto result = QPointer(widget); - widget->heightValue( - ) | rpl::skip(1) | rpl::start_with_next([=] { - resizeToContent(); - }, widget->lifetime()); - auto container = _scroll->setOwnedWidget( - object_ptr( - _scroll, - std::move(widget), - _st)); - container->resizeToWidth(_scroll->width()); - container->moveToLeft(0, 0); - container->show(); - result->show(); - return result; -} - -void InnerDropdown::setMaxHeight(int newMaxHeight) { - _maxHeight = newMaxHeight; - resizeToContent(); -} - -void InnerDropdown::resizeToContent() { - auto newWidth = _st.padding.left() + _st.scrollMargin.left() + _st.scrollMargin.right() + _st.padding.right(); - auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); - if (auto widget = static_cast(_scroll->widget())) { - widget->resizeToContent(); - newWidth += widget->width(); - newHeight += widget->height(); - } - if (_maxHeight > 0) { - accumulate_min(newHeight, _maxHeight); - } - if (newWidth != width() || newHeight != height()) { - resize(newWidth, newHeight); - update(); - finishAnimating(); - } -} - -void InnerDropdown::resizeEvent(QResizeEvent *e) { - _scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin)); - if (auto widget = static_cast(_scroll->widget())) { - widget->resizeToWidth(_scroll->width()); - onScroll(); - } -} - -void InnerDropdown::onScroll() { - if (auto widget = static_cast(_scroll->widget())) { - int visibleTop = _scroll->scrollTop(); - int visibleBottom = visibleTop + _scroll->height(); - widget->setVisibleTopBottom(visibleTop, visibleBottom); - } -} - -void InnerDropdown::paintEvent(QPaintEvent *e) { - QPainter p(this); - - if (_a_show.animating()) { - if (auto opacity = _a_opacity.value(_hiding ? 0. : 1.)) { - // _a_opacity.current(ms)->opacityAnimationCallback()->_showAnimation.reset() - if (_showAnimation) { - _showAnimation->paintFrame(p, 0, 0, width(), _a_show.value(1.), opacity); - } - } - } else if (_a_opacity.animating()) { - p.setOpacity(_a_opacity.value(0.)); - p.drawPixmap(0, 0, _cache); - } else if (_hiding || isHidden()) { - hideFinished(); - } else if (_showAnimation) { - _showAnimation->paintFrame(p, 0, 0, width(), 1., 1.); - _showAnimation.reset(); - showChildren(); - } else { - if (!_cache.isNull()) _cache = QPixmap(); - const auto inner = rect().marginsRemoved(_st.padding); - Shadow::paint(p, inner, width(), _st.shadow); - _roundRect.paint(p, inner); - } -} - -void InnerDropdown::enterEventHook(QEvent *e) { - if (_autoHiding) { - showAnimated(_origin); - } - return RpWidget::enterEventHook(e); -} - -void InnerDropdown::leaveEventHook(QEvent *e) { - if (_autoHiding) { - if (_a_show.animating() || _a_opacity.animating()) { - hideAnimated(); - } else { - _hideTimer.start(300); - } - } - return RpWidget::leaveEventHook(e); -} - -void InnerDropdown::otherEnter() { - if (_autoHiding) { - showAnimated(_origin); - } -} - -void InnerDropdown::otherLeave() { - if (_autoHiding) { - if (_a_show.animating() || _a_opacity.animating()) { - hideAnimated(); - } else { - _hideTimer.start(0); - } - } -} - -void InnerDropdown::setOrigin(PanelAnimation::Origin origin) { - _origin = origin; -} - -void InnerDropdown::showAnimated(PanelAnimation::Origin origin) { - setOrigin(origin); - showAnimated(); -} - -void InnerDropdown::showAnimated() { - _hideTimer.stop(); - showStarted(); -} - -void InnerDropdown::hideAnimated(HideOption option) { - if (isHidden()) return; - if (option == HideOption::IgnoreShow) { - _ignoreShowEvents = true; - } - if (_hiding) return; - - _hideTimer.stop(); - startOpacityAnimation(true); -} - -void InnerDropdown::finishAnimating() { - if (_a_show.animating()) { - _a_show.stop(); - showAnimationCallback(); - } - if (_showAnimation) { - _showAnimation.reset(); - showChildren(); - } - if (_a_opacity.animating()) { - _a_opacity.stop(); - opacityAnimationCallback(); - } -} - -void InnerDropdown::showFast() { - _hideTimer.stop(); - finishAnimating(); - if (isHidden()) { - showChildren(); - show(); - } - _hiding = false; -} - -void InnerDropdown::hideFast() { - if (isHidden()) return; - - _hideTimer.stop(); - finishAnimating(); - _hiding = false; - hideFinished(); -} - -void InnerDropdown::hideFinished() { - _a_show.stop(); - _showAnimation.reset(); - _cache = QPixmap(); - _ignoreShowEvents = false; - if (!isHidden()) { - if (_hiddenCallback) { - _hiddenCallback(); - } - hide(); - } -} - -void InnerDropdown::prepareCache() { - if (_a_opacity.animating()) return; - - auto showAnimation = base::take(_a_show); - auto showAnimationData = base::take(_showAnimation); - showChildren(); - _cache = GrabWidget(this); - _showAnimation = base::take(showAnimationData); - _a_show = base::take(showAnimation); - if (_a_show.animating()) { - hideChildren(); - } -} - -void InnerDropdown::startOpacityAnimation(bool hiding) { - if (hiding) { - if (_hideStartCallback) { - _hideStartCallback(); - } - } else if (_showStartCallback) { - _showStartCallback(); - } - - _hiding = false; - prepareCache(); - _hiding = hiding; - hideChildren(); - _a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration); -} - -void InnerDropdown::showStarted() { - if (_ignoreShowEvents) return; - if (isHidden()) { - show(); - startShowAnimation(); - return; - } else if (!_hiding) { - return; - } - startOpacityAnimation(false); -} - -void InnerDropdown::startShowAnimation() { - if (_showStartCallback) { - _showStartCallback(); - } - if (!_a_show.animating()) { - auto opacityAnimation = base::take(_a_opacity); - showChildren(); - auto cache = grabForPanelAnimation(); - _a_opacity = base::take(opacityAnimation); - - const auto pixelRatio = style::DevicePixelRatio(); - _showAnimation = std::make_unique(_st.animation, _origin); - auto inner = rect().marginsRemoved(_st.padding); - _showAnimation->setFinalImage(std::move(cache), QRect(inner.topLeft() * pixelRatio, inner.size() * pixelRatio)); - _showAnimation->setCornerMasks( - Images::CornersMask(ImageRoundRadius::Small)); - _showAnimation->start(); - } - hideChildren(); - _a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration); -} - -QImage InnerDropdown::grabForPanelAnimation() { - SendPendingMoveResizeEvents(this); - const auto pixelRatio = style::DevicePixelRatio(); - auto result = QImage(size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(pixelRatio); - result.fill(Qt::transparent); - { - QPainter p(&result); - _roundRect.paint(p, rect().marginsRemoved(_st.padding)); - for (const auto child : children()) { - if (const auto widget = qobject_cast(child)) { - RenderWidget(p, widget, widget->pos()); - } - } - } - return result; -} - -void InnerDropdown::opacityAnimationCallback() { - update(); - if (!_a_opacity.animating()) { - if (_hiding) { - _hiding = false; - hideFinished(); - } else if (!_a_show.animating()) { - showChildren(); - } - } -} - -void InnerDropdown::showAnimationCallback() { - update(); -} - -bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - otherEnter(); - } else if (e->type() == QEvent::Leave) { - otherLeave(); - } else if (e->type() == QEvent::MouseButtonRelease && static_cast(e)->button() == Qt::LeftButton) { - if (isHidden() || _hiding) { - otherEnter(); - } else { - otherLeave(); - } - } - return false; -} - -int InnerDropdown::resizeGetHeight(int newWidth) { - auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); - if (auto widget = static_cast(_scroll->widget())) { - auto containerWidth = newWidth - _st.padding.left() - _st.padding.right() - _st.scrollMargin.left() - _st.scrollMargin.right(); - widget->resizeToWidth(containerWidth); - newHeight += widget->height(); - } - if (_maxHeight > 0) { - accumulate_min(newHeight, _maxHeight); - } - return newHeight; -} - -InnerDropdown::Container::Container(QWidget *parent, object_ptr child, const style::InnerDropdown &st) : TWidget(parent) -, _child(std::move(child)) -, _st(st) { - _child->setParent(this); - _child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); -} - -void InnerDropdown::Container::visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) { - setChildVisibleTopBottom(_child, visibleTop, visibleBottom); -} - -void InnerDropdown::Container::resizeToContent() { - auto newWidth = _st.scrollPadding.left() + _st.scrollPadding.right(); - auto newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom(); - if (auto child = static_cast(children().front())) { - newWidth += child->width(); - newHeight += child->height(); - } - if (newWidth != width() || newHeight != height()) { - resize(newWidth, newHeight); - } -} - -int InnerDropdown::Container::resizeGetHeight(int newWidth) { - auto innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right(); - auto result = _st.scrollPadding.top() + _st.scrollPadding.bottom(); - _child->resizeToWidth(innerWidth); - _child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); - result += _child->height(); - return result; -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h deleted file mode 100644 index 5449dc1a1..000000000 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" -#include "ui/rp_widget.h" -#include "ui/round_rect.h" -#include "ui/effects/animations.h" -#include "ui/effects/panel_animation.h" -#include "base/object_ptr.h" - -#include - -namespace Ui { - -class ScrollArea; - -class InnerDropdown : public RpWidget { - Q_OBJECT - -public: - InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown); - - template - QPointer setOwnedWidget(object_ptr widget) { - auto result = doSetOwnedWidget(std::move(widget)); - return QPointer(static_cast(result.data())); - } - - bool overlaps(const QRect &globalRect) { - if (isHidden() || _a_show.animating() || _a_opacity.animating()) return false; - - return rect().marginsRemoved(_st.padding).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); - } - - void setAutoHiding(bool autoHiding) { - _autoHiding = autoHiding; - } - void setMaxHeight(int newMaxHeight); - void resizeToContent(); - - void otherEnter(); - void otherLeave(); - - void setShowStartCallback(Fn callback) { - _showStartCallback = std::move(callback); - } - void setHideStartCallback(Fn callback) { - _hideStartCallback = std::move(callback); - } - void setHiddenCallback(Fn callback) { - _hiddenCallback = std::move(callback); - } - - bool isHiding() const { - return _hiding && _a_opacity.animating(); - } - - enum class HideOption { - Default, - IgnoreShow, - }; - void showAnimated(); - void setOrigin(PanelAnimation::Origin origin); - void showAnimated(PanelAnimation::Origin origin); - void hideAnimated(HideOption option = HideOption::Default); - void finishAnimating(); - void showFast(); - void hideFast(); - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - bool eventFilter(QObject *obj, QEvent *e) override; - - int resizeGetHeight(int newWidth) override; - -private slots: - void onHideAnimated() { - hideAnimated(); - } - void onScroll(); - -private: - QPointer doSetOwnedWidget(object_ptr widget); - QImage grabForPanelAnimation(); - void startShowAnimation(); - void startOpacityAnimation(bool hiding); - void prepareCache(); - - class Container; - void showAnimationCallback(); - void opacityAnimationCallback(); - - void hideFinished(); - void showStarted(); - - void updateHeight(); - - const style::InnerDropdown &_st; - - RoundRect _roundRect; - PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; - std::unique_ptr _showAnimation; - Animations::Simple _a_show; - - bool _autoHiding = true; - bool _hiding = false; - QPixmap _cache; - Animations::Simple _a_opacity; - - QTimer _hideTimer; - bool _ignoreShowEvents = false; - Fn _showStartCallback; - Fn _hideStartCallback; - Fn _hiddenCallback; - - object_ptr _scroll; - - int _maxHeight = 0; - -}; - -class InnerDropdown::Container : public TWidget { -public: - Container(QWidget *parent, object_ptr child, const style::InnerDropdown &st); - - void resizeToContent(); - -protected: - int resizeGetHeight(int newWidth) override; - void visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) override; - -private: - object_ptr _child; - const style::InnerDropdown &_st; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp deleted file mode 100644 index 1c0b13c0b..000000000 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ /dev/null @@ -1,4023 +0,0 @@ -/* -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 "ui/widgets/input_fields.h" - -#include "ui/widgets/popup_menu.h" -#include "ui/text/text.h" -#include "ui/emoji_config.h" -#include "ui/ui_utility.h" -#include "base/openssl_help.h" -#include "chat_helpers/emoji_suggestions_helper.h" -#include "platform/platform_info.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Ui { -namespace { - -constexpr auto kInstantReplaceRandomId = QTextFormat::UserProperty; -constexpr auto kInstantReplaceWhatId = QTextFormat::UserProperty + 1; -constexpr auto kInstantReplaceWithId = QTextFormat::UserProperty + 2; -constexpr auto kReplaceTagId = QTextFormat::UserProperty + 3; -constexpr auto kTagProperty = QTextFormat::UserProperty + 4; -const auto kObjectReplacementCh = QChar(QChar::ObjectReplacementCharacter); -const auto kObjectReplacement = QString::fromRawData( - &kObjectReplacementCh, - 1); -const auto &kTagBold = InputField::kTagBold; -const auto &kTagItalic = InputField::kTagItalic; -const auto &kTagUnderline = InputField::kTagUnderline; -const auto &kTagStrikeOut = InputField::kTagStrikeOut; -const auto &kTagCode = InputField::kTagCode; -const auto &kTagPre = InputField::kTagPre; -const auto kNewlineChars = QString("\r\n") - + QChar(0xfdd0) // QTextBeginningOfFrame - + QChar(0xfdd1) // QTextEndOfFrame - + QChar(QChar::ParagraphSeparator) - + QChar(QChar::LineSeparator); - -class InputDocument : public QTextDocument { -public: - InputDocument(QObject *parent, const style::InputField &st); - -protected: - QVariant loadResource(int type, const QUrl &name) override; - -private: - const style::InputField &_st; - std::map _emojiCache; - rpl::lifetime _lifetime; - -}; - -InputDocument::InputDocument(QObject *parent, const style::InputField &st) -: QTextDocument(parent) -, _st(st) { - Emoji::Updated( - ) | rpl::start_with_next([=] { - _emojiCache.clear(); - }, _lifetime); -} - -QVariant InputDocument::loadResource(int type, const QUrl &name) { - if (type != QTextDocument::ImageResource - || name.scheme() != qstr("emoji")) { - return QTextDocument::loadResource(type, name); - } - const auto i = _emojiCache.find(name); - if (i != _emojiCache.end()) { - return i->second; - } - auto result = [&] { - if (const auto emoji = Emoji::FromUrl(name.toDisplayString())) { - const auto height = std::max( - _st.font->height * style::DevicePixelRatio(), - Emoji::GetSizeNormal()); - return QVariant(Emoji::SinglePixmap(emoji, height)); - } - return QVariant(); - }(); - _emojiCache.emplace(name, result); - return result; -} - -bool IsNewline(QChar ch) { - return (kNewlineChars.indexOf(ch) >= 0); -} - -QString GetFullSimpleTextTag(const TextWithTags &textWithTags) { - const auto &text = textWithTags.text; - const auto &tags = textWithTags.tags; - const auto tag = (tags.size() == 1) ? tags[0] : TextWithTags::Tag(); - auto from = 0; - auto till = int(text.size()); - for (; from != till; ++from) { - if (!IsNewline(text[from]) && !chIsSpace(text[from])) { - break; - } - } - while (till != from) { - if (!IsNewline(text[till - 1]) && !chIsSpace(text[till - 1])) { - break; - } - --till; - } - return ((tag.offset <= from) - && (tag.offset + tag.length >= till)) - ? (tag.id == kTagPre ? kTagCode : tag.id) - : QString(); -} - -class TagAccumulator { -public: - TagAccumulator(TextWithTags::Tags &tags) : _tags(tags) { - } - - bool changed() const { - return _changed; - } - - void feed(const QString &randomTagId, int currentPosition) { - if (randomTagId == _currentTagId) { - return; - } - - if (!_currentTagId.isEmpty()) { - const auto tag = TextWithTags::Tag { - _currentStart, - currentPosition - _currentStart, - _currentTagId - }; - if (tag.length > 0) { - if (_currentTag >= _tags.size()) { - _changed = true; - _tags.push_back(tag); - } else if (_tags[_currentTag] != tag) { - _changed = true; - _tags[_currentTag] = tag; - } - ++_currentTag; - } - } - _currentTagId = randomTagId; - _currentStart = currentPosition; - }; - - void finish() { - if (_currentTag < _tags.size()) { - _tags.resize(_currentTag); - _changed = true; - } - } - -private: - TextWithTags::Tags &_tags; - bool _changed = false; - - int _currentTag = 0; - int _currentStart = 0; - QString _currentTagId; - -}; - -struct TagStartExpression { - QString tag; - QString goodBefore; - QString badAfter; - QString badBefore; - QString goodAfter; -}; - -constexpr auto kTagBoldIndex = 0; -constexpr auto kTagItalicIndex = 1; -//constexpr auto kTagUnderlineIndex = 2; -constexpr auto kTagStrikeOutIndex = 2; -constexpr auto kTagCodeIndex = 3; -constexpr auto kTagPreIndex = 4; -constexpr auto kInvalidPosition = std::numeric_limits::max() / 2; - -class TagSearchItem { -public: - enum class Edge { - Open, - Close, - }; - - int matchPosition(Edge edge) const { - return (_position >= 0) ? _position : kInvalidPosition; - } - - void applyOffset(int offset) { - if (_position < offset) { - _position = -1; - } - accumulate_max(_offset, offset); - } - - void fill( - const QString &text, - Edge edge, - const TagStartExpression &expression) { - const auto length = text.size(); - const auto &tag = expression.tag; - const auto tagLength = tag.size(); - const auto isGoodBefore = [&](QChar ch) { - return expression.goodBefore.isEmpty() - || (expression.goodBefore.indexOf(ch) >= 0); - }; - const auto isBadAfter = [&](QChar ch) { - return !expression.badAfter.isEmpty() - && (expression.badAfter.indexOf(ch) >= 0); - }; - const auto isBadBefore = [&](QChar ch) { - return !expression.badBefore.isEmpty() - && (expression.badBefore.indexOf(ch) >= 0); - }; - const auto isGoodAfter = [&](QChar ch) { - return expression.goodAfter.isEmpty() - || (expression.goodAfter.indexOf(ch) >= 0); - }; - const auto check = [&](Edge edge) { - if (_position > 0) { - const auto before = text[_position - 1]; - if ((edge == Edge::Open && !isGoodBefore(before)) - || (edge == Edge::Close && isBadBefore(before))) { - return false; - } - } - if (_position + tagLength < length) { - const auto after = text[_position + tagLength]; - if ((edge == Edge::Open && isBadAfter(after)) - || (edge == Edge::Close && !isGoodAfter(after))) { - return false; - } - } - return true; - }; - const auto edgeIndex = static_cast(edge); - if (_position >= 0) { - if (_checked[edgeIndex]) { - return; - } else if (check(edge)) { - _checked[edgeIndex] = true; - return; - } else { - _checked = { { false, false } }; - } - } - while (true) { - _position = text.indexOf(tag, _offset); - if (_position < 0) { - _offset = _position = kInvalidPosition; - break; - } - _offset = _position + tagLength; - if (check(edge)) { - break; - } else { - continue; - } - } - if (_position == kInvalidPosition) { - _checked = { { true, true } }; - } else { - _checked = { { false, false } }; - _checked[edgeIndex] = true; - } - } - -private: - int _offset = 0; - int _position = -1; - std::array _checked = { { false, false } }; - -}; - -const std::vector &TagStartExpressions() { - static auto cached = std::vector { - { - kTagBold, - TextUtilities::MarkdownBoldGoodBefore(), - TextUtilities::MarkdownBoldBadAfter(), - TextUtilities::MarkdownBoldBadAfter(), - TextUtilities::MarkdownBoldGoodBefore() - }, - { - kTagItalic, - TextUtilities::MarkdownItalicGoodBefore(), - TextUtilities::MarkdownItalicBadAfter(), - TextUtilities::MarkdownItalicBadAfter(), - TextUtilities::MarkdownItalicGoodBefore() - }, - //{ - // kTagUnderline, - // TextUtilities::MarkdownUnderlineGoodBefore(), - // TextUtilities::MarkdownUnderlineBadAfter(), - // TextUtilities::MarkdownUnderlineBadAfter(), - // TextUtilities::MarkdownUnderlineGoodBefore() - //}, - { - kTagStrikeOut, - TextUtilities::MarkdownStrikeOutGoodBefore(), - TextUtilities::MarkdownStrikeOutBadAfter(), - TextUtilities::MarkdownStrikeOutBadAfter(), - QString(), - }, - { - kTagCode, - TextUtilities::MarkdownCodeGoodBefore(), - TextUtilities::MarkdownCodeBadAfter(), - TextUtilities::MarkdownCodeBadAfter(), - TextUtilities::MarkdownCodeGoodBefore() - }, - { - kTagPre, - TextUtilities::MarkdownPreGoodBefore(), - TextUtilities::MarkdownPreBadAfter(), - TextUtilities::MarkdownPreBadAfter(), - TextUtilities::MarkdownPreGoodBefore() - }, - }; - return cached; -} - -const std::map &TagIndices() { - static auto cached = std::map { - { kTagBold, kTagBoldIndex }, - { kTagItalic, kTagItalicIndex }, - //{ kTagUnderline, kTagUnderlineIndex }, - { kTagStrikeOut, kTagStrikeOutIndex }, - { kTagCode, kTagCodeIndex }, - { kTagPre, kTagPreIndex }, - }; - return cached; -} - -bool DoesTagFinishByNewline(const QString &tag) { - return (tag == kTagCode); -} - -class MarkdownTagAccumulator { -public: - using Edge = TagSearchItem::Edge; - - MarkdownTagAccumulator(std::vector *tags) - : _tags(tags) - , _expressions(TagStartExpressions()) - , _tagIndices(TagIndices()) - , _items(_expressions.size()) { - } - - // Here we use the fact that text either contains only emoji - // { adjustedTextLength = text.size() * (emojiLength - 1) } - // or contains no emoji at all and can have tag edges in the middle - // { adjustedTextLength = 0 }. - // - // Otherwise we would have to pass emoji positions inside text. - void feed( - const QString &text, - int adjustedTextLength, - const QString &textTag) { - if (!_tags) { - return; - } - const auto guard = gsl::finally([&] { - _currentInternalLength += text.size(); - _currentAdjustedLength += adjustedTextLength; - }); - if (!textTag.isEmpty()) { - finishTags(); - return; - } - for (auto &item : _items) { - item = TagSearchItem(); - } - auto tryFinishTag = _currentTag; - while (true) { - for (; tryFinishTag != _currentFreeTag; ++tryFinishTag) { - auto &tag = (*_tags)[tryFinishTag]; - if (tag.internalLength >= 0) { - continue; - } - - const auto i = _tagIndices.find(tag.tag); - Assert(i != end(_tagIndices)); - const auto tagIndex = i->second; - - const auto atLeastOffset = - tag.internalStart - + tag.tag.size() - + 1 - - _currentInternalLength; - _items[tagIndex].applyOffset(atLeastOffset); - - fillItem( - tagIndex, - text, - Edge::Close); - if (finishByNewline(tryFinishTag, text, tagIndex)) { - continue; - } - const auto position = matchPosition(tagIndex, Edge::Close); - if (position < kInvalidPosition) { - const auto till = position + tag.tag.size(); - finishTag(tryFinishTag, till, true); - _items[tagIndex].applyOffset(till); - } - } - for (auto i = 0, count = int(_items.size()); i != count; ++i) { - fillItem(i, text, Edge::Open); - } - const auto min = minIndex(Edge::Open); - if (min < 0) { - return; - } - startTag(matchPosition(min, Edge::Open), _expressions[min].tag); - } - } - - void finish() { - if (!_tags) { - return; - } - finishTags(); - if (_currentTag < _tags->size()) { - _tags->resize(_currentTag); - } - } - -private: - void finishTag(int index, int offsetFromAccumulated, bool closed) { - Expects(_tags != nullptr); - Expects(index >= 0 && index < _tags->size()); - - auto &tag = (*_tags)[index]; - if (tag.internalLength < 0) { - tag.internalLength = _currentInternalLength - + offsetFromAccumulated - - tag.internalStart; - tag.adjustedLength = _currentAdjustedLength - + offsetFromAccumulated - - tag.adjustedStart; - tag.closed = closed; - } - if (index == _currentTag) { - ++_currentTag; - } - } - bool finishByNewline( - int index, - const QString &text, - int tagIndex) { - Expects(_tags != nullptr); - Expects(index >= 0 && index < _tags->size()); - - auto &tag = (*_tags)[index]; - - if (!DoesTagFinishByNewline(tag.tag)) { - return false; - } - const auto endPosition = newlinePosition( - text, - std::max(0, tag.internalStart + 1 - _currentInternalLength)); - if (matchPosition(tagIndex, Edge::Close) <= endPosition) { - return false; - } - finishTag(index, endPosition, false); - return true; - } - void finishTags() { - while (_currentTag != _currentFreeTag) { - finishTag(_currentTag, 0, false); - } - } - void startTag(int offsetFromAccumulated, const QString &tag) { - Expects(_tags != nullptr); - - const auto newTag = InputField::MarkdownTag{ - _currentInternalLength + offsetFromAccumulated, - -1, - _currentAdjustedLength + offsetFromAccumulated, - -1, - false, - tag - }; - if (_currentFreeTag < _tags->size()) { - (*_tags)[_currentFreeTag] = newTag; - } else { - _tags->push_back(newTag); - } - ++_currentFreeTag; - } - void fillItem(int index, const QString &text, Edge edge) { - Expects(index >= 0 && index < _items.size()); - - _items[index].fill(text, edge, _expressions[index]); - } - int matchPosition(int index, Edge edge) const { - Expects(index >= 0 && index < _items.size()); - - return _items[index].matchPosition(edge); - } - int newlinePosition(const QString &text, int offset) const { - const auto length = text.size(); - if (offset < length) { - const auto begin = text.data(); - const auto end = begin + length; - for (auto ch = begin + offset; ch != end; ++ch) { - if (IsNewline(*ch)) { - return (ch - begin); - } - } - } - return kInvalidPosition; - } - int minIndex(Edge edge) const { - auto result = -1; - auto minPosition = kInvalidPosition; - for (auto i = 0, count = int(_items.size()); i != count; ++i) { - const auto position = matchPosition(i, edge); - if (position < minPosition) { - minPosition = position; - result = i; - } - } - return result; - } - int minIndexForFinish(const std::vector &indices) const { - const auto tagIndex = indices[0]; - auto result = -1; - auto minPosition = kInvalidPosition; - for (auto i : indices) { - const auto edge = (i == tagIndex) ? Edge::Close : Edge::Open; - const auto position = matchPosition(i, edge); - if (position < minPosition) { - minPosition = position; - result = i; - } - } - return result; - } - - std::vector *_tags = nullptr; - const std::vector &_expressions; - const std::map &_tagIndices; - std::vector _items; - - int _currentTag = 0; - int _currentFreeTag = 0; - int _currentInternalLength = 0; - int _currentAdjustedLength = 0; - -}; - -template -class InputStyle : public QCommonStyle { -public: - InputStyle() { - setParent(QCoreApplication::instance()); - } - - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override { - } - QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = nullptr) const override { - switch (r) { - case SE_LineEditContents: - const auto w = widget ? qobject_cast(widget) : nullptr; - return w ? w->getTextRect() : QCommonStyle::subElementRect(r, opt, widget); - break; - } - return QCommonStyle::subElementRect(r, opt, widget); - } - - static InputStyle *instance() { - if (!_instance) { - if (!QGuiApplication::instance()) { - return nullptr; - } - _instance = new InputStyle(); - } - return _instance; - } - - ~InputStyle() { - _instance = nullptr; - } - -private: - static InputStyle *_instance; - -}; - -template -InputStyle *InputStyle::_instance = nullptr; - -template -QString AccumulateText(Iterator begin, Iterator end) { - auto result = QString(); - result.reserve(end - begin); - for (auto i = end; i != begin;) { - result.push_back(*--i); - } - return result; -} - -QTextImageFormat PrepareEmojiFormat(EmojiPtr emoji, const QFont &font) { - const auto factor = style::DevicePixelRatio(); - const auto size = Emoji::GetSizeNormal(); - const auto width = size + st::emojiPadding * factor * 2; - const auto height = std::max(QFontMetrics(font).height() * factor, size); - auto result = QTextImageFormat(); - result.setWidth(width / factor); - result.setHeight(height / factor); - result.setName(emoji->toUrl()); - result.setVerticalAlignment(QTextCharFormat::AlignBaseline); - return result; -} - -// Optimization: with null page size document does not re-layout -// on each insertText / mergeCharFormat. -void PrepareFormattingOptimization(not_null document) { - if (!document->pageSize().isNull()) { - document->setPageSize(QSizeF(0, 0)); - } -} - -void RemoveDocumentTags( - const style::InputField &st, - not_null document, - int from, - int end) { - auto cursor = QTextCursor(document->docHandle(), from); - cursor.setPosition(end, QTextCursor::KeepAnchor); - - auto format = QTextCharFormat(); - format.setProperty(kTagProperty, QString()); - format.setProperty(kReplaceTagId, QString()); - format.setForeground(st.textFg); - format.setFont(st.font); - cursor.mergeCharFormat(format); -} - -style::font AdjustFont( - const style::font &font, - const style::font &original) { - return (font->size() != original->size() - || font->flags() != original->flags()) - ? style::font(original->size(), original->flags(), font->family()) - : font; -} - -bool IsValidMarkdownLink(const QString &link) { - return (link.indexOf('.') >= 0) || (link.indexOf(':') >= 0); -} - -QTextCharFormat PrepareTagFormat( - const style::InputField &st, - QString tag) { - auto result = QTextCharFormat(); - if (IsValidMarkdownLink(tag)) { - result.setForeground(st::defaultTextPalette.linkFg); - result.setFont(st.font); - } else if (tag == kTagBold) { - auto semibold = st::semiboldFont; - if (semibold->size() != st.font->size() - || semibold->flags() != st.font->flags()) { - semibold = style::font( - st.font->size(), - st.font->flags(), - semibold->family()); - } - result.setForeground(st.textFg); - result.setFont(AdjustFont(st::semiboldFont, st.font)); - } else if (tag == kTagItalic) { - result.setForeground(st.textFg); - result.setFont(st.font->italic()); - } else if (tag == kTagUnderline) { - result.setForeground(st.textFg); - result.setFont(st.font->underline()); - } else if (tag == kTagStrikeOut) { - result.setForeground(st.textFg); - result.setFont(st.font->strikeout()); - } else if (tag == kTagCode || tag == kTagPre) { - result.setForeground(st::defaultTextPalette.monoFg); - result.setFont(AdjustFont(style::MonospaceFont(), st.font)); - } else { - result.setForeground(st.textFg); - result.setFont(st.font); - } - result.setProperty(kTagProperty, tag); - return result; -} - -void ApplyTagFormat(QTextCharFormat &to, const QTextCharFormat &from) { - to.setProperty(kTagProperty, from.property(kTagProperty)); - to.setProperty(kReplaceTagId, from.property(kReplaceTagId)); - to.setFont(from.font()); - to.setForeground(from.foreground()); -} - -// Returns the position of the first inserted tag or "changedEnd" value if none found. -int ProcessInsertedTags( - const style::InputField &st, - not_null document, - int changedPosition, - int changedEnd, - const TextWithTags::Tags &tags, - InputField::TagMimeProcessor *processor) { - int firstTagStart = changedEnd; - int applyNoTagFrom = changedEnd; - for (const auto &tag : tags) { - int tagFrom = changedPosition + tag.offset; - int tagTo = tagFrom + tag.length; - accumulate_max(tagFrom, changedPosition); - accumulate_min(tagTo, changedEnd); - auto tagId = processor ? processor->tagFromMimeTag(tag.id) : tag.id; - if (tagTo > tagFrom && !tagId.isEmpty()) { - accumulate_min(firstTagStart, tagFrom); - - PrepareFormattingOptimization(document); - - if (applyNoTagFrom < tagFrom) { - RemoveDocumentTags( - st, - document, - applyNoTagFrom, - tagFrom); - } - QTextCursor c(document->docHandle(), 0); - c.setPosition(tagFrom); - c.setPosition(tagTo, QTextCursor::KeepAnchor); - - c.mergeCharFormat(PrepareTagFormat(st, tagId)); - - applyNoTagFrom = tagTo; - } - } - if (applyNoTagFrom < changedEnd) { - RemoveDocumentTags(st, document, applyNoTagFrom, changedEnd); - } - - return firstTagStart; -} - -// When inserting a part of text inside a tag we need to have -// a way to know if the insertion replaced the end of the tag -// or it was strictly inside (in the middle) of the tag. -bool WasInsertTillTheEndOfTag( - QTextBlock block, - QTextBlock::iterator fragmentIt, - int insertionEnd) { - const auto format = fragmentIt.fragment().charFormat(); - const auto insertTagName = format.property(kTagProperty); - while (true) { - for (; !fragmentIt.atEnd(); ++fragmentIt) { - const auto fragment = fragmentIt.fragment(); - const auto position = fragment.position(); - const auto outsideInsertion = (position >= insertionEnd); - if (outsideInsertion) { - const auto format = fragment.charFormat(); - return (format.property(kTagProperty) != insertTagName); - } - const auto end = position + fragment.length(); - const auto notFullFragmentInserted = (end > insertionEnd); - if (notFullFragmentInserted) { - return false; - } - } - if (block.isValid()) { - fragmentIt = block.begin(); - block = block.next(); - } else { - break; - } - } - // Insertion goes till the end of the text => not strictly inside a tag. - return true; -} - -struct FormattingAction { - enum class Type { - Invalid, - InsertEmoji, - TildeFont, - RemoveTag, - RemoveNewline, - ClearInstantReplace, - }; - - Type type = Type::Invalid; - EmojiPtr emoji = nullptr; - bool isTilde = false; - QString tildeTag; - int intervalStart = 0; - int intervalEnd = 0; - -}; - -} // namespace - -// kTagUnderline is not used for Markdown. - -const QString InputField::kTagBold = QStringLiteral("**"); -const QString InputField::kTagItalic = QStringLiteral("__"); -const QString InputField::kTagUnderline = QStringLiteral("^^"); -const QString InputField::kTagStrikeOut = QStringLiteral("~~"); -const QString InputField::kTagCode = QStringLiteral("`"); -const QString InputField::kTagPre = QStringLiteral("```"); - -class InputField::Inner final : public QTextEdit { -public: - Inner(not_null parent) : QTextEdit(parent) { - } - -protected: - bool viewportEvent(QEvent *e) override { - return outer()->viewportEventInner(e); - } - void focusInEvent(QFocusEvent *e) override { - return outer()->focusInEventInner(e); - } - void focusOutEvent(QFocusEvent *e) override { - return outer()->focusOutEventInner(e); - } - void keyPressEvent(QKeyEvent *e) override { - return outer()->keyPressEventInner(e); - } - void contextMenuEvent(QContextMenuEvent *e) override { - return outer()->contextMenuEventInner(e); - } - void dropEvent(QDropEvent *e) override { - return outer()->dropEventInner(e); - } - void inputMethodEvent(QInputMethodEvent *e) override { - return outer()->inputMethodEventInner(e); - } - - bool canInsertFromMimeData(const QMimeData *source) const override { - return outer()->canInsertFromMimeDataInner(source); - } - void insertFromMimeData(const QMimeData *source) override { - return outer()->insertFromMimeDataInner(source); - } - QMimeData *createMimeDataFromSelection() const override { - return outer()->createMimeDataFromSelectionInner(); - } - -private: - not_null outer() const { - return static_cast(parentWidget()); - } - friend class InputField; - -}; - -void InsertEmojiAtCursor(QTextCursor cursor, EmojiPtr emoji) { - const auto currentFormat = cursor.charFormat(); - auto format = PrepareEmojiFormat(emoji, currentFormat.font()); - ApplyTagFormat(format, currentFormat); - cursor.insertText(kObjectReplacement, format); -} - -void InstantReplaces::add(const QString &what, const QString &with) { - auto node = &reverseMap; - for (auto i = what.end(), b = what.begin(); i != b;) { - node = &node->tail.emplace(*--i, Node()).first->second; - } - node->text = with; - accumulate_max(maxLength, int(what.size())); -} - -const InstantReplaces &InstantReplaces::Default() { - static const auto result = [] { - auto result = InstantReplaces(); - result.add("--", QString(1, QChar(8212))); - result.add("<<", QString(1, QChar(171))); - result.add(">>", QString(1, QChar(187))); - result.add( - ":shrug:", - QChar(175) + QString("\\_(") + QChar(12484) + ")_/" + QChar(175)); - result.add(":o ", QString(1, QChar(0xD83D)) + QChar(0xDE28)); - result.add("xD ", QString(1, QChar(0xD83D)) + QChar(0xDE06)); - const auto &replacements = Emoji::internal::GetAllReplacements(); - for (const auto &one : replacements) { - const auto with = Emoji::QStringFromUTF16(one.emoji); - const auto what = Emoji::QStringFromUTF16(one.replacement); - result.add(what, with); - } - const auto &pairs = Emoji::internal::GetReplacementPairs(); - for (const auto &[what, index] : pairs) { - const auto emoji = Emoji::internal::ByIndex(index); - Assert(emoji != nullptr); - result.add(what, emoji->text()); - } - return result; - }(); - return result; -} - -const InstantReplaces &InstantReplaces::TextOnly() { - static const auto result = [] { - auto result = InstantReplaces(); - result.add("--", QString(1, QChar(8212))); - result.add("<<", QString(1, QChar(171))); - result.add(">>", QString(1, QChar(187))); - result.add( - ":shrug:", - QChar(175) + QString("\\_(") + QChar(12484) + ")_/" + QChar(175)); - return result; - }(); - return result; -} - -FlatInput::FlatInput( - QWidget *parent, - const style::FlatInput &st, - rpl::producer placeholder, - const QString &v) -: RpWidgetWrap(v, parent) -, _oldtext(v) -, _placeholderFull(std::move(placeholder)) -, _placeholderVisible(!v.length()) -, _st(st) -, _textMrg(_st.textMrg) { - setCursor(style::cur_text); - resize(_st.width, _st.height); - - setFont(_st.font->f); - setAlignment(_st.align); - - _placeholderFull.value( - ) | rpl::start_with_next([=](const QString &text) { - refreshPlaceholder(text); - }, lifetime()); - - style::PaletteChanged( - ) | rpl::start_with_next([=] { - updatePalette(); - }, lifetime()); - updatePalette(); - - connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChange(const QString &))); - connect(this, SIGNAL(textEdited(const QString &)), this, SLOT(onTextEdited())); - connect(this, &FlatInput::selectionChanged, [] { - Integration::Instance().textActionsUpdated(); - }); - - setStyle(InputStyle::instance()); - QLineEdit::setTextMargins(0, 0, 0, 0); - setContentsMargins(0, 0, 0, 0); - - setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); -} - -void FlatInput::updatePalette() { - auto p = palette(); - p.setColor(QPalette::Text, _st.textColor->c); - setPalette(p); -} - -void FlatInput::customUpDown(bool custom) { - _customUpDown = custom; -} - -void FlatInput::onTouchTimer() { - _touchRightButton = true; -} - -bool FlatInput::eventHook(QEvent *e) { - if (e->type() == QEvent::TouchBegin - || e->type() == QEvent::TouchUpdate - || e->type() == QEvent::TouchEnd - || e->type() == QEvent::TouchCancel) { - const auto ev = static_cast(e); - if (ev->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(ev); - } - } - return Parent::eventHook(e); -} - -void FlatInput::touchEvent(QTouchEvent *e) { - switch (e->type()) { - case QEvent::TouchBegin: { - if (_touchPress || e->touchPoints().isEmpty()) return; - _touchTimer.start(QApplication::startDragTime()); - _touchPress = true; - _touchMove = _touchRightButton = false; - _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); - } break; - - case QEvent::TouchUpdate: { - if (!_touchPress || e->touchPoints().isEmpty()) return; - if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { - _touchMove = true; - } - } break; - - case QEvent::TouchEnd: { - if (!_touchPress) return; - auto weak = MakeWeak(this); - if (!_touchMove && window()) { - Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); - QPoint mapped(mapFromGlobal(_touchStart)), winMapped(window()->mapFromGlobal(_touchStart)); - - if (_touchRightButton) { - QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart); - contextMenuEvent(&contextEvent); - } - } - if (weak) { - _touchTimer.stop(); - _touchPress = _touchMove = _touchRightButton = false; - } - } break; - - case QEvent::TouchCancel: { - _touchPress = false; - _touchTimer.stop(); - } break; - } -} - -void FlatInput::setTextMrg(const QMargins &textMrg) { - _textMrg = textMrg; - refreshPlaceholder(_placeholderFull.current()); - update(); -} - -QRect FlatInput::getTextRect() const { - return rect().marginsRemoved(_textMrg + QMargins(-2, -1, -2, -1)); -} - -void FlatInput::finishAnimations() { - _placeholderFocusedAnimation.stop(); - _placeholderVisibleAnimation.stop(); -} - -void FlatInput::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto ms = crl::now(); - auto placeholderFocused = _placeholderFocusedAnimation.value(_focused ? 1. : 0.); - auto pen = anim::pen(_st.borderColor, _st.borderActive, placeholderFocused); - pen.setWidth(_st.borderWidth); - p.setPen(pen); - p.setBrush(anim::brush(_st.bgColor, _st.bgActive, placeholderFocused)); - { - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(QRectF(0, 0, width(), height()).marginsRemoved(QMarginsF(_st.borderWidth / 2., _st.borderWidth / 2., _st.borderWidth / 2., _st.borderWidth / 2.)), st::buttonRadius - (_st.borderWidth / 2.), st::buttonRadius - (_st.borderWidth / 2.)); - } - - if (!_st.icon.empty()) { - _st.icon.paint(p, 0, 0, width()); - } - - const auto placeholderOpacity = _placeholderVisibleAnimation.value( - _placeholderVisible ? 1. : 0.); - if (placeholderOpacity > 0.) { - p.setOpacity(placeholderOpacity); - - auto left = anim::interpolate(_st.phShift, 0, placeholderOpacity); - - p.save(); - p.setClipRect(rect()); - QRect phRect(placeholderRect()); - phRect.moveLeft(phRect.left() + left); - phPrepare(p, placeholderFocused); - p.drawText(phRect, _placeholder, QTextOption(_st.phAlign)); - p.restore(); - } - QLineEdit::paintEvent(e); -} - -void FlatInput::focusInEvent(QFocusEvent *e) { - if (!_focused) { - _focused = true; - _placeholderFocusedAnimation.start( - [=] { update(); }, - 0., - 1., - _st.phDuration); - update(); - } - QLineEdit::focusInEvent(e); - emit focused(); -} - -void FlatInput::focusOutEvent(QFocusEvent *e) { - if (_focused) { - _focused = false; - _placeholderFocusedAnimation.start( - [=] { update(); }, - 1., - 0., - _st.phDuration); - update(); - } - QLineEdit::focusOutEvent(e); - emit blurred(); -} - -void FlatInput::resizeEvent(QResizeEvent *e) { - refreshPlaceholder(_placeholderFull.current()); - return QLineEdit::resizeEvent(e); -} - -void FlatInput::setPlaceholder(rpl::producer placeholder) { - _placeholderFull = std::move(placeholder); -} - -void FlatInput::refreshPlaceholder(const QString &text) { - const auto availw = width() - _textMrg.left() - _textMrg.right() - _st.phPos.x() - 1; - if (_st.font->width(text) > availw) { - _placeholder = _st.font->elided(text, availw); - } else { - _placeholder = text; - } - update(); -} - -void FlatInput::contextMenuEvent(QContextMenuEvent *e) { - if (auto menu = createStandardContextMenu()) { - (new PopupMenu(this, menu))->popup(e->globalPos()); - } -} - -QSize FlatInput::sizeHint() const { - return geometry().size(); -} - -QSize FlatInput::minimumSizeHint() const { - return geometry().size(); -} - -void FlatInput::updatePlaceholder() { - auto hasText = !text().isEmpty(); - if (!hasText) { - hasText = _lastPreEditTextNotEmpty; - } else { - _lastPreEditTextNotEmpty = false; - } - auto placeholderVisible = !hasText; - if (_placeholderVisible != placeholderVisible) { - _placeholderVisible = placeholderVisible; - _placeholderVisibleAnimation.start( - [=] { update(); }, - _placeholderVisible ? 0. : 1., - _placeholderVisible ? 1. : 0., - _st.phDuration); - } -} - -void FlatInput::inputMethodEvent(QInputMethodEvent *e) { - QLineEdit::inputMethodEvent(e); - auto lastPreEditTextNotEmpty = !e->preeditString().isEmpty(); - if (_lastPreEditTextNotEmpty != lastPreEditTextNotEmpty) { - _lastPreEditTextNotEmpty = lastPreEditTextNotEmpty; - updatePlaceholder(); - } -} - -QRect FlatInput::placeholderRect() const { - return QRect(_textMrg.left() + _st.phPos.x(), _textMrg.top() + _st.phPos.y(), width() - _textMrg.left() - _textMrg.right(), height() - _textMrg.top() - _textMrg.bottom()); -} - -void FlatInput::correctValue(const QString &was, QString &now) { -} - -void FlatInput::phPrepare(QPainter &p, float64 placeholderFocused) { - p.setFont(_st.font); - p.setPen(anim::pen(_st.phColor, _st.phFocusColor, placeholderFocused)); -} - -void FlatInput::keyPressEvent(QKeyEvent *e) { - QString wasText(_oldtext); - - if (_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)) { - e->ignore(); - } else { - QLineEdit::keyPressEvent(e); - } - - QString newText(text()); - if (wasText == newText) { // call correct manually - correctValue(wasText, newText); - _oldtext = newText; - if (wasText != _oldtext) emit changed(); - updatePlaceholder(); - } - if (e->key() == Qt::Key_Escape) { - emit cancelled(); - } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - emit submitted(e->modifiers()); -#ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - auto selected = selectedText(); - if (!selected.isEmpty() && echoMode() == QLineEdit::Normal) { - QGuiApplication::clipboard()->setText(selected, QClipboard::FindBuffer); - } -#endif // Q_OS_MAC - } -} - -void FlatInput::onTextEdited() { - QString wasText(_oldtext), newText(text()); - - correctValue(wasText, newText); - _oldtext = newText; - if (wasText != _oldtext) emit changed(); - updatePlaceholder(); - - Integration::Instance().textActionsUpdated(); -} - -void FlatInput::onTextChange(const QString &text) { - _oldtext = text; - Integration::Instance().textActionsUpdated(); -} - -InputField::InputField( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &value) -: InputField( - parent, - st, - Mode::SingleLine, - std::move(placeholder), - { value, {} }) { -} - -InputField::InputField( - QWidget *parent, - const style::InputField &st, - Mode mode, - rpl::producer placeholder, - const QString &value) -: InputField( - parent, - st, - mode, - std::move(placeholder), - { value, {} }) { -} - -InputField::InputField( - QWidget *parent, - const style::InputField &st, - Mode mode, - rpl::producer placeholder, - const TextWithTags &value) -: RpWidget(parent) -, _st(st) -, _mode(mode) -, _minHeight(st.heightMin) -, _maxHeight(st.heightMax) -, _inner(std::make_unique(this)) -, _lastTextWithTags(value) -, _placeholderFull(std::move(placeholder)) { - _inner->setDocument(CreateChild(_inner.get(), _st)); - - _inner->setAcceptRichText(false); - resize(_st.width, _minHeight); - - if (_st.textBg->c.alphaF() >= 1.) { - setAttribute(Qt::WA_OpaquePaintEvent); - } - - _inner->setFont(_st.font->f); - _inner->setAlignment(_st.textAlign); - if (_mode == Mode::SingleLine) { - _inner->setWordWrapMode(QTextOption::NoWrap); - } - - _placeholderFull.value( - ) | rpl::start_with_next([=](const QString &text) { - refreshPlaceholder(text); - }, lifetime()); - - style::PaletteChanged( - ) | rpl::start_with_next([=] { - updatePalette(); - }, lifetime()); - - _defaultCharFormat = _inner->textCursor().charFormat(); - updatePalette(); - _inner->textCursor().setCharFormat(_defaultCharFormat); - - _inner->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - _inner->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - _inner->setFrameStyle(QFrame::NoFrame | QFrame::Plain); - _inner->viewport()->setAutoFillBackground(false); - - _inner->setContentsMargins(0, 0, 0, 0); - _inner->document()->setDocumentMargin(0); - - setAttribute(Qt::WA_AcceptTouchEvents); - _inner->viewport()->setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - - connect(_inner->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onDocumentContentsChange(int,int,int))); - connect(_inner.get(), SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); - connect(_inner.get(), SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); - connect(_inner.get(), SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged())); - connect(_inner.get(), &Inner::selectionChanged, [] { - Integration::Instance().textActionsUpdated(); - }); - - const auto bar = _inner->verticalScrollBar(); - _scrollTop = bar->value(); - connect(bar, &QScrollBar::valueChanged, [=] { - _scrollTop = bar->value(); - }); - - setCursor(style::cur_text); - heightAutoupdated(); - - if (!_lastTextWithTags.text.isEmpty()) { - setTextWithTags(_lastTextWithTags, HistoryAction::Clear); - } - - startBorderAnimation(); - startPlaceholderAnimation(); - finishAnimating(); -} - -const rpl::variable &InputField::scrollTop() const { - return _scrollTop; -} - -int InputField::scrollTopMax() const { - return _inner->verticalScrollBar()->maximum(); -} - -void InputField::scrollTo(int top) { - _inner->verticalScrollBar()->setValue(top); -} - -bool InputField::viewportEventInner(QEvent *e) { - if (e->type() == QEvent::TouchBegin - || e->type() == QEvent::TouchUpdate - || e->type() == QEvent::TouchEnd - || e->type() == QEvent::TouchCancel) { - const auto ev = static_cast(e); - if (ev->device()->type() == QTouchDevice::TouchScreen) { - handleTouchEvent(ev); - } - } - return _inner->QTextEdit::viewportEvent(e); -} - -void InputField::updatePalette() { - auto p = _inner->palette(); - p.setColor(QPalette::Text, _st.textFg->c); - _inner->setPalette(p); - - _defaultCharFormat.merge(PrepareTagFormat(_st, QString())); - auto cursor = textCursor(); - - const auto document = _inner->document(); - auto block = document->begin(); - const auto end = document->end(); - for (; block != end; block = block.next()) { - auto till = block.position(); - for (auto i = block.begin(); !i.atEnd();) { - for (; !i.atEnd(); ++i) { - const auto fragment = i.fragment(); - if (!fragment.isValid() || fragment.position() < till) { - continue; - } - till = fragment.position() + fragment.length(); - - auto format = fragment.charFormat(); - const auto tag = format.property(kTagProperty).toString(); - format.setForeground(PrepareTagFormat(_st, tag).foreground()); - cursor.setPosition(fragment.position()); - cursor.setPosition(till, QTextCursor::KeepAnchor); - cursor.mergeCharFormat(format); - i = block.begin(); - break; - } - } - } - - cursor = textCursor(); - if (!cursor.hasSelection()) { - auto format = cursor.charFormat(); - format.merge(PrepareTagFormat( - _st, - format.property(kTagProperty).toString())); - cursor.setCharFormat(format); - setTextCursor(cursor); - } -} - -void InputField::onTouchTimer() { - _touchRightButton = true; -} - -void InputField::setInstantReplaces(const InstantReplaces &replaces) { - _mutableInstantReplaces = replaces; -} - -void InputField::setInstantReplacesEnabled(rpl::producer enabled) { - std::move( - enabled - ) | rpl::start_with_next([=](bool value) { - _instantReplacesEnabled = value; - }, lifetime()); -} - -void InputField::setMarkdownReplacesEnabled(rpl::producer enabled) { - std::move( - enabled - ) | rpl::start_with_next([=](bool value) { - if (_markdownEnabled != value) { - _markdownEnabled = value; - if (_markdownEnabled) { - handleContentsChanged(); - } else { - _lastMarkdownTags = {}; - } - } - }, lifetime()); -} - -void InputField::setTagMimeProcessor( - std::unique_ptr &&processor) { - _tagMimeProcessor = std::move(processor); -} - -void InputField::setAdditionalMargin(int margin) { - _inner->setStyleSheet( - QString::fromLatin1("QTextEdit { margin: %1px; }").arg(margin)); - _additionalMargin = margin; - checkContentHeight(); -} - -void InputField::setMaxLength(int length) { - if (_maxLength != length) { - _maxLength = length; - if (_maxLength > 0) { - const auto document = _inner->document(); - _correcting = true; - QTextCursor(document->docHandle(), 0).joinPreviousEditBlock(); - const auto guard = gsl::finally([&] { - _correcting = false; - QTextCursor(document->docHandle(), 0).endEditBlock(); - handleContentsChanged(); - }); - - auto cursor = QTextCursor(document->docHandle(), 0); - cursor.movePosition(QTextCursor::End); - chopByMaxLength(0, cursor.position()); - } - } -} - -void InputField::setMinHeight(int height) { - _minHeight = height; -} - -void InputField::setMaxHeight(int height) { - _maxHeight = height; -} - -void InputField::insertTag(const QString &text, QString tagId) { - auto cursor = textCursor(); - const auto position = cursor.position(); - - const auto document = _inner->document(); - auto block = document->findBlock(position); - for (auto iter = block.begin(); !iter.atEnd(); ++iter) { - auto fragment = iter.fragment(); - Assert(fragment.isValid()); - - const auto fragmentPosition = fragment.position(); - const auto fragmentEnd = (fragmentPosition + fragment.length()); - if (fragmentPosition >= position || fragmentEnd < position) { - continue; - } - - const auto format = fragment.charFormat(); - if (format.isImageFormat()) { - continue; - } - - auto mentionInCommand = false; - const auto fragmentText = fragment.text(); - for (auto i = position - fragmentPosition; i > 0; --i) { - const auto previous = fragmentText[i - 1]; - if (previous == '@' || previous == '#' || previous == '/') { - if ((i == position - fragmentPosition - || (previous == '/' - ? fragmentText[i].isLetterOrNumber() - : fragmentText[i].isLetter()) - || previous == '#') && - (i < 2 || !(fragmentText[i - 2].isLetterOrNumber() - || fragmentText[i - 2] == '_'))) { - cursor.setPosition(fragmentPosition + i - 1); - auto till = fragmentPosition + i; - for (; (till < fragmentEnd && till < position); ++till) { - const auto ch = fragmentText[till - fragmentPosition]; - if (!ch.isLetterOrNumber() && ch != '_' && ch != '@') { - break; - } - } - if (till < fragmentEnd - && fragmentText[till - fragmentPosition] == ' ') { - ++till; - } - cursor.setPosition(till, QTextCursor::KeepAnchor); - break; - } else if ((i == position - fragmentPosition - || fragmentText[i].isLetter()) - && fragmentText[i - 1] == '@' - && (i > 2) - && (fragmentText[i - 2].isLetterOrNumber() - || fragmentText[i - 2] == '_') - && !mentionInCommand) { - mentionInCommand = true; - --i; - continue; - } - break; - } - if (position - fragmentPosition - i > 127 - || (!mentionInCommand - && (position - fragmentPosition - i > 63)) - || (!fragmentText[i - 1].isLetterOrNumber() - && fragmentText[i - 1] != '_')) { - break; - } - } - break; - } - if (tagId.isEmpty()) { - cursor.insertText(text + ' ', _defaultCharFormat); - } else { - _insertedTags.clear(); - _insertedTags.push_back({ 0, text.size(), tagId }); - _insertedTagsAreFromMime = false; - cursor.insertText(text + ' '); - _insertedTags.clear(); - } -} - -bool InputField::heightAutoupdated() { - if (_minHeight < 0 - || _maxHeight < 0 - || _inHeightCheck - || _mode == Mode::SingleLine) { - return false; - } - _inHeightCheck = true; - const auto guard = gsl::finally([&] { _inHeightCheck = false; }); - - SendPendingMoveResizeEvents(this); - - const auto contentHeight = int(std::ceil(document()->size().height())) - + _st.textMargins.top() - + _st.textMargins.bottom() - + 2 * _additionalMargin; - const auto newHeight = std::clamp(contentHeight, _minHeight, _maxHeight); - if (height() != newHeight) { - resize(width(), newHeight); - return true; - } - return false; -} - -void InputField::checkContentHeight() { - if (heightAutoupdated()) { - emit resized(); - } -} - -void InputField::handleTouchEvent(QTouchEvent *e) { - switch (e->type()) { - case QEvent::TouchBegin: { - if (_touchPress || e->touchPoints().isEmpty()) return; - _touchTimer.start(QApplication::startDragTime()); - _touchPress = true; - _touchMove = _touchRightButton = false; - _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); - } break; - - case QEvent::TouchUpdate: { - if (!_touchPress || e->touchPoints().isEmpty()) return; - if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { - _touchMove = true; - } - } break; - - case QEvent::TouchEnd: { - if (!_touchPress) return; - auto weak = MakeWeak(this); - if (!_touchMove && window()) { - Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); - QPoint mapped(mapFromGlobal(_touchStart)), winMapped(window()->mapFromGlobal(_touchStart)); - - if (_touchRightButton) { - QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart); - contextMenuEvent(&contextEvent); - } - } - if (weak) { - _touchTimer.stop(); - _touchPress = _touchMove = _touchRightButton = false; - } - } break; - - case QEvent::TouchCancel: { - _touchPress = false; - _touchTimer.stop(); - } break; - } -} - -void InputField::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto r = rect().intersected(e->rect()); - if (_st.textBg->c.alphaF() > 0.) { - p.fillRect(r, _st.textBg); - } - if (_st.border) { - p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg); - } - auto errorDegree = _a_error.value(_error ? 1. : 0.); - auto focusedDegree = _a_focused.value(_focused ? 1. : 0.); - auto borderShownDegree = _a_borderShown.value(1.); - auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); - if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = std::clamp(_borderAnimationStart, 0, width()); - auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); - auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); - if (borderTo > borderFrom) { - auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree); - p.setOpacity(borderOpacity); - p.fillRect(borderFrom, height() - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg); - p.setOpacity(1); - } - } - - if (_st.placeholderScale > 0. && !_placeholderPath.isEmpty()) { - auto placeholderShiftDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.); - p.save(); - p.setClipRect(r); - - auto placeholderTop = anim::interpolate(0, _st.placeholderShift, placeholderShiftDegree); - - QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins)); - r.moveTop(r.top() + placeholderTop); - if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); - - auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree; - auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree); - placeholderFg = anim::color(placeholderFg, _st.placeholderFgError, errorDegree); - - PainterHighQualityEnabler hq(p); - p.setPen(Qt::NoPen); - p.setBrush(placeholderFg); - p.translate(r.topLeft()); - p.scale(placeholderScale, placeholderScale); - p.drawPath(_placeholderPath); - - p.restore(); - } else if (!_placeholder.isEmpty()) { - const auto placeholderHiddenDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.); - if (placeholderHiddenDegree < 1.) { - p.setOpacity(1. - placeholderHiddenDegree); - p.save(); - p.setClipRect(r); - - const auto placeholderLeft = anim::interpolate(0, -_st.placeholderShift, placeholderHiddenDegree); - - p.setFont(_st.placeholderFont); - p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, focusedDegree)); - - if (_st.placeholderAlign == style::al_topleft && _placeholderAfterSymbols > 0) { - const auto skipWidth = placeholderSkipWidth(); - p.drawText( - _st.textMargins.left() + _st.placeholderMargins.left() + skipWidth, - _st.textMargins.top() + _st.placeholderMargins.top() + _st.placeholderFont->ascent, - _placeholder); - } else { - auto r = rect().marginsRemoved(_st.textMargins + _st.placeholderMargins); - r.moveLeft(r.left() + placeholderLeft); - if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); - p.drawText(r, _placeholder, _st.placeholderAlign); - } - - p.restore(); - } - } - RpWidget::paintEvent(e); -} - -int InputField::placeholderSkipWidth() const { - if (!_placeholderAfterSymbols) { - return 0; - } - const auto &text = getTextWithTags().text; - auto result = _st.font->width(text.mid(0, _placeholderAfterSymbols)); - if (_placeholderAfterSymbols > text.size()) { - result += _st.font->spacew; - } - return result; -} - -void InputField::startBorderAnimation() { - auto borderVisible = (_error || _focused); - if (_borderVisible != borderVisible) { - _borderVisible = borderVisible; - if (_borderVisible) { - if (_a_borderOpacity.animating()) { - _a_borderOpacity.start([this] { update(); }, 0., 1., _st.duration); - } else { - _a_borderShown.start([this] { update(); }, 0., 1., _st.duration); - } - } else { - _a_borderOpacity.start([this] { update(); }, 1., 0., _st.duration); - } - } -} - -void InputField::focusInEvent(QFocusEvent *e) { - _borderAnimationStart = (e->reason() == Qt::MouseFocusReason) - ? mapFromGlobal(QCursor::pos()).x() - : (width() / 2); - QTimer::singleShot(0, this, SLOT(onFocusInner())); -} - -void InputField::mousePressEvent(QMouseEvent *e) { - _borderAnimationStart = e->pos().x(); - QTimer::singleShot(0, this, SLOT(onFocusInner())); -} - -void InputField::onFocusInner() { - auto borderStart = _borderAnimationStart; - _inner->setFocus(); - _borderAnimationStart = borderStart; -} - -int InputField::borderAnimationStart() const { - return _borderAnimationStart; -} - -void InputField::contextMenuEvent(QContextMenuEvent *e) { - _inner->contextMenuEvent(e); -} - -void InputField::focusInEventInner(QFocusEvent *e) { - _borderAnimationStart = (e->reason() == Qt::MouseFocusReason) - ? mapFromGlobal(QCursor::pos()).x() - : (width() / 2); - setFocused(true); - _inner->QTextEdit::focusInEvent(e); - emit focused(); -} - -void InputField::focusOutEventInner(QFocusEvent *e) { - setFocused(false); - _inner->QTextEdit::focusOutEvent(e); - emit blurred(); -} - -void InputField::setFocused(bool focused) { - if (_focused != focused) { - _focused = focused; - _a_focused.start([this] { update(); }, _focused ? 0. : 1., _focused ? 1. : 0., _st.duration); - startPlaceholderAnimation(); - startBorderAnimation(); - } -} - -QSize InputField::sizeHint() const { - return geometry().size(); -} - -QSize InputField::minimumSizeHint() const { - return geometry().size(); -} - -bool InputField::hasText() const { - const auto document = _inner->document(); - const auto from = document->begin(); - const auto till = document->end(); - - if (from == till) { - return false; - } - - for (auto item = from.begin(); !item.atEnd(); ++item) { - const auto fragment = item.fragment(); - if (!fragment.isValid()) { - continue; - } else if (!fragment.text().isEmpty()) { - return true; - } - } - return (from.next() != till); -} - -QString InputField::getTextPart( - int start, - int end, - TagList &outTagsList, - bool &outTagsChanged, - std::vector *outMarkdownTags) const { - Expects((start == 0 && end < 0) || outMarkdownTags == nullptr); - - if (end >= 0 && end <= start) { - outTagsChanged = !outTagsList.isEmpty(); - outTagsList.clear(); - return QString(); - } - - if (start < 0) { - start = 0; - } - const auto full = (start == 0 && end < 0); - - auto lastTag = QString(); - TagAccumulator tagAccumulator(outTagsList); - MarkdownTagAccumulator markdownTagAccumulator(outMarkdownTags); - const auto newline = outMarkdownTags ? QString(1, '\n') : QString(); - - const auto document = _inner->document(); - const auto from = full ? document->begin() : document->findBlock(start); - auto till = (end < 0) ? document->end() : document->findBlock(end); - if (till.isValid()) { - till = till.next(); - } - - auto possibleLength = 0; - for (auto block = from; block != till; block = block.next()) { - possibleLength += block.length(); - } - auto result = QString(); - result.reserve(possibleLength); - if (!full && end < 0) { - end = possibleLength; - } - - for (auto block = from; block != till;) { - for (auto item = block.begin(); !item.atEnd(); ++item) { - const auto fragment = item.fragment(); - if (!fragment.isValid()) { - continue; - } - - const auto fragmentPosition = full ? 0 : fragment.position(); - const auto fragmentEnd = full - ? 0 - : (fragmentPosition + fragment.length()); - const auto format = fragment.charFormat(); - if (!full) { - if (fragmentPosition == end) { - tagAccumulator.feed( - format.property(kTagProperty).toString(), - result.size()); - break; - } else if (fragmentPosition > end) { - break; - } else if (fragmentEnd <= start) { - continue; - } - } - - const auto emojiText = [&] { - if (format.isImageFormat()) { - const auto imageName = format.toImageFormat().name(); - if (const auto emoji = Emoji::FromUrl(imageName)) { - return emoji->text(); - } - } - return QString(); - }(); - auto text = [&] { - const auto result = fragment.text(); - if (!full) { - if (fragmentPosition < start) { - return result.mid(start - fragmentPosition, end - start); - } else if (fragmentEnd > end) { - return result.mid(0, end - fragmentPosition); - } - } - return result; - }(); - - if (full || !text.isEmpty()) { - lastTag = format.property(kTagProperty).toString(); - tagAccumulator.feed(lastTag, result.size()); - } - - auto begin = text.data(); - auto ch = begin; - auto adjustedLength = text.size(); - for (const auto end = begin + text.size(); ch != end; ++ch) { - if (IsNewline(*ch) && ch->unicode() != '\r') { - *ch = QLatin1Char('\n'); - } else switch (ch->unicode()) { - case QChar::Nbsp: { - *ch = QLatin1Char(' '); - } break; - case QChar::ObjectReplacementCharacter: { - if (ch > begin) { - result.append(begin, ch - begin); - } - adjustedLength += (emojiText.size() - 1); - if (!emojiText.isEmpty()) { - result.append(emojiText); - } - begin = ch + 1; - } break; - } - } - if (ch > begin) { - result.append(begin, ch - begin); - } - - if (full || !text.isEmpty()) { - markdownTagAccumulator.feed(text, adjustedLength, lastTag); - } - } - - block = block.next(); - if (block != till) { - result.append('\n'); - markdownTagAccumulator.feed(newline, 1, lastTag); - } - } - - tagAccumulator.feed(QString(), result.size()); - tagAccumulator.finish(); - markdownTagAccumulator.finish(); - - outTagsChanged = tagAccumulator.changed(); - return result; -} - -bool InputField::isUndoAvailable() const { - return _undoAvailable; -} - -bool InputField::isRedoAvailable() const { - return _redoAvailable; -} - -void InputField::processFormatting(int insertPosition, int insertEnd) { - // Tilde formatting. - const auto tildeFormatting = (_st.font->f.pixelSize() * style::DevicePixelRatio() == 13) - && (_st.font->f.family() == qstr("Open Sans")); - auto isTildeFragment = false; - const auto tildeFixedFont = AdjustFont(st::semiboldFont, _st.font); - - // First tag handling (the one we inserted text to). - bool startTagFound = false; - bool breakTagOnNotLetter = false; - - auto document = _inner->document(); - - // Apply inserted tags. - auto insertedTagsProcessor = _insertedTagsAreFromMime - ? _tagMimeProcessor.get() - : nullptr; - const auto breakTagOnNotLetterTill = ProcessInsertedTags( - _st, - document, - insertPosition, - insertEnd, - _insertedTags, - insertedTagsProcessor); - using ActionType = FormattingAction::Type; - while (true) { - FormattingAction action; - - auto fromBlock = document->findBlock(insertPosition); - auto tillBlock = document->findBlock(insertEnd); - if (tillBlock.isValid()) tillBlock = tillBlock.next(); - - for (auto block = fromBlock; block != tillBlock; block = block.next()) { - for (auto fragmentIt = block.begin(); !fragmentIt.atEnd(); ++fragmentIt) { - auto fragment = fragmentIt.fragment(); - Assert(fragment.isValid()); - - int fragmentPosition = fragment.position(); - if (insertPosition >= fragmentPosition + fragment.length()) { - continue; - } - int changedPositionInFragment = insertPosition - fragmentPosition; // Can be negative. - int changedEndInFragment = insertEnd - fragmentPosition; - if (changedEndInFragment <= 0) { - break; - } - - auto format = fragment.charFormat(); - if (!format.hasProperty(kTagProperty)) { - action.type = ActionType::RemoveTag; - action.intervalStart = fragmentPosition; - action.intervalEnd = fragmentPosition + fragment.length(); - break; - } - if (tildeFormatting) { - isTildeFragment = (format.font() == tildeFixedFont); - } - - auto fragmentText = fragment.text(); - auto *textStart = fragmentText.constData(); - auto *textEnd = textStart + fragmentText.size(); - - const auto with = format.property(kInstantReplaceWithId); - if (with.isValid()) { - const auto string = with.toString(); - if (fragmentText != string) { - action.type = ActionType::ClearInstantReplace; - action.intervalStart = fragmentPosition - + (fragmentText.startsWith(string) - ? string.size() - : 0); - action.intervalEnd = fragmentPosition - + fragmentText.size(); - break; - } - } - - if (!startTagFound) { - startTagFound = true; - auto tagName = format.property(kTagProperty).toString(); - if (!tagName.isEmpty()) { - breakTagOnNotLetter = WasInsertTillTheEndOfTag( - block, - fragmentIt, - insertEnd); - } - } - - auto *ch = textStart + qMax(changedPositionInFragment, 0); - for (; ch < textEnd; ++ch) { - const auto removeNewline = (_mode != Mode::MultiLine) - && IsNewline(*ch); - if (removeNewline) { - if (action.type == ActionType::Invalid) { - action.type = ActionType::RemoveNewline; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = action.intervalStart + 1; - } - break; - } - - auto emojiLength = 0; - if (const auto emoji = Emoji::Find(ch, textEnd, &emojiLength)) { - // Replace emoji if no current action is prepared. - if (action.type == ActionType::Invalid) { - action.type = ActionType::InsertEmoji; - action.emoji = emoji; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = action.intervalStart + emojiLength; - } - break; - } - - if (breakTagOnNotLetter && !ch->isLetter()) { - // Remove tag name till the end if no current action is prepared. - if (action.type != ActionType::Invalid) { - break; - } - breakTagOnNotLetter = false; - if (fragmentPosition + (ch - textStart) < breakTagOnNotLetterTill) { - action.type = ActionType::RemoveTag; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = breakTagOnNotLetterTill; - break; - } - } - if (tildeFormatting) { // Tilde symbol fix in OpenSans. - bool tilde = (ch->unicode() == '~'); - if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) { - if (action.type == ActionType::Invalid) { - action.type = ActionType::TildeFont; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = action.intervalStart + 1; - action.tildeTag = format.property(kTagProperty).toString(); - action.isTilde = tilde; - } else { - ++action.intervalEnd; - } - } else if (action.type == ActionType::TildeFont) { - break; - } - } - - if (ch + 1 < textEnd && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) { - ++ch; - } - } - if (action.type != ActionType::Invalid) { - break; - } - } - if (action.type != ActionType::Invalid) { - break; - } else if (_mode != Mode::MultiLine - && block.next() != document->end()) { - action.type = ActionType::RemoveNewline; - action.intervalStart = block.next().position() - 1; - action.intervalEnd = action.intervalStart + 1; - break; - } - } - if (action.type != ActionType::Invalid) { - PrepareFormattingOptimization(document); - - auto cursor = QTextCursor( - document->docHandle(), - action.intervalStart); - cursor.setPosition(action.intervalEnd, QTextCursor::KeepAnchor); - if (action.type == ActionType::InsertEmoji) { - InsertEmojiAtCursor(cursor, action.emoji); - insertPosition = action.intervalStart + 1; - if (insertEnd >= action.intervalEnd) { - insertEnd -= action.intervalEnd - - action.intervalStart - - 1; - } - } else if (action.type == ActionType::RemoveTag) { - RemoveDocumentTags( - _st, - document, - action.intervalStart, - action.intervalEnd); - } else if (action.type == ActionType::TildeFont) { - auto format = QTextCharFormat(); - format.setFont(action.isTilde - ? tildeFixedFont - : PrepareTagFormat(_st, action.tildeTag).font()); - cursor.mergeCharFormat(format); - insertPosition = action.intervalEnd; - } else if (action.type == ActionType::ClearInstantReplace) { - auto format = _defaultCharFormat; - ApplyTagFormat(format, cursor.charFormat()); - cursor.setCharFormat(format); - } else if (action.type == ActionType::RemoveNewline) { - cursor.removeSelectedText(); - insertPosition = action.intervalStart; - if (insertEnd >= action.intervalEnd) { - insertEnd -= action.intervalEnd - action.intervalStart; - } - } - } else { - break; - } - } -} - -void InputField::onDocumentContentsChange( - int position, - int charsRemoved, - int charsAdded) { - if (_correcting) { - return; - } - - const auto document = _inner->document(); - - // Qt bug workaround https://bugreports.qt.io/browse/QTBUG-49062 - if (!position) { - auto cursor = QTextCursor(document->docHandle(), 0); - cursor.movePosition(QTextCursor::End); - if (position + charsAdded > cursor.position()) { - const auto delta = position + charsAdded - cursor.position(); - if (charsRemoved >= delta) { - charsAdded -= delta; - charsRemoved -= delta; - } - } - } - - const auto insertPosition = (_realInsertPosition >= 0) - ? _realInsertPosition - : position; - const auto insertLength = (_realInsertPosition >= 0) - ? _realCharsAdded - : charsAdded; - - const auto removePosition = position; - const auto removeLength = charsRemoved; - - _correcting = true; - QTextCursor(document->docHandle(), 0).joinPreviousEditBlock(); - const auto guard = gsl::finally([&] { - _correcting = false; - QTextCursor(document->docHandle(), 0).endEditBlock(); - handleContentsChanged(); - }); - - chopByMaxLength(insertPosition, insertLength); - - if (document->availableRedoSteps() == 0 && insertLength > 0) { - const auto pageSize = document->pageSize(); - processFormatting(insertPosition, insertPosition + insertLength); - if (document->pageSize() != pageSize) { - document->setPageSize(pageSize); - } - } -} - -void InputField::onCursorPositionChanged() { - auto cursor = textCursor(); - if (!cursor.hasSelection() && !cursor.position()) { - cursor.setCharFormat(_defaultCharFormat); - setTextCursor(cursor); - } -} - -void InputField::chopByMaxLength(int insertPosition, int insertLength) { - Expects(_correcting); - - if (_maxLength < 0) { - return; - } - - auto cursor = QTextCursor(document()->docHandle(), 0); - cursor.movePosition(QTextCursor::End); - const auto fullSize = cursor.position(); - const auto toRemove = fullSize - _maxLength; - if (toRemove > 0) { - if (toRemove > insertLength) { - if (insertLength) { - cursor.setPosition(insertPosition); - cursor.setPosition( - (insertPosition + insertLength), - QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - } - cursor.setPosition(fullSize - (toRemove - insertLength)); - cursor.setPosition(fullSize, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - } else { - cursor.setPosition( - insertPosition + (insertLength - toRemove)); - cursor.setPosition( - insertPosition + insertLength, - QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - } - } -} - -void InputField::handleContentsChanged() { - setErrorShown(false); - - auto tagsChanged = false; - const auto currentText = getTextPart( - 0, - -1, - _lastTextWithTags.tags, - tagsChanged, - _markdownEnabled ? &_lastMarkdownTags : nullptr); - - //highlightMarkdown(); - - if (tagsChanged || (_lastTextWithTags.text != currentText)) { - _lastTextWithTags.text = currentText; - const auto weak = MakeWeak(this); - emit changed(); - if (!weak) { - return; - } - checkContentHeight(); - } - startPlaceholderAnimation(); - Integration::Instance().textActionsUpdated(); -} - -void InputField::highlightMarkdown() { - // Highlighting may interfere with markdown parsing -> inaccurate. - // For debug. - auto from = 0; - auto applyColor = [&](int a, int b, QColor color) { - auto cursor = textCursor(); - cursor.setPosition(a); - cursor.setPosition(b, QTextCursor::KeepAnchor); - auto format = QTextCharFormat(); - format.setForeground(color); - cursor.mergeCharFormat(format); - from = b; - }; - for (const auto &tag : _lastMarkdownTags) { - if (tag.internalStart > from) { - applyColor(from, tag.internalStart, QColor(0, 0, 0)); - } else if (tag.internalStart < from) { - continue; - } - applyColor( - tag.internalStart, - tag.internalStart + tag.internalLength, - (tag.closed - ? QColor(0, 128, 0) - : QColor(128, 0, 0))); - } - auto cursor = textCursor(); - cursor.movePosition(QTextCursor::End); - if (const auto till = cursor.position(); till > from) { - applyColor(from, till, QColor(0, 0, 0)); - } -} - -void InputField::onUndoAvailable(bool avail) { - _undoAvailable = avail; - Integration::Instance().textActionsUpdated(); -} - -void InputField::onRedoAvailable(bool avail) { - _redoAvailable = avail; - Integration::Instance().textActionsUpdated(); -} - -void InputField::setDisplayFocused(bool focused) { - setFocused(focused); - finishAnimating(); -} - -void InputField::selectAll() { - auto cursor = _inner->textCursor(); - cursor.setPosition(0); - cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); - _inner->setTextCursor(cursor); -} - -void InputField::finishAnimating() { - _a_focused.stop(); - _a_error.stop(); - _a_placeholderShifted.stop(); - _a_borderShown.stop(); - _a_borderOpacity.stop(); - update(); -} - -void InputField::setPlaceholderHidden(bool forcePlaceholderHidden) { - _forcePlaceholderHidden = forcePlaceholderHidden; - startPlaceholderAnimation(); -} - -void InputField::startPlaceholderAnimation() { - const auto textLength = [&] { - return getTextWithTags().text.size() + _lastPreEditText.size(); - }; - const auto placeholderShifted = _forcePlaceholderHidden - || (_focused && _st.placeholderScale > 0.) - || (textLength() > _placeholderAfterSymbols); - if (_placeholderShifted != placeholderShifted) { - _placeholderShifted = placeholderShifted; - _a_placeholderShifted.start( - [=] { update(); }, - _placeholderShifted ? 0. : 1., - _placeholderShifted ? 1. : 0., - _st.duration); - } -} - -QMimeData *InputField::createMimeDataFromSelectionInner() const { - const auto cursor = _inner->textCursor(); - const auto start = cursor.selectionStart(); - const auto end = cursor.selectionEnd(); - return TextUtilities::MimeDataFromText((end > start) - ? getTextWithTagsPart(start, end) - : TextWithTags() - ).release(); -} - -void InputField::customUpDown(bool isCustom) { - _customUpDown = isCustom; -} - -void InputField::customTab(bool isCustom) { - _customTab = isCustom; -} - -void InputField::setSubmitSettings(SubmitSettings settings) { - _submitSettings = settings; -} - -not_null InputField::document() { - return _inner->document(); -} - -not_null InputField::document() const { - return _inner->document(); -} - -void InputField::setTextCursor(const QTextCursor &cursor) { - return _inner->setTextCursor(cursor); -} - -QTextCursor InputField::textCursor() const { - return _inner->textCursor(); -} - -void InputField::setCursorPosition(int pos) { - auto cursor = _inner->textCursor(); - cursor.setPosition(pos); - _inner->setTextCursor(cursor); -} - -void InputField::setText(const QString &text) { - setTextWithTags({ text, {} }); -} - -void InputField::setTextWithTags( - const TextWithTags &textWithTags, - HistoryAction historyAction) { - _insertedTags = textWithTags.tags; - _insertedTagsAreFromMime = false; - _realInsertPosition = 0; - _realCharsAdded = textWithTags.text.size(); - const auto document = _inner->document(); - auto cursor = QTextCursor(document->docHandle(), 0); - if (historyAction == HistoryAction::Clear) { - document->setUndoRedoEnabled(false); - cursor.beginEditBlock(); - } else if (historyAction == HistoryAction::MergeEntry) { - cursor.joinPreviousEditBlock(); - } else { - cursor.beginEditBlock(); - } - cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); - cursor.insertText(textWithTags.text); - cursor.movePosition(QTextCursor::End); - cursor.endEditBlock(); - if (historyAction == HistoryAction::Clear) { - document->setUndoRedoEnabled(true); - } - _insertedTags.clear(); - _realInsertPosition = -1; - finishAnimating(); -} - -TextWithTags InputField::getTextWithTagsPart(int start, int end) const { - auto changed = false; - auto result = TextWithTags(); - result.text = getTextPart(start, end, result.tags, changed); - return result; -} - -TextWithTags InputField::getTextWithAppliedMarkdown() const { - if (!_markdownEnabled || _lastMarkdownTags.empty()) { - return getTextWithTags(); - } - const auto &originalText = _lastTextWithTags.text; - const auto &originalTags = _lastTextWithTags.tags; - - // Ignore tags that partially intersect some http-links. - // This will allow sending http://test.com/__test__/test correctly. - const auto links = TextUtilities::ParseEntities( - originalText, - 0).entities; - - auto result = TextWithTags(); - result.text.reserve(originalText.size()); - result.tags.reserve(originalTags.size() + _lastMarkdownTags.size()); - auto removed = 0; - auto originalTag = originalTags.begin(); - const auto originalTagsEnd = originalTags.end(); - const auto addOriginalTagsUpTill = [&](int offset) { - while (originalTag != originalTagsEnd - && originalTag->offset + originalTag->length <= offset) { - result.tags.push_back(*originalTag++); - result.tags.back().offset -= removed; - } - }; - auto from = 0; - const auto addOriginalTextUpTill = [&](int offset) { - if (offset > from) { - result.text.append(originalText.midRef(from, offset - from)); - } - }; - auto link = links.begin(); - const auto linksEnd = links.end(); - for (const auto &tag : _lastMarkdownTags) { - const auto tagLength = int(tag.tag.size()); - if (!tag.closed || tag.adjustedStart < from) { - continue; - } - auto entityLength = tag.adjustedLength - 2 * tagLength; - if (entityLength <= 0) { - continue; - } - addOriginalTagsUpTill(tag.adjustedStart); - const auto tagAdjustedEnd = tag.adjustedStart + tag.adjustedLength; - if (originalTag != originalTagsEnd - && originalTag->offset < tagAdjustedEnd) { - continue; - } - while (link != linksEnd - && link->offset() + link->length() <= tag.adjustedStart) { - ++link; - } - if (link != linksEnd - && link->offset() < tagAdjustedEnd - && (link->offset() + link->length() > tagAdjustedEnd - || link->offset() < tag.adjustedStart)) { - continue; - } - addOriginalTextUpTill(tag.adjustedStart); - - auto entityStart = tag.adjustedStart + tagLength; - if (tag.tag == kTagPre) { - // Remove redundant newlines for pre. - // If ``` is on a separate line add only one newline. - if (IsNewline(originalText[entityStart]) - && (result.text.isEmpty() - || IsNewline(result.text[result.text.size() - 1]))) { - ++entityStart; - --entityLength; - } - const auto entityEnd = entityStart + entityLength; - if (IsNewline(originalText[entityEnd - 1]) - && (originalText.size() <= entityEnd + tagLength - || IsNewline(originalText[entityEnd + tagLength]))) { - --entityLength; - } - } - - if (entityLength > 0) { - // Add tag text and entity. - result.tags.push_back(TextWithTags::Tag{ - int(result.text.size()), - entityLength, - tag.tag }); - result.text.append(originalText.midRef( - entityStart, - entityLength)); - } - - from = tag.adjustedStart + tag.adjustedLength; - removed += (tag.adjustedLength - entityLength); - } - addOriginalTagsUpTill(originalText.size()); - addOriginalTextUpTill(originalText.size()); - return result; -} - -void InputField::clear() { - _inner->clear(); - startPlaceholderAnimation(); -} - -bool InputField::hasFocus() const { - return _inner->hasFocus(); -} - -void InputField::setFocus() { - _inner->setFocus(); -} - -void InputField::clearFocus() { - _inner->clearFocus(); -} - -void InputField::ensureCursorVisible() { - _inner->ensureCursorVisible(); -} - -not_null InputField::rawTextEdit() { - return _inner.get(); -} - -not_null InputField::rawTextEdit() const { - return _inner.get(); -} - -bool InputField::ShouldSubmit( - SubmitSettings settings, - Qt::KeyboardModifiers modifiers) { - const auto shift = modifiers.testFlag(Qt::ShiftModifier); - const auto ctrl = modifiers.testFlag(Qt::ControlModifier) - || modifiers.testFlag(Qt::MetaModifier); - return (ctrl && shift) - || (ctrl - && settings != SubmitSettings::None - && settings != SubmitSettings::Enter) - || (!ctrl - && !shift - && settings != SubmitSettings::None - && settings != SubmitSettings::CtrlEnter); -} - -void InputField::keyPressEventInner(QKeyEvent *e) { - bool shift = e->modifiers().testFlag(Qt::ShiftModifier), alt = e->modifiers().testFlag(Qt::AltModifier); - bool macmeta = Platform::IsMac() && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier); - bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier); - bool enterSubmit = (_mode != Mode::MultiLine) - || ShouldSubmit(_submitSettings, e->modifiers()); - bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); - if (e->key() == Qt::Key_Left - || e->key() == Qt::Key_Right - || e->key() == Qt::Key_Up - || e->key() == Qt::Key_Down - || e->key() == Qt::Key_Home - || e->key() == Qt::Key_End) { - _reverseMarkdownReplacement = false; - } - - if (macmeta && e->key() == Qt::Key_Backspace) { - QTextCursor tc(textCursor()), start(tc); - start.movePosition(QTextCursor::StartOfLine); - tc.setPosition(start.position(), QTextCursor::KeepAnchor); - tc.removeSelectedText(); - } else if (e->key() == Qt::Key_Backspace - && e->modifiers() == 0 - && revertFormatReplace()) { - e->accept(); - } else if (enter && enterSubmit) { - emit submitted(e->modifiers()); - } else if (e->key() == Qt::Key_Escape) { - e->ignore(); - emit cancelled(); - } else if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) { - if (alt || ctrl) { - e->ignore(); - } else if (_customTab) { - emit tabbed(); - } else if (!focusNextPrevChild(e->key() == Qt::Key_Tab && !shift)) { - e->ignore(); - } - } else if (e->key() == Qt::Key_Search || e == QKeySequence::Find) { - e->ignore(); - } else if (handleMarkdownKey(e)) { - e->accept(); - } else if (_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)) { - e->ignore(); -#ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - const auto cursor = textCursor(); - const auto start = cursor.selectionStart(); - const auto end = cursor.selectionEnd(); - if (end > start) { - QGuiApplication::clipboard()->setText( - getTextWithTagsPart(start, end).text, - QClipboard::FindBuffer); - } -#endif // Q_OS_MAC - } else { - const auto text = e->text(); - const auto oldPosition = textCursor().position(); - if (enter && ctrl) { - e->setModifiers(e->modifiers() & ~Qt::ControlModifier); - } - _inner->QTextEdit::keyPressEvent(e); - auto cursor = textCursor(); - if (cursor.position() == oldPosition) { - bool check = false; - if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_Up) { - cursor.movePosition(QTextCursor::Start, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - check = true; - } else if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_Down) { - cursor.movePosition(QTextCursor::End, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - check = true; - } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right || e->key() == Qt::Key_Backspace) { - e->ignore(); - } - if (check) { - if (oldPosition == cursor.position()) { - e->ignore(); - } else { - setTextCursor(cursor); - } - } - } - if (!processMarkdownReplaces(text)) { - processInstantReplaces(text); - } - } -} - -TextWithTags InputField::getTextWithTagsSelected() const { - const auto cursor = textCursor(); - const auto start = cursor.selectionStart(); - const auto end = cursor.selectionEnd(); - return (end > start) ? getTextWithTagsPart(start, end) : TextWithTags(); -} - -bool InputField::handleMarkdownKey(QKeyEvent *e) { - if (!_markdownEnabled) { - return false; - } - const auto matches = [&](const QKeySequence &sequence) { - const auto searchKey = (e->modifiers() | e->key()) - & ~(Qt::KeypadModifier | Qt::GroupSwitchModifier); - const auto events = QKeySequence(searchKey); - return sequence.matches(events) == QKeySequence::ExactMatch; - }; - if (e == QKeySequence::Bold) { - toggleSelectionMarkdown(kTagBold); - } else if (e == QKeySequence::Italic) { - toggleSelectionMarkdown(kTagItalic); - } else if (e == QKeySequence::Underline) { - toggleSelectionMarkdown(kTagUnderline); - } else if (matches(kStrikeOutSequence)) { - toggleSelectionMarkdown(kTagStrikeOut); - } else if (matches(kMonospaceSequence)) { - toggleSelectionMarkdown(kTagCode); - } else if (matches(kClearFormatSequence)) { - clearSelectionMarkdown(); - } else if (matches(kEditLinkSequence) && _editLinkCallback) { - const auto cursor = textCursor(); - editMarkdownLink({ - cursor.selectionStart(), - cursor.selectionEnd() - }); - } else { - return false; - } - return true; -} - -auto InputField::selectionEditLinkData(EditLinkSelection selection) const --> EditLinkData { - Expects(_editLinkCallback != nullptr); - - const auto position = (selection.from == selection.till - && selection.from > 0) - ? (selection.from - 1) - : selection.from; - const auto link = [&] { - return (position != selection.till) - ? GetFullSimpleTextTag( - getTextWithTagsPart(position, selection.till)) - : QString(); - }(); - const auto simple = EditLinkData { - selection.from, - selection.till, - QString() - }; - if (!_editLinkCallback(selection, {}, link, EditLinkAction::Check)) { - return simple; - } - Assert(!link.isEmpty()); - - struct State { - QTextBlock block; - QTextBlock::iterator i; - }; - const auto document = _inner->document(); - const auto skipInvalid = [&](State &state) { - if (state.block == document->end()) { - return false; - } - while (state.i.atEnd()) { - state.block = state.block.next(); - if (state.block == document->end()) { - return false; - } - state.i = state.block.begin(); - } - return true; - }; - const auto moveToNext = [&](State &state) { - Expects(state.block != document->end()); - Expects(!state.i.atEnd()); - - ++state.i; - }; - const auto moveToPrevious = [&](State &state) { - Expects(state.block != document->end()); - Expects(!state.i.atEnd()); - - while (state.i == state.block.begin()) { - if (state.block == document->begin()) { - state.block = document->end(); - return false; - } - state.block = state.block.previous(); - state.i = state.block.end(); - } - --state.i; - return true; - }; - const auto stateTag = [&](const State &state) { - const auto format = state.i.fragment().charFormat(); - return format.property(kTagProperty).toString(); - }; - const auto stateStart = [&](const State &state) { - return state.i.fragment().position(); - }; - const auto stateEnd = [&](const State &state) { - const auto fragment = state.i.fragment(); - return fragment.position() + fragment.length(); - }; - auto state = State{ document->findBlock(position) }; - if (state.block != document->end()) { - state.i = state.block.begin(); - } - for (; skipInvalid(state); moveToNext(state)) { - const auto fragmentStart = stateStart(state); - const auto fragmentEnd = stateEnd(state); - if (fragmentEnd <= position) { - continue; - } else if (fragmentStart >= selection.till) { - break; - } - if (stateTag(state) == link) { - auto start = fragmentStart; - auto finish = fragmentEnd; - auto copy = state; - while (moveToPrevious(copy) && (stateTag(copy) == link)) { - start = stateStart(copy); - } - while (skipInvalid(state) && (stateTag(state) == link)) { - finish = stateEnd(state); - moveToNext(state); - } - return { start, finish, link }; - } - } - return simple; -} - -auto InputField::editLinkSelection(QContextMenuEvent *e) const --> EditLinkSelection { - const auto cursor = textCursor(); - if (!cursor.hasSelection() && e->reason() == QContextMenuEvent::Mouse) { - const auto clickCursor = _inner->cursorForPosition( - _inner->viewport()->mapFromGlobal(e->globalPos())); - if (!clickCursor.isNull() && !clickCursor.hasSelection()) { - return { - clickCursor.position(), - clickCursor.position() - }; - } - } - return { - cursor.selectionStart(), - cursor.selectionEnd() - }; -} - -void InputField::editMarkdownLink(EditLinkSelection selection) { - if (!_editLinkCallback) { - return; - } - const auto data = selectionEditLinkData(selection); - _editLinkCallback( - selection, - getTextWithTagsPart(data.from, data.till).text, - data.link, - EditLinkAction::Edit); -} - -void InputField::inputMethodEventInner(QInputMethodEvent *e) { - const auto preedit = e->preeditString(); - if (_lastPreEditText != preedit) { - _lastPreEditText = preedit; - startPlaceholderAnimation(); - } - const auto text = e->commitString(); - _inner->QTextEdit::inputMethodEvent(e); - if (!processMarkdownReplaces(text)) { - processInstantReplaces(text); - } -} - -const InstantReplaces &InputField::instantReplaces() const { - return _mutableInstantReplaces; -} - -// Disable markdown instant replacement. -bool InputField::processMarkdownReplaces(const QString &appended) { - //if (appended.size() != 1 || !_markdownEnabled) { - // return false; - //} - //const auto ch = appended[0]; - //if (ch == '`') { - // return processMarkdownReplace(kTagCode) - // || processMarkdownReplace(kTagPre); - //} else if (ch == '*') { - // return processMarkdownReplace(kTagBold); - //} else if (ch == '_') { - // return processMarkdownReplace(kTagItalic); - //} - return false; -} - -//bool InputField::processMarkdownReplace(const QString &tag) { -// const auto position = textCursor().position(); -// const auto tagLength = tag.size(); -// const auto start = [&] { -// for (const auto &possible : _lastMarkdownTags) { -// const auto end = possible.start + possible.length; -// if (possible.start + 2 * tagLength >= position) { -// return MarkdownTag(); -// } else if (end >= position || end + tagLength == position) { -// if (possible.tag == tag) { -// return possible; -// } -// } -// } -// return MarkdownTag(); -// }(); -// if (start.tag.isEmpty()) { -// return false; -// } -// return commitMarkdownReplacement(start.start, position, tag, tag); -//} - -void InputField::processInstantReplaces(const QString &appended) { - const auto &replaces = instantReplaces(); - if (appended.size() != 1 - || !_instantReplacesEnabled - || !replaces.maxLength) { - return; - } - const auto it = replaces.reverseMap.tail.find(appended[0]); - if (it == end(replaces.reverseMap.tail)) { - return; - } - const auto position = textCursor().position(); - for (const auto &tag : _lastMarkdownTags) { - if (tag.internalStart < position - && tag.internalStart + tag.internalLength >= position - && (tag.tag == kTagCode || tag.tag == kTagPre)) { - return; - } - } - const auto typed = getTextWithTagsPart( - std::max(position - replaces.maxLength, 0), - position - 1).text; - auto node = &it->second; - auto i = typed.size(); - do { - if (!node->text.isEmpty()) { - applyInstantReplace(typed.mid(i) + appended, node->text); - return; - } else if (!i) { - return; - } - const auto it = node->tail.find(typed[--i]); - if (it == end(node->tail)) { - return; - } - node = &it->second; - } while (true); -} - -void InputField::applyInstantReplace( - const QString &what, - const QString &with) { - const auto length = int(what.size()); - const auto cursor = textCursor(); - const auto position = cursor.position(); - if (cursor.hasSelection()) { - return; - } else if (position < length) { - return; - } - commitInstantReplacement(position - length, position, with, what, true); -} - -void InputField::commitInstantReplacement( - int from, - int till, - const QString &with) { - commitInstantReplacement(from, till, with, std::nullopt, false); -} - -void InputField::commitInstantReplacement( - int from, - int till, - const QString &with, - std::optional checkOriginal, - bool checkIfInMonospace) { - const auto original = getTextWithTagsPart(from, till).text; - if (checkOriginal - && checkOriginal->compare(original, Qt::CaseInsensitive) != 0) { - return; - } - - auto cursor = textCursor(); - if (checkIfInMonospace) { - const auto currentTag = cursor.charFormat().property(kTagProperty); - if (currentTag == kTagPre || currentTag == kTagCode) { - return; - } - } - cursor.setPosition(from); - cursor.setPosition(till, QTextCursor::KeepAnchor); - - auto format = [&]() -> QTextCharFormat { - auto emojiLength = 0; - const auto emoji = Emoji::Find(with, &emojiLength); - if (!emoji || with.size() != emojiLength) { - return _defaultCharFormat; - } - const auto use = Integration::Instance().defaultEmojiVariant( - emoji); - return PrepareEmojiFormat(use, _st.font); - }(); - const auto replacement = format.isImageFormat() - ? kObjectReplacement - : with; - format.setProperty(kInstantReplaceWhatId, original); - format.setProperty(kInstantReplaceWithId, replacement); - format.setProperty( - kInstantReplaceRandomId, - openssl::RandomValue()); - ApplyTagFormat(format, cursor.charFormat()); - cursor.insertText(replacement, format); -} - -bool InputField::commitMarkdownReplacement( - int from, - int till, - const QString &tag, - const QString &edge) { - const auto end = [&] { - auto cursor = QTextCursor(document()->docHandle(), 0); - cursor.movePosition(QTextCursor::End); - return cursor.position(); - }(); - - // In case of 'pre' tag extend checked text by one symbol. - // So that we'll know if we need to insert additional newlines. - // "Test ```test``` Test" should become three-line text. - const auto blocktag = (tag == kTagPre); - const auto extendLeft = (blocktag && from > 0) ? 1 : 0; - const auto extendRight = (blocktag && till < end) ? 1 : 0; - const auto extended = getTextWithTagsPart( - from - extendLeft, - till + extendRight).text; - const auto outer = extended.midRef( - extendLeft, - extended.size() - extendLeft - extendRight); - if ((outer.size() <= 2 * edge.size()) - || (!edge.isEmpty() - && !(outer.startsWith(edge) && outer.endsWith(edge)))) { - return false; - } - - // In case of 'pre' tag check if we need to remove one of two newlines. - // "Test\n```\ntest\n```" should become two-line text + newline. - const auto innerRight = edge.size(); - const auto checkIfTwoNewlines = blocktag - && (extendLeft > 0) - && IsNewline(extended[0]); - const auto innerLeft = [&] { - const auto simple = edge.size(); - if (!checkIfTwoNewlines) { - return simple; - } - const auto last = outer.size() - innerRight; - for (auto check = simple; check != last; ++check) { - const auto ch = outer.at(check); - if (IsNewline(ch)) { - return check + 1; - } else if (!chIsSpace(ch)) { - break; - } - } - return simple; - }(); - const auto innerLength = outer.size() - innerLeft - innerRight; - - // Prepare the final "insert" replacement for the "outer" text part. - const auto newlineleft = blocktag - && (extendLeft > 0) - && !IsNewline(extended[0]) - && !IsNewline(outer.at(innerLeft)); - const auto newlineright = blocktag - && (!extendRight || !IsNewline(extended[extended.size() - 1])) - && !IsNewline(outer.at(outer.size() - innerRight - 1)); - const auto insert = (newlineleft ? "\n" : "") - + outer.mid(innerLeft, innerLength).toString() - + (newlineright ? "\n" : ""); - - // Trim inserted tag, so that all newlines are left outside. - _insertedTags.clear(); - auto tagFrom = newlineleft ? 1 : 0; - auto tagTill = insert.size() - (newlineright ? 1 : 0); - for (; tagFrom != tagTill; ++tagFrom) { - const auto ch = insert.at(tagFrom); - if (!IsNewline(ch)) { - break; - } - } - for (; tagTill != tagFrom; --tagTill) { - const auto ch = insert.at(tagTill - 1); - if (!IsNewline(ch)) { - break; - } - } - if (tagTill > tagFrom) { - _insertedTags.push_back({ - tagFrom, - tagTill - tagFrom, - tag, - }); - } - - // Replace. - auto cursor = _inner->textCursor(); - cursor.setPosition(from); - cursor.setPosition(till, QTextCursor::KeepAnchor); - auto format = _defaultCharFormat; - if (!edge.isEmpty()) { - format.setProperty(kReplaceTagId, edge); - _reverseMarkdownReplacement = true; - } - _insertedTagsAreFromMime = false; - cursor.insertText(insert, format); - _insertedTags.clear(); - - cursor.setCharFormat(_defaultCharFormat); - _inner->setTextCursor(cursor); - return true; -} - -bool InputField::IsValidMarkdownLink(const QString &link) { - return ::Ui::IsValidMarkdownLink(link); -} - -void InputField::commitMarkdownLinkEdit( - EditLinkSelection selection, - const QString &text, - const QString &link) { - if (text.isEmpty() - || !IsValidMarkdownLink(link) - || !_editLinkCallback) { - return; - } - _insertedTags.clear(); - _insertedTags.push_back({ 0, text.size(), link }); - - auto cursor = textCursor(); - const auto editData = selectionEditLinkData(selection); - cursor.setPosition(editData.from); - cursor.setPosition(editData.till, QTextCursor::KeepAnchor); - auto format = _defaultCharFormat; - _insertedTagsAreFromMime = false; - cursor.insertText( - (editData.from == editData.till) ? (text + QChar(' ')) : text, - _defaultCharFormat); - _insertedTags.clear(); - - _reverseMarkdownReplacement = false; - cursor.setCharFormat(_defaultCharFormat); - _inner->setTextCursor(cursor); -} - -void InputField::toggleSelectionMarkdown(const QString &tag) { - _reverseMarkdownReplacement = false; - const auto cursor = textCursor(); - const auto position = cursor.position(); - const auto from = cursor.selectionStart(); - const auto till = cursor.selectionEnd(); - if (from == till) { - return; - } - if (tag.isEmpty() - || GetFullSimpleTextTag(getTextWithTagsSelected()) == tag) { - RemoveDocumentTags(_st, document(), from, till); - return; - } - const auto commitTag = [&] { - if (tag != kTagCode) { - return tag; - } - const auto leftForBlock = [&] { - if (!from) { - return true; - } - const auto text = getTextWithTagsPart(from - 1, from + 1).text; - return text.isEmpty() - || IsNewline(text[0]) - || IsNewline(text[text.size() - 1]); - }(); - const auto rightForBlock = [&] { - const auto text = getTextWithTagsPart(till - 1, till + 1).text; - return text.isEmpty() - || IsNewline(text[0]) - || IsNewline(text[text.size() - 1]); - }(); - return (leftForBlock && rightForBlock) ? kTagPre : kTagCode; - }(); - commitMarkdownReplacement(from, till, commitTag); - auto restorePosition = textCursor(); - restorePosition.setPosition((position == till) ? from : till); - restorePosition.setPosition(position, QTextCursor::KeepAnchor); - setTextCursor(restorePosition); -} - -void InputField::clearSelectionMarkdown() { - toggleSelectionMarkdown(QString()); -} - -bool InputField::revertFormatReplace() { - const auto cursor = textCursor(); - const auto position = cursor.position(); - if (position <= 0 || cursor.hasSelection()) { - return false; - } - const auto inside = position - 1; - const auto document = _inner->document(); - const auto block = document->findBlock(inside); - if (block == document->end()) { - return false; - } - for (auto i = block.begin(); !i.atEnd(); ++i) { - const auto fragment = i.fragment(); - const auto fragmentStart = fragment.position(); - const auto fragmentEnd = fragmentStart + fragment.length(); - if (fragmentEnd <= inside) { - continue; - } else if (fragmentStart > inside || fragmentEnd != position) { - return false; - } - const auto current = fragment.charFormat(); - if (current.hasProperty(kInstantReplaceWithId)) { - const auto with = current.property(kInstantReplaceWithId); - const auto string = with.toString(); - if (fragment.text() != string) { - return false; - } - auto replaceCursor = cursor; - replaceCursor.setPosition(fragmentStart); - replaceCursor.setPosition(fragmentEnd, QTextCursor::KeepAnchor); - const auto what = current.property(kInstantReplaceWhatId); - auto format = _defaultCharFormat; - ApplyTagFormat(format, current); - replaceCursor.insertText(what.toString(), format); - return true; - } else if (_reverseMarkdownReplacement - && current.hasProperty(kReplaceTagId)) { - const auto tag = current.property(kReplaceTagId).toString(); - if (tag.isEmpty()) { - return false; - } else if (auto test = i; !(++test).atEnd()) { - const auto format = test.fragment().charFormat(); - if (format.property(kReplaceTagId).toString() == tag) { - return false; - } - } else if (auto test = block; test.next() != document->end()) { - const auto begin = test.begin(); - if (begin != test.end()) { - const auto format = begin.fragment().charFormat(); - if (format.property(kReplaceTagId).toString() == tag) { - return false; - } - } - } - - const auto first = [&] { - auto checkBlock = block; - auto checkLast = i; - while (true) { - for (auto j = checkLast; j != checkBlock.begin();) { - --j; - const auto format = j.fragment().charFormat(); - if (format.property(kReplaceTagId) != tag) { - return ++j; - } - } - if (checkBlock == document->begin()) { - return checkBlock.begin(); - } - checkBlock = checkBlock.previous(); - checkLast = checkBlock.end(); - } - }(); - const auto from = first.fragment().position(); - const auto till = fragmentEnd; - auto replaceCursor = cursor; - replaceCursor.setPosition(from); - replaceCursor.setPosition(till, QTextCursor::KeepAnchor); - replaceCursor.insertText( - tag + getTextWithTagsPart(from, till).text + tag, - _defaultCharFormat); - return true; - } - return false; - } - return false; -} - -void InputField::contextMenuEventInner(QContextMenuEvent *e) { - if (const auto menu = _inner->createStandardContextMenu()) { - addMarkdownActions(menu, e); - _contextMenu = base::make_unique_q(this, menu); - _contextMenu->popup(e->globalPos()); - } -} - -void InputField::addMarkdownActions( - not_null menu, - QContextMenuEvent *e) { - if (!_markdownEnabled) { - return; - } - auto &integration = Integration::Instance(); - - const auto formatting = new QAction( - integration.phraseFormattingTitle(), - menu); - addMarkdownMenuAction(menu, formatting); - - const auto submenu = new QMenu(menu); - formatting->setMenu(submenu); - - const auto textWithTags = getTextWithTagsSelected(); - const auto &text = textWithTags.text; - const auto &tags = textWithTags.tags; - const auto hasText = !text.isEmpty(); - const auto hasTags = !tags.isEmpty(); - const auto disabled = (!_editLinkCallback && !hasText); - formatting->setDisabled(disabled); - if (disabled) { - return; - } - const auto fullTag = GetFullSimpleTextTag(textWithTags); - const auto add = [&]( - const QString &base, - QKeySequence sequence, - bool disabled, - auto callback) { - const auto add = sequence.isEmpty() - ? QString() - : QChar('\t') + sequence.toString(QKeySequence::NativeText); - const auto action = new QAction(base + add, submenu); - connect(action, &QAction::triggered, this, callback); - action->setDisabled(disabled); - submenu->addAction(action); - }; - const auto addtag = [&]( - const QString &base, - QKeySequence sequence, - const QString &tag) { - const auto disabled = (fullTag == tag) - || (fullTag == kTagPre && tag == kTagCode); - add(base, sequence, (!hasText || fullTag == tag), [=] { - toggleSelectionMarkdown(tag); - }); - }; - const auto addlink = [&] { - const auto selection = editLinkSelection(e); - const auto data = selectionEditLinkData(selection); - const auto base = data.link.isEmpty() - ? integration.phraseFormattingLinkCreate() - : integration.phraseFormattingLinkEdit(); - add(base, kEditLinkSequence, false, [=] { - editMarkdownLink(selection); - }); - }; - const auto addclear = [&] { - const auto disabled = !hasText || !hasTags; - add(integration.phraseFormattingClear(), kClearFormatSequence, disabled, [=] { - clearSelectionMarkdown(); - }); - }; - - addtag(integration.phraseFormattingBold(), QKeySequence::Bold, kTagBold); - addtag(integration.phraseFormattingItalic(), QKeySequence::Italic, kTagItalic); - addtag(integration.phraseFormattingUnderline(), QKeySequence::Underline, kTagUnderline); - addtag(integration.phraseFormattingStrikeOut(), kStrikeOutSequence, kTagStrikeOut); - addtag(integration.phraseFormattingMonospace(), kMonospaceSequence, kTagCode); - - if (_editLinkCallback) { - submenu->addSeparator(); - addlink(); - } - - submenu->addSeparator(); - addclear(); -} - -void InputField::addMarkdownMenuAction( - not_null menu, - not_null action) { - const auto actions = menu->actions(); - const auto before = [&] { - auto seenAfter = false; - for (const auto action : actions) { - if (seenAfter) { - return action; - } else if (action->objectName() == qstr("edit-delete")) { - seenAfter = true; - } - } - return (QAction*)nullptr; - }(); - menu->insertSeparator(before); - menu->insertAction(before, action); -} - -void InputField::dropEventInner(QDropEvent *e) { - _inDrop = true; - _inner->QTextEdit::dropEvent(e); - _inDrop = false; - _insertedTags.clear(); - _realInsertPosition = -1; - window()->activateWindow(); -} - -bool InputField::canInsertFromMimeDataInner(const QMimeData *source) const { - if (source - && _mimeDataHook - && _mimeDataHook(source, MimeAction::Check)) { - return true; - } - return _inner->QTextEdit::canInsertFromMimeData(source); -} - -void InputField::insertFromMimeDataInner(const QMimeData *source) { - if (source - && _mimeDataHook - && _mimeDataHook(source, MimeAction::Insert)) { - return; - } - const auto text = [&] { - const auto textMime = TextUtilities::TagsTextMimeType(); - const auto tagsMime = TextUtilities::TagsMimeType(); - if (!source->hasFormat(textMime) || !source->hasFormat(tagsMime)) { - _insertedTags.clear(); - return source->text(); - } - auto result = QString::fromUtf8(source->data(textMime)); - _insertedTags = TextUtilities::DeserializeTags( - source->data(tagsMime), - result.size()); - _insertedTagsAreFromMime = true; - return result; - }(); - auto cursor = textCursor(); - _realInsertPosition = cursor.selectionStart(); - _realCharsAdded = text.size(); - if (_realCharsAdded > 0) { - cursor.insertFragment(QTextDocumentFragment::fromPlainText(text)); - } - ensureCursorVisible(); - if (!_inDrop) { - _insertedTags.clear(); - _realInsertPosition = -1; - } -} - -void InputField::resizeEvent(QResizeEvent *e) { - refreshPlaceholder(_placeholderFull.current()); - _inner->setGeometry(rect().marginsRemoved(_st.textMargins)); - _borderAnimationStart = width() / 2; - RpWidget::resizeEvent(e); - checkContentHeight(); -} - -void InputField::refreshPlaceholder(const QString &text) { - const auto availableWidth = width() - _st.textMargins.left() - _st.textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1; - if (_st.placeholderScale > 0.) { - auto placeholderFont = _st.placeholderFont->f; - placeholderFont.setStyleStrategy(QFont::PreferMatch); - const auto metrics = QFontMetrics(placeholderFont); - _placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth); - _placeholderPath = QPainterPath(); - if (!_placeholder.isEmpty()) { - _placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder); - } - } else { - _placeholder = _st.placeholderFont->elided(text, availableWidth); - } - update(); -} - -void InputField::setPlaceholder( - rpl::producer placeholder, - int afterSymbols) { - _placeholderFull = std::move(placeholder); - if (_placeholderAfterSymbols != afterSymbols) { - _placeholderAfterSymbols = afterSymbols; - startPlaceholderAnimation(); - } -} - -void InputField::setEditLinkCallback( - Fn callback) { - _editLinkCallback = std::move(callback); -} - -void InputField::showError() { - setErrorShown(true); - if (!hasFocus()) { - _inner->setFocus(); - } -} - -void InputField::setErrorShown(bool error) { - if (_error != error) { - _error = error; - _a_error.start([this] { update(); }, _error ? 0. : 1., _error ? 1. : 0., _st.duration); - startBorderAnimation(); - } -} - -InputField::~InputField() = default; - -MaskedInputField::MaskedInputField( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val) -: Parent(val, parent) -, _st(st) -, _oldtext(val) -, _placeholderFull(std::move(placeholder)) { - resize(_st.width, _st.heightMin); - - setFont(_st.font); - setAlignment(_st.textAlign); - - _placeholderFull.value( - ) | rpl::start_with_next([=](const QString &text) { - refreshPlaceholder(text); - }, lifetime()); - - style::PaletteChanged( - ) | rpl::start_with_next([=] { - updatePalette(); - }, lifetime()); - updatePalette(); - - setAttribute(Qt::WA_OpaquePaintEvent); - - connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChange(const QString&))); - connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int))); - - connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(onTextEdited())); - connect(this, &MaskedInputField::selectionChanged, [] { - Integration::Instance().textActionsUpdated(); - }); - - setStyle(InputStyle::instance()); - QLineEdit::setTextMargins(0, 0, 0, 0); - setContentsMargins(0, 0, 0, 0); - - setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - - setTextMargins(_st.textMargins); - - startPlaceholderAnimation(); - startBorderAnimation(); - finishAnimating(); -} - -void MaskedInputField::updatePalette() { - auto p = palette(); - p.setColor(QPalette::Text, _st.textFg->c); - setPalette(p); -} - -void MaskedInputField::setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos) { - if (newPos < 0 || newPos > newText.size()) { - newPos = newText.size(); - } - auto updateText = (newText != now); - if (updateText) { - now = newText; - setText(now); - startPlaceholderAnimation(); - } - auto updateCursorPosition = (newPos != nowCursor) || updateText; - if (updateCursorPosition) { - nowCursor = newPos; - setCursorPosition(nowCursor); - } -} - -void MaskedInputField::customUpDown(bool custom) { - _customUpDown = custom; -} - -int MaskedInputField::borderAnimationStart() const { - return _borderAnimationStart; -} - -void MaskedInputField::setTextMargins(const QMargins &mrg) { - _textMargins = mrg; - refreshPlaceholder(_placeholderFull.current()); -} - -void MaskedInputField::onTouchTimer() { - _touchRightButton = true; -} - -bool MaskedInputField::eventHook(QEvent *e) { - auto type = e->type(); - if (type == QEvent::TouchBegin - || type == QEvent::TouchUpdate - || type == QEvent::TouchEnd - || type == QEvent::TouchCancel) { - auto event = static_cast(e); - if (event->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(event); - } - } - return Parent::eventHook(e); -} - -void MaskedInputField::touchEvent(QTouchEvent *e) { - switch (e->type()) { - case QEvent::TouchBegin: { - if (_touchPress || e->touchPoints().isEmpty()) return; - _touchTimer.start(QApplication::startDragTime()); - _touchPress = true; - _touchMove = _touchRightButton = false; - _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); - } break; - - case QEvent::TouchUpdate: { - if (!_touchPress || e->touchPoints().isEmpty()) return; - if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { - _touchMove = true; - } - } break; - - case QEvent::TouchEnd: { - if (!_touchPress) return; - auto weak = MakeWeak(this); - if (!_touchMove && window()) { - Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); - QPoint mapped(mapFromGlobal(_touchStart)), winMapped(window()->mapFromGlobal(_touchStart)); - - if (_touchRightButton) { - QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart); - contextMenuEvent(&contextEvent); - } - } - if (weak) { - _touchTimer.stop(); - _touchPress = _touchMove = _touchRightButton = false; - } - } break; - - case QEvent::TouchCancel: { - _touchPress = false; - _touchTimer.stop(); - } break; - } -} - -QRect MaskedInputField::getTextRect() const { - return rect().marginsRemoved(_textMargins + QMargins(-2, -1, -2, -1)); -} - -void MaskedInputField::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto r = rect().intersected(e->rect()); - p.fillRect(r, _st.textBg); - if (_st.border) { - p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b); - } - auto errorDegree = _a_error.value(_error ? 1. : 0.); - auto focusedDegree = _a_focused.value(_focused ? 1. : 0.); - auto borderShownDegree = _a_borderShown.value(1.); - auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); - if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = std::clamp(_borderAnimationStart, 0, width()); - auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); - auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); - if (borderTo > borderFrom) { - auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree); - p.setOpacity(borderOpacity); - p.fillRect(borderFrom, height() - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg); - p.setOpacity(1); - } - } - - p.setClipRect(r); - if (_st.placeholderScale > 0. && !_placeholderPath.isEmpty()) { - auto placeholderShiftDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.); - p.save(); - p.setClipRect(r); - - auto placeholderTop = anim::interpolate(0, _st.placeholderShift, placeholderShiftDegree); - - QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins)); - r.moveTop(r.top() + placeholderTop); - if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); - - auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree; - auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree); - placeholderFg = anim::color(placeholderFg, _st.placeholderFgError, errorDegree); - - PainterHighQualityEnabler hq(p); - p.setPen(Qt::NoPen); - p.setBrush(placeholderFg); - p.translate(r.topLeft()); - p.scale(placeholderScale, placeholderScale); - p.drawPath(_placeholderPath); - - p.restore(); - } else if (!_placeholder.isEmpty()) { - auto placeholderHiddenDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.); - if (placeholderHiddenDegree < 1.) { - p.setOpacity(1. - placeholderHiddenDegree); - p.save(); - p.setClipRect(r); - - auto placeholderLeft = anim::interpolate(0, -_st.placeholderShift, placeholderHiddenDegree); - - QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins)); - r.moveLeft(r.left() + placeholderLeft); - if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); - - p.setFont(_st.placeholderFont); - p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, focusedDegree)); - p.drawText(r, _placeholder, _st.placeholderAlign); - - p.restore(); - p.setOpacity(1.); - } - } - - paintAdditionalPlaceholder(p); - QLineEdit::paintEvent(e); -} - -void MaskedInputField::startBorderAnimation() { - auto borderVisible = (_error || _focused); - if (_borderVisible != borderVisible) { - _borderVisible = borderVisible; - if (_borderVisible) { - if (_a_borderOpacity.animating()) { - _a_borderOpacity.start([this] { update(); }, 0., 1., _st.duration); - } else { - _a_borderShown.start([this] { update(); }, 0., 1., _st.duration); - } - } else if (qFuzzyCompare(_a_borderShown.value(1.), 0.)) { - _a_borderShown.stop(); - _a_borderOpacity.stop(); - } else { - _a_borderOpacity.start([this] { update(); }, 1., 0., _st.duration); - } - } -} - -void MaskedInputField::focusInEvent(QFocusEvent *e) { - _borderAnimationStart = (e->reason() == Qt::MouseFocusReason) ? mapFromGlobal(QCursor::pos()).x() : (width() / 2); - setFocused(true); - QLineEdit::focusInEvent(e); - emit focused(); -} - -void MaskedInputField::focusOutEvent(QFocusEvent *e) { - setFocused(false); - QLineEdit::focusOutEvent(e); - emit blurred(); -} - -void MaskedInputField::setFocused(bool focused) { - if (_focused != focused) { - _focused = focused; - _a_focused.start([this] { update(); }, _focused ? 0. : 1., _focused ? 1. : 0., _st.duration); - startPlaceholderAnimation(); - startBorderAnimation(); - } -} - -void MaskedInputField::resizeEvent(QResizeEvent *e) { - refreshPlaceholder(_placeholderFull.current()); - _borderAnimationStart = width() / 2; - QLineEdit::resizeEvent(e); -} - -void MaskedInputField::refreshPlaceholder(const QString &text) { - const auto availableWidth = width() - _textMargins.left() - _textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1; - if (_st.placeholderScale > 0.) { - auto placeholderFont = _st.placeholderFont->f; - placeholderFont.setStyleStrategy(QFont::PreferMatch); - const auto metrics = QFontMetrics(placeholderFont); - _placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth); - _placeholderPath = QPainterPath(); - if (!_placeholder.isEmpty()) { - _placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder); - } - } else { - _placeholder = _st.placeholderFont->elided(text, availableWidth); - } - update(); -} - -void MaskedInputField::setPlaceholder(rpl::producer placeholder) { - _placeholderFull = std::move(placeholder); -} - -void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) { - if (const auto menu = createStandardContextMenu()) { - (new PopupMenu(this, menu))->popup(e->globalPos()); - } -} - -void MaskedInputField::inputMethodEvent(QInputMethodEvent *e) { - QLineEdit::inputMethodEvent(e); - _lastPreEditText = e->preeditString(); - update(); -} - -void MaskedInputField::showError() { - setErrorShown(true); - if (!hasFocus()) { - setFocus(); - } -} - -void MaskedInputField::setErrorShown(bool error) { - if (_error != error) { - _error = error; - _a_error.start([this] { update(); }, _error ? 0. : 1., _error ? 1. : 0., _st.duration); - startBorderAnimation(); - } -} - -QSize MaskedInputField::sizeHint() const { - return geometry().size(); -} - -QSize MaskedInputField::minimumSizeHint() const { - return geometry().size(); -} - -void MaskedInputField::setDisplayFocused(bool focused) { - setFocused(focused); - finishAnimating(); -} - -void MaskedInputField::finishAnimating() { - _a_focused.stop(); - _a_error.stop(); - _a_placeholderShifted.stop(); - _a_borderShown.stop(); - _a_borderOpacity.stop(); - update(); -} - -void MaskedInputField::setPlaceholderHidden(bool forcePlaceholderHidden) { - _forcePlaceholderHidden = forcePlaceholderHidden; - startPlaceholderAnimation(); -} - -void MaskedInputField::startPlaceholderAnimation() { - auto placeholderShifted = _forcePlaceholderHidden || (_focused && _st.placeholderScale > 0.) || !getLastText().isEmpty(); - if (_placeholderShifted != placeholderShifted) { - _placeholderShifted = placeholderShifted; - _a_placeholderShifted.start([this] { update(); }, _placeholderShifted ? 0. : 1., _placeholderShifted ? 1. : 0., _st.duration); - } -} - -QRect MaskedInputField::placeholderRect() const { - return rect().marginsRemoved(_textMargins + _st.placeholderMargins); -} - -void MaskedInputField::placeholderAdditionalPrepare(Painter &p) { - p.setFont(_st.font); - p.setPen(_st.placeholderFg); -} - -void MaskedInputField::keyPressEvent(QKeyEvent *e) { - QString wasText(_oldtext); - int32 wasCursor(_oldcursor); - - if (_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)) { - e->ignore(); - } else { - QLineEdit::keyPressEvent(e); - } - - auto newText = text(); - auto newCursor = cursorPosition(); - if (wasText == newText && wasCursor == newCursor) { // call correct manually - correctValue(wasText, wasCursor, newText, newCursor); - _oldtext = newText; - _oldcursor = newCursor; - if (wasText != _oldtext) emit changed(); - startPlaceholderAnimation(); - } - if (e->key() == Qt::Key_Escape) { - e->ignore(); - emit cancelled(); - } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - emit submitted(e->modifiers()); -#ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - auto selected = selectedText(); - if (!selected.isEmpty() && echoMode() == QLineEdit::Normal) { - QGuiApplication::clipboard()->setText(selected, QClipboard::FindBuffer); - } -#endif // Q_OS_MAC - } -} - -void MaskedInputField::onTextEdited() { - QString wasText(_oldtext), newText(text()); - int32 wasCursor(_oldcursor), newCursor(cursorPosition()); - - correctValue(wasText, wasCursor, newText, newCursor); - _oldtext = newText; - _oldcursor = newCursor; - if (wasText != _oldtext) emit changed(); - startPlaceholderAnimation(); - - Integration::Instance().textActionsUpdated(); -} - -void MaskedInputField::onTextChange(const QString &text) { - _oldtext = QLineEdit::text(); - setErrorShown(false); - Integration::Instance().textActionsUpdated(); -} - -void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) { - _oldcursor = position; -} - -PasswordInput::PasswordInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val) -: MaskedInputField(parent, st, std::move(placeholder), val) { - setEchoMode(QLineEdit::Password); -} - -PortInput::PortInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val) -: MaskedInputField(parent, st, std::move(placeholder), val) { - if (!val.toInt() || val.toInt() > 65535) { - setText(QString()); - } -} - -void PortInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - QString newText; - newText.reserve(now.size()); - auto newPos = nowCursor; - for (auto i = 0, l = now.size(); i < l; ++i) { - if (now.at(i).isDigit()) { - newText.append(now.at(i)); - } else if (i < nowCursor) { - --newPos; - } - } - if (!newText.toInt()) { - newText = QString(); - newPos = 0; - } else if (newText.toInt() > 65535) { - newText = was; - newPos = wasCursor; - } - setCorrectedText(now, nowCursor, newText, newPos); -} - -HexInput::HexInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val) -: MaskedInputField(parent, st, std::move(placeholder), val) { - if (!QRegularExpression("^[a-fA-F0-9]+$").match(val).hasMatch()) { - setText(QString()); - } -} - -void HexInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - QString newText; - newText.reserve(now.size()); - auto newPos = nowCursor; - for (auto i = 0, l = now.size(); i < l; ++i) { - const auto ch = now[i]; - if ((ch >= '0' && ch <= '9') - || (ch >= 'a' && ch <= 'f') - || (ch >= 'A' && ch <= 'F')) { - newText.append(ch); - } else if (i < nowCursor) { - --newPos; - } - } - setCorrectedText(now, nowCursor, newText, newPos); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.h b/Telegram/SourceFiles/ui/widgets/input_fields.h deleted file mode 100644 index ae480a05c..000000000 --- a/Telegram/SourceFiles/ui/widgets/input_fields.h +++ /dev/null @@ -1,696 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/emoji_config.h" -#include "ui/rp_widget.h" -#include "ui/effects/animations.h" -#include "ui/text/text_entity.h" -#include "styles/style_widgets.h" - -#include -#include -#include - -class QTouchEvent; -class Painter; - -namespace Ui { - -const auto kClearFormatSequence = QKeySequence("ctrl+shift+n"); -const auto kStrikeOutSequence = QKeySequence("ctrl+shift+x"); -const auto kMonospaceSequence = QKeySequence("ctrl+shift+m"); -const auto kEditLinkSequence = QKeySequence("ctrl+k"); - -class PopupMenu; - -void InsertEmojiAtCursor(QTextCursor cursor, EmojiPtr emoji); - -struct InstantReplaces { - struct Node { - QString text; - std::map tail; - }; - - void add(const QString &what, const QString &with); - - static const InstantReplaces &Default(); - static const InstantReplaces &TextOnly(); - - int maxLength = 0; - Node reverseMap; - -}; - -enum class InputSubmitSettings { - Enter, - CtrlEnter, - Both, - None, -}; - -class FlatInput : public RpWidgetWrap { - // The Q_OBJECT meta info is used for qobject_cast! - Q_OBJECT - - using Parent = RpWidgetWrap; -public: - FlatInput( - QWidget *parent, - const style::FlatInput &st, - rpl::producer placeholder = nullptr, - const QString &val = QString()); - - void updatePlaceholder(); - void setPlaceholder(rpl::producer placeholder); - QRect placeholderRect() const; - - void finishAnimations(); - - void setTextMrg(const QMargins &textMrg); - QRect getTextRect() const; - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - void customUpDown(bool isCustom); - const QString &getLastText() const { - return _oldtext; - } - -public slots: - void onTextChange(const QString &text); - void onTextEdited(); - - void onTouchTimer(); - -signals: - void changed(); - void cancelled(); - void submitted(Qt::KeyboardModifiers); - void focused(); - void blurred(); - -protected: - bool eventHook(QEvent *e) override; - void touchEvent(QTouchEvent *e); - void paintEvent(QPaintEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - void inputMethodEvent(QInputMethodEvent *e) override; - - virtual void correctValue(const QString &was, QString &now); - - style::font phFont() { - return _st.font; - } - - void phPrepare(QPainter &p, float64 placeholderFocused); - -private: - void updatePalette(); - void refreshPlaceholder(const QString &text); - - QString _oldtext; - rpl::variable _placeholderFull; - QString _placeholder; - - bool _customUpDown = false; - - bool _focused = false; - bool _placeholderVisible = true; - Animations::Simple _placeholderFocusedAnimation; - Animations::Simple _placeholderVisibleAnimation; - bool _lastPreEditTextNotEmpty = false; - - const style::FlatInput &_st; - QMargins _textMrg; - - QTimer _touchTimer; - bool _touchPress, _touchRightButton, _touchMove; - QPoint _touchStart; -}; - -class InputField : public RpWidget { - Q_OBJECT - -public: - enum class Mode { - SingleLine, - NoNewlines, - MultiLine, - }; - using TagList = TextWithTags::Tags; - - struct MarkdownTag { - // With each emoji being QChar::ObjectReplacementCharacter. - int internalStart = 0; - int internalLength = 0; - - // Adjusted by emoji to match _lastTextWithTags. - int adjustedStart = 0; - int adjustedLength = 0; - - bool closed = false; - QString tag; - }; - static const QString kTagBold; - static const QString kTagItalic; - static const QString kTagUnderline; - static const QString kTagStrikeOut; - static const QString kTagCode; - static const QString kTagPre; - - InputField( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &value = QString()); - InputField( - QWidget *parent, - const style::InputField &st, - Mode mode, - rpl::producer placeholder, - const QString &value); - InputField( - QWidget *parent, - const style::InputField &st, - Mode mode = Mode::SingleLine, - rpl::producer placeholder = nullptr, - const TextWithTags &value = TextWithTags()); - - void showError(); - - void setMaxLength(int maxLength); - void setMinHeight(int minHeight); - void setMaxHeight(int maxHeight); - - const TextWithTags &getTextWithTags() const { - return _lastTextWithTags; - } - const std::vector &getMarkdownTags() const { - return _lastMarkdownTags; - } - TextWithTags getTextWithTagsPart(int start, int end = -1) const; - TextWithTags getTextWithAppliedMarkdown() const; - void insertTag(const QString &text, QString tagId = QString()); - bool empty() const { - return _lastTextWithTags.text.isEmpty(); - } - enum class HistoryAction { - NewEntry, - MergeEntry, - Clear, - }; - void setTextWithTags( - const TextWithTags &textWithTags, - HistoryAction historyAction = HistoryAction::NewEntry); - - // If you need to make some preparations of tags before putting them to QMimeData - // (and then to clipboard or to drag-n-drop object), here is a strategy for that. - class TagMimeProcessor { - public: - virtual QString tagFromMimeTag(const QString &mimeTag) = 0; - virtual ~TagMimeProcessor() = default; - }; - void setTagMimeProcessor(std::unique_ptr &&processor); - - struct EditLinkSelection { - int from = 0; - int till = 0; - }; - enum class EditLinkAction { - Check, - Edit, - }; - void setEditLinkCallback( - Fn callback); - - void setAdditionalMargin(int margin); - - void setInstantReplaces(const InstantReplaces &replaces); - void setInstantReplacesEnabled(rpl::producer enabled); - void setMarkdownReplacesEnabled(rpl::producer enabled); - void commitInstantReplacement(int from, int till, const QString &with); - void commitMarkdownLinkEdit( - EditLinkSelection selection, - const QString &text, - const QString &link); - static bool IsValidMarkdownLink(const QString &link); - - const QString &getLastText() const { - return _lastTextWithTags.text; - } - void setPlaceholder( - rpl::producer placeholder, - int afterSymbols = 0); - void setPlaceholderHidden(bool forcePlaceholderHidden); - void setDisplayFocused(bool focused); - void finishAnimating(); - void setFocusFast() { - setDisplayFocused(true); - setFocus(); - } - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - bool hasText() const; - void selectAll(); - - bool isUndoAvailable() const; - bool isRedoAvailable() const; - - bool isMarkdownEnabled() const { - return _markdownEnabled; - } - - using SubmitSettings = InputSubmitSettings; - void setSubmitSettings(SubmitSettings settings); - static bool ShouldSubmit( - SubmitSettings settings, - Qt::KeyboardModifiers modifiers); - void customUpDown(bool isCustom); - void customTab(bool isCustom); - int borderAnimationStart() const; - - not_null document(); - not_null document() const; - void setTextCursor(const QTextCursor &cursor); - void setCursorPosition(int position); - QTextCursor textCursor() const; - void setText(const QString &text); - void clear(); - bool hasFocus() const; - void setFocus(); - void clearFocus(); - void ensureCursorVisible(); - not_null rawTextEdit(); - not_null rawTextEdit() const; - - enum class MimeAction { - Check, - Insert, - }; - using MimeDataHook = Fn data, - MimeAction action)>; - void setMimeDataHook(MimeDataHook hook) { - _mimeDataHook = std::move(hook); - } - - const rpl::variable &scrollTop() const; - int scrollTopMax() const; - void scrollTo(int top); - - ~InputField(); - -private slots: - void onTouchTimer(); - - void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); - void onCursorPositionChanged(); - - void onUndoAvailable(bool avail); - void onRedoAvailable(bool avail); - - void onFocusInner(); - -signals: - void changed(); - void submitted(Qt::KeyboardModifiers); - void cancelled(); - void tabbed(); - void focused(); - void blurred(); - void resized(); - -protected: - void startPlaceholderAnimation(); - void startBorderAnimation(); - - void paintEvent(QPaintEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - class Inner; - friend class Inner; - - void handleContentsChanged(); - bool viewportEventInner(QEvent *e); - void handleTouchEvent(QTouchEvent *e); - - void updatePalette(); - void refreshPlaceholder(const QString &text); - int placeholderSkipWidth() const; - - bool heightAutoupdated(); - void checkContentHeight(); - void setErrorShown(bool error); - - void focusInEventInner(QFocusEvent *e); - void focusOutEventInner(QFocusEvent *e); - void setFocused(bool focused); - void keyPressEventInner(QKeyEvent *e); - void contextMenuEventInner(QContextMenuEvent *e); - void dropEventInner(QDropEvent *e); - void inputMethodEventInner(QInputMethodEvent *e); - - QMimeData *createMimeDataFromSelectionInner() const; - bool canInsertFromMimeDataInner(const QMimeData *source) const; - void insertFromMimeDataInner(const QMimeData *source); - TextWithTags getTextWithTagsSelected() const; - - // "start" and "end" are in coordinates of text where emoji are replaced - // by ObjectReplacementCharacter. If "end" = -1 means get text till the end. - QString getTextPart( - int start, - int end, - TagList &outTagsList, - bool &outTagsChanged, - std::vector *outMarkdownTags = nullptr) const; - - // After any characters added we must postprocess them. This includes: - // 1. Replacing font family to semibold for ~ characters, if we used Open Sans 13px. - // 2. Replacing font family from semibold for all non-~ characters, if we used ... - // 3. Replacing emoji code sequences by ObjectReplacementCharacters with emoji pics. - // 4. Interrupting tags in which the text was inserted by any char except a letter. - // 5. Applying tags from "_insertedTags" in case we pasted text with tags, not just text. - // Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end). - void processFormatting(int changedPosition, int changedEnd); - - void chopByMaxLength(int insertPosition, int insertLength); - - bool processMarkdownReplaces(const QString &appended); - //bool processMarkdownReplace(const QString &tag); - void addMarkdownActions(not_null menu, QContextMenuEvent *e); - void addMarkdownMenuAction( - not_null menu, - not_null action); - bool handleMarkdownKey(QKeyEvent *e); - - // We don't want accidentally detach InstantReplaces map. - // So we access it only by const reference from this method. - const InstantReplaces &instantReplaces() const; - void processInstantReplaces(const QString &appended); - void applyInstantReplace(const QString &what, const QString &with); - - struct EditLinkData { - int from = 0; - int till = 0; - QString link; - }; - EditLinkData selectionEditLinkData(EditLinkSelection selection) const; - EditLinkSelection editLinkSelection(QContextMenuEvent *e) const; - void editMarkdownLink(EditLinkSelection selection); - - void commitInstantReplacement( - int from, - int till, - const QString &with, - std::optional checkOriginal, - bool checkIfInMonospace); - bool commitMarkdownReplacement( - int from, - int till, - const QString &tag, - const QString &edge = QString()); - void toggleSelectionMarkdown(const QString &tag); - void clearSelectionMarkdown(); - - bool revertFormatReplace(); - - void highlightMarkdown(); - - const style::InputField &_st; - - Mode _mode = Mode::SingleLine; - int _maxLength = -1; - int _minHeight = -1; - int _maxHeight = -1; - bool _forcePlaceholderHidden = false; - bool _reverseMarkdownReplacement = false; - - const std::unique_ptr _inner; - - TextWithTags _lastTextWithTags; - std::vector _lastMarkdownTags; - QString _lastPreEditText; - Fn _editLinkCallback; - - // Tags list which we should apply while setText() call or insert from mime data. - TagList _insertedTags; - bool _insertedTagsAreFromMime; - - // Override insert position and charsAdded from complex text editing - // (like drag-n-drop in the same text edit field). - int _realInsertPosition = -1; - int _realCharsAdded = 0; - - std::unique_ptr _tagMimeProcessor; - - SubmitSettings _submitSettings = SubmitSettings::Enter; - bool _markdownEnabled = false; - bool _undoAvailable = false; - bool _redoAvailable = false; - bool _inDrop = false; - bool _inHeightCheck = false; - int _additionalMargin = 0; - - bool _customUpDown = false; - bool _customTab = false; - - rpl::variable _placeholderFull; - QString _placeholder; - int _placeholderAfterSymbols = 0; - Animations::Simple _a_placeholderShifted; - bool _placeholderShifted = false; - QPainterPath _placeholderPath; - - Animations::Simple _a_borderShown; - int _borderAnimationStart = 0; - Animations::Simple _a_borderOpacity; - bool _borderVisible = false; - - Animations::Simple _a_focused; - Animations::Simple _a_error; - - bool _focused = false; - bool _error = false; - - QTimer _touchTimer; - bool _touchPress = false; - bool _touchRightButton = false; - bool _touchMove = false; - QPoint _touchStart; - - bool _correcting = false; - MimeDataHook _mimeDataHook; - base::unique_qptr _contextMenu; - - QTextCharFormat _defaultCharFormat; - - rpl::variable _scrollTop; - - InstantReplaces _mutableInstantReplaces; - bool _instantReplacesEnabled = true; - -}; - -class MaskedInputField : public RpWidgetWrap { - // The Q_OBJECT meta info is used for qobject_cast! - Q_OBJECT - - using Parent = RpWidgetWrap; -public: - MaskedInputField( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder = nullptr, - const QString &val = QString()); - - void showError(); - - QRect getTextRect() const; - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - void customUpDown(bool isCustom); - int borderAnimationStart() const; - - const QString &getLastText() const { - return _oldtext; - } - void setPlaceholder(rpl::producer placeholder); - void setPlaceholderHidden(bool forcePlaceholderHidden); - void setDisplayFocused(bool focused); - void finishAnimating(); - void setFocusFast() { - setDisplayFocused(true); - setFocus(); - } - - void setText(const QString &text) { - QLineEdit::setText(text); - startPlaceholderAnimation(); - } - void clear() { - QLineEdit::clear(); - startPlaceholderAnimation(); - } - -public slots: - void onTextChange(const QString &text); - void onCursorPositionChanged(int oldPosition, int position); - - void onTextEdited(); - - void onTouchTimer(); - -signals: - void changed(); - void cancelled(); - void submitted(Qt::KeyboardModifiers); - void focused(); - void blurred(); - -protected: - QString getDisplayedText() const { - auto result = getLastText(); - if (!_lastPreEditText.isEmpty()) { - result = result.mid(0, _oldcursor) + _lastPreEditText + result.mid(_oldcursor); - } - return result; - } - void startBorderAnimation(); - void startPlaceholderAnimation(); - - bool eventHook(QEvent *e) override; - void touchEvent(QTouchEvent *e); - void paintEvent(QPaintEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - void inputMethodEvent(QInputMethodEvent *e) override; - - virtual void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - } - void setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos); - - virtual void paintAdditionalPlaceholder(Painter &p) { - } - - style::font phFont() { - return _st.font; - } - - void placeholderAdditionalPrepare(Painter &p); - QRect placeholderRect() const; - - void setTextMargins(const QMargins &mrg); - const style::InputField &_st; - -private: - void updatePalette(); - void refreshPlaceholder(const QString &text); - void setErrorShown(bool error); - - void setFocused(bool focused); - - int _maxLength = -1; - bool _forcePlaceholderHidden = false; - - QString _oldtext; - int _oldcursor = 0; - QString _lastPreEditText; - - bool _undoAvailable = false; - bool _redoAvailable = false; - - bool _customUpDown = false; - - rpl::variable _placeholderFull; - QString _placeholder; - Animations::Simple _a_placeholderShifted; - bool _placeholderShifted = false; - QPainterPath _placeholderPath; - - Animations::Simple _a_borderShown; - int _borderAnimationStart = 0; - Animations::Simple _a_borderOpacity; - bool _borderVisible = false; - - Animations::Simple _a_focused; - Animations::Simple _a_error; - - bool _focused = false; - bool _error = false; - - style::margins _textMargins; - - QTimer _touchTimer; - bool _touchPress = false; - bool _touchRightButton = false; - bool _touchMove = false; - QPoint _touchStart; -}; - -class PasswordInput : public MaskedInputField { -public: - PasswordInput(QWidget *parent, const style::InputField &st, rpl::producer placeholder = nullptr, const QString &val = QString()); - -}; - -class PortInput : public MaskedInputField { -public: - PortInput(QWidget *parent, const style::InputField &st, rpl::producer placeholder, const QString &val); - -protected: - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - -}; - -class HexInput : public MaskedInputField { -public: - HexInput(QWidget *parent, const style::InputField &st, rpl::producer placeholder, const QString &val); - -protected: - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp deleted file mode 100644 index 75d6e0e7b..000000000 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ /dev/null @@ -1,894 +0,0 @@ -/* -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 "ui/widgets/labels.h" - -#include "ui/text/text_entity.h" -#include "ui/widgets/popup_menu.h" -#include "ui/basic_click_handlers.h" // UrlClickHandler -#include "ui/inactive_press.h" - -#include -#include -#include -#include -#include - -namespace Ui { -namespace { - -TextParseOptions _labelOptions = { - TextParseMultiline, // flags - 0, // maxw - 0, // maxh - Qt::LayoutDirectionAuto, // dir -}; - -TextParseOptions _labelMarkedOptions = { - TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMarkdown, // flags - 0, // maxw - 0, // maxh - Qt::LayoutDirectionAuto, // dir -}; - -} // namespace - -CrossFadeAnimation::CrossFadeAnimation(style::color bg) : _bg(bg) { -} - -void CrossFadeAnimation::addLine(Part was, Part now) { - _lines.push_back(Line(std::move(was), std::move(now))); -} - -void CrossFadeAnimation::paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow) { - if (_lines.isEmpty()) return; - - for (const auto &line : std::as_const(_lines)) { - paintLine(p, line, positionReady, alphaWas, alphaNow); - } -} - -void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow) { - auto &snapshotWas = line.was.snapshot; - auto &snapshotNow = line.now.snapshot; - if (snapshotWas.isNull() && snapshotNow.isNull()) { - // This can happen if both labels have an empty line or if one - // label has an empty line where the second one already ended. - // In this case lineWidth is zero and snapshot is null. - return; - } - - const auto pixelRatio = style::DevicePixelRatio(); - auto positionWas = line.was.position; - auto positionNow = line.now.position; - auto left = anim::interpolate(positionWas.x(), positionNow.x(), positionReady); - auto topDelta = (snapshotNow.height() / pixelRatio) - (snapshotWas.height() / pixelRatio); - auto widthDelta = (snapshotNow.width() / pixelRatio) - (snapshotWas.width() / pixelRatio); - auto topWas = anim::interpolate(positionWas.y(), positionNow.y() + topDelta, positionReady); - auto topNow = topWas - topDelta; - - p.setOpacity(alphaWas); - if (!snapshotWas.isNull()) { - p.drawPixmap(left, topWas, snapshotWas); - if (topDelta > 0) { - p.fillRect(left, topWas - topDelta, snapshotWas.width() / pixelRatio, topDelta, _bg); - } - } - if (widthDelta > 0) { - p.fillRect(left + (snapshotWas.width() / pixelRatio), topNow, widthDelta, snapshotNow.height() / pixelRatio, _bg); - } - - p.setOpacity(alphaNow); - if (!snapshotNow.isNull()) { - p.drawPixmap(left, topNow, snapshotNow); - if (topDelta < 0) { - p.fillRect(left, topNow + topDelta, snapshotNow.width() / pixelRatio, -topDelta, _bg); - } - } - if (widthDelta < 0) { - p.fillRect(left + (snapshotNow.width() / pixelRatio), topWas, -widthDelta, snapshotWas.height() / pixelRatio, _bg); - } -} - -LabelSimple::LabelSimple( - QWidget *parent, - const style::LabelSimple &st, - const QString &value) -: RpWidget(parent) -, _st(st) { - setText(value); -} - -void LabelSimple::setText(const QString &value, bool *outTextChanged) { - if (_fullText == value) { - if (outTextChanged) *outTextChanged = false; - return; - } - - _fullText = value; - _fullTextWidth = _st.font->width(_fullText); - if (!_st.maxWidth || _fullTextWidth <= _st.maxWidth) { - _text = _fullText; - _textWidth = _fullTextWidth; - } else { - auto newText = _st.font->elided(_fullText, _st.maxWidth); - if (newText == _text) { - if (outTextChanged) *outTextChanged = false; - return; - } - _text = newText; - _textWidth = _st.font->width(_text); - } - resize(_textWidth, _st.font->height); - update(); - if (outTextChanged) *outTextChanged = true; -} - -void LabelSimple::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setFont(_st.font); - p.setPen(_st.textFg); - p.drawTextLeft(0, 0, width(), _text, _textWidth); -} - -FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st) -: RpWidget(parent) -, _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) { - init(); -} - -FlatLabel::FlatLabel( - QWidget *parent, - const QString &text, - const style::FlatLabel &st) -: RpWidget(parent) -, _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) { - setText(text); - init(); -} - -FlatLabel::FlatLabel( - QWidget *parent, - rpl::producer &&text, - const style::FlatLabel &st) -: RpWidget(parent) -, _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) { - textUpdated(); - std::move( - text - ) | rpl::start_with_next([this](const QString &value) { - setText(value); - }, lifetime()); - init(); -} - -FlatLabel::FlatLabel( - QWidget *parent, - rpl::producer &&text, - const style::FlatLabel &st) -: RpWidget(parent) -, _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) { - textUpdated(); - std::move( - text - ) | rpl::start_with_next([this](const TextWithEntities &value) { - setMarkedText(value); - }, lifetime()); - init(); -} - -void FlatLabel::init() { - _contextCopyText = Integration::Instance().phraseContextCopyText(); - - _trippleClickTimer.setSingleShot(true); - - _touchSelectTimer.setSingleShot(true); - connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect())); -} - -void FlatLabel::textUpdated() { - refreshSize(); - setMouseTracking(_selectable || _text.hasLinks()); - update(); -} - -void FlatLabel::setText(const QString &text) { - _text.setText(_st.style, text, _labelOptions); - textUpdated(); -} - -void FlatLabel::setRichText(const QString &text) { - _text.setRichText(_st.style, text, _labelOptions); - textUpdated(); -} - -void FlatLabel::setMarkedText(const TextWithEntities &textWithEntities) { - _text.setMarkedText(_st.style, textWithEntities, _labelMarkedOptions); - textUpdated(); -} - -void FlatLabel::setSelectable(bool selectable) { - _selectable = selectable; - setMouseTracking(_selectable || _text.hasLinks()); -} - -void FlatLabel::setDoubleClickSelectsParagraph(bool doubleClickSelectsParagraph) { - _doubleClickSelectsParagraph = doubleClickSelectsParagraph; -} - -void FlatLabel::setContextCopyText(const QString ©Text) { - _contextCopyText = copyText; -} - -void FlatLabel::setBreakEverywhere(bool breakEverywhere) { - _breakEverywhere = breakEverywhere; -} - -void FlatLabel::setTryMakeSimilarLines(bool tryMakeSimilarLines) { - _tryMakeSimilarLines = tryMakeSimilarLines; -} - -int FlatLabel::resizeGetHeight(int newWidth) { - _allowedWidth = newWidth; - _textWidth = countTextWidth(); - return countTextHeight(_textWidth); -} - -int FlatLabel::naturalWidth() const { - return _text.maxWidth(); -} - -QMargins FlatLabel::getMargins() const { - return _st.margin; -} - -int FlatLabel::countTextWidth() const { - const auto available = _allowedWidth - ? _allowedWidth - : (_st.minWidth ? _st.minWidth : _text.maxWidth()); - if (_allowedWidth > 0 - && _allowedWidth < _text.maxWidth() - && _tryMakeSimilarLines) { - auto large = _allowedWidth; - auto small = _allowedWidth / 2; - const auto largeHeight = _text.countHeight(large); - while (large - small > 1) { - const auto middle = (large + small) / 2; - if (largeHeight == _text.countHeight(middle)) { - large = middle; - } else { - small = middle; - } - } - return large; - } - return available; -} - -int FlatLabel::countTextHeight(int textWidth) { - _fullTextHeight = _text.countHeight(textWidth); - return _st.maxHeight ? qMin(_fullTextHeight, _st.maxHeight) : _fullTextHeight; -} - -void FlatLabel::refreshSize() { - int textWidth = countTextWidth(); - int textHeight = countTextHeight(textWidth); - int fullWidth = _st.margin.left() + textWidth + _st.margin.right(); - int fullHeight = _st.margin.top() + textHeight + _st.margin.bottom(); - resize(fullWidth, fullHeight); -} - -void FlatLabel::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) { - _text.setLink(lnkIndex, lnk); -} - -void FlatLabel::setLinksTrusted() { - static const auto TrustedLinksFilter = []( - const ClickHandlerPtr &link, - Qt::MouseButton button) { - if (const auto url = dynamic_cast(link.get())) { - url->UrlClickHandler::onClick({ button }); - return false; - } - return true; - }; - setClickHandlerFilter(TrustedLinksFilter); - -} - -void FlatLabel::setClickHandlerFilter(ClickHandlerFilter &&filter) { - _clickHandlerFilter = std::move(filter); -} - -void FlatLabel::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - dragActionUpdate(); -} - -void FlatLabel::mousePressEvent(QMouseEvent *e) { - if (_contextMenu) { - e->accept(); - return; // ignore mouse press, that was hiding context menu - } - dragActionStart(e->globalPos(), e->button()); -} - -Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButton button) { - _lastMousePos = p; - auto state = dragActionUpdate(); - - if (button != Qt::LeftButton) return state; - - ClickHandler::pressed(); - _dragAction = NoDrag; - _dragWasInactive = WasInactivePress(window()); - if (_dragWasInactive) { - MarkInactivePress(window(), false); - } - - if (ClickHandler::getPressed()) { - _dragStartPosition = mapFromGlobal(_lastMousePos); - _dragAction = PrepareDrag; - } - if (!_selectable || _dragAction != NoDrag) { - return state; - } - - if (_trippleClickTimer.isActive() && (_lastMousePos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { - if (state.uponSymbol) { - _selection = { state.symbol, state.symbol }; - _savedSelection = { 0, 0 }; - _dragSymbol = state.symbol; - _dragAction = Selecting; - _selectionType = TextSelectType::Paragraphs; - updateHover(state); - _trippleClickTimer.start(QApplication::doubleClickInterval()); - update(); - } - } - if (_selectionType != TextSelectType::Paragraphs) { - _dragSymbol = state.symbol; - bool uponSelected = state.uponSymbol; - if (uponSelected) { - if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) { - uponSelected = false; - } - } - if (uponSelected) { - _dragStartPosition = mapFromGlobal(_lastMousePos); - _dragAction = PrepareDrag; // start text drag - } else if (!_dragWasInactive) { - if (state.afterSymbol) ++_dragSymbol; - _selection = { _dragSymbol, _dragSymbol }; - _savedSelection = { 0, 0 }; - _dragAction = Selecting; - update(); - } - } - return state; -} - -Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton button) { - _lastMousePos = p; - auto state = dragActionUpdate(); - - auto activated = ClickHandler::unpressed(); - if (_dragAction == Dragging) { - activated = nullptr; - } else if (_dragAction == PrepareDrag) { - _selection = { 0, 0 }; - _savedSelection = { 0, 0 }; - update(); - } - _dragAction = NoDrag; - _selectionType = TextSelectType::Letters; - - if (activated) { - const auto guard = window(); - if (!_clickHandlerFilter - || _clickHandlerFilter(activated, button)) { - ActivateClickHandler(guard, activated, button); - } - } - -#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - if (!_selection.empty()) { - TextUtilities::SetClipboardText( - _text.toTextForMimeData(_selection), - QClipboard::Selection); - } -#endif // Q_OS_LINUX32 || Q_OS_LINUX64 - - return state; -} - -void FlatLabel::mouseReleaseEvent(QMouseEvent *e) { - dragActionFinish(e->globalPos(), e->button()); - if (!rect().contains(e->pos())) { - leaveEvent(e); - } -} - -void FlatLabel::mouseDoubleClickEvent(QMouseEvent *e) { - auto state = dragActionStart(e->globalPos(), e->button()); - if (((_dragAction == Selecting) || (_dragAction == NoDrag)) && _selectionType == TextSelectType::Letters) { - if (state.uponSymbol) { - _dragSymbol = state.symbol; - _selectionType = _doubleClickSelectsParagraph ? TextSelectType::Paragraphs : TextSelectType::Words; - if (_dragAction == NoDrag) { - _dragAction = Selecting; - _selection = { state.symbol, state.symbol }; - _savedSelection = { 0, 0 }; - } - mouseMoveEvent(e); - - _trippleClickPoint = e->globalPos(); - _trippleClickTimer.start(QApplication::doubleClickInterval()); - } - } -} - -void FlatLabel::enterEventHook(QEvent *e) { - _lastMousePos = QCursor::pos(); - dragActionUpdate(); -} - -void FlatLabel::leaveEventHook(QEvent *e) { - ClickHandler::clearActive(this); -} - -void FlatLabel::focusOutEvent(QFocusEvent *e) { - if (!_selection.empty()) { - if (_contextMenu) { - _savedSelection = _selection; - } - _selection = { 0, 0 }; - update(); - } -} - -void FlatLabel::focusInEvent(QFocusEvent *e) { - if (!_savedSelection.empty()) { - _selection = _savedSelection; - _savedSelection = { 0, 0 }; - update(); - } -} - -void FlatLabel::keyPressEvent(QKeyEvent *e) { - e->ignore(); - if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) { - if (!_selection.empty()) { - onCopySelectedText(); - e->accept(); - } -#ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; - if (!selection.empty()) { - TextUtilities::SetClipboardText(_text.toTextForMimeData(selection), QClipboard::FindBuffer); - } -#endif // Q_OS_MAC - } -} - -void FlatLabel::contextMenuEvent(QContextMenuEvent *e) { - if (!_selectable) return; - - showContextMenu(e, ContextMenuReason::FromEvent); -} - -bool FlatLabel::eventHook(QEvent *e) { - if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { - QTouchEvent *ev = static_cast(e); - if (ev->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(ev); - return true; - } - } - return RpWidget::eventHook(e); -} - -void FlatLabel::touchEvent(QTouchEvent *e) { - const Qt::TouchPointStates &states(e->touchPointStates()); - if (e->type() == QEvent::TouchCancel) { // cancel - if (!_touchInProgress) return; - _touchInProgress = false; - _touchSelectTimer.stop(); - _touchSelect = false; - _dragAction = NoDrag; - return; - } - - if (!e->touchPoints().isEmpty()) { - _touchPrevPos = _touchPos; - _touchPos = e->touchPoints().cbegin()->screenPos().toPoint(); - } - - switch (e->type()) { - case QEvent::TouchBegin: { - if (_contextMenu) { - e->accept(); - return; // ignore mouse press, that was hiding context menu - } - if (_touchInProgress) return; - if (e->touchPoints().isEmpty()) return; - - _touchInProgress = true; - _touchSelectTimer.start(QApplication::startDragTime()); - _touchSelect = false; - _touchStart = _touchPrevPos = _touchPos; - } break; - - case QEvent::TouchUpdate: { - if (!_touchInProgress) return; - if (_touchSelect) { - _lastMousePos = _touchPos; - dragActionUpdate(); - } - } break; - - case QEvent::TouchEnd: { - if (!_touchInProgress) return; - _touchInProgress = false; - auto weak = MakeWeak(this); - if (_touchSelect) { - dragActionFinish(_touchPos, Qt::RightButton); - QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos); - showContextMenu(&contextMenu, ContextMenuReason::FromTouch); - } else { // one short tap -- like mouse click - dragActionStart(_touchPos, Qt::LeftButton); - dragActionFinish(_touchPos, Qt::LeftButton); - } - if (weak) { - _touchSelectTimer.stop(); - _touchSelect = false; - } - } break; - } -} - -void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) { - if (_contextMenu) { - _contextMenu->deleteLater(); - _contextMenu = nullptr; - } - - if (e->reason() == QContextMenuEvent::Mouse) { - _lastMousePos = e->globalPos(); - } else { - _lastMousePos = QCursor::pos(); - } - auto state = dragActionUpdate(); - - bool hasSelection = !_selection.empty(); - bool uponSelection = state.uponSymbol && (state.symbol >= _selection.from) && (state.symbol < _selection.to); - bool fullSelection = _text.isFullSelection(_selection); - if (reason == ContextMenuReason::FromTouch && hasSelection && !uponSelection) { - uponSelection = hasSelection; - } - - _contextMenu = new PopupMenu(this); - - if (fullSelection && !_contextCopyText.isEmpty()) { - _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); - } else if (uponSelection && !fullSelection) { - const auto text = Integration::Instance().phraseContextCopySelected(); - _contextMenu->addAction(text, this, SLOT(onCopySelectedText())); - } else if (!hasSelection && !_contextCopyText.isEmpty()) { - _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); - } - - if (const auto link = ClickHandler::getActive()) { - const auto actionText = link->copyToClipboardContextItemText(); - if (!actionText.isEmpty()) { - _contextMenu->addAction( - actionText, - [text = link->copyToClipboardText()] { - QGuiApplication::clipboard()->setText(text); - }); - } - } - - if (_contextMenu->actions().empty()) { - delete _contextMenu; - _contextMenu = nullptr; - } else { - connect(_contextMenu, SIGNAL(destroyed(QObject*)), this, SLOT(onContextMenuDestroy(QObject*))); - _contextMenu->popup(e->globalPos()); - e->accept(); - } -} - -void FlatLabel::onCopySelectedText() { - const auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; - if (!selection.empty()) { - TextUtilities::SetClipboardText(_text.toTextForMimeData(selection)); - } -} - -void FlatLabel::onCopyContextText() { - TextUtilities::SetClipboardText(_text.toTextForMimeData()); -} - -void FlatLabel::onTouchSelect() { - _touchSelect = true; - dragActionStart(_touchPos, Qt::LeftButton); -} - -void FlatLabel::onContextMenuDestroy(QObject *obj) { - if (obj == _contextMenu) { - _contextMenu = nullptr; - } -} - -void FlatLabel::onExecuteDrag() { - if (_dragAction != Dragging) return; - - auto state = getTextState(_dragStartPosition); - bool uponSelected = state.uponSymbol && _selection.from <= state.symbol; - if (uponSelected) { - if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) { - uponSelected = false; - } - } - - const auto pressedHandler = ClickHandler::getPressed(); - const auto selectedText = [&] { - if (uponSelected) { - return _text.toTextForMimeData(_selection); - } else if (pressedHandler) { - return TextForMimeData::Simple(pressedHandler->dragText()); - } - return TextForMimeData(); - }(); - if (auto mimeData = TextUtilities::MimeDataFromText(selectedText)) { - auto drag = new QDrag(window()); - drag->setMimeData(mimeData.release()); - drag->exec(Qt::CopyAction); - - // We don't receive mouseReleaseEvent when drag is finished. - ClickHandler::unpressed(); - } -} - -void FlatLabel::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { - update(); -} - -void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool active) { - update(); -} - -std::unique_ptr FlatLabel::CrossFade( - not_null from, - not_null to, - style::color bg, - QPoint fromPosition, - QPoint toPosition) { - auto result = std::make_unique(bg); - - struct Data { - QImage full; - QVector lineWidths; - int lineHeight = 0; - int lineAddTop = 0; - }; - auto prepareData = [&bg](not_null label) { - auto result = Data(); - result.full = GrabWidgetToImage(label, QRect(), bg->c); - auto textWidth = label->width() - label->_st.margin.left() - label->_st.margin.right(); - label->_text.countLineWidths(textWidth, &result.lineWidths); - result.lineHeight = label->_st.style.font->height; - auto addedHeight = (label->_st.style.lineHeight - result.lineHeight); - if (addedHeight > 0) { - result.lineAddTop = addedHeight / 2; - result.lineHeight += addedHeight; - } - return result; - }; - auto was = prepareData(from); - auto now = prepareData(to); - - auto maxLines = qMax(was.lineWidths.size(), now.lineWidths.size()); - auto fillDataTill = [maxLines](Data &data) { - for (auto i = data.lineWidths.size(); i != maxLines; ++i) { - data.lineWidths.push_back(-1); - } - }; - fillDataTill(was); - fillDataTill(now); - auto preparePart = [](FlatLabel *label, QPoint position, Data &data, int index, Data &other) { - auto result = CrossFadeAnimation::Part(); - auto lineWidth = data.lineWidths[index]; - if (lineWidth < 0) { - lineWidth = other.lineWidths[index]; - } - const auto pixelRatio = style::DevicePixelRatio(); - auto fullWidth = data.full.width() / pixelRatio; - auto top = index * data.lineHeight + data.lineAddTop; - auto left = 0; - if (label->_st.align & Qt::AlignHCenter) { - left += (fullWidth - lineWidth) / 2; - } else if (label->_st.align & Qt::AlignRight) { - left += (fullWidth - lineWidth); - } - auto snapshotRect = data.full.rect().intersected(QRect(left * pixelRatio, top * pixelRatio, lineWidth * pixelRatio, label->_st.style.font->height * pixelRatio)); - if (!snapshotRect.isEmpty()) { - result.snapshot = PixmapFromImage(data.full.copy(snapshotRect)); - result.snapshot.setDevicePixelRatio(pixelRatio); - } - auto positionBase = position + label->pos(); - result.position = positionBase + QPoint(label->_st.margin.left() + left, label->_st.margin.top() + top); - return result; - }; - for (int i = 0; i != maxLines; ++i) { - result->addLine(preparePart(from, fromPosition, was, i, now), preparePart(to, toPosition, now, i, was)); - } - - return result; -} - -Text::StateResult FlatLabel::dragActionUpdate() { - auto m = mapFromGlobal(_lastMousePos); - auto state = getTextState(m); - updateHover(state); - - if (_dragAction == PrepareDrag && (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { - _dragAction = Dragging; - QTimer::singleShot(1, this, SLOT(onExecuteDrag())); - } - - return state; -} - -void FlatLabel::updateHover(const Text::StateResult &state) { - bool lnkChanged = ClickHandler::setActive(state.link, this); - - if (!_selectable) { - refreshCursor(state.uponSymbol); - return; - } - - Qt::CursorShape cur = style::cur_default; - if (_dragAction == NoDrag) { - if (state.link) { - cur = style::cur_pointer; - } else if (state.uponSymbol) { - cur = style::cur_text; - } - } else { - if (_dragAction == Selecting) { - uint16 second = state.symbol; - if (state.afterSymbol && _selectionType == TextSelectType::Letters) { - ++second; - } - auto selection = _text.adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _selectionType); - if (_selection != selection) { - _selection = selection; - _savedSelection = { 0, 0 }; - setFocus(); - update(); - } - } else if (_dragAction == Dragging) { - } - - if (ClickHandler::getPressed()) { - cur = style::cur_pointer; - } else if (_dragAction == Selecting) { - cur = style::cur_text; - } - } - if (_dragAction == Selecting) { - // checkSelectingScroll(); - } else { - // noSelectingScroll(); - } - - if (_dragAction == NoDrag && (lnkChanged || cur != _cursor)) { - setCursor(_cursor = cur); - } -} - -void FlatLabel::refreshCursor(bool uponSymbol) { - if (_dragAction != NoDrag) { - return; - } - bool needTextCursor = _selectable && uponSymbol; - style::cursor newCursor = needTextCursor ? style::cur_text : style::cur_default; - if (ClickHandler::getActive()) { - newCursor = style::cur_pointer; - } - if (newCursor != _cursor) { - _cursor = newCursor; - setCursor(_cursor); - } -} - -Text::StateResult FlatLabel::getTextState(const QPoint &m) const { - Text::StateRequestElided request; - request.align = _st.align; - if (_selectable) { - request.flags |= Text::StateRequest::Flag::LookupSymbol; - } - int textWidth = width() - _st.margin.left() - _st.margin.right(); - - Text::StateResult state; - bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth()); - bool renderElided = _breakEverywhere || heightExceeded; - if (renderElided) { - auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height); - auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2); - request.lines = lines; - if (_breakEverywhere) { - request.flags |= Text::StateRequest::Flag::BreakEverywhere; - } - state = _text.getStateElided(m - QPoint(_st.margin.left(), _st.margin.top()), textWidth, request); - } else { - state = _text.getState(m - QPoint(_st.margin.left(), _st.margin.top()), textWidth, request); - } - - return state; -} - -void FlatLabel::setOpacity(float64 o) { - _opacity = o; - update(); -} - -void FlatLabel::setTextColorOverride(std::optional color) { - _textColorOverride = color; - update(); -} - -void FlatLabel::paintEvent(QPaintEvent *e) { - Painter p(this); - p.setOpacity(_opacity); - if (_textColorOverride) { - p.setPen(*_textColorOverride); - } else { - p.setPen(_st.textFg); - } - p.setTextPalette(_st.palette); - const auto textWidth = _textWidth - ? _textWidth - : (width() - _st.margin.left() - _st.margin.right()); - const auto textLeft = _textWidth - ? ((_st.align & Qt::AlignLeft) - ? _st.margin.left() - : (_st.align & Qt::AlignHCenter) - ? ((width() - _textWidth) / 2) - : (width() - _st.margin.right() - _textWidth)) - : _st.margin.left(); - auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; - bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth()); - bool renderElided = _breakEverywhere || heightExceeded; - if (renderElided) { - auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height); - auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2); - _text.drawElided(p, textLeft, _st.margin.top(), textWidth, lines, _st.align, e->rect().y(), e->rect().bottom(), 0, _breakEverywhere, selection); - } else { - _text.draw(p, textLeft, _st.margin.top(), textWidth, _st.align, e->rect().y(), e->rect().bottom(), selection); - } -} - -int DividerLabel::naturalWidth() const { - return -1; -} - -void DividerLabel::resizeEvent(QResizeEvent *e) { - _background->lower(); - _background->setGeometry(rect()); - return PaddingWrap::resizeEvent(e); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/labels.h b/Telegram/SourceFiles/ui/widgets/labels.h deleted file mode 100644 index 5ce3d89c2..000000000 --- a/Telegram/SourceFiles/ui/widgets/labels.h +++ /dev/null @@ -1,240 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/text/text.h" -#include "ui/click_handler.h" -#include "boxes/abstract_box.h" -#include "styles/style_widgets.h" - -#include - -class QTouchEvent; - -namespace Ui { - -class PopupMenu; - -class CrossFadeAnimation { -public: - CrossFadeAnimation(style::color bg); - - struct Part { - QPixmap snapshot; - QPoint position; - }; - void addLine(Part was, Part now); - - void paintFrame(Painter &p, float64 dt) { - auto progress = anim::linear(1., dt); - paintFrame(p, progress, 1. - progress, progress); - } - - void paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow); - -private: - struct Line { - Line(Part was, Part now) : was(std::move(was)), now(std::move(now)) { - } - Part was; - Part now; - }; - void paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow); - - style::color _bg; - QList _lines; - -}; - -class LabelSimple : public RpWidget { -public: - LabelSimple( - QWidget *parent, - const style::LabelSimple &st = st::defaultLabelSimple, - const QString &value = QString()); - - // This method also resizes the label. - void setText(const QString &newText, bool *outTextChanged = nullptr); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - QString _fullText; - int _fullTextWidth; - - QString _text; - int _textWidth; - - const style::LabelSimple &_st; - -}; - -class FlatLabel : public RpWidget, public ClickHandlerHost { - Q_OBJECT - -public: - FlatLabel(QWidget *parent, const style::FlatLabel &st = st::defaultFlatLabel); - - FlatLabel( - QWidget *parent, - const QString &text, - const style::FlatLabel &st = st::defaultFlatLabel); - - FlatLabel( - QWidget *parent, - rpl::producer &&text, - const style::FlatLabel &st = st::defaultFlatLabel); - FlatLabel( - QWidget *parent, - rpl::producer &&text, - const style::FlatLabel &st = st::defaultFlatLabel); - - void setOpacity(float64 o); - void setTextColorOverride(std::optional color); - - void setText(const QString &text); - void setRichText(const QString &text); - void setMarkedText(const TextWithEntities &textWithEntities); - void setSelectable(bool selectable); - void setDoubleClickSelectsParagraph(bool doubleClickSelectsParagraph); - void setContextCopyText(const QString ©Text); - void setBreakEverywhere(bool breakEverywhere); - void setTryMakeSimilarLines(bool tryMakeSimilarLines); - - int naturalWidth() const override; - QMargins getMargins() const override; - - void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); - void setLinksTrusted(); - - using ClickHandlerFilter = Fn; - void setClickHandlerFilter(ClickHandlerFilter &&filter); - - // ClickHandlerHost interface - void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; - void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; - - static std::unique_ptr CrossFade( - not_null from, - not_null to, - style::color bg, - QPoint fromPosition = QPoint(), - QPoint toPosition = QPoint()); - -protected: - void paintEvent(QPaintEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseDoubleClickEvent(QMouseEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - bool eventHook(QEvent *e) override; // calls touchEvent when necessary - void touchEvent(QTouchEvent *e); - - int resizeGetHeight(int newWidth) override; - -private slots: - void onCopySelectedText(); - void onCopyContextText(); - - void onTouchSelect(); - void onContextMenuDestroy(QObject *obj); - - void onExecuteDrag(); - -private: - void init(); - void textUpdated(); - - Text::StateResult dragActionUpdate(); - Text::StateResult dragActionStart(const QPoint &p, Qt::MouseButton button); - Text::StateResult dragActionFinish(const QPoint &p, Qt::MouseButton button); - void updateHover(const Text::StateResult &state); - Text::StateResult getTextState(const QPoint &m) const; - void refreshCursor(bool uponSymbol); - - int countTextWidth() const; - int countTextHeight(int textWidth); - void refreshSize(); - - enum class ContextMenuReason { - FromEvent, - FromTouch, - }; - void showContextMenu(QContextMenuEvent *e, ContextMenuReason reason); - - Text::String _text; - const style::FlatLabel &_st; - std::optional _textColorOverride; - float64 _opacity = 1.; - - int _allowedWidth = 0; - int _textWidth = 0; - int _fullTextHeight = 0; - bool _breakEverywhere = false; - bool _tryMakeSimilarLines = false; - - style::cursor _cursor = style::cur_default; - bool _selectable = false; - TextSelection _selection, _savedSelection; - TextSelectType _selectionType = TextSelectType::Letters; - bool _doubleClickSelectsParagraph = false; - - enum DragAction { - NoDrag = 0x00, - PrepareDrag = 0x01, - Dragging = 0x02, - Selecting = 0x04, - }; - DragAction _dragAction = NoDrag; - QPoint _dragStartPosition; - uint16 _dragSymbol = 0; - bool _dragWasInactive = false; - - QPoint _lastMousePos; - - QPoint _trippleClickPoint; - QTimer _trippleClickTimer; - - PopupMenu *_contextMenu = nullptr; - QString _contextCopyText; - - ClickHandlerFilter _clickHandlerFilter; - - // text selection and context menu by touch support (at least Windows Surface tablets) - bool _touchSelect = false; - bool _touchInProgress = false; - QPoint _touchStart, _touchPrevPos, _touchPos; - QTimer _touchSelectTimer; - -}; - -class DividerLabel : public PaddingWrap { -public: - using PaddingWrap::PaddingWrap; - - int naturalWidth() const override; - -protected: - void resizeEvent(QResizeEvent *e) override; - -private: - object_ptr _background - = object_ptr(this); - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp deleted file mode 100644 index ba103cba8..000000000 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/* -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 "ui/widgets/menu.h" - -#include "ui/effects/ripple_animation.h" -#include "ui/widgets/checkbox.h" -#include "ui/text/text.h" - -#include - -namespace Ui { - -struct Menu::ActionData { - Text::String text; - QString shortcut; - const style::icon *icon = nullptr; - const style::icon *iconOver = nullptr; - std::unique_ptr ripple; - std::unique_ptr toggle; - int textWidth = 0; - bool hasSubmenu = false; -}; - -Menu::Menu(QWidget *parent, const style::Menu &st) -: RpWidget(parent) -, _st(st) -, _itemHeight(_st.itemPadding.top() + _st.itemStyle.font->height + _st.itemPadding.bottom()) -, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) { - init(); -} - -Menu::Menu(QWidget *parent, QMenu *menu, const style::Menu &st) -: RpWidget(parent) -, _st(st) -, _wappedMenu(menu) -, _itemHeight(_st.itemPadding.top() + _st.itemStyle.font->height + _st.itemPadding.bottom()) -, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) { - init(); - - _wappedMenu->setParent(this); - for (auto action : _wappedMenu->actions()) { - addAction(action); - } - _wappedMenu->hide(); -} - -Menu::~Menu() = default; - -void Menu::init() { - resize(_forceWidth ? _forceWidth : _st.widthMin, _st.skip * 2); - - setMouseTracking(true); - - setAttribute(Qt::WA_OpaquePaintEvent); -} - -not_null Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { - const auto action = addAction(new QAction(text, this), icon, iconOver); - connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection); - return action; -} - -not_null Menu::addAction(const QString &text, Fn callback, const style::icon *icon, const style::icon *iconOver) { - const auto action = addAction(new QAction(text, this), icon, iconOver); - connect(action, &QAction::triggered, action, std::move(callback), Qt::QueuedConnection); - return action; -} - -not_null Menu::addAction(not_null action, const style::icon *icon, const style::icon *iconOver) { - connect(action, &QAction::changed, this, [=] { - actionChanged(); - }); - _actions.emplace_back(action); - _actionsData.push_back([&] { - auto data = ActionData(); - data.icon = icon; - data.iconOver = iconOver ? iconOver : icon; - data.hasSubmenu = (action->menu() != nullptr); - return data; - }()); - - auto newWidth = qMax(width(), _st.widthMin); - newWidth = processAction(action, _actions.size() - 1, newWidth); - auto newHeight = height() + (action->isSeparator() ? _separatorHeight : _itemHeight); - resize(_forceWidth ? _forceWidth : newWidth, newHeight); - if (_resizedCallback) { - _resizedCallback(); - } - updateSelected(QCursor::pos()); - update(); - - return action; -} - -not_null Menu::addSeparator() { - const auto separator = new QAction(this); - separator->setSeparator(true); - return addAction(separator); -} - -void Menu::clearActions() { - setSelected(-1); - setPressed(-1); - _actionsData.clear(); - for (auto action : base::take(_actions)) { - if (action->parent() == this) { - delete action; - } - } - resize(_forceWidth ? _forceWidth : _st.widthMin, _st.skip * 2); - if (_resizedCallback) { - _resizedCallback(); - } -} - -void Menu::finishAnimating() { - for (auto &data : _actionsData) { - if (data.ripple) { - data.ripple.reset(); - } - if (data.toggle) { - data.toggle->finishAnimating(); - } - } -} - -int Menu::processAction(not_null action, int index, int width) { - auto &data = _actionsData[index]; - if (action->isSeparator() || action->text().isEmpty()) { - data.shortcut = QString(); - data.text.clear(); - } else { - auto actionTextParts = action->text().split('\t'); - auto actionText = actionTextParts.empty() ? QString() : actionTextParts[0]; - auto actionShortcut = (actionTextParts.size() > 1) ? actionTextParts[1] : QString(); - data.text.setText(_st.itemStyle, actionText); - const auto textw = data.text.maxWidth(); - int goodw = _st.itemPadding.left() + textw + _st.itemPadding.right(); - if (data.hasSubmenu) { - goodw += _st.itemPadding.right() + _st.arrow.width(); - } else if (!actionShortcut.isEmpty()) { - goodw += _st.itemPadding.right() + _st.itemStyle.font->width(actionShortcut); - } - if (action->isCheckable()) { - auto updateCallback = [this, index] { updateItem(index); }; - if (data.toggle) { - data.toggle->setUpdateCallback(updateCallback); - data.toggle->setChecked(action->isChecked(), anim::type::normal); - } else { - data.toggle = std::make_unique(_st.itemToggle, action->isChecked(), updateCallback); - } - goodw += _st.itemPadding.right() + data.toggle->getSize().width() - _st.itemToggleShift; - } else { - data.toggle.reset(); - } - width = std::clamp(goodw, width, _st.widthMax); - data.textWidth = width - (goodw - textw); - data.shortcut = actionShortcut; - } - return width; -} - -void Menu::setShowSource(TriggeredSource source) { - _mouseSelection = (source == TriggeredSource::Mouse); - setSelected((source == TriggeredSource::Mouse || _actions.empty()) ? -1 : 0); -} - -const std::vector> &Menu::actions() const { - return _actions; -} - -void Menu::setForceWidth(int forceWidth) { - _forceWidth = forceWidth; - resize(_forceWidth, height()); -} - -void Menu::actionChanged() { - auto newWidth = _st.widthMin; - auto index = 0; - for (const auto action : _actions) { - newWidth = processAction(action, index++, newWidth); - } - if (newWidth != width() && !_forceWidth) { - resize(newWidth, height()); - if (_resizedCallback) { - _resizedCallback(); - } - } - update(); -} - -void Menu::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto clip = e->rect(); - - auto topskip = QRect(0, 0, width(), _st.skip); - auto bottomskip = QRect(0, height() - _st.skip, width(), _st.skip); - if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st.itemBg); - if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st.itemBg); - - int top = _st.skip; - p.translate(0, top); - p.setFont(_st.itemStyle.font); - for (int i = 0, count = int(_actions.size()); i != count; ++i) { - if (clip.top() + clip.height() <= top) break; - - const auto action = _actions[i]; - auto &data = _actionsData[i]; - auto actionHeight = action->isSeparator() ? _separatorHeight : _itemHeight; - top += actionHeight; - if (clip.top() < top) { - if (action->isSeparator()) { - p.fillRect(0, 0, width(), actionHeight, _st.itemBg); - p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg); - } else { - auto enabled = action->isEnabled(); - auto selected = ((i == _selected || i == _pressed) && enabled); - p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg); - if (data.ripple) { - data.ripple->paint(p, 0, 0, width()); - if (data.ripple->empty()) { - data.ripple.reset(); - } - } - if (auto icon = (selected ? data.iconOver : data.icon)) { - icon->paint(p, _st.itemIconPosition, width()); - } - p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled)); - data.text.drawLeftElided(p, _st.itemPadding.left(), _st.itemPadding.top(), data.textWidth, width()); - if (data.hasSubmenu) { - const auto left = width() - _st.itemPadding.right() - _st.arrow.width(); - const auto top = (_itemHeight - _st.arrow.height()) / 2; - if (enabled) { - _st.arrow.paint(p, left, top, width()); - } else { - _st.arrow.paint( - p, - left, - top, - width(), - _st.itemFgDisabled->c); - } - } else if (!data.shortcut.isEmpty()) { - p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled)); - p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut); - } else if (data.toggle) { - auto toggleSize = data.toggle->getSize(); - data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize.width() + _st.itemToggleShift, (_itemHeight - toggleSize.height()) / 2, width()); - } - } - } - p.translate(0, actionHeight); - } -} - -void Menu::updateSelected(QPoint globalPosition) { - if (!_mouseSelection) return; - - auto p = mapFromGlobal(globalPosition) - QPoint(0, _st.skip); - auto selected = -1, top = 0; - while (top <= p.y() && ++selected < _actions.size()) { - top += _actions[selected]->isSeparator() ? _separatorHeight : _itemHeight; - } - setSelected((selected >= 0 && selected < _actions.size() && _actions[selected]->isEnabled() && !_actions[selected]->isSeparator()) ? selected : -1); -} - -void Menu::itemPressed(TriggeredSource source) { - if (source == TriggeredSource::Mouse && !_mouseSelection) { - return; - } - if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) { - setPressed(_selected); - if (source == TriggeredSource::Mouse) { - if (!_actionsData[_pressed].ripple) { - auto mask = RippleAnimation::rectMask(QSize(width(), _itemHeight)); - _actionsData[_pressed].ripple = std::make_unique(_st.ripple, std::move(mask), [this, selected = _pressed] { - updateItem(selected); - }); - } - _actionsData[_pressed].ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, itemTop(_pressed))); - } else { - itemReleased(source); - } - } -} - -void Menu::itemReleased(TriggeredSource source) { - if (_pressed >= 0 && _pressed < _actions.size()) { - auto pressed = _pressed; - setPressed(-1); - if (source == TriggeredSource::Mouse && _actionsData[pressed].ripple) { - _actionsData[pressed].ripple->lastStop(); - } - if (pressed == _selected && _triggeredCallback) { - _triggeredCallback(_actions[_selected], itemTop(_selected), source); - } - } -} - -void Menu::keyPressEvent(QKeyEvent *e) { - auto key = e->key(); - if (!_keyPressDelegate || !_keyPressDelegate(key)) { - handleKeyPress(key); - } -} - -void Menu::handleKeyPress(int key) { - if (key == Qt::Key_Enter || key == Qt::Key_Return) { - itemPressed(TriggeredSource::Keyboard); - return; - } - if (key == (style::RightToLeft() ? Qt::Key_Left : Qt::Key_Right)) { - if (_selected >= 0 && _actionsData[_selected].hasSubmenu) { - itemPressed(TriggeredSource::Keyboard); - return; - } else if (_selected < 0 && !_actions.empty()) { - _mouseSelection = false; - setSelected(0); - } - } - if ((key != Qt::Key_Up && key != Qt::Key_Down) || _actions.empty()) { - return; - } - - auto delta = (key == Qt::Key_Down ? 1 : -1), start = _selected; - if (start < 0 || start >= _actions.size()) { - start = (delta > 0) ? (_actions.size() - 1) : 0; - } - auto newSelected = start; - do { - newSelected += delta; - if (newSelected < 0) { - newSelected += _actions.size(); - } else if (newSelected >= _actions.size()) { - newSelected -= _actions.size(); - } - } while (newSelected != start && (!_actions[newSelected]->isEnabled() || _actions[newSelected]->isSeparator())); - - if (_actions[newSelected]->isEnabled() && !_actions[newSelected]->isSeparator()) { - _mouseSelection = false; - setSelected(newSelected); - } -} - -void Menu::clearSelection() { - _mouseSelection = false; - setSelected(-1); -} - -void Menu::clearMouseSelection() { - if (_mouseSelection && !_childShown) { - clearSelection(); - } -} - -void Menu::enterEventHook(QEvent *e) { - QPoint mouse = QCursor::pos(); - if (!rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) { - clearMouseSelection(); - } - return TWidget::enterEventHook(e); -} - -void Menu::leaveEventHook(QEvent *e) { - clearMouseSelection(); - return TWidget::leaveEventHook(e); -} - -void Menu::setSelected(int selected) { - if (selected >= _actions.size()) { - selected = -1; - } - if (_selected != selected) { - updateSelectedItem(); - if (_selected >= 0 && _selected != _pressed && _actionsData[_selected].toggle) { - _actionsData[_selected].toggle->setStyle(_st.itemToggle); - } - _selected = selected; - if (_selected >= 0 && _actionsData[_selected].toggle && _actions[_selected]->isEnabled()) { - _actionsData[_selected].toggle->setStyle(_st.itemToggleOver); - } - updateSelectedItem(); - if (_activatedCallback) { - auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard; - _activatedCallback( - (_selected >= 0) ? _actions[_selected].get() : nullptr, - itemTop(_selected), - source); - } - } -} - -void Menu::setPressed(int pressed) { - if (pressed >= _actions.size()) { - pressed = -1; - } - if (_pressed != pressed) { - if (_pressed >= 0 && _pressed != _selected && _actionsData[_pressed].toggle) { - _actionsData[_pressed].toggle->setStyle(_st.itemToggle); - } - _pressed = pressed; - if (_pressed >= 0 && _actionsData[_pressed].toggle && _actions[_pressed]->isEnabled()) { - _actionsData[_pressed].toggle->setStyle(_st.itemToggleOver); - } - } -} - -int Menu::itemTop(int index) { - if (index > _actions.size()) { - index = _actions.size(); - } - int top = _st.skip; - for (int i = 0; i < index; ++i) { - top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight; - } - return top; -} - -void Menu::updateItem(int index) { - if (index >= 0 && index < _actions.size()) { - update(0, itemTop(index), width(), _actions[index]->isSeparator() ? _separatorHeight : _itemHeight); - } -} - -void Menu::updateSelectedItem() { - updateItem(_selected); -} - -void Menu::mouseMoveEvent(QMouseEvent *e) { - handleMouseMove(e->globalPos()); -} - -void Menu::handleMouseMove(QPoint globalPosition) { - auto inner = rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)); - auto localPosition = mapFromGlobal(globalPosition); - if (inner.contains(localPosition)) { - _mouseSelection = true; - updateSelected(globalPosition); - } else { - clearMouseSelection(); - if (_mouseMoveDelegate) { - _mouseMoveDelegate(globalPosition); - } - } -} - -void Menu::mousePressEvent(QMouseEvent *e) { - handleMousePress(e->globalPos()); -} - -void Menu::mouseReleaseEvent(QMouseEvent *e) { - handleMouseRelease(e->globalPos()); -} - -void Menu::handleMousePress(QPoint globalPosition) { - handleMouseMove(globalPosition); - if (rect().contains(mapFromGlobal(globalPosition))) { - itemPressed(TriggeredSource::Mouse); - } else if (_mousePressDelegate) { - _mousePressDelegate(globalPosition); - } -} - -void Menu::handleMouseRelease(QPoint globalPosition) { - handleMouseMove(globalPosition); - itemReleased(TriggeredSource::Mouse); - if (!rect().contains(mapFromGlobal(globalPosition)) && _mouseReleaseDelegate) { - _mouseReleaseDelegate(globalPosition); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/menu.h b/Telegram/SourceFiles/ui/widgets/menu.h deleted file mode 100644 index 1b47bc71e..000000000 --- a/Telegram/SourceFiles/ui/widgets/menu.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "styles/style_widgets.h" - -#include - -namespace Ui { - -class ToggleView; -class RippleAnimation; - -class Menu : public RpWidget { -public: - Menu(QWidget *parent, const style::Menu &st = st::defaultMenu); - Menu(QWidget *parent, QMenu *menu, const style::Menu &st = st::defaultMenu); - ~Menu(); - - not_null addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addAction(const QString &text, Fn callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addSeparator(); - void clearActions(); - void finishAnimating(); - - void clearSelection(); - - enum class TriggeredSource { - Mouse, - Keyboard, - }; - void setChildShown(bool shown) { - _childShown = shown; - } - void setShowSource(TriggeredSource source); - void setForceWidth(int forceWidth); - - const std::vector> &actions() const; - - void setResizedCallback(Fn callback) { - _resizedCallback = std::move(callback); - } - - void setActivatedCallback(Fn callback) { - _activatedCallback = std::move(callback); - } - void setTriggeredCallback(Fn callback) { - _triggeredCallback = std::move(callback); - } - - void setKeyPressDelegate(Fn delegate) { - _keyPressDelegate = std::move(delegate); - } - void handleKeyPress(int key); - - void setMouseMoveDelegate(Fn delegate) { - _mouseMoveDelegate = std::move(delegate); - } - void handleMouseMove(QPoint globalPosition); - - void setMousePressDelegate(Fn delegate) { - _mousePressDelegate = std::move(delegate); - } - void handleMousePress(QPoint globalPosition); - - void setMouseReleaseDelegate(Fn delegate) { - _mouseReleaseDelegate = std::move(delegate); - } - void handleMouseRelease(QPoint globalPosition); - -protected: - void paintEvent(QPaintEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - -private: - struct ActionData; - - void updateSelected(QPoint globalPosition); - void actionChanged(); - void init(); - - // Returns the new width. - int processAction(not_null action, int index, int width); - not_null addAction(not_null action, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - - void setSelected(int selected); - void setPressed(int pressed); - void clearMouseSelection(); - - int itemTop(int index); - void updateItem(int index); - void updateSelectedItem(); - void itemPressed(TriggeredSource source); - void itemReleased(TriggeredSource source); - - const style::Menu &_st; - - Fn _resizedCallback; - Fn _activatedCallback; - Fn _triggeredCallback; - Fn _keyPressDelegate; - Fn _mouseMoveDelegate; - Fn _mousePressDelegate; - Fn _mouseReleaseDelegate; - - QMenu *_wappedMenu = nullptr; - std::vector> _actions; - std::vector _actionsData; - - int _forceWidth = 0; - int _itemHeight, _separatorHeight; - - bool _mouseSelection = false; - - int _selected = -1; - int _pressed = -1; - bool _childShown = false; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp deleted file mode 100644 index 3ff6f189a..000000000 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ /dev/null @@ -1,524 +0,0 @@ -/* -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 "ui/widgets/popup_menu.h" - -#include "ui/widgets/shadow.h" -#include "ui/image/image_prepare.h" -#include "ui/platform/ui_platform_utility.h" -#include "ui/ui_utility.h" -#include "ui/delayed_activation.h" -#include "platform/platform_info.h" - -#include -#include -#include -#include - -namespace Ui { - -PopupMenu::PopupMenu(QWidget *parent, const style::PopupMenu &st) -: RpWidget(parent) -, _st(st) -, _roundRect(ImageRoundRadius::Small, _st.menu.itemBg) -, _menu(this, _st.menu) { - init(); -} - -PopupMenu::PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st) -: RpWidget(parent) -, _st(st) -, _roundRect(ImageRoundRadius::Small, _st.menu.itemBg) -, _menu(this, menu, _st.menu) { - init(); - - for (auto action : actions()) { - if (auto submenu = action->menu()) { - auto it = _submenus.insert(action, new PopupMenu(parentWidget(), submenu, st)); - it.value()->deleteOnHide(false); - } - } -} - -void PopupMenu::init() { - using namespace rpl::mappers; - - Integration::Instance().forcePopupMenuHideRequests( - ) | rpl::start_with_next([=] { - hideMenu(true); - }, lifetime()); - - _menu->setResizedCallback([this] { handleMenuResize(); }); - _menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) { - handleActivated(action, actionTop, source); - }); - _menu->setTriggeredCallback([this](QAction *action, int actionTop, TriggeredSource source) { - handleTriggered(action, actionTop, source); - }); - _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); - _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); - _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); - _menu->setMouseReleaseDelegate([this](QPoint globalPosition) { handleMouseRelease(globalPosition); }); - - handleCompositingUpdate(); - - setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint); - setMouseTracking(true); - - hide(); - - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); -} - -void PopupMenu::handleCompositingUpdate() { - _padding = _useTransparency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); - _menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top()); - handleMenuResize(); -} - -void PopupMenu::handleMenuResize() { - auto newWidth = _padding.left() + _st.scrollPadding.left() + _menu->width() + _st.scrollPadding.right() + _padding.right(); - auto newHeight = _padding.top() + _st.scrollPadding.top() + _menu->height() + _st.scrollPadding.bottom() + _padding.bottom(); - resize(newWidth, newHeight); - _inner = rect().marginsRemoved(_padding); -} - -not_null PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { - return _menu->addAction(text, receiver, member, icon, iconOver); -} - -not_null PopupMenu::addAction(const QString &text, Fn callback, const style::icon *icon, const style::icon *iconOver) { - return _menu->addAction(text, std::move(callback), icon, iconOver); -} - -not_null PopupMenu::addSeparator() { - return _menu->addSeparator(); -} - -void PopupMenu::clearActions() { - for (const auto &submenu : base::take(_submenus)) { - delete submenu; - } - return _menu->clearActions(); -} - -const std::vector> &PopupMenu::actions() const { - return _menu->actions(); -} - -void PopupMenu::paintEvent(QPaintEvent *e) { - QPainter p(this); - - if (_useTransparency) { - Platform::StartTranslucentPaint(p, e); - } - - if (_a_show.animating()) { - if (auto opacity = _a_opacity.value(_hiding ? 0. : 1.)) { - _showAnimation->paintFrame(p, 0, 0, width(), _a_show.value(1.), opacity); - } - } else if (_a_opacity.animating()) { - p.setOpacity(_a_opacity.value(0.)); - p.drawPixmap(0, 0, _cache); - } else if (_hiding || isHidden()) { - hideFinished(); - } else if (_showAnimation) { - _showAnimation->paintFrame(p, 0, 0, width(), 1., 1.); - _showAnimation.reset(); - showChildren(); - } else { - paintBg(p); - } -} - -void PopupMenu::paintBg(QPainter &p) { - if (_useTransparency) { - Shadow::paint(p, _inner, width(), _st.shadow); - _roundRect.paint(p, _inner); - } else { - p.fillRect(0, 0, width() - _padding.right(), _padding.top(), _st.shadow.fallback); - p.fillRect(width() - _padding.right(), 0, _padding.right(), height() - _padding.bottom(), _st.shadow.fallback); - p.fillRect(_padding.left(), height() - _padding.bottom(), width() - _padding.left(), _padding.bottom(), _st.shadow.fallback); - p.fillRect(0, _padding.top(), _padding.left(), height() - _padding.top(), _st.shadow.fallback); - p.fillRect(_inner, _st.menu.itemBg); - } -} - -void PopupMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) { - if (source == TriggeredSource::Mouse) { - if (!popupSubmenuFromAction(action, actionTop, source)) { - if (auto currentSubmenu = base::take(_activeSubmenu)) { - currentSubmenu->hideMenu(true); - } - } - } -} - -void PopupMenu::handleTriggered(QAction *action, int actionTop, TriggeredSource source) { - if (!popupSubmenuFromAction(action, actionTop, source)) { - _triggering = true; - hideMenu(); - emit action->trigger(); - _triggering = false; - if (_deleteLater) { - _deleteLater = false; - deleteLater(); - } - } -} - -bool PopupMenu::popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source) { - if (auto submenu = _submenus.value(action)) { - if (_activeSubmenu == submenu) { - submenu->hideMenu(true); - } else { - popupSubmenu(submenu, actionTop, source); - } - return true; - } - return false; -} - -void PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) { - if (auto currentSubmenu = base::take(_activeSubmenu)) { - currentSubmenu->hideMenu(true); - } - if (submenu) { - QPoint p(_inner.x() + (style::RightToLeft() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop); - _activeSubmenu = submenu; - _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); - - _menu->setChildShown(true); - } else { - _menu->setChildShown(false); - } -} - -void PopupMenu::forwardKeyPress(int key) { - if (!handleKeyPress(key)) { - _menu->handleKeyPress(key); - } -} - -bool PopupMenu::handleKeyPress(int key) { - if (_activeSubmenu) { - _activeSubmenu->handleKeyPress(key); - return true; - } else if (key == Qt::Key_Escape) { - hideMenu(_parent ? true : false); - return true; - } else if (key == (style::RightToLeft() ? Qt::Key_Right : Qt::Key_Left)) { - if (_parent) { - hideMenu(true); - return true; - } - } - return false; -} - -void PopupMenu::handleMouseMove(QPoint globalPosition) { - if (_parent) { - _parent->forwardMouseMove(globalPosition); - } -} - -void PopupMenu::handleMousePress(QPoint globalPosition) { - if (_parent) { - _parent->forwardMousePress(globalPosition); - } else { - hideMenu(); - } -} - -void PopupMenu::handleMouseRelease(QPoint globalPosition) { - if (_parent) { - _parent->forwardMouseRelease(globalPosition); - } else { - hideMenu(); - } -} - -void PopupMenu::focusOutEvent(QFocusEvent *e) { - hideMenu(); -} - -void PopupMenu::hideEvent(QHideEvent *e) { - if (_deleteOnHide) { - if (_triggering) { - _deleteLater = true; - } else { - deleteLater(); - } - } -} - -void PopupMenu::keyPressEvent(QKeyEvent *e) { - forwardKeyPress(e->key()); -} - -void PopupMenu::mouseMoveEvent(QMouseEvent *e) { - forwardMouseMove(e->globalPos()); -} - -void PopupMenu::mousePressEvent(QMouseEvent *e) { - forwardMousePress(e->globalPos()); -} - -void PopupMenu::hideMenu(bool fast) { - if (isHidden()) return; - if (_parent && !_a_opacity.animating()) { - _parent->childHiding(this); - } - if (fast) { - hideFast(); - } else { - hideAnimated(); - if (_parent) { - _parent->hideMenu(); - } - } - if (_activeSubmenu) { - _activeSubmenu->hideMenu(fast); - } -} - -void PopupMenu::childHiding(PopupMenu *child) { - if (_activeSubmenu && _activeSubmenu == child) { - _activeSubmenu = SubmenuPointer(); - } - if (!_hiding && !isHidden()) { - activateWindow(); - } -} - -void PopupMenu::setOrigin(PanelAnimation::Origin origin) { - _origin = origin; -} - -void PopupMenu::showAnimated(PanelAnimation::Origin origin) { - setOrigin(origin); - showStarted(); -} - -void PopupMenu::hideAnimated() { - if (isHidden()) return; - if (_hiding) return; - - startOpacityAnimation(true); -} - -void PopupMenu::hideFast() { - if (isHidden()) return; - - _hiding = false; - _a_opacity.stop(); - hideFinished(); -} - -void PopupMenu::hideFinished() { - _a_show.stop(); - _cache = QPixmap(); - if (!isHidden()) { - hide(); - } -} - -void PopupMenu::prepareCache() { - if (_a_opacity.animating()) return; - - auto showAnimation = base::take(_a_show); - auto showAnimationData = base::take(_showAnimation); - showChildren(); - _cache = GrabWidget(this); - _showAnimation = base::take(showAnimationData); - _a_show = base::take(showAnimation); -} - -void PopupMenu::startOpacityAnimation(bool hiding) { - _hiding = false; - if (!_useTransparency) { - _a_opacity.stop(); - if (hiding) { - hideFinished(); - } else { - update(); - } - return; - } - prepareCache(); - _hiding = hiding; - hideChildren(); - _a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration); -} - -void PopupMenu::showStarted() { - if (isHidden()) { - show(); - startShowAnimation(); - return; - } else if (!_hiding) { - return; - } - startOpacityAnimation(false); -} - -void PopupMenu::startShowAnimation() { - if (!_useTransparency) { - _a_show.stop(); - update(); - return; - } - if (!_a_show.animating()) { - auto opacityAnimation = base::take(_a_opacity); - showChildren(); - auto cache = grabForPanelAnimation(); - _a_opacity = base::take(opacityAnimation); - - const auto pixelRatio = style::DevicePixelRatio(); - _showAnimation = std::make_unique(_st.animation, _origin); - _showAnimation->setFinalImage(std::move(cache), QRect(_inner.topLeft() * pixelRatio, _inner.size() * pixelRatio)); - if (_useTransparency) { - _showAnimation->setCornerMasks( - Images::CornersMask(ImageRoundRadius::Small)); - } else { - _showAnimation->setSkipShadow(true); - } - _showAnimation->start(); - } - hideChildren(); - _a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration); -} - -void PopupMenu::opacityAnimationCallback() { - update(); - if (!_a_opacity.animating()) { - if (_hiding) { - _hiding = false; - hideFinished(); - } else { - showChildren(); - } - } -} - -void PopupMenu::showAnimationCallback() { - update(); -} - -QImage PopupMenu::grabForPanelAnimation() { - SendPendingMoveResizeEvents(this); - const auto pixelRatio = style::DevicePixelRatio(); - auto result = QImage(size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(pixelRatio); - result.fill(Qt::transparent); - { - QPainter p(&result); - if (_useTransparency) { - _roundRect.paint(p, _inner); - } else { - p.fillRect(_inner, _st.menu.itemBg); - } - for (const auto child : children()) { - if (const auto widget = qobject_cast(child)) { - RenderWidget(p, widget, widget->pos()); - } - } - } - return result; -} - -void PopupMenu::deleteOnHide(bool del) { - _deleteOnHide = del; -} - -void PopupMenu::popup(const QPoint &p) { - showMenu(p, nullptr, TriggeredSource::Mouse); -} - -void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) { - if (!parent && ::Platform::IsMac() && !Platform::IsApplicationActive()) { - _hiding = false; - _a_opacity.stop(); - _a_show.stop(); - _cache = QPixmap(); - hide(); - if (_deleteOnHide) { - deleteLater(); - } - return; - } - _parent = parent; - - auto origin = PanelAnimation::Origin::TopLeft; - auto w = p - QPoint(0, _padding.top()); - auto r = QApplication::desktop()->screenGeometry(p); - _useTransparency = Platform::TranslucentWindowsSupported(p); - setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); - handleCompositingUpdate(); - if (style::RightToLeft()) { - if (w.x() - width() < r.x() - _padding.left()) { - if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { - w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); - } else { - w.setX(r.x() - _padding.left()); - } - } else { - w.setX(w.x() - width()); - } - } else { - if (w.x() + width() - _padding.right() > r.x() + r.width()) { - if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { - w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); - } else { - w.setX(p.x() - width() + _padding.right()); - } - origin = PanelAnimation::Origin::TopRight; - } - } - if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { - if (_parent) { - w.setY(r.y() + r.height() - height() + _padding.bottom()); - } else { - w.setY(p.y() - height() + _padding.bottom()); - origin = (origin == PanelAnimation::Origin::TopRight) ? PanelAnimation::Origin::BottomRight : PanelAnimation::Origin::BottomLeft; - } - } - if (w.x() < r.x()) { - w.setX(r.x()); - } - if (w.y() < r.y()) { - w.setY(r.y()); - } - move(w); - - setOrigin(origin); - _menu->setShowSource(source); - - startShowAnimation(); - - Platform::UpdateOverlayed(this); - show(); - Platform::ShowOverAll(this); - activateWindow(); -} - -PopupMenu::~PopupMenu() { - for (const auto &submenu : base::take(_submenus)) { - delete submenu; - } - if (const auto parent = parentWidget()) { - if (QApplication::focusWidget() != nullptr) { - ActivateWindowDelayed(parent); - } - } - if (_destroyedCallback) { - _destroyedCallback(); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h deleted file mode 100644 index 05ca5ceaa..000000000 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "styles/style_widgets.h" -#include "ui/widgets/menu.h" -#include "ui/effects/animations.h" -#include "ui/effects/panel_animation.h" -#include "ui/round_rect.h" -#include "ui/rp_widget.h" -#include "base/object_ptr.h" - -namespace Ui { - -class PopupMenu : public RpWidget { -public: - PopupMenu(QWidget *parent, const style::PopupMenu &st = st::defaultPopupMenu); - PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); - - not_null addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addAction(const QString &text, Fn callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); - not_null addSeparator(); - void clearActions(); - - const std::vector> &actions() const; - - void deleteOnHide(bool del); - void popup(const QPoint &p); - void hideMenu(bool fast = false); - - void setDestroyedCallback(Fn callback) { - _destroyedCallback = std::move(callback); - } - - ~PopupMenu(); - -protected: - void paintEvent(QPaintEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void hideEvent(QHideEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - -private: - void paintBg(QPainter &p); - void hideFast(); - void setOrigin(PanelAnimation::Origin origin); - void showAnimated(PanelAnimation::Origin origin); - void hideAnimated(); - - QImage grabForPanelAnimation(); - void startShowAnimation(); - void startOpacityAnimation(bool hiding); - void prepareCache(); - void childHiding(PopupMenu *child); - - void showAnimationCallback(); - void opacityAnimationCallback(); - - void init(); - - void hideFinished(); - void showStarted(); - - using TriggeredSource = Menu::TriggeredSource; - void handleCompositingUpdate(); - void handleMenuResize(); - void handleActivated(QAction *action, int actionTop, TriggeredSource source); - void handleTriggered(QAction *action, int actionTop, TriggeredSource source); - void forwardKeyPress(int key); - bool handleKeyPress(int key); - void forwardMouseMove(QPoint globalPosition) { - _menu->handleMouseMove(globalPosition); - } - void handleMouseMove(QPoint globalPosition); - void forwardMousePress(QPoint globalPosition) { - _menu->handleMousePress(globalPosition); - } - void handleMousePress(QPoint globalPosition); - void forwardMouseRelease(QPoint globalPosition) { - _menu->handleMouseRelease(globalPosition); - } - void handleMouseRelease(QPoint globalPosition); - - using SubmenuPointer = QPointer; - bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); - void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source); - void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source); - - const style::PopupMenu &_st; - - RoundRect _roundRect; - object_ptr _menu; - - using Submenus = QMap; - Submenus _submenus; - - PopupMenu *_parent = nullptr; - - QRect _inner; - style::margins _padding; - - SubmenuPointer _activeSubmenu; - - PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; - std::unique_ptr _showAnimation; - Animations::Simple _a_show; - - bool _useTransparency = true; - bool _hiding = false; - QPixmap _cache; - Animations::Simple _a_opacity; - - bool _deleteOnHide = true; - bool _triggering = false; - bool _deleteLater = false; - - Fn _destroyedCallback; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/scroll_area.cpp b/Telegram/SourceFiles/ui/widgets/scroll_area.cpp deleted file mode 100644 index 37187f476..000000000 --- a/Telegram/SourceFiles/ui/widgets/scroll_area.cpp +++ /dev/null @@ -1,725 +0,0 @@ -/* -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 "ui/widgets/scroll_area.h" - -#include "ui/painter.h" -#include "ui/ui_utility.h" - -#include -#include -#include -#include - -namespace Ui { - -// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html - -ScrollShadow::ScrollShadow(ScrollArea *parent, const style::ScrollArea *st) : QWidget(parent), _st(st) { - setVisible(false); - Assert(_st != nullptr); - Assert(_st->shColor.v() != nullptr); -} - -void ScrollShadow::paintEvent(QPaintEvent *e) { - QPainter p(this); - p.fillRect(rect(), _st->shColor); -} - -void ScrollShadow::changeVisibility(bool shown) { - setVisible(shown); -} - -ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::ScrollArea *st) : TWidget(parent) -, _st(st) -, _vertical(vert) -, _hiding(_st->hiding != 0) -, _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar()) -, _scrollMax(_connected->maximum()) { - recountSize(); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideTimer())); - - connect(_connected, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged())); - connect(_connected, SIGNAL(rangeChanged(int, int)), this, SLOT(onRangeChanged())); - - updateBar(); -} - -void ScrollBar::recountSize() { - setGeometry(_vertical ? QRect(style::RightToLeft() ? 0 : (area()->width() - _st->width), _st->deltat, _st->width, area()->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, area()->height() - _st->width, area()->width() - _st->deltat - _st->deltab, _st->width)); -} - -void ScrollBar::onValueChanged() { - area()->onScrolled(); - updateBar(); -} - -void ScrollBar::onRangeChanged() { - area()->onInnerResized(); - updateBar(); -} - -void ScrollBar::updateBar(bool force) { - QRect newBar; - if (_connected->maximum() != _scrollMax) { - int32 oldMax = _scrollMax, newMax = _connected->maximum(); - _scrollMax = newMax; - area()->rangeChanged(oldMax, newMax, _vertical); - } - if (_vertical) { - int sh = area()->scrollHeight(), rh = height(), h = sh ? int32((rh * int64(area()->height())) / sh) : 0; - if (h >= rh || !area()->scrollTopMax() || rh < _st->minHeight) { - if (!isHidden()) hide(); - bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0); - if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh); - if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh); - return; - } - - if (h <= _st->minHeight) h = _st->minHeight; - int stm = area()->scrollTopMax(), y = stm ? int32(((rh - h) * int64(area()->scrollTop())) / stm) : 0; - if (y > rh - h) y = rh - h; - - newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h); - } else { - int sw = area()->scrollWidth(), rw = width(), w = sw ? int32((rw * int64(area()->width())) / sw) : 0; - if (w >= rw || !area()->scrollLeftMax() || rw < _st->minHeight) { - if (!isHidden()) hide(); - return; - } - - if (w <= _st->minHeight) w = _st->minHeight; - int slm = area()->scrollLeftMax(), x = slm ? int32(((rw - w) * int64(area()->scrollLeft())) / slm) : 0; - if (x > rw - w) x = rw - w; - - newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax); - } - if (newBar != _bar) { - _bar = newBar; - update(); - } - if (_vertical) { - bool newTopSh = (_st->topsh < 0) || (area()->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (area()->scrollTop() < area()->scrollTopMax() - _st->bottomsh); - if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh); - if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh); - } - if (isHidden()) show(); -} - -void ScrollBar::onHideTimer() { - if (!_hiding) { - _hiding = true; - _a_opacity.start([this] { update(); }, 1., 0., _st->duration); - } -} - -ScrollArea *ScrollBar::area() { - return static_cast(parentWidget()); -} - -void ScrollBar::setOver(bool over) { - if (_over != over) { - auto wasOver = (_over || _moving); - _over = over; - auto nowOver = (_over || _moving); - if (wasOver != nowOver) { - _a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration); - } - if (nowOver && _hiding) { - _hiding = false; - _a_opacity.start([this] { update(); }, 0., 1., _st->duration); - } - } -} - -void ScrollBar::setOverBar(bool overbar) { - if (_overbar != overbar) { - auto wasBarOver = (_overbar || _moving); - _overbar = overbar; - auto nowBarOver = (_overbar || _moving); - if (wasBarOver != nowBarOver) { - _a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration); - } - } -} - -void ScrollBar::setMoving(bool moving) { - if (_moving != moving) { - auto wasOver = (_over || _moving); - auto wasBarOver = (_overbar || _moving); - _moving = moving; - auto nowBarOver = (_overbar || _moving); - if (wasBarOver != nowBarOver) { - _a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration); - } - auto nowOver = (_over || _moving); - if (wasOver != nowOver) { - _a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration); - } - if (!nowOver && _st->hiding && !_hiding) { - _hideTimer.start(_hideIn); - } - } -} - -void ScrollBar::paintEvent(QPaintEvent *e) { - if (!_bar.width() && !_bar.height()) { - hide(); - return; - } - auto opacity = _a_opacity.value(_hiding ? 0. : 1.); - if (opacity == 0.) return; - - QPainter p(this); - auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0; - auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax; - p.setPen(Qt::NoPen); - auto bg = anim::color(_st->bg, _st->bgOver, _a_over.value((_over || _moving) ? 1. : 0.)); - bg.setAlpha(anim::interpolate(0, bg.alpha(), opacity)); - auto bar = anim::color(_st->barBg, _st->barBgOver, _a_barOver.value((_overbar || _moving) ? 1. : 0.)); - bar.setAlpha(anim::interpolate(0, bar.alpha(), opacity)); - if (_st->round) { - PainterHighQualityEnabler hq(p); - p.setBrush(bg); - p.drawRoundedRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), _st->round, _st->round); - p.setBrush(bar); - p.drawRoundedRect(_bar, _st->round, _st->round); - } else { - p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), bg); - p.fillRect(_bar, bar); - } -} - -void ScrollBar::hideTimeout(crl::time dt) { - if (_hiding && dt > 0) { - _hiding = false; - _a_opacity.start([this] { update(); }, 0., 1., _st->duration); - } - _hideIn = dt; - if (!_moving) { - _hideTimer.start(_hideIn); - } -} - -void ScrollBar::enterEventHook(QEvent *e) { - _hideTimer.stop(); - setMouseTracking(true); - setOver(true); -} - -void ScrollBar::leaveEventHook(QEvent *e) { - if (!_moving) { - setMouseTracking(false); - } - setOver(false); - setOverBar(false); - if (_st->hiding && !_hiding) { - _hideTimer.start(_hideIn); - } -} - -void ScrollBar::mouseMoveEvent(QMouseEvent *e) { - setOverBar(_bar.contains(e->pos())); - if (_moving) { - int delta = 0, barDelta = _vertical ? (area()->height() - _bar.height()) : (area()->width() - _bar.width()); - if (barDelta > 0) { - QPoint d = (e->globalPos() - _dragStart); - delta = int32((_vertical ? (d.y() * int64(area()->scrollTopMax())) : (d.x() * int64(area()->scrollLeftMax()))) / barDelta); - } - _connected->setValue(_startFrom + delta); - } -} - -void ScrollBar::mousePressEvent(QMouseEvent *e) { - if (!width() || !height()) return; - - _dragStart = e->globalPos(); - setMoving(true); - if (_overbar) { - _startFrom = _connected->value(); - } else { - int32 val = _vertical ? e->pos().y() : e->pos().x(), div = _vertical ? height() : width(); - val = (val <= _st->deltat) ? 0 : (val - _st->deltat); - div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab); - _startFrom = _vertical ? int32((val * int64(area()->scrollTopMax())) / div) : ((val * int64(area()->scrollLeftMax())) / div); - _connected->setValue(_startFrom); - setOverBar(true); - } - - area()->setMovingByScrollBar(true); - emit area()->scrollStarted(); -} - -void ScrollBar::mouseReleaseEvent(QMouseEvent *e) { - if (_moving) { - setMoving(false); - - area()->setMovingByScrollBar(false); - emit area()->scrollFinished(); - } - if (!_over) { - setMouseTracking(false); - } -} - -void ScrollBar::resizeEvent(QResizeEvent *e) { - updateBar(); -} - -ScrollArea::ScrollArea(QWidget *parent, const style::ScrollArea &st, bool handleTouch) -: RpWidgetWrap(parent) -, _st(st) -, _horizontalBar(this, false, &_st) -, _verticalBar(this, true, &_st) -, _topShadow(this, &_st) -, _bottomShadow(this, &_st) -, _touchEnabled(handleTouch) { - setLayoutDirection(style::LayoutDirection()); - setFocusPolicy(Qt::NoFocus); - - connect(_verticalBar, SIGNAL(topShadowVisibility(bool)), _topShadow, SLOT(changeVisibility(bool))); - connect(_verticalBar, SIGNAL(bottomShadowVisibility(bool)), _bottomShadow, SLOT(changeVisibility(bool))); - _verticalBar->updateBar(true); - - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - setFrameStyle(QFrame::NoFrame | QFrame::Plain); - viewport()->setAutoFillBackground(false); - - _horizontalValue = horizontalScrollBar()->value(); - _verticalValue = verticalScrollBar()->value(); - - if (_touchEnabled) { - viewport()->setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - connect(&_touchScrollTimer, SIGNAL(timeout()), this, SLOT(onTouchScrollTimer())); - } -} - -void ScrollArea::touchDeaccelerate(int32 elapsed) { - int32 x = _touchSpeed.x(); - int32 y = _touchSpeed.y(); - _touchSpeed.setX((x == 0) ? x : (x > 0) ? qMax(0, x - elapsed) : qMin(0, x + elapsed)); - _touchSpeed.setY((y == 0) ? y : (y > 0) ? qMax(0, y - elapsed) : qMin(0, y + elapsed)); -} - -void ScrollArea::onScrolled() { - if (const auto inner = widget()) { - SendPendingMoveResizeEvents(inner); - } - - bool em = false; - int horizontalValue = horizontalScrollBar()->value(); - int verticalValue = verticalScrollBar()->value(); - if (_horizontalValue != horizontalValue) { - if (_disabled) { - horizontalScrollBar()->setValue(_horizontalValue); - } else { - _horizontalValue = horizontalValue; - if (_st.hiding) { - _horizontalBar->hideTimeout(_st.hiding); - } - em = true; - } - } - if (_verticalValue != verticalValue) { - if (_disabled) { - verticalScrollBar()->setValue(_verticalValue); - } else { - _verticalValue = verticalValue; - if (_st.hiding) { - _verticalBar->hideTimeout(_st.hiding); - } - em = true; - _scrollTopUpdated.fire_copy(_verticalValue); - } - } - if (em) { - emit scrolled(); - if (!_movingByScrollBar) { - SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); - } - } -} - -void ScrollArea::onInnerResized() { - emit innerResized(); -} - -int ScrollArea::scrollWidth() const { - QWidget *w(widget()); - return w ? qMax(w->width(), width()) : width(); -} - -int ScrollArea::scrollHeight() const { - QWidget *w(widget()); - return w ? qMax(w->height(), height()) : height(); -} - -int ScrollArea::scrollLeftMax() const { - return scrollWidth() - width(); -} - -int ScrollArea::scrollTopMax() const { - return scrollHeight() - height(); -} - -int ScrollArea::scrollLeft() const { - return _horizontalValue; -} - -int ScrollArea::scrollTop() const { - return _verticalValue; -} - -void ScrollArea::onTouchTimer() { - _touchRightButton = true; -} - -void ScrollArea::onTouchScrollTimer() { - auto nowTime = crl::now(); - if (_touchScrollState == TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { - _touchScrollState = TouchScrollState::Manual; - touchResetSpeed(); - } else if (_touchScrollState == TouchScrollState::Auto || _touchScrollState == TouchScrollState::Acceleration) { - int32 elapsed = int32(nowTime - _touchTime); - QPoint delta = _touchSpeed * elapsed / 1000; - bool hasScrolled = touchScroll(delta); - - if (_touchSpeed.isNull() || !hasScrolled) { - _touchScrollState = TouchScrollState::Manual; - _touchScroll = false; - _touchScrollTimer.stop(); - } else { - _touchTime = nowTime; - } - touchDeaccelerate(elapsed); - } -} - -void ScrollArea::touchUpdateSpeed() { - const auto nowTime = crl::now(); - if (_touchPrevPosValid) { - const int elapsed = nowTime - _touchSpeedTime; - if (elapsed) { - const QPoint newPixelDiff = (_touchPos - _touchPrevPos); - const QPoint pixelsPerSecond = newPixelDiff * (1000 / elapsed); - - // fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because - // of a small horizontal offset when scrolling vertically - const int newSpeedY = (qAbs(pixelsPerSecond.y()) > kFingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; - const int newSpeedX = (qAbs(pixelsPerSecond.x()) > kFingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; - if (_touchScrollState == TouchScrollState::Auto) { - const int oldSpeedY = _touchSpeed.y(); - const int oldSpeedX = _touchSpeed.x(); - if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) - && (oldSpeedX <= 0 && newSpeedX <= 0)) || (oldSpeedX >= 0 && newSpeedX >= 0)) { - _touchSpeed.setY(std::clamp((oldSpeedY + (newSpeedY / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated)); - _touchSpeed.setX(std::clamp((oldSpeedX + (newSpeedX / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated)); - } else { - _touchSpeed = QPoint(); - } - } else { - // we average the speed to avoid strange effects with the last delta - if (!_touchSpeed.isNull()) { - _touchSpeed.setX(std::clamp((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick)); - _touchSpeed.setY(std::clamp((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick)); - } else { - _touchSpeed = QPoint(newSpeedX, newSpeedY); - } - } - } - } else { - _touchPrevPosValid = true; - } - _touchSpeedTime = nowTime; - _touchPrevPos = _touchPos; -} - -void ScrollArea::touchResetSpeed() { - _touchSpeed = QPoint(); - _touchPrevPosValid = false; -} - -bool ScrollArea::eventFilter(QObject *obj, QEvent *e) { - bool res = QScrollArea::eventFilter(obj, e); - if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { - QTouchEvent *ev = static_cast(e); - if (_touchEnabled && ev->device()->type() == QTouchDevice::TouchScreen) { - if (obj == widget()) { - touchEvent(ev); - return true; - } - } - } - return res; -} - -bool ScrollArea::viewportEvent(QEvent *e) { - if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { - QTouchEvent *ev = static_cast(e); - if (_touchEnabled && ev->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(ev); - return true; - } - } - return QScrollArea::viewportEvent(e); -} - -void ScrollArea::touchEvent(QTouchEvent *e) { - if (!e->touchPoints().isEmpty()) { - _touchPrevPos = _touchPos; - _touchPos = e->touchPoints().cbegin()->screenPos().toPoint(); - } - - switch (e->type()) { - case QEvent::TouchBegin: { - if (_touchPress || e->touchPoints().isEmpty()) return; - _touchPress = true; - if (_touchScrollState == TouchScrollState::Auto) { - _touchScrollState = TouchScrollState::Acceleration; - _touchWaitingAcceleration = true; - _touchAccelerationTime = crl::now(); - touchUpdateSpeed(); - _touchStart = _touchPos; - } else { - _touchScroll = false; - _touchTimer.start(QApplication::startDragTime()); - } - _touchStart = _touchPrevPos = _touchPos; - _touchRightButton = false; - } break; - - case QEvent::TouchUpdate: { - if (!_touchPress) return; - if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { - _touchTimer.stop(); - _touchScroll = true; - touchUpdateSpeed(); - } - if (_touchScroll) { - if (_touchScrollState == TouchScrollState::Manual) { - touchScrollUpdated(_touchPos); - } else if (_touchScrollState == TouchScrollState::Acceleration) { - touchUpdateSpeed(); - _touchAccelerationTime = crl::now(); - if (_touchSpeed.isNull()) { - _touchScrollState = TouchScrollState::Manual; - } - } - } - } break; - - case QEvent::TouchEnd: { - if (!_touchPress) return; - _touchPress = false; - auto weak = MakeWeak(this); - if (_touchScroll) { - if (_touchScrollState == TouchScrollState::Manual) { - _touchScrollState = TouchScrollState::Auto; - _touchPrevPosValid = false; - _touchScrollTimer.start(15); - _touchTime = crl::now(); - } else if (_touchScrollState == TouchScrollState::Auto) { - _touchScrollState = TouchScrollState::Manual; - _touchScroll = false; - touchResetSpeed(); - } else if (_touchScrollState == TouchScrollState::Acceleration) { - _touchScrollState = TouchScrollState::Auto; - _touchWaitingAcceleration = false; - _touchPrevPosValid = false; - } - } else if (window()) { // one short tap -- like left mouse click, one long tap -- like right mouse click - Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); - - if (weak) SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton, _touchStart); - if (weak) SendSynteticMouseEvent(this, QEvent::MouseButtonPress, btn, _touchStart); - if (weak) SendSynteticMouseEvent(this, QEvent::MouseButtonRelease, btn, _touchStart); - - if (weak && _touchRightButton) { - auto windowHandle = window()->windowHandle(); - auto localPoint = windowHandle->mapFromGlobal(_touchStart); - QContextMenuEvent ev(QContextMenuEvent::Mouse, localPoint, _touchStart, QGuiApplication::keyboardModifiers()); - ev.setTimestamp(crl::now()); - QGuiApplication::sendEvent(windowHandle, &ev); - } - } - if (weak) { - _touchTimer.stop(); - _touchRightButton = false; - } - } break; - - case QEvent::TouchCancel: { - _touchPress = false; - _touchScroll = false; - _touchScrollState = TouchScrollState::Manual; - _touchTimer.stop(); - } break; - } -} - -void ScrollArea::touchScrollUpdated(const QPoint &screenPos) { - _touchPos = screenPos; - touchScroll(_touchPos - _touchPrevPos); - touchUpdateSpeed(); -} - -void ScrollArea::disableScroll(bool dis) { - _disabled = dis; - if (_disabled && _st.hiding) { - _horizontalBar->hideTimeout(0); - _verticalBar->hideTimeout(0); - } -} - -void ScrollArea::scrollContentsBy(int dx, int dy) { - if (_disabled) { - return; - } - QScrollArea::scrollContentsBy(dx, dy); -} - -bool ScrollArea::touchScroll(const QPoint &delta) { - int32 scTop = scrollTop(), scMax = scrollTopMax(), scNew = std::clamp(scTop - delta.y(), 0, scMax); - if (scNew == scTop) return false; - - scrollToY(scNew); - return true; -} - -void ScrollArea::resizeEvent(QResizeEvent *e) { - QScrollArea::resizeEvent(e); - _horizontalBar->recountSize(); - _verticalBar->recountSize(); - _topShadow->setGeometry(QRect(0, 0, width(), qAbs(_st.topsh))); - _bottomShadow->setGeometry(QRect(0, height() - qAbs(_st.bottomsh), width(), qAbs(_st.bottomsh))); - emit geometryChanged(); -} - -void ScrollArea::moveEvent(QMoveEvent *e) { - QScrollArea::moveEvent(e); - emit geometryChanged(); -} - -void ScrollArea::keyPressEvent(QKeyEvent *e) { - if ((e->key() == Qt::Key_Up || e->key() == Qt::Key_Down) && e->modifiers().testFlag(Qt::AltModifier)) { - e->ignore(); - } else if(e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) { - ((QObject*)widget())->event(e); - } else { - QScrollArea::keyPressEvent(e); - } -} - -void ScrollArea::enterEventHook(QEvent *e) { - if (_disabled) return; - if (_st.hiding) { - _horizontalBar->hideTimeout(_st.hiding); - _verticalBar->hideTimeout(_st.hiding); - } - return QScrollArea::enterEvent(e); -} - -void ScrollArea::leaveEventHook(QEvent *e) { - if (_st.hiding) { - _horizontalBar->hideTimeout(0); - _verticalBar->hideTimeout(0); - } - return QScrollArea::leaveEvent(e); -} - -void ScrollArea::scrollTo(ScrollToRequest request) { - scrollToY(request.ymin, request.ymax); -} - -void ScrollArea::scrollToWidget(not_null widget) { - if (auto local = this->widget()) { - auto globalPosition = widget->mapToGlobal(QPoint(0, 0)); - auto localPosition = local->mapFromGlobal(globalPosition); - auto localTop = localPosition.y(); - auto localBottom = localTop + widget->height(); - scrollToY(localTop, localBottom); - } -} - -void ScrollArea::scrollToY(int toTop, int toBottom) { - if (const auto inner = widget()) { - SendPendingMoveResizeEvents(inner); - } - SendPendingMoveResizeEvents(this); - - int toMin = 0, toMax = scrollTopMax(); - if (toTop < toMin) { - toTop = toMin; - } else if (toTop > toMax) { - toTop = toMax; - } - bool exact = (toBottom < 0); - - int curTop = scrollTop(), curHeight = height(), curBottom = curTop + curHeight, scToTop = toTop; - if (!exact && toTop >= curTop) { - if (toBottom < toTop) toBottom = toTop; - if (toBottom <= curBottom) return; - - scToTop = toBottom - curHeight; - if (scToTop > toTop) scToTop = toTop; - if (scToTop == curTop) return; - } else { - scToTop = toTop; - } - verticalScrollBar()->setValue(scToTop); -} - -void ScrollArea::doSetOwnedWidget(object_ptr w) { - if (widget() && _touchEnabled) { - widget()->removeEventFilter(this); - if (!_widgetAcceptsTouch) widget()->setAttribute(Qt::WA_AcceptTouchEvents, false); - } - _widget = std::move(w); - QScrollArea::setWidget(_widget); - if (_widget) { - _widget->setAutoFillBackground(false); - if (_touchEnabled) { - _widget->installEventFilter(this); - _widgetAcceptsTouch = _widget->testAttribute(Qt::WA_AcceptTouchEvents); - _widget->setAttribute(Qt::WA_AcceptTouchEvents); - } - } -} - -object_ptr ScrollArea::doTakeWidget() { - QScrollArea::takeWidget(); - return std::move(_widget); -} - -void ScrollArea::rangeChanged(int oldMax, int newMax, bool vertical) { -} - -void ScrollArea::updateBars() { - _horizontalBar->updateBar(true); - _verticalBar->updateBar(true); -} - -bool ScrollArea::focusNextPrevChild(bool next) { - if (QWidget::focusNextPrevChild(next)) { -// if (QWidget *fw = focusWidget()) -// ensureWidgetVisible(fw); - return true; - } - return false; -} - -void ScrollArea::setMovingByScrollBar(bool movingByScrollBar) { - _movingByScrollBar = movingByScrollBar; -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/scroll_area.h b/Telegram/SourceFiles/ui/widgets/scroll_area.h deleted file mode 100644 index ee9b3221d..000000000 --- a/Telegram/SourceFiles/ui/widgets/scroll_area.h +++ /dev/null @@ -1,249 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/effects/animations.h" -#include "base/object_ptr.h" -#include "styles/style_widgets.h" - -#include -#include -#include - -namespace Ui { - -// 37px per 15ms while select-by-drag. -inline constexpr auto kMaxScrollSpeed = 37; - -// Touch flick ignore 3px. -inline constexpr auto kFingerAccuracyThreshold = 3; - -// 4000px per second. -inline constexpr auto kMaxScrollAccelerated = 4000; - -// 2500px per second. -inline constexpr auto kMaxScrollFlick = 2500; - -enum class TouchScrollState { - Manual, // Scrolling manually with the finger on the screen - Auto, // Scrolling automatically - Acceleration // Scrolling automatically but a finger is on the screen -}; - -class ScrollArea; - -struct ScrollToRequest { - ScrollToRequest(int ymin, int ymax) - : ymin(ymin) - , ymax(ymax) { - } - - int ymin = 0; - int ymax = 0; - -}; - -class ScrollShadow : public QWidget { - Q_OBJECT - -public: - ScrollShadow(ScrollArea *parent, const style::ScrollArea *st); - - void paintEvent(QPaintEvent *e); - -public slots: - void changeVisibility(bool shown); - -private: - const style::ScrollArea *_st; - -}; - -class ScrollBar : public TWidget { - Q_OBJECT - -public: - ScrollBar(ScrollArea *parent, bool vertical, const style::ScrollArea *st); - - void recountSize(); - void updateBar(bool force = false); - - void hideTimeout(crl::time dt); - -private slots: - void onValueChanged(); - void onRangeChanged(); - void onHideTimer(); - -signals: - void topShadowVisibility(bool); - void bottomShadowVisibility(bool); - -protected: - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - ScrollArea *area(); - - void setOver(bool over); - void setOverBar(bool overbar); - void setMoving(bool moving); - - const style::ScrollArea *_st; - - bool _vertical = true; - bool _hiding = false; - bool _over = false; - bool _overbar = false; - bool _moving = false; - bool _topSh = false; - bool _bottomSh = false; - - QPoint _dragStart; - QScrollBar *_connected; - - int32 _startFrom, _scrollMax; - - crl::time _hideIn = 0; - QTimer _hideTimer; - - Animations::Simple _a_over; - Animations::Simple _a_barOver; - Animations::Simple _a_opacity; - - QRect _bar; -}; - -class ScrollArea : public RpWidgetWrap { - Q_OBJECT - -public: - ScrollArea(QWidget *parent, const style::ScrollArea &st = st::defaultScrollArea, bool handleTouch = true); - - int scrollWidth() const; - int scrollHeight() const; - int scrollLeftMax() const; - int scrollTopMax() const; - int scrollLeft() const; - int scrollTop() const; - - template - QPointer setOwnedWidget(object_ptr widget) { - auto result = QPointer(widget); - doSetOwnedWidget(std::move(widget)); - return result; - } - template - object_ptr takeWidget() { - return object_ptr::fromRaw( - static_cast(doTakeWidget().release())); - } - - void rangeChanged(int oldMax, int newMax, bool vertical); - - void updateBars(); - - bool focusNextPrevChild(bool next) override; - void setMovingByScrollBar(bool movingByScrollBar); - - bool viewportEvent(QEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - - auto scrollTopValue() const { - return _scrollTopUpdated.events_starting_with(scrollTop()); - } - auto scrollTopChanges() const { - return _scrollTopUpdated.events(); - } - - void scrollTo(ScrollToRequest request); - void scrollToWidget(not_null widget); - -protected: - bool eventFilter(QObject *obj, QEvent *e) override; - - void resizeEvent(QResizeEvent *e) override; - void moveEvent(QMoveEvent *e) override; - void touchEvent(QTouchEvent *e); - - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - -public slots: - void scrollToY(int toTop, int toBottom = -1); - void disableScroll(bool dis); - void onScrolled(); - void onInnerResized(); - - void onTouchTimer(); - void onTouchScrollTimer(); - -signals: - void scrolled(); - void innerResized(); - void scrollStarted(); - void scrollFinished(); - void geometryChanged(); - -protected: - void scrollContentsBy(int dx, int dy) override; - -private: - void doSetOwnedWidget(object_ptr widget); - object_ptr doTakeWidget(); - - void setWidget(QWidget *widget); - - bool touchScroll(const QPoint &delta); - - void touchScrollUpdated(const QPoint &screenPos); - - void touchResetSpeed(); - void touchUpdateSpeed(); - void touchDeaccelerate(int32 elapsed); - - bool _disabled = false; - bool _movingByScrollBar = false; - - const style::ScrollArea &_st; - object_ptr _horizontalBar, _verticalBar; - object_ptr _topShadow, _bottomShadow; - int _horizontalValue, _verticalValue; - - bool _touchEnabled; - QTimer _touchTimer; - bool _touchScroll = false; - bool _touchPress = false; - bool _touchRightButton = false; - QPoint _touchStart, _touchPrevPos, _touchPos; - - TouchScrollState _touchScrollState = TouchScrollState::Manual; - bool _touchPrevPosValid = false; - bool _touchWaitingAcceleration = false; - QPoint _touchSpeed; - crl::time _touchSpeedTime = 0; - crl::time _touchAccelerationTime = 0; - crl::time _touchTime = 0; - QTimer _touchScrollTimer; - - bool _widgetAcceptsTouch = false; - - object_ptr _widget = { nullptr }; - - rpl::event_stream _scrollTopUpdated; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/shadow.cpp b/Telegram/SourceFiles/ui/widgets/shadow.cpp deleted file mode 100644 index a4bd8c929..000000000 --- a/Telegram/SourceFiles/ui/widgets/shadow.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* -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 "ui/widgets/shadow.h" - -#include "ui/ui_utility.h" -#include "styles/style_widgets.h" -#include "styles/palette.h" - -#include -#include - -namespace Ui { - -PlainShadow::PlainShadow(QWidget *parent) -: PlainShadow(parent, st::shadowFg) { -} - -PlainShadow::PlainShadow(QWidget *parent, style::color color) -: RpWidget(parent) -, _color(color) { - resize(st::lineWidth, st::lineWidth); -} - -void PlainShadow::paintEvent(QPaintEvent *e) { - QPainter(this).fillRect(e->rect(), _color); -} - -void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) { - auto left = (sides & RectPart::Left); - auto top = (sides & RectPart::Top); - auto right = (sides & RectPart::Right); - auto bottom = (sides & RectPart::Bottom); - if (left) { - auto from = box.y(); - auto to = from + box.height(); - if (top && !st.topLeft.empty()) { - st.topLeft.paint(p, box.x() - st.extend.left(), box.y() - st.extend.top(), outerWidth); - from += st.topLeft.height() - st.extend.top(); - } - if (bottom && !st.bottomLeft.empty()) { - st.bottomLeft.paint(p, box.x() - st.extend.left(), box.y() + box.height() + st.extend.bottom() - st.bottomLeft.height(), outerWidth); - to -= st.bottomLeft.height() - st.extend.bottom(); - } - if (to > from && !st.left.empty()) { - st.left.fill(p, style::rtlrect(box.x() - st.extend.left(), from, st.left.width(), to - from, outerWidth)); - } - } - if (right) { - auto from = box.y(); - auto to = from + box.height(); - if (top && !st.topRight.empty()) { - st.topRight.paint(p, box.x() + box.width() + st.extend.right() - st.topRight.width(), box.y() - st.extend.top(), outerWidth); - from += st.topRight.height() - st.extend.top(); - } - if (bottom && !st.bottomRight.empty()) { - st.bottomRight.paint(p, box.x() + box.width() + st.extend.right() - st.bottomRight.width(), box.y() + box.height() + st.extend.bottom() - st.bottomRight.height(), outerWidth); - to -= st.bottomRight.height() - st.extend.bottom(); - } - if (to > from && !st.right.empty()) { - st.right.fill(p, style::rtlrect(box.x() + box.width() + st.extend.right() - st.right.width(), from, st.right.width(), to - from, outerWidth)); - } - } - if (top && !st.top.empty()) { - auto from = box.x(); - auto to = from + box.width(); - if (left && !st.topLeft.empty()) from += st.topLeft.width() - st.extend.left(); - if (right && !st.topRight.empty()) to -= st.topRight.width() - st.extend.right(); - if (to > from) { - st.top.fill(p, style::rtlrect(from, box.y() - st.extend.top(), to - from, st.top.height(), outerWidth)); - } - } - if (bottom && !st.bottom.empty()) { - auto from = box.x(); - auto to = from + box.width(); - if (left && !st.bottomLeft.empty()) from += st.bottomLeft.width() - st.extend.left(); - if (right && !st.bottomRight.empty()) to -= st.bottomRight.width() - st.extend.right(); - if (to > from) { - st.bottom.fill(p, style::rtlrect(from, box.y() + box.height() + st.extend.bottom() - st.bottom.height(), to - from, st.bottom.height(), outerWidth)); - } - } -} - -QPixmap Shadow::grab( - not_null target, - const style::Shadow &shadow, - RectParts sides) { - SendPendingMoveResizeEvents(target); - auto rect = target->rect(); - auto extend = QMargins( - (sides & RectPart::Left) ? shadow.extend.left() : 0, - (sides & RectPart::Top) ? shadow.extend.top() : 0, - (sides & RectPart::Right) ? shadow.extend.right() : 0, - (sides & RectPart::Bottom) ? shadow.extend.bottom() : 0 - ); - auto full = QRect(0, 0, extend.left() + rect.width() + extend.right(), extend.top() + rect.height() + extend.bottom()); - auto result = QPixmap(full.size() * style::DevicePixelRatio()); - result.setDevicePixelRatio(style::DevicePixelRatio()); - result.fill(Qt::transparent); - { - QPainter p(&result); - Shadow::paint(p, full.marginsRemoved(extend), full.width(), shadow); - RenderWidget(p, target, QPoint(extend.left(), extend.top())); - } - return result; -} - -void Shadow::paintEvent(QPaintEvent *e) { - QPainter p(this); - paint(p, rect().marginsRemoved(_st.extend), width(), _st, _sides); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/shadow.h b/Telegram/SourceFiles/ui/widgets/shadow.h deleted file mode 100644 index 9bbd11e67..000000000 --- a/Telegram/SourceFiles/ui/widgets/shadow.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/rect_part.h" - -namespace style { -struct Shadow; -} // namespace style - -namespace Ui { - -class PlainShadow : public RpWidget { -public: - PlainShadow(QWidget *parent); - PlainShadow(QWidget *parent, style::color color); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - style::color _color; - -}; - -class Shadow : public TWidget { -public: - Shadow( - QWidget *parent, - const style::Shadow &st, - RectParts sides = RectPart::AllSides) - : TWidget(parent) - , _st(st) - , _sides(sides) { - } - - static void paint( - QPainter &p, - const QRect &box, - int outerWidth, - const style::Shadow &st, - RectParts sides = RectPart::AllSides); - - static QPixmap grab( - not_null target, - const style::Shadow &shadow, - RectParts sides = RectPart::AllSides); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - const style::Shadow &_st; - RectParts _sides; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.cpp b/Telegram/SourceFiles/ui/widgets/tooltip.cpp deleted file mode 100644 index 982d4b24a..000000000 --- a/Telegram/SourceFiles/ui/widgets/tooltip.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* -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 "ui/widgets/tooltip.h" - -#include "ui/ui_utility.h" -#include "ui/platform/ui_platform_utility.h" -#include "base/invoke_queued.h" -#include "styles/style_widgets.h" - -#include -#include - -namespace Ui { - -Tooltip *TooltipInstance = nullptr; - -const style::Tooltip *AbstractTooltipShower::tooltipSt() const { - return &st::defaultTooltip; -} - -AbstractTooltipShower::~AbstractTooltipShower() { - if (TooltipInstance && TooltipInstance->_shower == this) { - TooltipInstance->_shower = 0; - } -} - -Tooltip::Tooltip() : RpWidget(nullptr) { - TooltipInstance = this; - - setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::ToolTip); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); - - _showTimer.setCallback([=] { performShow(); }); - _hideByLeaveTimer.setCallback([=] { Hide(); }); -} - -void Tooltip::performShow() { - if (_shower) { - auto text = _shower->tooltipWindowActive() - ? _shower->tooltipText() - : QString(); - if (text.isEmpty()) { - Hide(); - } else { - TooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt()); - } - } -} - -bool Tooltip::eventFilter(QObject *o, QEvent *e) { - if (e->type() == QEvent::Leave) { - _hideByLeaveTimer.callOnce(10); - } else if (e->type() == QEvent::Enter) { - _hideByLeaveTimer.cancel(); - } else if (e->type() == QEvent::MouseMove) { - if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) { - Hide(); - } - } - return RpWidget::eventFilter(o, e); -} - -Tooltip::~Tooltip() { - if (TooltipInstance == this) { - TooltipInstance = nullptr; - } -} - -void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) { - if (!_isEventFilter) { - _isEventFilter = true; - QCoreApplication::instance()->installEventFilter(this); - } - - _point = m; - _st = st; - _text = Text::String(_st->textStyle, text, _textPlainOptions, _st->widthMax, true); - - _useTransparency = Platform::TranslucentWindowsSupported(_point); - setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); - - int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right(); - int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom(); - - // count tooltip size - QSize s(addw + _text.maxWidth(), addh + _text.minHeight()); - if (s.width() > _st->widthMax) { - s.setWidth(addw + _text.countWidth(_st->widthMax - addw)); - s.setHeight(addh + _text.countHeight(s.width() - addw)); - } - int32 maxh = addh + (_st->linesMax * _st->textStyle.font->height); - if (s.height() > maxh) { - s.setHeight(maxh); - } - - // count tooltip position - QPoint p(m + _st->shift); - if (style::RightToLeft()) { - p.setX(m.x() - s.width() - _st->shift.x()); - } - if (s.width() < 2 * _st->shift.x()) { - p.setX(m.x() - (s.width() / 2)); - } - - // adjust tooltip position - QRect r(QApplication::desktop()->screenGeometry(m)); - if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) { - p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width())); - } - if (r.x() + _st->skip > p.x() && p.x() < m.x()) { - p.setX(qMin(m.x(), r.x() + int32(_st->skip))); - } - if (r.y() + r.height() - _st->skip < p.y() + s.height()) { - p.setY(m.y() - s.height() - _st->skip); - } - if (r.y() > p.x()) { - p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height())); - } - - setGeometry(QRect(p, s)); - - _hideByLeaveTimer.cancel(); - show(); -} - -void Tooltip::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (_useTransparency) { - Platform::StartTranslucentPaint(p, e); - - p.setPen(_st->textBorder); - p.setBrush(_st->textBg); - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(QRectF(0.5, 0.5, width() - 1., height() - 1.), st::buttonRadius, st::buttonRadius); - } else { - p.fillRect(rect(), _st->textBg); - - p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - } - int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textStyle.font->height); - - p.setPen(_st->textFg); - _text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines); -} - -void Tooltip::hideEvent(QHideEvent *e) { - if (TooltipInstance == this) { - Hide(); - } -} - -void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) { - if (!TooltipInstance) { - new Tooltip(); - } - TooltipInstance->_shower = shower; - if (delay >= 0) { - TooltipInstance->_showTimer.callOnce(delay); - } else { - TooltipInstance->performShow(); - } -} - -void Tooltip::Hide() { - if (auto instance = TooltipInstance) { - TooltipInstance = nullptr; - instance->_showTimer.cancel(); - instance->_hideByLeaveTimer.cancel(); - instance->hide(); - InvokeQueued(instance, [=] { instance->deleteLater(); }); - } -} - -ImportantTooltip::ImportantTooltip(QWidget *parent, object_ptr content, const style::ImportantTooltip &st) : TWidget(parent) -, _st(st) -, _content(std::move(content)) { - _content->setParent(this); - _hideTimer.setCallback([this] { toggleAnimated(false); }); - hide(); -} - -void ImportantTooltip::pointAt(QRect area, RectParts side) { - if (_area == area && _side == side) { - return; - } - setArea(area); - countApproachSide(side); - updateGeometry(); - update(); -} - -void ImportantTooltip::setArea(QRect area) { - Expects(parentWidget() != nullptr); - _area = area; - auto point = parentWidget()->mapToGlobal(_area.center()); - _useTransparency = Platform::TranslucentWindowsSupported(point); - setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); - - auto contentWidth = parentWidget()->rect().marginsRemoved(_st.padding).width(); - accumulate_min(contentWidth, _content->naturalWidth()); - _content->resizeToWidth(contentWidth); - - auto size = _content->rect().marginsAdded(_st.padding).size(); - if (_useTransparency) { - size.setHeight(size.height() + _st.arrow); - } - if (size.width() < 2 * (_st.arrowSkipMin + _st.arrow)) { - size.setWidth(2 * (_st.arrowSkipMin + _st.arrow)); - } - resize(size); -} - -void ImportantTooltip::countApproachSide(RectParts preferSide) { - Expects(parentWidget() != nullptr); - auto requiredSpace = countInner().height() + _st.shift; - if (_useTransparency) { - requiredSpace += _st.arrow; - } - auto available = parentWidget()->rect(); - auto availableAbove = _area.y() - available.y(); - auto availableBelow = (available.y() + available.height()) - (_area.y() + _area.height()); - auto allowedAbove = (availableAbove >= requiredSpace + _st.margin.top()); - auto allowedBelow = (availableBelow >= requiredSpace + _st.margin.bottom()); - if ((allowedAbove && allowedBelow) || (!allowedAbove && !allowedBelow)) { - _side = preferSide; - } else { - _side = (allowedAbove ? RectPart::Top : RectPart::Bottom) - | (preferSide & (RectPart::Left | RectPart::Center | RectPart::Right)); - } - if (_useTransparency) { - auto arrow = QImage( - QSize(_st.arrow * 2, _st.arrow) * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - arrow.fill(Qt::transparent); - arrow.setDevicePixelRatio(style::DevicePixelRatio()); - { - Painter p(&arrow); - PainterHighQualityEnabler hq(p); - - QPainterPath path; - path.moveTo(0, 0); - path.lineTo(2 * _st.arrow, 0); - path.lineTo(_st.arrow, _st.arrow); - path.lineTo(0, 0); - p.fillPath(path, _st.bg); - } - if (_side & RectPart::Bottom) { - arrow = std::move(arrow).transformed(QTransform(1, 0, 0, -1, 0, 0)); - } - _arrow = PixmapFromImage(std::move(arrow)); - } -} - -void ImportantTooltip::toggleAnimated(bool visible) { - if (_visible == isHidden()) { - setVisible(_visible); - } - if (_visible != visible) { - updateGeometry(); - _visible = visible; - refreshAnimationCache(); - if (_visible) { - show(); - } else if (isHidden()) { - return; - } - hideChildren(); - _visibleAnimation.start([this] { animationCallback(); }, _visible ? 0. : 1., _visible ? 1. : 0., _st.duration, anim::easeOutCirc); - } -} - -void ImportantTooltip::hideAfter(crl::time timeout) { - _hideTimer.callOnce(timeout); -} - -void ImportantTooltip::animationCallback() { - updateGeometry(); - update(); - checkAnimationFinish(); -} - -void ImportantTooltip::refreshAnimationCache() { - if (_cache.isNull() && _useTransparency) { - auto animation = base::take(_visibleAnimation); - auto visible = std::exchange(_visible, true); - showChildren(); - _cache = GrabWidget(this); - _visible = base::take(visible); - _visibleAnimation = base::take(animation); - } -} - -void ImportantTooltip::toggleFast(bool visible) { - if (_visible == isHidden()) { - setVisible(_visible); - } - if (_visibleAnimation.animating() || _visible != visible) { - _visibleAnimation.stop(); - _visible = visible; - checkAnimationFinish(); - } -} - -void ImportantTooltip::checkAnimationFinish() { - if (!_visibleAnimation.animating()) { - _cache = QPixmap(); - showChildren(); - setVisible(_visible); - if (_visible) { - update(); - } else if (_hiddenCallback) { - _hiddenCallback(); - } - } -} - -void ImportantTooltip::updateGeometry() { - Expects(parentWidget() != nullptr); - auto parent = parentWidget(); - auto areaMiddle = _area.x() + (_area.width() / 2); - auto left = areaMiddle - (width() / 2); - if (_side & RectPart::Left) { - left = areaMiddle + _st.arrowSkip - width(); - } else if (_side & RectPart::Right) { - left = areaMiddle - _st.arrowSkip; - } - accumulate_min(left, parent->width() - _st.margin.right() - width()); - accumulate_max(left, _st.margin.left()); - accumulate_max(left, areaMiddle + _st.arrow + _st.arrowSkipMin - width()); - accumulate_min(left, areaMiddle - _st.arrow - _st.arrowSkipMin); - - auto countTop = [this] { - auto shift = anim::interpolate(_st.shift, 0, _visibleAnimation.value(_visible ? 1. : 0.)); - if (_side & RectPart::Top) { - return _area.y() - height() - shift; - } - return _area.y() + _area.height() + shift; - }; - move(left, countTop()); -} - -void ImportantTooltip::resizeEvent(QResizeEvent *e) { - auto inner = countInner(); - auto contentTop = _st.padding.top(); - if (_useTransparency && (_side & RectPart::Bottom)) { - contentTop += _st.arrow; - } - _content->moveToLeft(_st.padding.left(), contentTop); -} - -QRect ImportantTooltip::countInner() const { - return _content->geometry().marginsAdded(_st.padding); -} - -void ImportantTooltip::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto inner = countInner(); - if (_useTransparency) { - if (!_cache.isNull()) { - auto opacity = _visibleAnimation.value(_visible ? 1. : 0.); - p.setOpacity(opacity); - p.drawPixmap(0, 0, _cache); - } else { - if (!_visible) { - return; - } - p.setBrush(_st.bg); - p.setPen(Qt::NoPen); - { - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(inner, _st.radius, _st.radius); - } - auto areaMiddle = _area.x() + (_area.width() / 2) - x(); - auto arrowLeft = areaMiddle - _st.arrow; - if (_side & RectPart::Top) { - p.drawPixmapLeft(arrowLeft, inner.y() + inner.height(), width(), _arrow); - } else { - p.drawPixmapLeft(arrowLeft, inner.y() - _st.arrow, width(), _arrow); - } - } - } else { - p.fillRect(inner, QColor(_st.bg->c.red(), _st.bg->c.green(), _st.bg->c.blue())); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.h b/Telegram/SourceFiles/ui/widgets/tooltip.h deleted file mode 100644 index d2bc51f4f..000000000 --- a/Telegram/SourceFiles/ui/widgets/tooltip.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/timer.h" -#include "base/object_ptr.h" -#include "ui/effects/animations.h" -#include "ui/text/text.h" -#include "ui/rp_widget.h" -#include "ui/rect_part.h" - -namespace style { -struct Tooltip; -struct ImportantTooltip; -} // namespace style - -namespace Ui { - -class AbstractTooltipShower { -public: - virtual QString tooltipText() const = 0; - virtual QPoint tooltipPos() const = 0; - virtual bool tooltipWindowActive() const = 0; - virtual const style::Tooltip *tooltipSt() const; - virtual ~AbstractTooltipShower(); - -}; - -class Tooltip : public RpWidget { -public: - static void Show(int32 delay, const AbstractTooltipShower *shower); - static void Hide(); - -protected: - void paintEvent(QPaintEvent *e) override; - void hideEvent(QHideEvent *e) override; - - bool eventFilter(QObject *o, QEvent *e) override; - -private: - void performShow(); - - Tooltip(); - ~Tooltip(); - - void popup(const QPoint &p, const QString &text, const style::Tooltip *st); - - friend class AbstractTooltipShower; - const AbstractTooltipShower *_shower = nullptr; - base::Timer _showTimer; - - Text::String _text; - QPoint _point; - - const style::Tooltip *_st = nullptr; - - base::Timer _hideByLeaveTimer; - bool _isEventFilter = false; - bool _useTransparency = true; - -}; - -class ImportantTooltip : public TWidget { -public: - ImportantTooltip(QWidget *parent, object_ptr content, const style::ImportantTooltip &st); - - void pointAt(QRect area, RectParts preferSide = RectPart::Top | RectPart::Left); - - void toggleAnimated(bool visible); - void toggleFast(bool visible); - void hideAfter(crl::time timeout); - - void setHiddenCallback(Fn callback) { - _hiddenCallback = std::move(callback); - } - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - -private: - void animationCallback(); - QRect countInner() const; - void setArea(QRect area); - void countApproachSide(RectParts preferSide); - void updateGeometry(); - void checkAnimationFinish(); - void refreshAnimationCache(); - - base::Timer _hideTimer; - const style::ImportantTooltip &_st; - object_ptr _content; - QRect _area; - RectParts _side = RectPart::Top | RectPart::Left; - QPixmap _arrow; - - Ui::Animations::Simple _visibleAnimation; - bool _visible = false; - Fn _hiddenCallback; - bool _useTransparency = true; - QPixmap _cache; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style deleted file mode 100644 index 21d53e285..000000000 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ /dev/null @@ -1,1191 +0,0 @@ -/* -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 -*/ - -using "basic.style"; - -LabelSimple { - font: font; - maxWidth: pixels; - textFg: color; -} - -FlatLabel { - margin: margins; - minWidth: pixels; - align: align; - textFg: color; - maxHeight: pixels; - style: TextStyle; - palette: TextPalette; -} - -LinkButton { - color: color; - overColor: color; - font: font; - overFont: font; - padding: margins; -} - -RippleAnimation { - color: color; - showDuration: int; - hideDuration: int; -} - -InfiniteRadialAnimation { - color: color; - thickness: pixels; - size: size; - linearPeriod: int; - sinePeriod: int; - sineDuration: int; - sineShift: int; - arcMin: double; - arcMax: double; -} - -defaultInfiniteRadialAnimation: InfiniteRadialAnimation { - linearPeriod: 1000; - sinePeriod: 3000; - sineDuration: 1000; - sineShift: 1500; - arcMin: 0.0625; - arcMax: 0.75; -} - -FlatButton { - color: color; - overColor: color; - - bgColor: color; - overBgColor: color; - - width: pixels; - height: pixels; - - textTop: pixels; - - font: font; - overFont: font; - - ripple: RippleAnimation; -} - -RoundButton { - textFg: color; - textFgOver: color; - textBg: color; // rect of textBg with rounded rect of textBgOver upon it - textBgOver: color; - - numbersTextFg: color; - numbersTextFgOver: color; - numbersSkip: pixels; - - width: pixels; - height: pixels; - padding: margins; - - textTop: pixels; - - icon: icon; - iconPosition: point; - - font: font; - - ripple: RippleAnimation; -} - -Toggle { - toggledBg: color; - toggledFg: color; - untoggledBg: color; - untoggledFg: color; - duration: int; - border: pixels; - shift: pixels; - diameter: pixels; - width: pixels; - xsize: pixels; - vsize: pixels; - vshift: pixels; - stroke: pixels; - lockIcon: icon; - rippleAreaPadding: pixels; -} - -Check { - bg: color; - untoggledFg: color; - toggledFg: color; - diameter: pixels; - thickness: pixels; - icon: icon; - duration: int; - rippleAreaPadding: pixels; -} - -Radio { - bg: color; - untoggledFg: color; - toggledFg: color; - diameter: pixels; - thickness: pixels; - skip: pixels; - duration: int; - rippleAreaPadding: pixels; -} - -Checkbox { - textFg: color; - textFgActive: color; - - width: pixels; - margin: margins; - - textPosition: point; - checkPosition: point; - - style: TextStyle; - - rippleAreaPosition: point; - rippleBg: color; - rippleBgActive: color; - ripple: RippleAnimation; - - disabledOpacity: double; -} - -ScrollArea { - bg: color; - bgOver: color; - barBg: color; - barBgOver: color; - - round: pixels; - - width: pixels; - minHeight: pixels; - deltax: pixels; - deltat: pixels; - deltab: pixels; - - topsh: pixels; - bottomsh: pixels; - shColor: color; - - duration: int; - hiding: int; -} - -FlatInput { - textColor: color; - bgColor: color; - bgActive: color; - width: pixels; - height: pixels; - textMrg: margins; - align: align; - font: font; - - icon: icon; - - borderWidth: pixels; - borderColor: color; - borderActive: color; - - phColor: color; - phFocusColor: color; - phPos: point; - phAlign: align; - phShift: pixels; - phDuration: int; -} - -InputField { - textBg: color; - textFg: color; - textMargins: margins; - textAlign: align; - - placeholderFg: color; - placeholderFgActive: color; - placeholderFgError: color; - placeholderMargins: margins; - placeholderAlign: align; - placeholderScale: double; - placeholderShift: pixels; - placeholderFont: font; - - duration: int; - - borderFg: color; - borderFgActive: color; - borderFgError: color; - - border: pixels; - borderActive: pixels; - - font: font; - - width: pixels; - heightMin: pixels; - heightMax: pixels; -} - -OutlineButton { - textBg: color; - textBgOver: color; - - textFg: color; - textFgOver: color; - - font: font; - padding: margins; - - ripple: RippleAnimation; -} - -IconButton { - width: pixels; - height: pixels; - - icon: icon; - iconOver: icon; - iconPosition: point; - - duration: int; - - rippleAreaPosition: point; - rippleAreaSize: pixels; - ripple: RippleAnimation; -} - -Shadow { - left: icon; - topLeft: icon; - top: icon; - topRight: icon; - right: icon; - bottomRight: icon; - bottom: icon; - bottomLeft: icon; - extend: margins; - fallback: color; -} - -MediaSlider { - width: pixels; - activeFg: color; - inactiveFg: color; - activeFgOver: color; - inactiveFgOver: color; - activeFgDisabled: color; - inactiveFgDisabled: color; - receivedTillFg: color; - seekSize: size; - duration: int; -} - -FilledSlider { - fullWidth: pixels; - lineWidth: pixels; - activeFg: color; - inactiveFg: color; - disabledFg: color; - duration: int; -} - -RoundCheckbox { - border: color; - bgInactive: color; - bgActive: color; - width: pixels; - size: pixels; - sizeSmall: double; - duration: int; - bgDuration: double; - fgDuration: double; - check: icon; -} - -RoundImageCheckbox { - imageRadius: pixels; - imageSmallRadius: pixels; - selectWidth: pixels; - selectFg: color; - selectDuration: int; - 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; - loadingPeriod: int; - - ripple: RippleAnimation; -} - -MultiSelectItem { - padding: margins; - maxWidth: pixels; - height: pixels; - style: TextStyle; - textBg: color; - textFg: color; - textActiveBg: color; - textActiveFg: color; - duration: int; - deleteFg: color; - deleteCross: CrossAnimation; - minScale: double; -} - -MultiSelect { - bg: color; - padding: margins; - maxHeight: pixels; - scroll: ScrollArea; - - item: MultiSelectItem; - itemSkip: pixels; - - field: InputField; - fieldMinWidth: pixels; - fieldIcon: icon; - fieldIconSkip: pixels; - fieldCancel: CrossButton; - fieldCancelSkip: pixels; -} - -CallButton { - button: IconButton; - bg: color; - angle: double; - outerRadius: pixels; - outerBg: color; -} - -Menu { - skip: pixels; - - itemBg: color; - itemBgOver: color; - itemFg: color; - itemFgOver: color; - itemFgDisabled: color; - itemFgShortcut: color; - itemFgShortcutOver: color; - itemFgShortcutDisabled: color; - itemPadding: margins; - itemIconPosition: point; - itemStyle: TextStyle; - itemToggle: Toggle; - itemToggleOver: Toggle; - itemToggleShift: pixels; - - separatorPadding: margins; - separatorWidth: pixels; - separatorFg: color; - - arrow: icon; - - widthMin: pixels; - widthMax: pixels; - - ripple: RippleAnimation; -} - -PanelAnimation { - startWidth: double; - widthDuration: double; - startHeight: double; - heightDuration: double; - startOpacity: double; - opacityDuration: double; - startFadeTop: double; - fadeHeight: double; - fadeOpacity: double; - fadeBg: color; - shadow: Shadow; -} - -PopupMenu { - shadow: Shadow; - scrollPadding: margins; - animation: PanelAnimation; - - menu: Menu; - - duration: int; - showDuration: int; -} - -InnerDropdown { - padding: margins; - shadow: Shadow; - animation: PanelAnimation; - - duration: int; - showDuration: int; - width: pixels; - - bg: color; - scroll: ScrollArea; - scrollMargin: margins; - scrollPadding: margins; -} - -DropdownMenu { - wrap: InnerDropdown; - menu: Menu; -} - -Tooltip { - textBg: color; - textFg: color; - textStyle: TextStyle; - textBorder: color; - textPadding: margins; - - shift: point; - skip: pixels; - - widthMax: pixels; - linesMax: int; -} - -ImportantTooltip { - bg: color; - margin: margins; - padding: margins; - radius: pixels; - arrow: pixels; - arrowSkipMin: pixels; - arrowSkip: pixels; - shift: pixels; - duration: int; -} - -UserpicButton { - size: size; - photoSize: pixels; - photoPosition: point; - changeButton: RoundButton; - changeIcon: icon; - changeIconPosition: point; - duration: int; - uploadHeight: pixels; - uploadBg: color; - uploadIcon: icon; - uploadIconPosition: point; -} - -FeedUserpicButton { - size: size; - innerSize: pixels; - innerPosition: point; - innerPart: UserpicButton; -} - -InfoProfileButton { - textFg: color; - textFgOver: color; - textBg: color; - textBgOver: color; - - font: font; - - height: pixels; - padding: margins; - - toggle: Toggle; - toggleOver: Toggle; - toggleSkip: pixels; - - ripple: RippleAnimation; -} - -InfoProfileCountButton { - button: InfoProfileButton; - icon: icon; - iconPosition: point; - label: FlatLabel; - labelPosition: point; -} - -PassportScanRow { - padding: margins; - size: pixels; - textLeft: pixels; - nameTop: pixels; - statusTop: pixels; - border: pixels; - borderFg: color; - remove: IconButton; - restore: RoundButton; -} - -defaultLabelSimple: LabelSimple { - font: normalFont; - maxWidth: 0px; - textFg: windowFg; -} - -defaultFlatLabel: FlatLabel { - minWidth: 0px; - maxHeight: 0px; - align: align(left); - textFg: windowFg; - style: defaultTextStyle; - palette: defaultTextPalette; -} - -defaultLinkButton: LinkButton { - color: windowActiveTextFg; - overColor: windowActiveTextFg; - font: linkFont; - overFont: linkOverFont; -} - -defaultRippleAnimation: RippleAnimation { - color: windowBgRipple; - showDuration: 650; - hideDuration: 200; -} - -emptyRippleAnimation: RippleAnimation { -} - -defaultActiveButton: RoundButton { - textFg: activeButtonFg; - textFgOver: activeButtonFgOver; - numbersTextFg: activeButtonSecondaryFg; - numbersTextFgOver: activeButtonSecondaryFgOver; - textBg: activeButtonBg; - textBgOver: activeButtonBgOver; - - numbersSkip: 7px; - - width: -34px; - height: 34px; - padding: margins(0px, 0px, 0px, 0px); - - textTop: 8px; - - iconPosition: point(0px, 0px); - - font: semiboldFont; - - ripple: RippleAnimation(defaultRippleAnimation) { - color: activeButtonBgRipple; - } -} - -defaultLightButton: RoundButton(defaultActiveButton) { - textFg: lightButtonFg; - textFgOver: lightButtonFgOver; - numbersTextFg: lightButtonFg; - numbersTextFgOver: lightButtonFgOver; - textBg: lightButtonBg; - textBgOver: lightButtonBgOver; - - ripple: RippleAnimation(defaultRippleAnimation) { - color: lightButtonBgRipple; - } -} - -defaultScrollArea: ScrollArea { - bg: scrollBg; - bgOver: scrollBgOver; - barBg: scrollBarBg; - barBgOver: scrollBarBgOver; - - round: 2px; - - width: 10px; - minHeight: 20px; - deltax: 3px; - deltat: 3px; - deltab: 3px; - - topsh: 2px; - bottomsh: 2px; - shColor: shadowFg; - - duration: 150; - hiding: 1000; -} - -defaultSolidScroll: ScrollArea(defaultScrollArea) { - deltax: 5px; - width: 14px; - deltat: 6px; - deltab: 6px; - - topsh: 0px; - bottomsh: 0px; - - hiding: 0; -} - -defaultInputFont: font(17px); -defaultFlatInput: FlatInput { - textColor: windowFg; - bgColor: filterInputInactiveBg; - bgActive: filterInputActiveBg; - width: 210px; - height: 40px; - align: align(left); - textMrg: margins(5px, 5px, 5px, 5px); - font: defaultInputFont; - - borderWidth: 2px; - borderColor: filterInputInactiveBg; - borderActive: filterInputBorderFg; - - phColor: placeholderFg; - phFocusColor: placeholderFgActive; - phAlign: align(left); - phPos: point(2px, 0px); - phShift: 50px; - phDuration: 100; -} - -defaultInputField: InputField { - textBg: windowBg; - textFg: windowFg; - textMargins: margins(0px, 26px, 0px, 4px); - textAlign: align(topleft); - - placeholderFg: windowSubTextFg; - placeholderFgActive: windowActiveTextFg; - placeholderFgError: attentionButtonFg; - placeholderMargins: margins(0px, 0px, 0px, 0px); - placeholderAlign: align(topleft); - placeholderScale: 0.9; - placeholderShift: -20px; - placeholderFont: font(semibold 14px); - duration: 150; - - borderFg: inputBorderFg; - borderFgActive: activeLineFg; - borderFgError: activeLineFgError; - - border: 1px; - borderActive: 2px; - - font: boxTextFont; - - heightMin: 52px; - heightMax: 148px; -} - -defaultCheckboxIcon: icon {{ "default_checkbox_check", overviewCheckFgActive, point(4px, 7px) }}; - -defaultCheck: Check { - bg: transparent; - untoggledFg: checkboxFg; - toggledFg: windowBgActive; - diameter: 22px; - thickness: 2px; - icon: defaultCheckboxIcon; - duration: 120; - rippleAreaPadding: 8px; -} -defaultRadio: Radio { - bg: transparent; - untoggledFg: checkboxFg; - toggledFg: windowBgActive; - diameter: 22px; - thickness: 2px; - skip: 65px; // * 0.1 - duration: 120; - rippleAreaPadding: 8px; -} -defaultToggle: Toggle { - toggledBg: windowBg; - toggledFg: windowBgActive; - untoggledBg: windowBg; - untoggledFg: checkboxFg; - duration: 120; - border: 2px; - shift: 1px; - diameter: 16px; - width: 14px; - xsize: 0px; - vsize: 0px; - vshift: 0px; - stroke: 0px; - rippleAreaPadding: 8px; -} -defaultCheckbox: Checkbox { - textFg: windowFg; - - width: -44px; - margin: margins(8px, 8px, 8px, 8px); - - textPosition: point(10px, 2px); - checkPosition: point(8px, 8px); - - style: defaultTextStyle; - - rippleAreaPosition: point(-8px, -8px); - rippleBg: windowBgOver; - rippleBgActive: lightButtonBgOver; - ripple: defaultRippleAnimation; - - disabledOpacity: 0.5; -} - -defaultIconButton: IconButton { - iconPosition: point(-1px, -1px); -} - -defaultMultiSelectItem: MultiSelectItem { - padding: margins(6px, 7px, 12px, 0px); - maxWidth: 128px; - height: 32px; - style: defaultTextStyle; - textBg: contactsBgOver; - textFg: windowFg; - textActiveBg: activeButtonBg; - textActiveFg: activeButtonFg; - deleteFg: activeButtonFg; - deleteCross: CrossAnimation { - size: 32px; - skip: 10px; - stroke: 2px; - minScale: 0.3; - } - duration: 150; - minScale: 0.3; -} - -widgetSlideDuration: 200; -widgetFadeDuration: 200; - -fieldSearchIcon: icon {{ "box_search", menuIconFg, point(9px, 8px) }}; -boxFieldSearchIcon: icon {{ "box_search", menuIconFg, point(10px, 9px) }}; - -SettingsSlider { - height: pixels; - barTop: pixels; - barSkip: pixels; - barStroke: pixels; - barFg: color; - barFgActive: color; - labelTop: pixels; - labelFont: font; - labelFg: color; - labelFgActive: color; - duration: int; - rippleBottomSkip: pixels; - rippleBg: color; - rippleBgActive: color; - ripple: RippleAnimation; -} - -defaultSettingsSlider: SettingsSlider { - height: 39px; - barTop: 5px; - barSkip: 3px; - barStroke: 3px; - barFg: sliderBgInactive; - barFgActive: sliderBgActive; - labelTop: 17px; - labelFont: normalFont; - labelFg: windowActiveTextFg; - labelFgActive: windowActiveTextFg; - duration: 150; -} - -defaultTabsSlider: SettingsSlider(defaultSettingsSlider) { - height: 53px; - barTop: 50px; - barSkip: 0px; - barFg: transparent; - labelTop: 19px; - labelFont: semiboldFont; - labelFg: windowSubTextFg; - labelFgActive: lightButtonFg; - rippleBottomSkip: 1px; - rippleBg: windowBgOver; - rippleBgActive: lightButtonBgOver; - ripple: defaultRippleAnimation; -} - -defaultRoundShadow: Shadow { - left: icon {{ "round_shadow_left", windowShadowFg }}; - topLeft: icon {{ "round_shadow_top_left", windowShadowFg }}; - top: icon {{ "round_shadow_top", windowShadowFg }}; - topRight: icon {{ "round_shadow_top_left-flip_horizontal", windowShadowFg }}; - right: icon {{ "round_shadow_left-flip_horizontal", windowShadowFg }}; - bottomRight: icon {{ "round_shadow_bottom_left-flip_horizontal", windowShadowFg }}; - bottom: icon {{ "round_shadow_bottom", windowShadowFg }}; - bottomLeft: icon {{ "round_shadow_bottom_left", windowShadowFg }}; - extend: margins(3px, 2px, 3px, 4px); - fallback: windowShadowFgFallback; -} -defaultEmptyShadow: Shadow { - fallback: windowBg; -} - -defaultPanelAnimation: PanelAnimation { - startWidth: 0.5; - widthDuration: 0.6; - startHeight: 0.3; - heightDuration: 0.9; - startOpacity: 0.2; - opacityDuration: 0.3; - startFadeTop: 0.; - fadeHeight: 0.2; - fadeOpacity: 1.0; - fadeBg: menuBg; - shadow: defaultRoundShadow; -} - -defaultContinuousSlider: MediaSlider { - width: 3px; - activeFg: mediaPlayerActiveFg; - inactiveFg: mediaPlayerInactiveFg; - activeFgOver: mediaPlayerActiveFg; - inactiveFgOver: mediaPlayerInactiveFg; - activeFgDisabled: mediaPlayerInactiveFg; - inactiveFgDisabled: windowBg; - receivedTillFg: mediaPlayerInactiveFg; - seekSize: size(9px, 9px); - duration: 150; -} - -defaultRoundCheckbox: RoundCheckbox { - border: windowBg; - bgActive: windowBgActive; - width: 2px; - duration: 160; - bgDuration: 0.75; - fgDuration: 1.; -} - -defaultMenuArrow: icon {{ "dropdown_submenu_arrow", menuSubmenuArrowFg }}; -defaultMenuToggle: Toggle(defaultToggle) { - untoggledFg: menuIconFg; -} -defaultMenuToggleOver: Toggle(defaultToggle) { - untoggledFg: menuIconFgOver; -} -defaultMenu: Menu { - skip: 0px; - - itemBg: windowBg; - itemBgOver: windowBgOver; - itemFg: windowFg; - itemFgOver: windowFgOver; - itemFgDisabled: menuFgDisabled; - itemFgShortcut: windowSubTextFg; - itemFgShortcutOver: windowSubTextFgOver; - itemFgShortcutDisabled: menuFgDisabled; - itemIconPosition: point(0px, 0px); - itemPadding: margins(17px, 8px, 17px, 7px); - itemStyle: defaultTextStyle; - itemToggle: defaultMenuToggle; - itemToggleOver: defaultMenuToggleOver; - itemToggleShift: 0px; - - separatorPadding: margins(0px, 5px, 0px, 5px); - separatorWidth: 1px; - separatorFg: menuSeparatorFg; - - arrow: defaultMenuArrow; - - widthMin: 180px; - widthMax: 300px; - - ripple: defaultRippleAnimation; -} -defaultPopupMenu: PopupMenu { - shadow: defaultRoundShadow; - animation: defaultPanelAnimation; - - scrollPadding: margins(0px, 8px, 0px, 8px); - - menu: defaultMenu; - - duration: 150; - showDuration: 200; -} -defaultInnerDropdown: InnerDropdown { - padding: margins(10px, 10px, 10px, 10px); - shadow: defaultRoundShadow; - animation: defaultPanelAnimation; - - duration: 150; - showDuration: 200; - - bg: menuBg; - scroll: defaultSolidScroll; -} -defaultDropdownMenu: DropdownMenu { - wrap: InnerDropdown(defaultInnerDropdown) { - scrollPadding: margins(0px, 8px, 0px, 8px); - } - menu: defaultMenu; -} - -defaultTooltip: Tooltip { - textBg: tooltipBg; - textFg: tooltipFg; - textStyle: defaultTextStyle; - textBorder: tooltipBorderFg; - textPadding: margins(5px, 2px, 5px, 2px); - - shift: point(-20px, 20px); - skip: 10px; - - widthMax: 800px; - linesMax: 12; -} - -defaultImportantTooltip: ImportantTooltip { - bg: importantTooltipBg; - margin: margins(4px, 4px, 4px, 4px); - padding: margins(13px, 9px, 13px, 10px); - radius: 6px; - arrow: 9px; - arrowSkipMin: 24px; - arrowSkip: 66px; - shift: 12px; - duration: 200; -} - -defaultImportantTooltipLabel: FlatLabel(defaultFlatLabel) { - style: TextStyle(defaultTextStyle) { - font: font(14px); - linkFont: font(14px); - linkFontOver: font(14px underline); - } - textFg: importantTooltipFg; - palette: TextPalette(defaultTextPalette) { - linkFg: importantTooltipFgLink; - selectLinkFg: importantTooltipFgLink; - } -} - -defaultChangeUserpicIcon: icon {{ "new_chat_photo", activeButtonFg }}; -defaultUploadUserpicIcon: icon {{ "upload_chat_photo", msgDateImgFg }}; -defaultUserpicButton: UserpicButton { - size: size(76px, 76px); - photoSize: 76px; - photoPosition: point(-1px, -1px); - changeButton: defaultActiveButton; - changeIcon: defaultChangeUserpicIcon; - changeIconPosition: point(23px, 25px); - duration: 500; - uploadHeight: 24px; - uploadBg: msgDateImgBgOver; - uploadIcon: defaultUploadUserpicIcon; - uploadIconPosition: point(-1px, 1px); -} -defaultFeedUserpicButton: FeedUserpicButton { - size: size(76px, 76px); - innerSize: 76px; - innerPosition: point(-1px, -1px); - innerPart: defaultUserpicButton; -} - -historyToDownBelow: icon { - { "history_down_shadow", historyToDownShadow }, - { "history_down_circle", historyToDownBg, point(4px, 4px) }, -}; -historyToDownBelowOver: icon { - { "history_down_shadow", historyToDownShadow }, - { "history_down_circle", historyToDownBgOver, point(4px, 4px) }, -}; -contactsAddIconBelow: icon { - { "history_down_shadow", historyToDownShadow }, - { "history_down_circle", activeButtonBg, point(4px, 4px) }, -}; -contactsAddIconBelowOver: icon { - { "history_down_shadow", historyToDownShadow }, - { "history_down_circle", activeButtonBgOver, point(4px, 4px) }, -}; - -BotKeyboardButton { - margin: pixels; - padding: pixels; - height: pixels; - textTop: pixels; - ripple: RippleAnimation; -} - -TwoIconButton { - width: pixels; - height: pixels; - - iconBelow: icon; - iconAbove: icon; - iconBelowOver: icon; - iconAboveOver: icon; - iconPosition: point; - - rippleAreaPosition: point; - rippleAreaSize: pixels; - ripple: RippleAnimation; -} - -historySendActionTypingDuration: 800; -historySendActionTypingHalfPeriod: 320; -historySendActionTypingDeltaTime: 150; -historySendActionTypingPosition: point(4px, -4px); -historySendActionTypingDelta: 6px; -historySendActionTypingLargeNumerator: 28px; -historySendActionTypingSmallNumerator: 16px; -historySendActionTypingDenominator: 12.; - -historySendActionRecordDuration: 500; -historySendActionRecordPosition: point(1px, -4px); -historySendActionRecordDelta: 4px; -historySendActionRecordStrokeNumerator: 16px; -historySendActionRecordDenominator: 8.; - -historySendActionUploadDuration: 500; -historySendActionUploadPosition: point(0px, -4px); -historySendActionUploadDelta: 5px; -historySendActionUploadStrokeNumerator: 16px; -historySendActionUploadSizeNumerator: 32px; -historySendActionUploadDenominator: 8.; - -MediaPlayerButton { - playPosition: point; - playOuter: size; - pausePosition: point; - pauseOuter: size; - pauseStroke: pixels; - cancelPosition: point; - cancelOuter: size; - cancelStroke: pixels; - - rippleAreaPosition: point; - rippleAreaSize: pixels; - ripple: RippleAnimation; -} - -PeerListItem { - left: pixels; - bottom: pixels; - height: pixels; - photoPosition: point; - namePosition: point; - nameStyle: TextStyle; - statusPosition: point; - photoSize: pixels; - maximalWidth: pixels; - - button: OutlineButton; - statusFg: color; - statusFgOver: color; - statusFgActive: color; -} - -PeerList { - padding: margins; - item: PeerListItem; -} - -defaultPeerListButton: OutlineButton { - textBg: windowBg; - textBgOver: windowBgOver; - - textFg: windowSubTextFg; - textFgOver: windowSubTextFgOver; - - font: normalFont; - padding: margins(11px, 5px, 11px, 5px); - - ripple: defaultRippleAnimation; -} - -defaultPeerListItem: PeerListItem { - height: 58px; - photoPosition: point(12px, 6px); - namePosition: point(68px, 11px); - nameStyle: TextStyle(defaultTextStyle) { - font: semiboldFont; - linkFont: semiboldFont; - linkFontOver: semiboldFont; - } - statusPosition: point(68px, 31px); - photoSize: 46px; - button: defaultPeerListButton; - statusFg: windowSubTextFg; - statusFgOver: windowSubTextFgOver; - statusFgActive: windowActiveTextFg; -} - -defaultPeerList: PeerList { - padding: margins(0px, 0px, 0px, 0px); - item: defaultPeerListItem; -} - -SearchFieldRow { - height: pixels; - padding: margins; - field: InputField; - fieldIcon: icon; - fieldIconSkip: pixels; - fieldCancel: CrossButton; - fieldCancelSkip: pixels; -} - -InfoTopBar { - height: pixels; - back: IconButton; - title: FlatLabel; - titlePosition: point; - bg: color; - mediaCancel: IconButton; - mediaActionsSkip: pixels; - mediaForward: IconButton; - mediaDelete: IconButton; - search: IconButton; - searchRow: SearchFieldRow; - highlightBg: color; - highlightDuration: int; -} - -LevelMeter { - height: pixels; - lineWidth: pixels; - lineSpacing: pixels; - lineCount: int; - activeFg: color; - inactiveFg: color; -} - -defaultLevelMeter: LevelMeter { - height: 18px; - lineWidth: 3px; - lineSpacing: 5px; - lineCount: 44; - activeFg: mediaPlayerActiveFg; - inactiveFg: mediaPlayerInactiveFg; -} diff --git a/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp b/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp deleted file mode 100644 index e9e6f094a..000000000 --- a/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* -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 "ui/wrap/fade_wrap.h" - -#include "ui/widgets/shadow.h" -#include "ui/painter.h" -#include "styles/palette.h" - -namespace Ui { - -FadeWrap::FadeWrap( - QWidget *parent, - object_ptr &&child, - float64 scale) -: Parent(parent, std::move(child)) -, _animation(this, scale) -, _duration(st::fadeWrapDuration) { - if (auto weak = wrapped()) { - weak->show(); - } -} - -FadeWrap *FadeWrap::setDuration(int duration) { - _duration = duration; - return this; -} - -FadeWrap *FadeWrap::toggle( - bool shown, - anim::type animated) { - auto changed = (shown != _animation.visible()); - if (shown) { - if (animated == anim::type::normal) { - if (!_animation.animating()) { - wrapped()->show(); - } - _animation.fadeIn(_duration); - if (_animation.animating()) { - wrapped()->hide(); - } - } else { - _animation.show(); - if (!_animation.animating()) { - wrapped()->show(); - } - } - } else { - if (animated == anim::type::normal) { - if (!_animation.animating()) { - wrapped()->show(); - } - _animation.fadeOut(_duration); - if (_animation.animating()) { - wrapped()->hide(); - } - } else { - _animation.hide(); - if (!_animation.animating()) { - wrapped()->show(); - } - } - } - if (changed) { - _toggledChanged.fire_copy(shown); - } - return this; -} - -FadeWrap *FadeWrap::finishAnimating() { - _animation.finish(); - wrapped()->show(); - return this; -} - -FadeWrap *FadeWrap::toggleOn( - rpl::producer &&shown) { - std::move( - shown - ) | rpl::start_with_next([this](bool shown) { - toggle(shown, anim::type::normal); - }, lifetime()); - finishAnimating(); - return this; -} - -void FadeWrap::paintEvent(QPaintEvent *e) { - Painter p(this); - if (_animation.paint(p)) { - if (!_animation.animating() && _animation.visible()) { - crl::on_main(this, [=] { - if (!_animation.animating() && _animation.visible()) { - wrapped()->show(); - } - }); - } - return; - } - if (!_animation.animating()) { - wrapped()->show(); - } -} - -FadeShadow::FadeShadow(QWidget *parent) -: FadeShadow(parent, st::shadowFg) { -} - -FadeShadow::FadeShadow(QWidget *parent, style::color color) -: Parent(parent, object_ptr(parent, color)) { - hide(anim::type::instant); -} - -} // namespace Ui - diff --git a/Telegram/SourceFiles/ui/wrap/fade_wrap.h b/Telegram/SourceFiles/ui/wrap/fade_wrap.h deleted file mode 100644 index 13e8e7486..000000000 --- a/Telegram/SourceFiles/ui/wrap/fade_wrap.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/wrap/wrap.h" -#include "ui/effects/fade_animation.h" - -namespace Ui { - -template -class FadeWrapScaled; - -template -class FadeWrap; - -template <> -class FadeWrap : public Wrap { - using Parent = Wrap; - -public: - FadeWrap( - QWidget *parent, - object_ptr &&child, - float64 scale = 1.); - - FadeWrap *setDuration(int duration); - FadeWrap *toggle(bool shown, anim::type animated); - FadeWrap *show(anim::type animated) { - return toggle(true, animated); - } - FadeWrap *hide(anim::type animated) { - return toggle(false, animated); - } - FadeWrap *finishAnimating(); - FadeWrap *toggleOn(rpl::producer &&shown); - - bool animating() const { - return _animation.animating(); - } - bool toggled() const { - return _animation.visible(); - } - auto toggledValue() const { - return _toggledChanged.events_starting_with( - _animation.visible()); - } - -protected: - void paintEvent(QPaintEvent *e) final override; - -private: - rpl::event_stream _toggledChanged; - FadeAnimation _animation; - int _duration = 0; - -}; - -template -class FadeWrap : public Wrap> { - using Parent = Wrap>; - -public: - FadeWrap( - QWidget *parent, - object_ptr &&child, - float64 scale = 1.) - : Parent(parent, std::move(child), scale) { - } - - FadeWrap *setDuration(int duration) { - return chain(Parent::setDuration(duration)); - } - FadeWrap *toggle(bool shown, anim::type animated) { - return chain(Parent::toggle(shown, animated)); - } - FadeWrap *show(anim::type animated) { - return chain(Parent::show(animated)); - } - FadeWrap *hide(anim::type animated) { - return chain(Parent::hide(animated)); - } - FadeWrap *finishAnimating() { - return chain(Parent::finishAnimating()); - } - FadeWrap *toggleOn(rpl::producer &&shown) { - return chain(Parent::toggleOn(std::move(shown))); - } - -private: - FadeWrap *chain(FadeWrap *result) { - return static_cast(result); - } - -}; - -template -class FadeWrapScaled : public FadeWrap { - using Parent = FadeWrap; - -public: - FadeWrapScaled(QWidget *parent, object_ptr &&child) - : Parent(parent, std::move(child), 0.) { - } - -}; - -class PlainShadow; -class FadeShadow : public FadeWrap { - using Parent = FadeWrap; - -public: - FadeShadow(QWidget *parent); - FadeShadow(QWidget *parent, style::color color); - -}; - -} // namespace Ui - diff --git a/Telegram/SourceFiles/ui/wrap/padding_wrap.cpp b/Telegram/SourceFiles/ui/wrap/padding_wrap.cpp deleted file mode 100644 index 33ec9ba23..000000000 --- a/Telegram/SourceFiles/ui/wrap/padding_wrap.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 "ui/wrap/padding_wrap.h" - -namespace Ui { - -PaddingWrap::PaddingWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding) -: Parent(parent, std::move(child)) { - setPadding(padding); -} - -void PaddingWrap::setPadding(const style::margins &padding) { - if (_padding != padding) { - auto oldWidth = width() - _padding.left() - _padding.top(); - _padding = padding; - - if (auto weak = wrapped()) { - wrappedSizeUpdated(weak->size()); - - auto margins = weak->getMargins(); - weak->moveToLeft( - _padding.left() + margins.left(), - _padding.top() + margins.top()); - } else { - resize(QSize( - _padding.left() + oldWidth + _padding.right(), - _padding.top() + _padding.bottom())); - } - } -} - -void PaddingWrap::wrappedSizeUpdated(QSize size) { - resize(QRect(QPoint(), size).marginsAdded(_padding).size()); -} - -int PaddingWrap::naturalWidth() const { - auto inner = [this] { - if (auto weak = wrapped()) { - return weak->naturalWidth(); - } - return RpWidget::naturalWidth(); - }(); - return (inner < 0) - ? inner - : (_padding.left() + inner + _padding.right()); -} - -int PaddingWrap::resizeGetHeight(int newWidth) { - if (auto weak = wrapped()) { - weak->resizeToWidth(newWidth - - _padding.left() - - _padding.right()); - SendPendingMoveResizeEvents(weak); - } else { - resize(QSize( - _padding.left() + newWidth + _padding.right(), - _padding.top() + _padding.bottom())); - } - return heightNoMargins(); -} - -CenterWrap::CenterWrap( - QWidget *parent, - object_ptr &&child) -: Parent(parent, std::move(child)) { - if (const auto weak = wrapped()) { - wrappedSizeUpdated(weak->size()); - } -} - -int CenterWrap::naturalWidth() const { - return -1; -} - -int CenterWrap::resizeGetHeight(int newWidth) { - updateWrappedPosition(newWidth); - return heightNoMargins(); -} - -void CenterWrap::wrappedSizeUpdated(QSize size) { - updateWrappedPosition(width()); -} - -void CenterWrap::updateWrappedPosition(int forWidth) { - if (const auto weak = wrapped()) { - const auto margins = weak->getMargins(); - weak->moveToLeft( - (forWidth - weak->width()) / 2 + margins.left(), - margins.top()); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/wrap/padding_wrap.h b/Telegram/SourceFiles/ui/wrap/padding_wrap.h deleted file mode 100644 index d13d3477e..000000000 --- a/Telegram/SourceFiles/ui/wrap/padding_wrap.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/wrap/wrap.h" - -namespace Ui { - -template -class PaddingWrap; - -template <> -class PaddingWrap : public Wrap { - using Parent = Wrap; - -public: - PaddingWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding); - - style::margins padding() const { - return _padding; - } - void setPadding(const style::margins &padding); - - int naturalWidth() const override; - -protected: - int resizeGetHeight(int newWidth) override; - void wrappedSizeUpdated(QSize size) override; - -private: - style::margins _padding; - -}; - -template -class PaddingWrap : public Wrap> { - using Parent = Wrap>; - -public: - PaddingWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding) - : Parent(parent, std::move(child), padding) { - } - -}; - -template -class CenterWrap; - -template <> -class CenterWrap : public Wrap { - using Parent = Wrap; - -public: - CenterWrap( - QWidget *parent, - object_ptr &&child); - - int naturalWidth() const override; - -protected: - int resizeGetHeight(int newWidth) override; - void wrappedSizeUpdated(QSize size) override; - -private: - void updateWrappedPosition(int forWidth); - -}; - -template -class CenterWrap : public Wrap> { - using Parent = Wrap>; - -public: - CenterWrap( - QWidget *parent, - object_ptr &&child) - : Parent(parent, std::move(child)) { - } - -}; - -class FixedHeightWidget : public RpWidget { -public: - explicit FixedHeightWidget(QWidget *parent, int height = 0) - : RpWidget(parent) { - resize(width(), height); - } - -}; - -inline object_ptr CreateSkipWidget( - QWidget *parent, - int skip) { - return object_ptr( - parent, - skip); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp b/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp deleted file mode 100644 index fe99f0f75..000000000 --- a/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* -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 "ui/wrap/slide_wrap.h" - -#include "styles/style_basic.h" - -#include -#include - -namespace Ui { - -SlideWrap::SlideWrap( - QWidget *parent, - object_ptr &&child) -: SlideWrap( - parent, - std::move(child), - style::margins()) { -} - -SlideWrap::SlideWrap( - QWidget *parent, - const style::margins &padding) -: SlideWrap(parent, nullptr, padding) { -} - -SlideWrap::SlideWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding) -: Parent( - parent, - object_ptr>( - parent, - std::move(child), - padding)) -, _duration(st::slideWrapDuration) { -} - -SlideWrap *SlideWrap::setDuration(int duration) { - _duration = duration; - return this; -} - -SlideWrap *SlideWrap::toggle( - bool shown, - anim::type animated) { - auto animate = (animated == anim::type::normal) && _duration; - auto changed = (_toggled != shown); - if (changed) { - _toggled = shown; - if (animate) { - _animation.start( - [this] { animationStep(); }, - _toggled ? 0. : 1., - _toggled ? 1. : 0., - _duration, - anim::linear); - } - } - if (animate) { - animationStep(); - } else { - finishAnimating(); - } - if (changed) { - _toggledChanged.fire_copy(_toggled); - } - return this; -} - -SlideWrap *SlideWrap::finishAnimating() { - _animation.stop(); - animationStep(); - return this; -} - -SlideWrap *SlideWrap::toggleOn( - rpl::producer &&shown) { - std::move( - shown - ) | rpl::start_with_next([this](bool shown) { - toggle(shown, anim::type::normal); - }, lifetime()); - finishAnimating(); - return this; -} - -void SlideWrap::animationStep() { - auto newWidth = width(); - if (auto weak = wrapped()) { - auto margins = getMargins(); - weak->moveToLeft(margins.left(), margins.top()); - newWidth = weak->width(); - } - auto current = _animation.value(_toggled ? 1. : 0.); - auto newHeight = wrapped() - ? (_animation.animating() - ? anim::interpolate(0, wrapped()->heightNoMargins(), current) - : (_toggled ? wrapped()->height() : 0)) - : 0; - if (newWidth != width() || newHeight != height()) { - resize(newWidth, newHeight); - } - auto shouldBeHidden = !_toggled && !_animation.animating(); - if (shouldBeHidden != isHidden()) { - const auto guard = MakeWeak(this); - setVisible(!shouldBeHidden); - if (shouldBeHidden && guard) { - SendPendingMoveResizeEvents(this); - } - } -} - -QMargins SlideWrap::getMargins() const { - auto result = wrapped()->getMargins(); - return (animating() || !_toggled) - ? QMargins(result.left(), 0, result.right(), 0) - : result; -} - -int SlideWrap::resizeGetHeight(int newWidth) { - if (wrapped()) { - wrapped()->resizeToWidth(newWidth); - } - return heightNoMargins(); -} - -void SlideWrap::wrappedSizeUpdated(QSize size) { - if (_animation.animating()) { - animationStep(); - } else if (_toggled) { - resize(size); - } -} - -rpl::producer MultiSlideTracker::atLeastOneShownValue() const { - auto shown = std::vector>(); - shown.reserve(_widgets.size()); - for (auto &widget : _widgets) { - shown.push_back(widget->toggledValue()); - } - return rpl::combine( - std::move(shown), - [](const std::vector &values) { - return ranges::find(values, true) != values.end(); - }); -} - -} // namespace Ui - diff --git a/Telegram/SourceFiles/ui/wrap/slide_wrap.h b/Telegram/SourceFiles/ui/wrap/slide_wrap.h deleted file mode 100644 index f95bb0f51..000000000 --- a/Telegram/SourceFiles/ui/wrap/slide_wrap.h +++ /dev/null @@ -1,142 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/effects/animations.h" -#include "ui/wrap/padding_wrap.h" - -namespace Ui { - -template -class SlideWrap; - -template <> -class SlideWrap : public Wrap> { - using Parent = Wrap>; - -public: - SlideWrap( - QWidget *parent, - object_ptr &&child); - SlideWrap( - QWidget *parent, - const style::margins &padding); - SlideWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding); - - SlideWrap *setDuration(int duration); - SlideWrap *toggle(bool shown, anim::type animated); - SlideWrap *show(anim::type animated) { - return toggle(true, animated); - } - SlideWrap *hide(anim::type animated) { - return toggle(false, animated); - } - SlideWrap *finishAnimating(); - SlideWrap *toggleOn(rpl::producer &&shown); - - bool animating() const { - return _animation.animating(); - } - bool toggled() const { - return _toggled; - } - auto toggledValue() const { - return _toggledChanged.events_starting_with_copy(_toggled); - } - - QMargins getMargins() const override; - -protected: - int resizeGetHeight(int newWidth) override; - void wrappedSizeUpdated(QSize size) override; - -private: - void animationStep(); - - bool _toggled = true; - rpl::event_stream _toggledChanged; - Animations::Simple _animation; - int _duration = 0; - -}; - -template -class SlideWrap : public Wrap, SlideWrap> { - using Parent = Wrap, SlideWrap>; - -public: - SlideWrap( - QWidget *parent, - object_ptr &&child) - : Parent(parent, std::move(child)) { - } - SlideWrap( - QWidget *parent, - const style::margins &padding) - : Parent(parent, padding) { - } - SlideWrap( - QWidget *parent, - object_ptr &&child, - const style::margins &padding) - : Parent(parent, std::move(child), padding) { - } - - SlideWrap *setDuration(int duration) { - return chain(Parent::setDuration(duration)); - } - SlideWrap *toggle(bool shown, anim::type animated) { - return chain(Parent::toggle(shown, animated)); - } - SlideWrap *show(anim::type animated) { - return chain(Parent::show(animated)); - } - SlideWrap *hide(anim::type animated) { - return chain(Parent::hide(animated)); - } - SlideWrap *finishAnimating() { - return chain(Parent::finishAnimating()); - } - SlideWrap *toggleOn(rpl::producer &&shown) { - return chain(Parent::toggleOn(std::move(shown))); - } - -private: - SlideWrap *chain(SlideWrap *result) { - return static_cast(result); - } - -}; - -inline object_ptr> CreateSlideSkipWidget( - QWidget *parent, - int skip) { - return object_ptr>( - parent, - QMargins(0, 0, 0, skip)); -} - -class MultiSlideTracker { -public: - template - void track(const SlideWrap *wrap) { - _widgets.push_back(wrap); - } - - rpl::producer atLeastOneShownValue() const; - -private: - std::vector*> _widgets; - -}; - -} // namespace Ui - diff --git a/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp b/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp deleted file mode 100644 index 2f946d46c..000000000 --- a/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* -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 "ui/wrap/vertical_layout.h" - -#include "ui/ui_utility.h" - -namespace Ui { - -QMargins VerticalLayout::getMargins() const { - auto result = QMargins(); - if (!_rows.empty()) { - auto &top = _rows.front(); - auto topMargin = top.widget->getMargins().top(); - result.setTop( - qMax(topMargin - top.margin.top(), 0)); - auto &bottom = _rows.back(); - auto bottomMargin = bottom.widget->getMargins().bottom(); - result.setBottom( - qMax(bottomMargin - bottom.margin.bottom(), 0)); - for (auto &row : _rows) { - auto margins = row.widget->getMargins(); - result.setLeft(qMax( - margins.left() - row.margin.left(), - result.left())); - result.setRight(qMax( - margins.right() - row.margin.right(), - result.right())); - } - } - return result; -} - -int VerticalLayout::naturalWidth() const { - auto result = 0; - for (auto &row : _rows) { - const auto natural = row.widget->naturalWidth(); - if (natural < 0) { - return natural; - } - accumulate_max( - result, - row.margin.left() + natural + row.margin.right()); - } - return result; -} - -int VerticalLayout::resizeGetHeight(int newWidth) { - _inResize = true; - auto guard = gsl::finally([&] { _inResize = false; }); - - auto margins = getMargins(); - auto result = 0; - for (auto &row : _rows) { - updateChildGeometry( - margins, - row.widget, - row.margin, - newWidth, - result); - result += row.margin.top() - + row.widget->heightNoMargins() - + row.margin.bottom(); - } - return result; -} - -void VerticalLayout::visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) { - for (auto &row : _rows) { - setChildVisibleTopBottom( - row.widget, - visibleTop, - visibleBottom); - } -} - -void VerticalLayout::updateChildGeometry( - const style::margins &margins, - RpWidget *child, - const style::margins &margin, - int width, - int top) const { - auto availRowWidth = width - - margin.left() - - margin.right(); - child->resizeToNaturalWidth(availRowWidth); - child->moveToLeft( - margins.left() + margin.left(), - margins.top() + margin.top() + top, - width); -} - -RpWidget *VerticalLayout::insertChild( - int atPosition, - object_ptr child, - const style::margins &margin) { - Expects(atPosition >= 0 && atPosition <= _rows.size()); - - if (const auto weak = AttachParentChild(this, child)) { - _rows.insert( - begin(_rows) + atPosition, - { std::move(child), margin }); - const auto margins = getMargins(); - updateChildGeometry( - margins, - weak, - margin, - width() - margins.left() - margins.right(), - height() - margins.top() - margins.bottom()); - weak->heightValue( - ) | rpl::start_with_next_done([=] { - if (!_inResize) { - childHeightUpdated(weak); - } - }, [=] { - removeChild(weak); - }, lifetime()); - return weak; - } - return nullptr; -} - -void VerticalLayout::childHeightUpdated(RpWidget *child) { - auto it = ranges::find_if(_rows, [child](const Row &row) { - return (row.widget == child); - }); - - auto margins = getMargins(); - auto top = [&] { - if (it == _rows.begin()) { - return margins.top(); - } - auto prev = it - 1; - return prev->widget->bottomNoMargins() + prev->margin.bottom(); - }() - margins.top(); - for (auto end = _rows.end(); it != end; ++it) { - auto &row = *it; - auto margin = row.margin; - auto widget = row.widget.data(); - widget->moveToLeft( - margins.left() + margin.left(), - margins.top() + top + margin.top()); - top += margin.top() - + widget->heightNoMargins() - + margin.bottom(); - } - resize(width(), margins.top() + top + margins.bottom()); -} - -void VerticalLayout::removeChild(RpWidget *child) { - auto it = ranges::find_if(_rows, [child](const Row &row) { - return (row.widget == child); - }); - auto end = _rows.end(); - Assert(it != end); - - auto margins = getMargins(); - auto top = [&] { - if (it == _rows.begin()) { - return margins.top(); - } - auto prev = it - 1; - return prev->widget->bottomNoMargins() + prev->margin.bottom(); - }() - margins.top(); - for (auto next = it + 1; next != end; ++next) { - auto &row = *next; - auto margin = row.margin; - auto widget = row.widget.data(); - widget->moveToLeft( - margins.left() + margin.left(), - margins.top() + top + margin.top()); - top += margin.top() - + widget->heightNoMargins() - + margin.bottom(); - } - it->widget = nullptr; - _rows.erase(it); - - resize(width(), margins.top() + top + margins.bottom()); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/wrap/vertical_layout.h b/Telegram/SourceFiles/ui/wrap/vertical_layout.h deleted file mode 100644 index bdc247793..000000000 --- a/Telegram/SourceFiles/ui/wrap/vertical_layout.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "base/object_ptr.h" - -namespace Ui { - -class VerticalLayout : public RpWidget { -public: - using RpWidget::RpWidget; - - int count() const { - return _rows.size(); - } - - template < - typename Widget, - typename = std::enable_if_t< - std::is_base_of_v>> - Widget *insert( - int atPosition, - object_ptr &&child, - const style::margins &margin = style::margins()) { - return static_cast(insertChild( - atPosition, - std::move(child), - margin)); - } - - template < - typename Widget, - typename = std::enable_if_t< - std::is_base_of_v>> - Widget *add( - object_ptr &&child, - const style::margins &margin = style::margins()) { - return insert(count(), std::move(child), margin); - } - - QMargins getMargins() const override; - int naturalWidth() const override; - -protected: - int resizeGetHeight(int newWidth) override; - void visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) override; - -private: - RpWidget *insertChild( - int addPosition, - object_ptr child, - const style::margins &margin); - void childHeightUpdated(RpWidget *child); - void removeChild(RpWidget *child); - void updateChildGeometry( - const style::margins &margins, - RpWidget *child, - const style::margins &margin, - int width, - int top) const; - - struct Row { - object_ptr widget; - style::margins margin; - }; - std::vector _rows; - bool _inResize = false; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/wrap/wrap.h b/Telegram/SourceFiles/ui/wrap/wrap.h deleted file mode 100644 index d578c40a4..000000000 --- a/Telegram/SourceFiles/ui/wrap/wrap.h +++ /dev/null @@ -1,201 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/ui_utility.h" -#include "base/object_ptr.h" - -namespace Ui { - -template -class Wrap; - -namespace details { - -struct UnwrapHelper { - template < - typename Widget, - typename = typename std::decay_t::WrapParentType> - static std::true_type Is(Widget &&widget); - static std::false_type Is(...); - - template - static auto Unwrap(Entity *entity, std::true_type) { - return entity - ? entity->entity() - : nullptr; - } - template - static Entity *Unwrap(Entity *entity, std::false_type) { - return entity; - } - template - static auto Unwrap(Entity *entity) { - using Selector = decltype(Is(std::declval())); - return Unwrap( - entity, - Selector()); - } - -}; - -} // namespace details - -template -class Wrap : public RpWidget { -public: - Wrap(QWidget *parent, object_ptr &&child); - - Widget *wrapped() { - return _wrapped; - } - const Widget *wrapped() const { - return _wrapped; - } - auto entity() { - return details::UnwrapHelper::Unwrap(wrapped()); - } - auto entity() const { - return details::UnwrapHelper::Unwrap(wrapped()); - } - - QMargins getMargins() const override { - if (auto weak = wrapped()) { - return weak->getMargins(); - } - return RpWidget::getMargins(); - } - int naturalWidth() const override { - if (auto weak = wrapped()) { - return weak->naturalWidth(); - } - return RpWidget::naturalWidth(); - } - -protected: - int resizeGetHeight(int newWidth) override { - if (auto weak = wrapped()) { - weak->resizeToWidth(newWidth); - return weak->heightNoMargins(); - } - return heightNoMargins(); - } - void visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) override { - setChildVisibleTopBottom( - wrapped(), - visibleTop, - visibleBottom); - } - virtual void wrappedSizeUpdated(QSize size) { - resize(size); - } - -private: - object_ptr _wrapped; - -}; - -template -Wrap::Wrap( - QWidget *parent, - object_ptr &&child) -: RpWidget(parent) -, _wrapped(std::move(child)) { - if (_wrapped) { - _wrapped->sizeValue( - ) | rpl::start_with_next([this](const QSize &value) { - wrappedSizeUpdated(value); - }, lifetime()); - AttachParentChild(this, _wrapped); - _wrapped->move(0, 0); - _wrapped->alive( - ) | rpl::start_with_done([this] { - _wrapped->setParent(nullptr); - _wrapped = nullptr; - delete this; - }, lifetime()); - } -} - -template -class Wrap : public ParentType { -public: - using ParentType::ParentType; - - Widget *wrapped() { - return static_cast(ParentType::wrapped()); - } - const Widget *wrapped() const { - return static_cast(ParentType::wrapped()); - } - auto entity() { - return details::UnwrapHelper::Unwrap(wrapped()); - } - auto entity() const { - return details::UnwrapHelper::Unwrap(wrapped()); - } - - using WrapParentType = ParentType; - -}; - -class OverrideMargins : public Wrap { - using Parent = Wrap; - -public: - OverrideMargins( - QWidget *parent, - object_ptr &&child, - QMargins margins = QMargins()) - : Parent(parent, std::move(child)), _margins(margins) { - if (auto weak = wrapped()) { - auto margins = weak->getMargins(); - resizeToWidth(weak->width() - - margins.left() - - margins.right()); - } - } - - QMargins getMargins() const override { - return _margins; - } - -protected: - int resizeGetHeight(int newWidth) override { - if (auto weak = wrapped()) { - weak->resizeToWidth(newWidth); - weak->moveToLeft(_margins.left(), _margins.top()); - return weak->heightNoMargins(); - } - return height(); - } - -private: - void wrappedSizeUpdated(QSize size) override { - auto margins = wrapped()->getMargins(); - resize( - (size.width() - - margins.left() - - margins.right() - + _margins.left() - + _margins.right()), - (size.height() - - margins.top() - - margins.bottom() - + _margins.top() - + _margins.bottom())); - } - - QMargins _margins; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 0a2562d6b..583198ce4 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "platform/platform_window_title.h" -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "history/history.h" #include "window/themes/window_theme.h" #include "window/window_session_controller.h" diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 719ba2130..1a561099c 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -5,8 +5,7 @@ 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 */ - -using "basic.style"; +using "ui/basic.style"; using "ui/widgets/widgets.style"; using "history/history.style"; diff --git a/Telegram/SourceFiles/window/window_outdated_bar.cpp b/Telegram/SourceFiles/window/window_outdated_bar.cpp index a53ef766d..b585edf02 100644 --- a/Telegram/SourceFiles/window/window_outdated_bar.cpp +++ b/Telegram/SourceFiles/window/window_outdated_bar.cpp @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" // Ui::IconButton #include "ui/wrap/slide_wrap.h" // Ui::SlideWrap #include "ui/text/text_utilities.h" // Ui::Text::ToUpper -#include "platform/platform_info.h" +#include "base/platform/base_platform_info.h" #include "lang/lang_keys.h" #include "styles/style_window.h" diff --git a/Telegram/ThirdParty/codegen b/Telegram/ThirdParty/codegen new file mode 160000 index 000000000..1fd522333 --- /dev/null +++ b/Telegram/ThirdParty/codegen @@ -0,0 +1 @@ +Subproject commit 1fd52233329a60f3e6a0c389873dfffc1ce403c8 diff --git a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp deleted file mode 100644 index 4c05b8b77..000000000 --- a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* -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 "emoji_suggestions.h" - -#include -#include "emoji_suggestions_data.h" - -#ifndef Expects -#include -#define Expects(condition) assert(condition) -#endif // Expects - -namespace Ui { -namespace Emoji { -namespace internal { -namespace { - -checksum Crc32Table[256]; -class Crc32Initializer { -public: - Crc32Initializer() { - checksum poly = 0x04C11DB7U; - for (auto i = 0; i != 256; ++i) { - Crc32Table[i] = reflect(i, 8) << 24; - for (auto j = 0; j != 8; ++j) { - Crc32Table[i] = (Crc32Table[i] << 1) ^ (Crc32Table[i] & (1 << 31) ? poly : 0); - } - Crc32Table[i] = reflect(Crc32Table[i], 32); - } - } - -private: - checksum reflect(checksum val, char ch) { - checksum result = 0; - for (int i = 1; i < (ch + 1); ++i) { - if (val & 1) { - result |= 1 << (ch - i); - } - val >>= 1; - } - return result; - } - -}; - -} // namespace - -checksum countChecksum(const void *data, std::size_t size) { - static Crc32Initializer InitTable; - - auto buffer = static_cast(data); - auto result = checksum(0xFFFFFFFFU); - for (auto i = std::size_t(0); i != size; ++i) { - result = (result >> 8) ^ Crc32Table[(result & 0xFFU) ^ buffer[i]]; - } - return (result ^ 0xFFFFFFFFU); -} - -} // namespace internal - -namespace { - -class string_span { -public: - string_span() = default; - string_span(const utf16string *data, std::size_t size) : begin_(data), size_(size) { - } - string_span(const std::vector &data) : begin_(data.data()), size_(data.size()) { - } - string_span(const string_span &other) = default; - string_span &operator=(const string_span &other) = default; - - const utf16string *begin() const { - return begin_; - } - const utf16string *end() const { - return begin_ + size_; - } - std::size_t size() const { - return size_; - } - - string_span subspan(std::size_t offset, std::size_t size) { - return string_span(begin_ + offset, size); - } - -private: - const utf16string *begin_ = nullptr; - std::size_t size_ = 0; - -}; - -bool IsNumber(utf16char ch) { - return (ch >= '0' && ch <= '9'); -} - -bool IsLetterOrNumber(utf16char ch) { - return (ch >= 'a' && ch <= 'z') || IsNumber(ch); -} - -using Replacement = internal::Replacement; - -class Completer { -public: - Completer(utf16string query); - - std::vector resolve(); - -private: - struct Result { - const Replacement *replacement; - int wordsUsed; - }; - - static std::vector NormalizeQuery(utf16string query); - void addResult(const Replacement *replacement); - bool isDuplicateOfLastResult(const Replacement *replacement) const; - bool isBetterThanLastResult(const Replacement *replacement) const; - void processInitialList(); - void filterInitialList(); - void initWordsTracking(); - bool matchQueryForCurrentItem(); - bool matchQueryTailStartingFrom(int position); - string_span findWordsStartingWith(utf16char ch); - int findEqualCharsCount(int position, const utf16string *word); - std::vector prepareResult(); - bool startsWithQuery(utf16string word); - bool isExactMatch(utf16string replacement); - - std::vector _result; - - utf16string _initialQuery; - const std::vector _query; - const utf16char *_queryBegin = nullptr; - int _querySize = 0; - - const std::vector *_initialList = nullptr; - - string_span _currentItemWords; - int _currentItemWordsUsedCount = 0; - - class UsedWordGuard { - public: - UsedWordGuard(std::vector &map, int index); - UsedWordGuard(const UsedWordGuard &other) = delete; - UsedWordGuard(UsedWordGuard &&other); - UsedWordGuard &operator=(const UsedWordGuard &other) = delete; - UsedWordGuard &operator=(UsedWordGuard &&other) = delete; - explicit operator bool() const; - ~UsedWordGuard(); - - private: - std::vector &_map; - int _index = 0; - small _guarded = 0; - - }; - std::vector _currentItemWordsUsedMap; - -}; - -Completer::UsedWordGuard::UsedWordGuard(std::vector &map, int index) : _map(map), _index(index) { - Expects(_map.size() > _index); - if (!_map[_index]) { - _guarded = _map[_index] = 1; - } -} - -Completer::UsedWordGuard::UsedWordGuard(UsedWordGuard &&other) : _map(other._map), _index(other._index), _guarded(other._guarded) { - other._guarded = 0; -} - -Completer::UsedWordGuard::operator bool() const { - return _guarded; -} - -Completer::UsedWordGuard::~UsedWordGuard() { - if (_guarded) { - _map[_index] = 0; - } -} - -Completer::Completer(utf16string query) : _initialQuery(query), _query(NormalizeQuery(query)) { -} - -// Remove all non-letters-or-numbers. -// Leave '-' and '+' only if they're followed by a number or -// at the end of the query (so it is possibly followed by a number). -std::vector Completer::NormalizeQuery(utf16string query) { - auto result = std::vector(); - result.reserve(query.size()); - auto copyFrom = query.data(); - auto e = copyFrom + query.size(); - auto copyTo = result.data(); - for (auto i = query.data(); i != e; ++i) { - if (IsLetterOrNumber(*i)) { - continue; - } else if (*i == '-' || *i == '+') { - if (i + 1 == e || IsNumber(*(i + 1))) { - continue; - } - } - if (i > copyFrom) { - result.resize(result.size() + (i - copyFrom)); - memcpy(copyTo, copyFrom, (i - copyFrom) * sizeof(utf16char)); - copyTo += (i - copyFrom); - } - copyFrom = i + 1; - } - if (e > copyFrom) { - result.resize(result.size() + (e - copyFrom)); - memcpy(copyTo, copyFrom, (e - copyFrom) * sizeof(utf16char)); - copyTo += (e - copyFrom); - } - return result; -} - -std::vector Completer::resolve() { - _queryBegin = _query.data(); - _querySize = _query.size(); - if (!_querySize) { - return std::vector(); - } - _initialList = internal::GetReplacements(*_queryBegin); - if (!_initialList) { - return std::vector(); - } - _result.reserve(_initialList->size()); - processInitialList(); - return prepareResult(); -} - -bool Completer::isDuplicateOfLastResult(const Replacement *item) const { - if (_result.empty()) { - return false; - } - return (_result.back().replacement->emoji == item->emoji); -} - -bool Completer::isBetterThanLastResult(const Replacement *item) const { - Expects(!_result.empty()); - auto &last = _result.back(); - if (_currentItemWordsUsedCount < last.wordsUsed) { - return true; - } - - auto firstCharOfQuery = _query[0]; - auto firstCharAfterColonLast = last.replacement->replacement[1]; - auto firstCharAfterColonCurrent = item->replacement[1]; - auto goodLast = (firstCharAfterColonLast == firstCharOfQuery); - auto goodCurrent = (firstCharAfterColonCurrent == firstCharOfQuery); - return !goodLast && goodCurrent; -} - -void Completer::addResult(const Replacement *item) { - if (!isDuplicateOfLastResult(item)) { - _result.push_back({ item, _currentItemWordsUsedCount }); - } else if (isBetterThanLastResult(item)) { - _result.back() = { item, _currentItemWordsUsedCount }; - } -} - -void Completer::processInitialList() { - if (_querySize > 1) { - filterInitialList(); - } else { - _currentItemWordsUsedCount = 1; - for (auto item : *_initialList) { - addResult(item); - } - } -} - -void Completer::initWordsTracking() { - auto maxWordsCount = 0; - for (auto item : *_initialList) { - auto wordsCount = item->words.size(); - if (maxWordsCount < wordsCount) { - maxWordsCount = wordsCount; - } - } - _currentItemWordsUsedMap = std::vector(maxWordsCount, 0); -} - -void Completer::filterInitialList() { - initWordsTracking(); - for (auto item : *_initialList) { - _currentItemWords = string_span(item->words); - _currentItemWordsUsedCount = 1; - if (matchQueryForCurrentItem()) { - addResult(item); - } - _currentItemWordsUsedCount = 0; - } -} - -bool Completer::matchQueryForCurrentItem() { - Expects(_currentItemWords.size() != 0); - if (_currentItemWords.size() < 2) { - return startsWithQuery(*_currentItemWords.begin()); - } - return matchQueryTailStartingFrom(0); -} - -bool Completer::startsWithQuery(utf16string word) { - if (word.size() < _query.size()) { - return false; - } - for (auto i = std::size_t(0), size = _query.size(); i != size; ++i) { - if (word[i] != _query[i]) { - return false; - } - } - return true; -} - -bool Completer::isExactMatch(utf16string replacement) { - if (replacement.size() != _initialQuery.size() + 1) { - return false; - } - for (auto i = std::size_t(0), size = _initialQuery.size(); i != size; ++i) { - if (replacement[i] != _initialQuery[i]) { - return false; - } - } - return true; -} - -bool Completer::matchQueryTailStartingFrom(int position) { - auto charsLeftToMatch = (_querySize - position); - if (!charsLeftToMatch) { - return true; - } - - auto firstCharToMatch = *(_queryBegin + position); - auto foundWords = findWordsStartingWith(firstCharToMatch); - - for (auto word = foundWords.begin(), foundWordsEnd = word + foundWords.size(); word != foundWordsEnd; ++word) { - auto wordIndex = word - _currentItemWords.begin(); - if (auto guard = UsedWordGuard(_currentItemWordsUsedMap, wordIndex)) { - ++_currentItemWordsUsedCount; - auto equalCharsCount = findEqualCharsCount(position, word); - for (auto check = equalCharsCount; check != 0; --check) { - if (matchQueryTailStartingFrom(position + check)) { - return true; - } - } - --_currentItemWordsUsedCount; - } - } - return false; -} - -int Completer::findEqualCharsCount(int position, const utf16string *word) { - auto charsLeft = (_querySize - position); - auto wordBegin = word->data(); - auto wordSize = word->size(); - auto possibleEqualCharsCount = (charsLeft > wordSize ? wordSize : charsLeft); - for (auto equalTill = 1; equalTill != possibleEqualCharsCount; ++equalTill) { - auto wordCh = *(wordBegin + equalTill); - auto queryCh = *(_queryBegin + position + equalTill); - if (wordCh != queryCh) { - return equalTill; - } - } - return possibleEqualCharsCount; -} - -std::vector Completer::prepareResult() { - const auto firstCharOfQuery = _query[0]; - const auto reorder = [&](auto &&predicate) { - std::stable_partition( - std::begin(_result), - std::end(_result), - std::forward(predicate)); - }; - reorder([&](Result &result) { - const auto firstCharAfterColon = result.replacement->replacement[1]; - return (firstCharAfterColon == firstCharOfQuery); - }); - reorder([](Result &result) { - return (result.wordsUsed < 2); - }); - reorder([](Result &result) { - return (result.wordsUsed < 3); - }); - reorder([&](Result &result) { - return isExactMatch(result.replacement->replacement); - }); - - auto result = std::vector(); - result.reserve(_result.size()); - for (auto &item : _result) { - result.emplace_back( - item.replacement->emoji, - item.replacement->replacement, - item.replacement->replacement); - } - return result; -} - -string_span Completer::findWordsStartingWith(utf16char ch) { - auto begin = std::lower_bound( - std::begin(_currentItemWords), - std::end(_currentItemWords), - ch, - [](utf16string word, utf16char ch) { return word[0] < ch; }); - auto end = std::upper_bound( - std::begin(_currentItemWords), - std::end(_currentItemWords), - ch, - [](utf16char ch, utf16string word) { return ch < word[0]; }); - return _currentItemWords.subspan( - begin - _currentItemWords.begin(), - end - begin); -} - -} // namespace - -std::vector GetSuggestions(utf16string query) { - return Completer(query).resolve(); -} - -int GetSuggestionMaxLength() { - return internal::kReplacementMaxLength; -} - -} // namespace Emoji -} // namespace Ui diff --git a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.h b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.h deleted file mode 100644 index 0364d4492..000000000 --- a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -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 -*/ -#pragma once - -#include - -namespace Ui { -namespace Emoji { - -using small = unsigned char; -using medium = unsigned short; -using utf16char = unsigned short; - -static_assert(sizeof(utf16char) == 2, "Bad UTF-16 character size."); - -class utf16string { -public: - utf16string() = default; - utf16string(const utf16char *data, std::size_t size) : data_(data), size_(size) { - } - utf16string(const utf16string &other) = default; - utf16string &operator=(const utf16string &other) = default; - - const utf16char *data() const { - return data_; - } - std::size_t size() const { - return size_; - } - - utf16char operator[](int index) const { - return data_[index]; - } - -private: - const utf16char *data_ = nullptr; - std::size_t size_ = 0; - -}; - -inline bool operator==(utf16string a, utf16string b) { - return (a.size() == b.size()) && (!a.size() || !memcmp(a.data(), b.data(), a.size() * sizeof(utf16char))); -} - -namespace internal { - -using checksum = unsigned int; -checksum countChecksum(const void *data, std::size_t size); - -utf16string GetReplacementEmoji(utf16string replacement); - -} // namespace internal - -class Suggestion { -public: - Suggestion() = default; - Suggestion(utf16string emoji, utf16string label, utf16string replacement) : emoji_(emoji), label_(label), replacement_(replacement) { - } - Suggestion(const Suggestion &other) = default; - Suggestion &operator=(const Suggestion &other) = default; - - utf16string emoji() const { - return emoji_; - } - utf16string label() const { - return label_; - } - utf16string replacement() const { - return replacement_; - } - -private: - utf16string emoji_; - utf16string label_; - utf16string replacement_; - -}; - -std::vector GetSuggestions(utf16string query); - -inline utf16string GetSuggestionEmoji(utf16string replacement) { - return internal::GetReplacementEmoji(replacement); -} - -int GetSuggestionMaxLength(); - -} // namespace Emoji -} // namespace Ui diff --git a/Telegram/ThirdParty/gyp_helpers b/Telegram/ThirdParty/gyp_helpers index ac609eccb..df28126a0 160000 --- a/Telegram/ThirdParty/gyp_helpers +++ b/Telegram/ThirdParty/gyp_helpers @@ -1 +1 @@ -Subproject commit ac609eccb9de963b64821a9aacdc481eca12360f +Subproject commit df28126a0916edaa6b9c5d7a3ee2a0a71012308c diff --git a/Telegram/ThirdParty/lib_base b/Telegram/ThirdParty/lib_base index 7a5fe257d..349996d27 160000 --- a/Telegram/ThirdParty/lib_base +++ b/Telegram/ThirdParty/lib_base @@ -1 +1 @@ -Subproject commit 7a5fe257d9bffb813598913b5fc08f1725042099 +Subproject commit 349996d274cc3820a79757ba903803f075a5f61d diff --git a/Telegram/ThirdParty/lib_ui b/Telegram/ThirdParty/lib_ui new file mode 160000 index 000000000..9c711cca1 --- /dev/null +++ b/Telegram/ThirdParty/lib_ui @@ -0,0 +1 @@ +Subproject commit 9c711cca1a02fe8d3fce5976a3d8afc30f7eb7f4 diff --git a/Telegram/gyp/codegen.gyp b/Telegram/gyp/codegen.gyp deleted file mode 100644 index 244ff15a5..000000000 --- a/Telegram/gyp/codegen.gyp +++ /dev/null @@ -1,158 +0,0 @@ -# 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 - -{ - 'includes': [ - '../ThirdParty/gyp_helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'codegen_lang', - 'variables': { - 'src_loc': '../SourceFiles', - 'mac_target': '10.10', - }, - 'includes': [ - '../ThirdParty/gyp_helpers/common/executable.gypi', - '../ThirdParty/gyp_helpers/modules/qt.gypi', - ], - - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/codegen/common/basic_tokenized_file.cpp', - '<(src_loc)/codegen/common/basic_tokenized_file.h', - '<(src_loc)/codegen/common/checked_utf8_string.cpp', - '<(src_loc)/codegen/common/checked_utf8_string.h', - '<(src_loc)/codegen/common/clean_file.cpp', - '<(src_loc)/codegen/common/clean_file.h', - '<(src_loc)/codegen/common/clean_file_reader.h', - '<(src_loc)/codegen/common/const_utf8_string.h', - '<(src_loc)/codegen/common/cpp_file.cpp', - '<(src_loc)/codegen/common/cpp_file.h', - '<(src_loc)/codegen/common/logging.cpp', - '<(src_loc)/codegen/common/logging.h', - '<(src_loc)/codegen/lang/generator.cpp', - '<(src_loc)/codegen/lang/generator.h', - '<(src_loc)/codegen/lang/main.cpp', - '<(src_loc)/codegen/lang/options.cpp', - '<(src_loc)/codegen/lang/options.h', - '<(src_loc)/codegen/lang/parsed_file.cpp', - '<(src_loc)/codegen/lang/parsed_file.h', - '<(src_loc)/codegen/lang/processor.cpp', - '<(src_loc)/codegen/lang/processor.h', - ], - }, { - 'target_name': 'codegen_style', - 'variables': { - 'src_loc': '../SourceFiles', - 'mac_target': '10.10', - }, - 'includes': [ - '../ThirdParty/gyp_helpers/common/executable.gypi', - '../ThirdParty/gyp_helpers/modules/qt.gypi', - ], - 'dependencies': [ - '../ThirdParty/lib_base/lib_base.gyp:lib_base', - ], - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/codegen/common/basic_tokenized_file.cpp', - '<(src_loc)/codegen/common/basic_tokenized_file.h', - '<(src_loc)/codegen/common/checked_utf8_string.cpp', - '<(src_loc)/codegen/common/checked_utf8_string.h', - '<(src_loc)/codegen/common/clean_file.cpp', - '<(src_loc)/codegen/common/clean_file.h', - '<(src_loc)/codegen/common/clean_file_reader.h', - '<(src_loc)/codegen/common/const_utf8_string.h', - '<(src_loc)/codegen/common/cpp_file.cpp', - '<(src_loc)/codegen/common/cpp_file.h', - '<(src_loc)/codegen/common/logging.cpp', - '<(src_loc)/codegen/common/logging.h', - '<(src_loc)/codegen/style/generator.cpp', - '<(src_loc)/codegen/style/generator.h', - '<(src_loc)/codegen/style/main.cpp', - '<(src_loc)/codegen/style/module.cpp', - '<(src_loc)/codegen/style/module.h', - '<(src_loc)/codegen/style/options.cpp', - '<(src_loc)/codegen/style/options.h', - '<(src_loc)/codegen/style/parsed_file.cpp', - '<(src_loc)/codegen/style/parsed_file.h', - '<(src_loc)/codegen/style/processor.cpp', - '<(src_loc)/codegen/style/processor.h', - '<(src_loc)/codegen/style/structure_types.cpp', - '<(src_loc)/codegen/style/structure_types.h', - ], - }, { - 'target_name': 'codegen_numbers', - 'variables': { - 'src_loc': '../SourceFiles', - 'mac_target': '10.10', - }, - 'includes': [ - '../ThirdParty/gyp_helpers/common/executable.gypi', - '../ThirdParty/gyp_helpers/modules/qt.gypi', - ], - - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/codegen/common/basic_tokenized_file.cpp', - '<(src_loc)/codegen/common/basic_tokenized_file.h', - '<(src_loc)/codegen/common/checked_utf8_string.cpp', - '<(src_loc)/codegen/common/checked_utf8_string.h', - '<(src_loc)/codegen/common/clean_file.cpp', - '<(src_loc)/codegen/common/clean_file.h', - '<(src_loc)/codegen/common/clean_file_reader.h', - '<(src_loc)/codegen/common/const_utf8_string.h', - '<(src_loc)/codegen/common/cpp_file.cpp', - '<(src_loc)/codegen/common/cpp_file.h', - '<(src_loc)/codegen/common/logging.cpp', - '<(src_loc)/codegen/common/logging.h', - '<(src_loc)/codegen/numbers/generator.cpp', - '<(src_loc)/codegen/numbers/generator.h', - '<(src_loc)/codegen/numbers/main.cpp', - '<(src_loc)/codegen/numbers/options.cpp', - '<(src_loc)/codegen/numbers/options.h', - '<(src_loc)/codegen/numbers/parsed_file.cpp', - '<(src_loc)/codegen/numbers/parsed_file.h', - '<(src_loc)/codegen/numbers/processor.cpp', - '<(src_loc)/codegen/numbers/processor.h', - ], - }, { - 'target_name': 'codegen_emoji', - 'variables': { - 'src_loc': '../SourceFiles', - 'mac_target': '10.10', - }, - 'includes': [ - '../ThirdParty/gyp_helpers/common/executable.gypi', - '../ThirdParty/gyp_helpers/modules/qt.gypi', - ], - - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/codegen/common/cpp_file.cpp', - '<(src_loc)/codegen/common/cpp_file.h', - '<(src_loc)/codegen/common/logging.cpp', - '<(src_loc)/codegen/common/logging.h', - '<(src_loc)/codegen/emoji/data.cpp', - '<(src_loc)/codegen/emoji/data.h', - '<(src_loc)/codegen/emoji/generator.cpp', - '<(src_loc)/codegen/emoji/generator.h', - '<(src_loc)/codegen/emoji/main.cpp', - '<(src_loc)/codegen/emoji/options.cpp', - '<(src_loc)/codegen/emoji/options.h', - '<(src_loc)/codegen/emoji/replaces.cpp', - '<(src_loc)/codegen/emoji/replaces.h', - ], - }], -} diff --git a/Telegram/gyp/codegen/rules.gypi b/Telegram/gyp/codegen/rules.gypi index 9cad3ee41..499210a76 100644 --- a/Telegram/gyp/codegen/rules.gypi +++ b/Telegram/gyp/codegen/rules.gypi @@ -6,22 +6,6 @@ { 'actions': [{ - 'action_name': 'update_dependent_qrc', - 'inputs': [ - '<(DEPTH)/update_dependent.py', - '<@(qrc_files)', - ' 0 - -my_path = os.path.dirname(os.path.realpath(__file__)).replace('\\', '/') - -file_paths = [] -platform_rules = {} -next_input_path = 0 -input_path = '' -next_moc_prefix = 0 -moc_prefix = '' -next_replace = 0 -replaces = [] -next_exclude_for = 0 -exclude_for = '' -next_self = 1 -for arg in sys.argv: - if next_self != 0: - next_self = 0 - continue - - if arg == '--moc-prefix': - next_moc_prefix = 1 - continue - elif next_moc_prefix == 1: - next_moc_prefix = 0 - moc_prefix = arg.replace('SHARED_INTERMEDIATE_DIR', '<(SHARED_INTERMEDIATE_DIR)') - continue - - if arg == '--input': - next_input_path = 1 - continue - elif next_input_path == 1: - next_input_path = 0 - input_path = arg - continue - - if arg == '--replace': - next_replace = 1 - continue - elif next_replace == 1: - next_replace = 0 - replaces.append(arg) - continue - - if arg == '--exclude_for': - next_exclude_for = 1 - continue - elif next_exclude_for == 1: - next_exclude_for = 0 - exclude_for = arg - continue - - file_paths.append(arg) - -if input_path != '': - if len(file_paths) != 0: - eprint('You need to specify input file or input paths in command line.') - elif not os.path.isfile(input_path): - eprint('Input path not found.') - else: - platforms = [] - with open(input_path, 'r') as f: - for line in f: - file_path = line.strip() - if file_path[0:10] == 'platforms:': - platforms_list = file_path[10:].split(' ') - platforms = [] - for platform in file_path[10:].split(' '): - platform = platform.strip() - if platform != '': - platforms.append(platform) - elif file_path[0:2] != '//' and file_path != '': - file_paths.append(file_path) - if len(platforms): - platform_rules[file_path] = platforms - elif '/platform/win/' in file_path: - platform_rules[file_path] = [ 'win' ] - elif '/platform/mac/' in file_path: - platform_rules[file_path] = [ 'mac' ] - elif '/platform/linux/' in file_path: - platform_rules[file_path] = [ 'linux' ] - -for replace in replaces: - replace_parts = replace.split('=', 1) - if len(replace_parts) != 2: - eprint('Bad replace: ' + replace) - real_paths = [] - real_platform_rules = {} - for file_path in file_paths: - real_path = file_path.replace('<(' + replace_parts[0] + ')', replace_parts[1]) - real_paths.append(real_path) - if file_path in platform_rules: - real_platform_rules[real_path] = platform_rules[file_path] - file_paths = real_paths - platform_rules = real_platform_rules - -if exclude_for != '': - real_paths = [] - for file_path in file_paths: - if not file_path in platform_rules: - continue - if not should_exclude(platform_rules[file_path], exclude_for): - continue - real_paths.append(file_path) - file_paths = real_paths - -for file_path in file_paths: - print(file_path) -if moc_prefix != '': - for file_path in file_paths: - if check_non_empty_moc(file_path): - m = re.search(r'(^|/)([^/]+)\.h$', file_path) - if not m: - eprint('Bad file path: ' + file_path) - print(moc_prefix + m.group(2) + '.cpp') diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index 38d9d0e64..5780b9171 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -116,7 +116,6 @@ <(src_loc)/chat_helpers/emoji_list_widget.h <(src_loc)/chat_helpers/emoji_sets_manager.cpp <(src_loc)/chat_helpers/emoji_sets_manager.h -<(src_loc)/chat_helpers/emoji_suggestions_helper.h <(src_loc)/chat_helpers/emoji_suggestions_widget.cpp <(src_loc)/chat_helpers/emoji_suggestions_widget.h <(src_loc)/chat_helpers/field_autocomplete.cpp @@ -610,8 +609,6 @@ <(src_loc)/platform/linux/linux_libs.h <(src_loc)/platform/linux/file_utilities_linux.cpp <(src_loc)/platform/linux/file_utilities_linux.h -<(src_loc)/platform/linux/info_linux.cpp -<(src_loc)/platform/linux/info_linux.h <(src_loc)/platform/linux/launcher_linux.cpp <(src_loc)/platform/linux/launcher_linux.h <(src_loc)/platform/linux/main_window_linux.cpp @@ -622,13 +619,9 @@ <(src_loc)/platform/linux/specific_linux.h <(src_loc)/platform/mac/file_utilities_mac.mm <(src_loc)/platform/mac/file_utilities_mac.h -<(src_loc)/platform/mac/info_mac.mm -<(src_loc)/platform/mac/info_mac.h <(src_loc)/platform/mac/launcher_mac.mm <(src_loc)/platform/mac/launcher_mac.h <(src_loc)/platform/mac/mac_iconv_helper.c -<(src_loc)/platform/mac/mac_utilities.mm -<(src_loc)/platform/mac/mac_utilities.h <(src_loc)/platform/mac/main_window_mac.mm <(src_loc)/platform/mac/main_window_mac.h <(src_loc)/platform/mac/notifications_manager_mac.mm @@ -645,8 +638,6 @@ <(src_loc)/platform/win/audio_win.h <(src_loc)/platform/win/file_utilities_win.cpp <(src_loc)/platform/win/file_utilities_win.h -<(src_loc)/platform/win/info_win.cpp -<(src_loc)/platform/win/info_win.h <(src_loc)/platform/win/launcher_win.cpp <(src_loc)/platform/win/launcher_win.h <(src_loc)/platform/win/main_window_win.cpp @@ -663,11 +654,9 @@ <(src_loc)/platform/win/windows_dlls.h <(src_loc)/platform/win/windows_event_filter.cpp <(src_loc)/platform/win/windows_event_filter.h -<(src_loc)/platform/win/wrapper_windows_h.h <(src_loc)/platform/win/wrapper_wrl_implements_h.h <(src_loc)/platform/platform_audio.h <(src_loc)/platform/platform_file_utilities.h -<(src_loc)/platform/platform_info.h <(src_loc)/platform/platform_launcher.h <(src_loc)/platform/platform_main_window.h <(src_loc)/platform/platform_notifications_manager.h @@ -739,8 +728,6 @@ <(src_loc)/support/support_helper.h <(src_loc)/support/support_templates.cpp <(src_loc)/support/support_templates.h -<(src_loc)/ui/effects/fade_animation.cpp -<(src_loc)/ui/effects/fade_animation.h <(src_loc)/ui/effects/radial_animation.cpp <(src_loc)/ui/effects/radial_animation.h <(src_loc)/ui/effects/round_checkbox.cpp diff --git a/Telegram/gyp/telegram/telegram.gypi b/Telegram/gyp/telegram/telegram.gypi index 4b72fa121..093f42d4f 100644 --- a/Telegram/gyp/telegram/telegram.gypi +++ b/Telegram/gyp/telegram/telegram.gypi @@ -31,11 +31,12 @@ '<(src_loc)/window/window.style', ], 'dependent_style_files': [ - '<(res_loc)/colors.palette', - '<(res_loc)/basic.style', - '<(src_loc)/ui/widgets/widgets.style', + '<(submodules_loc)/lib_ui/ui/colors.palette', + '<(submodules_loc)/lib_ui/ui/basic.style', + '<(submodules_loc)/lib_ui/ui/widgets/widgets.style', ], 'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp', + 'qrc_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_qrc.timestamp', 'langpacks': [ 'en', 'de', @@ -46,34 +47,34 @@ 'pt-BR', ], 'build_defines%': '', - 'list_sources_command': 'python <(DEPTH)/list_sources.py --input <(DEPTH)/telegram/sources.txt --replace src_loc=<(src_loc)', + 'list_sources_command': 'python <(submodules_loc)/lib_base/gyp/list_sources.py --input <(DEPTH)/telegram/sources.txt --replace src_loc=<(src_loc)', 'pch_source': '<(src_loc)/stdafx.cpp', 'pch_header': '<(src_loc)/stdafx.h', }, 'includes': [ '../../ThirdParty/gyp_helpers/common/executable.gypi', + '../../ThirdParty/gyp_helpers/modules/openssl.gypi', + '../../ThirdParty/gyp_helpers/modules/qt.gypi', + '../../ThirdParty/gyp_helpers/modules/qt_moc.gypi', + '../../ThirdParty/gyp_helpers/modules/pch.gypi', + '../../ThirdParty/lib_ui/gyp/qrc_rule.gypi', + '../../ThirdParty/lib_ui/gyp/styles_rule.gypi', 'qrc.gypi', 'win.gypi', 'mac.gypi', 'linux.gypi', - '../../ThirdParty/gyp_helpers/modules/openssl.gypi', - '../../ThirdParty/gyp_helpers/modules/qt.gypi', - '../../ThirdParty/gyp_helpers/modules/qt_moc.gypi', - '../../ThirdParty/gyp_helpers/modules/qt_rcc.gypi', - '../../ThirdParty/gyp_helpers/modules/pch.gypi', - '../codegen/styles_rule.gypi', '../codegen/rules.gypi', ], 'dependencies': [ - 'codegen.gyp:codegen_emoji', - 'codegen.gyp:codegen_lang', - 'codegen.gyp:codegen_numbers', - 'codegen.gyp:codegen_style', - 'tests/tests.gyp:tests', - 'utils.gyp:Updater', + '../ThirdParty/codegen/codegen.gyp:codegen_lang', + '../ThirdParty/codegen/codegen.gyp:codegen_numbers', + '../ThirdParty/codegen/codegen.gyp:codegen_style', '../ThirdParty/libtgvoip/libtgvoip.gyp:libtgvoip', '../ThirdParty/lib_base/lib_base.gyp:lib_base', + '../ThirdParty/lib_ui/lib_ui.gyp:lib_ui', + 'tests/tests.gyp:tests', + 'utils.gyp:Updater', 'lib_export.gyp:lib_export', 'lib_storage.gyp:lib_storage', 'lib_lottie.gyp:lib_lottie', diff --git a/Telegram/gyp/update_dependent.py b/Telegram/gyp/update_dependent.py deleted file mode 100644 index 48fb75f99..000000000 --- a/Telegram/gyp/update_dependent.py +++ /dev/null @@ -1,171 +0,0 @@ -''' -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 -''' -from __future__ import print_function -import sys -import os -import re -import time - -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - sys.exit(1) - -my_path = os.path.dirname(os.path.realpath(__file__)).replace('\\', '/') - -def get_qrc_dependencies(file_path): - global one_modified - dependencies = {} - if not os.path.isfile(file_path): - eprint('File not found: ' + file_path) - dir_name = os.path.dirname(file_path).replace('\\', '/') - with open(file_path) as f: - for line in f: - file_match = re.match('^\s*]*)?>([^<]+)', line) - if file_match: - full_path = dir_name + '/' + file_match.group(2) - dependencies[full_path] = 1 - return dependencies - -def list_qrc_dependencies(file_path): - global one_modified - dependencies = get_qrc_dependencies(file_path) - for path in dependencies: - print(path) - sys.exit(0) - -one_modified = 0 -def handle_qrc_dependencies(file_path): - global one_modified - dependencies = get_qrc_dependencies(file_path) - file_modified = os.path.getmtime(file_path) - latest_modified = file_modified - for path in dependencies: - if os.path.isfile(path): - dependency_modified = os.path.getmtime(path) - if latest_modified < dependency_modified: - latest_modified = dependency_modified - else: - eprint('File not found: ' + path) - if file_modified < latest_modified: - os.utime(file_path, None); - one_modified = 1 - -def get_direct_style_dependencies(file_path): - dependencies = {} - dependencies[file_path] = 1 - if not os.path.isfile(file_path): - eprint('File not found: ' + file_path) - with open(file_path) as f: - for line in f: - using_match = re.match('^\s*using "([^"]+)"', line) - if using_match: - path = using_match.group(1) - found = 0 - for include_dir in include_dirs: - full_path = include_dir + '/' + path - if os.path.isfile(full_path): - try: - if dependencies[full_path]: - eprint('Cyclic dependencies: ' + full_path) - except KeyError: - dependencies[full_path] = 1 - found = 1 - break - if found != 1: - eprint('File not found: ' + path) - return dependencies - -include_dirs = [] -def handle_style_dependencies(file_path): - global one_modified - all_dependencies = {} - all_dependencies[file_path] = 1 - added_from = {} - while len(added_from) != len(all_dependencies): - for dependency in all_dependencies: - try: - if added_from[dependency]: - continue - except KeyError: - added_from[dependency] = 1 - add = get_direct_style_dependencies(dependency) - for new_dependency in add: - all_dependencies[new_dependency] = 1 - break - - file_modified = os.path.getmtime(file_path) - latest_modified = file_modified - for path in all_dependencies: - if path != file_path: - dependency_modified = os.path.getmtime(path) - if latest_modified < dependency_modified: - latest_modified = dependency_modified - if file_modified < latest_modified: - os.utime(file_path, None); - one_modified = 1 - -file_paths = [] -request = '' -output_file = '' -next_include_dir = 0 -next_output_file = 0 -next_self = 1 -for arg in sys.argv: - if next_self != 0: - next_self = 0 - continue - if arg == '--styles' or arg == '--qrc_list' or arg == '--qrc': - if request == '': - request = arg[2:] - else: - eprint('Only one request required.') - continue - if next_include_dir != 0: - next_include_dir = 0 - include_dirs.append(arg) - continue - if next_output_file != 0: - next_output_file = 0 - output_file = arg - continue - - include_dir_match = re.match(r'^\-I(.*)$', arg) - if include_dir_match: - include_dir = include_dir_match.group(1) - if include_dir == '': - next_include_dir = 1 - else: - include_dirs.append(include_dir) - continue - - output_match = re.match(r'^-o(.*)$', arg) - if output_match: - output_file = output_match.group(1) - if output_file == '': - next_output_file = 1 - continue - - file_paths.append(arg) - -if request == 'styles': - for file_path in file_paths: - handle_style_dependencies(file_path) -elif request == 'qrc': - for file_path in file_paths: - handle_qrc_dependencies(file_path) -elif request == 'qrc_list': - for file_path in file_paths: - list_qrc_dependencies(file_path) -else: - eprint('Request required.') - -if not os.path.isfile(output_file): - with open(output_file, "w") as f: - f.write('1') -elif one_modified != 0: - os.utime(output_file, None); From e3f5a505ebb375e1b43e2c852cd1668720ec3e0e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 18 Sep 2019 09:56:01 +0300 Subject: [PATCH 05/59] Fix folder structure for Xcode build. --- .gitmodules | 24 ++--- Telegram/ThirdParty/codegen | 1 - Telegram/ThirdParty/gyp_helpers | 1 - Telegram/ThirdParty/lib_base | 1 - Telegram/ThirdParty/lib_crl | 1 - Telegram/ThirdParty/lib_rpl | 1 - Telegram/ThirdParty/lib_ui | 1 - Telegram/codegen | 1 + Telegram/gyp/Telegram.gyp | 127 +++++++++++++++++++++- Telegram/gyp/helpers | 1 + Telegram/gyp/lib_export.gyp | 12 +-- Telegram/gyp/lib_ffmpeg.gyp | 15 ++- Telegram/gyp/lib_lottie.gyp | 17 ++- Telegram/gyp/lib_lz4.gyp | 11 +- Telegram/gyp/lib_mtproto.gyp | 10 +- Telegram/gyp/lib_rlottie.gyp | 11 +- Telegram/gyp/lib_scheme.gyp | 10 +- Telegram/gyp/lib_storage.gyp | 16 +-- Telegram/gyp/linux_glibc_wraps.gyp | 8 +- Telegram/gyp/telegram/linux.gypi | 4 + Telegram/gyp/telegram/mac.gypi | 16 +++ Telegram/gyp/telegram/telegram.gypi | 156 ---------------------------- Telegram/gyp/tests/common_test.gypi | 6 +- Telegram/gyp/tests/tests.gyp | 6 +- Telegram/gyp/utils.gyp | 8 +- Telegram/lib_base | 1 + Telegram/lib_crl | 1 + Telegram/lib_rpl | 1 + Telegram/lib_ui | 1 + 29 files changed, 233 insertions(+), 236 deletions(-) delete mode 160000 Telegram/ThirdParty/codegen delete mode 160000 Telegram/ThirdParty/gyp_helpers delete mode 160000 Telegram/ThirdParty/lib_base delete mode 160000 Telegram/ThirdParty/lib_crl delete mode 160000 Telegram/ThirdParty/lib_rpl delete mode 160000 Telegram/ThirdParty/lib_ui create mode 160000 Telegram/codegen create mode 160000 Telegram/gyp/helpers delete mode 100644 Telegram/gyp/telegram/telegram.gypi create mode 160000 Telegram/lib_base create mode 160000 Telegram/lib_crl create mode 160000 Telegram/lib_rpl create mode 160000 Telegram/lib_ui diff --git a/.gitmodules b/.gitmodules index 65ea7c340..1c097c1d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,21 +19,21 @@ [submodule "Telegram/ThirdParty/lz4"] path = Telegram/ThirdParty/lz4 url = https://github.com/lz4/lz4.git -[submodule "Telegram/ThirdParty/lib_crl"] - path = Telegram/ThirdParty/lib_crl +[submodule "Telegram/lib_crl"] + path = Telegram/lib_crl url = https://github.com/desktop-app/lib_crl.git -[submodule "Telegram/ThirdParty/lib_rpl"] - path = Telegram/ThirdParty/lib_rpl +[submodule "Telegram/lib_rpl"] + path = Telegram/lib_rpl url = https://github.com/desktop-app/lib_rpl.git -[submodule "Telegram/ThirdParty/lib_base"] - path = Telegram/ThirdParty/lib_base +[submodule "Telegram/lib_base"] + path = Telegram/lib_base url = https://github.com/desktop-app/lib_base.git -[submodule "Telegram/ThirdParty/gyp_helpers"] - path = Telegram/ThirdParty/gyp_helpers +[submodule "Telegram/gyp/helpers"] + path = Telegram/gyp/helpers url = https://github.com/desktop-app/gyp_helpers.git -[submodule "Telegram/ThirdParty/codegen"] - path = Telegram/ThirdParty/codegen +[submodule "Telegram/codegen"] + path = Telegram/codegen url = https://github.com/desktop-app/codegen.git -[submodule "Telegram/ThirdParty/lib_ui"] - path = Telegram/ThirdParty/lib_ui +[submodule "Telegram/lib_ui"] + path = Telegram/lib_ui url = https://github.com/desktop-app/lib_ui.git diff --git a/Telegram/ThirdParty/codegen b/Telegram/ThirdParty/codegen deleted file mode 160000 index 1fd522333..000000000 --- a/Telegram/ThirdParty/codegen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1fd52233329a60f3e6a0c389873dfffc1ce403c8 diff --git a/Telegram/ThirdParty/gyp_helpers b/Telegram/ThirdParty/gyp_helpers deleted file mode 160000 index df28126a0..000000000 --- a/Telegram/ThirdParty/gyp_helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit df28126a0916edaa6b9c5d7a3ee2a0a71012308c diff --git a/Telegram/ThirdParty/lib_base b/Telegram/ThirdParty/lib_base deleted file mode 160000 index 349996d27..000000000 --- a/Telegram/ThirdParty/lib_base +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 349996d274cc3820a79757ba903803f075a5f61d diff --git a/Telegram/ThirdParty/lib_crl b/Telegram/ThirdParty/lib_crl deleted file mode 160000 index 83ce4916d..000000000 --- a/Telegram/ThirdParty/lib_crl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 83ce4916d4f712300b7550900c59d3d0d420178b diff --git a/Telegram/ThirdParty/lib_rpl b/Telegram/ThirdParty/lib_rpl deleted file mode 160000 index 93465f785..000000000 --- a/Telegram/ThirdParty/lib_rpl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 93465f7854012f2237452f5eaddc5edcc7de2d48 diff --git a/Telegram/ThirdParty/lib_ui b/Telegram/ThirdParty/lib_ui deleted file mode 160000 index 9c711cca1..000000000 --- a/Telegram/ThirdParty/lib_ui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9c711cca1a02fe8d3fce5976a3d8afc30f7eb7f4 diff --git a/Telegram/codegen b/Telegram/codegen new file mode 160000 index 000000000..017228939 --- /dev/null +++ b/Telegram/codegen @@ -0,0 +1 @@ +Subproject commit 01722893988a6573e1ba910028fbc0c596c2ae5b diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 40723b255..4dedbfc4c 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -6,7 +6,130 @@ { 'includes': [ - '../ThirdParty/gyp_helpers/common/common.gypi', - 'telegram/telegram.gypi', + 'helpers/common/common.gypi', ], + 'targets': [{ + 'target_name': 'Telegram', + 'variables': { + 'src_loc': '../SourceFiles', + 'res_loc': '../Resources', + 'minizip_loc': '<(third_party_loc)/minizip', + 'sp_media_key_tap_loc': '<(third_party_loc)/SPMediaKeyTap', + 'emoji_suggestions_loc': '<(third_party_loc)/emoji_suggestions', + 'style_files': [ + '<(src_loc)/boxes/boxes.style', + '<(src_loc)/calls/calls.style', + '<(src_loc)/dialogs/dialogs.style', + '<(src_loc)/export/view/export.style', + '<(src_loc)/history/history.style', + '<(src_loc)/info/info.style', + '<(src_loc)/intro/intro.style', + '<(src_loc)/media/view/mediaview.style', + '<(src_loc)/media/player/media_player.style', + '<(src_loc)/overview/overview.style', + '<(src_loc)/passport/passport.style', + '<(src_loc)/profile/profile.style', + '<(src_loc)/settings/settings.style', + '<(src_loc)/chat_helpers/chat_helpers.style', + '<(src_loc)/window/window.style', + ], + 'dependent_style_files': [ + '<(submodules_loc)/lib_ui/ui/colors.palette', + '<(submodules_loc)/lib_ui/ui/basic.style', + '<(submodules_loc)/lib_ui/ui/widgets/widgets.style', + ], + 'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp', + 'qrc_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_qrc.timestamp', + 'langpacks': [ + 'en', + 'de', + 'es', + 'it', + 'nl', + 'ko', + 'pt-BR', + ], + 'build_defines%': '', + 'list_sources_command': 'python <(submodules_loc)/lib_base/gyp/list_sources.py --input <(DEPTH)/telegram/sources.txt --replace src_loc=<(src_loc)', + 'pch_source': '<(src_loc)/stdafx.cpp', + 'pch_header': '<(src_loc)/stdafx.h', + }, + 'includes': [ + 'helpers/common/executable.gypi', + 'helpers/modules/openssl.gypi', + 'helpers/modules/qt.gypi', + 'helpers/modules/qt_moc.gypi', + 'helpers/modules/pch.gypi', + '../lib_ui/gyp/qrc_rule.gypi', + '../lib_ui/gyp/styles_rule.gypi', + 'telegram/qrc.gypi', + 'telegram/win.gypi', + 'telegram/mac.gypi', + 'telegram/linux.gypi', + 'codegen/rules.gypi', + ], + + 'dependencies': [ + '<(submodules_loc)/codegen/codegen.gyp:codegen_lang', + '<(submodules_loc)/codegen/codegen.gyp:codegen_numbers', + '<(submodules_loc)/codegen/codegen.gyp:codegen_style', + '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', + '<(submodules_loc)/lib_ui/lib_ui.gyp:lib_ui', + '<(third_party_loc)/libtgvoip/libtgvoip.gyp:libtgvoip', + 'tests/tests.gyp:tests', + 'utils.gyp:Updater', + 'lib_export.gyp:lib_export', + 'lib_storage.gyp:lib_storage', + 'lib_lottie.gyp:lib_lottie', + 'lib_ffmpeg.gyp:lib_ffmpeg', + 'lib_mtproto.gyp:lib_mtproto', + ], + + 'defines': [ + 'AL_LIBTYPE_STATIC', + 'AL_ALEXT_PROTOTYPES', + 'TGVOIP_USE_CXX11_LIB', + 'TDESKTOP_API_ID=<(api_id)', + 'TDESKTOP_API_HASH=<(api_hash)', + ' Date: Wed, 18 Sep 2019 11:05:37 +0300 Subject: [PATCH 06/59] Use some icons from lib_ui. --- .gitignore | 21 +----- Telegram/Resources/icons/box_button_close.png | Bin 148 -> 0 bytes .../Resources/icons/box_button_close@2x.png | Bin 269 -> 0 bytes .../Resources/icons/box_button_close@3x.png | Bin 590 -> 0 bytes Telegram/Resources/icons/box_search.png | Bin 350 -> 0 bytes Telegram/Resources/icons/box_search@2x.png | Bin 707 -> 0 bytes Telegram/Resources/icons/box_search@3x.png | Bin 825 -> 0 bytes .../icons/default_checkbox_check.png | Bin 136 -> 0 bytes .../icons/default_checkbox_check@2x.png | Bin 255 -> 0 bytes .../icons/default_checkbox_check@3x.png | Bin 296 -> 0 bytes .../icons/dropdown_submenu_arrow.png | Bin 119 -> 0 bytes .../icons/dropdown_submenu_arrow@2x.png | Bin 166 -> 0 bytes .../icons/dropdown_submenu_arrow@3x.png | Bin 293 -> 0 bytes Telegram/Resources/icons/fade_horizontal.png | Bin 142 -> 0 bytes .../Resources/icons/fade_horizontal@2x.png | Bin 190 -> 0 bytes .../Resources/icons/fade_horizontal@3x.png | Bin 165 -> 0 bytes .../Resources/icons/round_shadow_bottom.png | Bin 86 -> 0 bytes .../icons/round_shadow_bottom@2x.png | Bin 107 -> 0 bytes .../icons/round_shadow_bottom@3x.png | Bin 108 -> 0 bytes .../icons/round_shadow_bottom_left.png | Bin 179 -> 0 bytes .../icons/round_shadow_bottom_left@2x.png | Bin 262 -> 0 bytes .../icons/round_shadow_bottom_left@3x.png | Bin 373 -> 0 bytes .../icons/round_shadow_box_bottom.png | Bin 98 -> 0 bytes .../icons/round_shadow_box_bottom@2x.png | Bin 111 -> 0 bytes .../icons/round_shadow_box_bottom@3x.png | Bin 117 -> 0 bytes .../icons/round_shadow_box_bottom_left.png | Bin 292 -> 0 bytes .../icons/round_shadow_box_bottom_left@2x.png | Bin 539 -> 0 bytes .../icons/round_shadow_box_bottom_left@3x.png | Bin 887 -> 0 bytes .../Resources/icons/round_shadow_box_left.png | Bin 93 -> 0 bytes .../icons/round_shadow_box_left@2x.png | Bin 98 -> 0 bytes .../icons/round_shadow_box_left@3x.png | Bin 106 -> 0 bytes .../Resources/icons/round_shadow_box_top.png | Bin 93 -> 0 bytes .../icons/round_shadow_box_top@2x.png | Bin 102 -> 0 bytes .../icons/round_shadow_box_top@3x.png | Bin 106 -> 0 bytes .../icons/round_shadow_box_top_left.png | Bin 219 -> 0 bytes .../icons/round_shadow_box_top_left@2x.png | Bin 423 -> 0 bytes .../icons/round_shadow_box_top_left@3x.png | Bin 741 -> 0 bytes .../Resources/icons/round_shadow_left.png | Bin 78 -> 0 bytes .../Resources/icons/round_shadow_left@2x.png | Bin 93 -> 0 bytes .../Resources/icons/round_shadow_left@3x.png | Bin 99 -> 0 bytes Telegram/Resources/icons/round_shadow_top.png | Bin 75 -> 0 bytes .../Resources/icons/round_shadow_top@2x.png | Bin 90 -> 0 bytes .../Resources/icons/round_shadow_top@3x.png | Bin 95 -> 0 bytes .../Resources/icons/round_shadow_top_left.png | Bin 141 -> 0 bytes .../icons/round_shadow_top_left@2x.png | Bin 195 -> 0 bytes .../icons/round_shadow_top_left@3x.png | Bin 269 -> 0 bytes Telegram/Resources/icons/simple_close.png | Bin 124 -> 0 bytes Telegram/Resources/icons/simple_close@2x.png | Bin 176 -> 0 bytes Telegram/Resources/icons/simple_close@3x.png | Bin 264 -> 0 bytes Telegram/Resources/icons/slide_shadow.png | Bin 103 -> 0 bytes Telegram/Resources/icons/slide_shadow@2x.png | Bin 116 -> 0 bytes Telegram/Resources/icons/slide_shadow@3x.png | Bin 130 -> 0 bytes Telegram/SourceFiles/boxes/boxes.style | 65 ++++++++++++------ Telegram/SourceFiles/history/history.style | 8 +++ .../info/profile/info_profile_cover.cpp | 3 +- Telegram/SourceFiles/window/window.style | 1 + Telegram/lib_ui | 2 +- 57 files changed, 59 insertions(+), 41 deletions(-) delete mode 100644 Telegram/Resources/icons/box_button_close.png delete mode 100644 Telegram/Resources/icons/box_button_close@2x.png delete mode 100644 Telegram/Resources/icons/box_button_close@3x.png delete mode 100644 Telegram/Resources/icons/box_search.png delete mode 100644 Telegram/Resources/icons/box_search@2x.png delete mode 100644 Telegram/Resources/icons/box_search@3x.png delete mode 100644 Telegram/Resources/icons/default_checkbox_check.png delete mode 100644 Telegram/Resources/icons/default_checkbox_check@2x.png delete mode 100644 Telegram/Resources/icons/default_checkbox_check@3x.png delete mode 100644 Telegram/Resources/icons/dropdown_submenu_arrow.png delete mode 100644 Telegram/Resources/icons/dropdown_submenu_arrow@2x.png delete mode 100644 Telegram/Resources/icons/dropdown_submenu_arrow@3x.png delete mode 100644 Telegram/Resources/icons/fade_horizontal.png delete mode 100644 Telegram/Resources/icons/fade_horizontal@2x.png delete mode 100644 Telegram/Resources/icons/fade_horizontal@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_bottom_left@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_bottom_left@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_left@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_box_top_left@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_left@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_top.png delete mode 100644 Telegram/Resources/icons/round_shadow_top@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_top@3x.png delete mode 100644 Telegram/Resources/icons/round_shadow_top_left.png delete mode 100644 Telegram/Resources/icons/round_shadow_top_left@2x.png delete mode 100644 Telegram/Resources/icons/round_shadow_top_left@3x.png delete mode 100644 Telegram/Resources/icons/simple_close.png delete mode 100644 Telegram/Resources/icons/simple_close@2x.png delete mode 100644 Telegram/Resources/icons/simple_close@3x.png delete mode 100644 Telegram/Resources/icons/slide_shadow.png delete mode 100644 Telegram/Resources/icons/slide_shadow@2x.png delete mode 100644 Telegram/Resources/icons/slide_shadow@3x.png diff --git a/.gitignore b/.gitignore index 0cc39366d..baa529015 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,8 @@ /out/ -/Debug/ -/Release/ -/Deploy/ +Debug/ +Release/ /ThirdParty/ /Telegram/build/target -/Telegram/GeneratedFiles/ -/Telegram/SourceFiles/art/grid.png -/Telegram/SourceFiles/art/grid_125x.png -/Telegram/SourceFiles/art/grid_150x.png -/Telegram/SourceFiles/art/grid_200x.png -/Telegram/SourceFiles/art/sprite_125x.png -/Telegram/SourceFiles/art/sprite_150x.png -/Telegram/Resources/art/grid.png -/Telegram/Resources/art/grid_125x.png -/Telegram/Resources/art/grid_150x.png -/Telegram/Resources/art/grid_200x.png -/Telegram/Resources/art/sprite_125x.png -/Telegram/Resources/art/sprite_150x.png -/Telegram/Debug/ -/Telegram/Release/ /Telegram/tests/ /Telegram/gyp/tests/*.test /Telegram/out/ @@ -32,7 +16,6 @@ *.VC.db *.aps *.xcodeproj -/Win32/ ipch/ .vs/ diff --git a/Telegram/Resources/icons/box_button_close.png b/Telegram/Resources/icons/box_button_close.png deleted file mode 100644 index 3b0402893ab0c64450b8d081ba33aa55f8cc5e01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4Xipc%5RHj*0}t{t7;-4Z9{xN3 z-9x1fY~Od>k2THo{UhNJkh08E>c9<^xii^3Z_9bgpUF39jCXR+YWbFR_}bpqbB_+W w-i@^4sx7&8`S{vZO)4%f(nd-OoT07kiV-r$UTbaZ09wP~>FVdQ&MBb@0Ck@)j zJym5Xg(O*Py@aHcSZgIomQor5fa@`ftmCf%fMRC52T`OKUp0hRkdt@=W)VmH14I#L zd<%GyKzt9JA;EYb7$Fmr;dqAhkYIcaj1ccMe#j5Te?Cz!zAVcKoEwlBb~_MRiK0x zYIsAC5J?CSLE;JAyTPS5JWbc&3-2`natsEFfRe6vW6#Boe#bPE0o%4X@X$ z?*tf%D1`+^Cy>Ig*=%b10Rt?GY`5EZJpK*kh9wc&YPD|q0RSe_H=9i?7P}^}L_#FA z+iis(sZOVJhm#f2Xmq_^i!|g!KNh>)u1G-&LDhytB9Y~CDUuPr^5eGG>xp1Qkh=Y< z0WB5_QKH}P`+PnN#s#+`47!%nm)~qQ6NLtY!TUM55CIVHaIzvC4o|03QSRd$VhRex zM+#3U6q-yXrgi{^@_0!N27{V@(I^lI*wY3PQam3izu&K;^QHr` zL={=Pn)~Uo*d-^nQRju(^MN?`={xsRJ6Eps3-Mom^Z8u2&GJZRDu)ye5N2Z^Ch-2{|he0a}g~F?k cxt{_I0NxbTCUcV|{r~^~07*qoM6N<$g7G2-RsaA1 diff --git a/Telegram/Resources/icons/box_search.png b/Telegram/Resources/icons/box_search.png deleted file mode 100644 index 04735260760a3bc5d9b99022d3d868882a5d59c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmV-k0iphhP)9C=+{pqNh0sP%Cb}xMGY>BB9&z+@4b>FxxY6BilUI2DGWnBbAZAy zl$oh0igp1rQ=aFo!*}F)E;AFLuQv0ls_HVHs;Y?N7=Ranx~@snv{N8W(?$~i>%qGz zyxf6vj>%+#_a6BB4*J06dH8%jlVuq}5HKE(na}4)V!z+BTCKQTE)7ov$T=rTa?W)| w*n*FS)dx1W+l}FH$aFgGjIXU)Khrz)4TY@wg>IlGNB{r;07*qoM6N<$f;RA;vH$=8 diff --git a/Telegram/Resources/icons/box_search@2x.png b/Telegram/Resources/icons/box_search@2x.png deleted file mode 100644 index 4d2c66f86e536edb168c150881677aeb32b04d57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 707 zcmV;!0zCbRP)S*txX<0KyF=T%t%I5CDh60RRAl!2l5X z?g@E^-EPNBCWEb33lFK+>tQ~hN3Yjwr5>O8!@~oO*APDCJ2!eo> zO68!@a5zLl2ogeYyWRepZ@1g%cDv65sMqUE(a~sxsZcKffju~-ZVA`MRZ-~`eu^JBmFxwUfpi@U|myy{oMlq%;)p70G7)ojh#-XHGoU% z?(UApyWP$jz{kf2jeS1fSpXi7hsLwn%o@O8FraZH5;+SX8jaGp*XuEJrdwz{9-B20 zOGB1rrWyJDe(R0s|0eSK`iinFa|0NLVb+dMH<7ouH(J0j3@-CrE=O~>+bt@J!Uq5V z7>~#C^YfDyB7|Tl6f(^bMG?#8GV|z<{6|g$sH(~U>{TolF&2xVBuUgX7!0DOX*iuu znYE2Z18p`NKY*&L%pv0t$SDVSIdiv}8OS4ly2&pL)X9&7Dps zomUhE^Z7g)hC#RLVzEF%2v)0Ao-NC7e&KOba~9wR=HCFRRO%My-vE*%(U0kh=Ikqw pBng|%<~7aHwx_t2u680irY{@T^!=co>}>!5002ovPDHLkV1f}EK==Rv diff --git a/Telegram/Resources/icons/box_search@3x.png b/Telegram/Resources/icons/box_search@3x.png deleted file mode 100644 index 3b681f682e3e977569984958ca6917c6910f6b16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmV-91IGM`P)w6XIAmI`W`lv*VrLQ({@Q5#)fdr+7iuDhB$cM=Tv zRo}gQ=gf(BX1?>~?CccZEM(FtR4SF<@3-6SS65eBt@ix<952V?alha1cDv1HvtF;i zzrTOQUt_geE0xN8K40&R#bSXn6n~;Hk zGMU|F?(gs8@wmxkQYw|dHh~0>u~@9r>Fm~ngpZn3DkY#U6bjhU`%x%BnE;qhr#XBn z0cj)>`8Yfr4h!HQWLKlp>2Q(>*UQU`%jFVF0?708Ga&&XXc+|w&Xm{dW#jR9Jc4t_ z!eX&ln7`C+08X>9+)w3lncl_H4+p)+OQn+N&OqPAfj~gIAe=iw2y!eT0(^ION8dNR z?lVvkS}Z}{|2T+5*CctdSmY&$rgAtOA_Sp_lf2bx@e)K+84Lyy$iZMhep@sIUV=bH zXoth02;_7+B}lnk&Px#b9ig4gW+FbBO!n8r0sPq>>#(57WI}Rb}r|5USDt=L9sv@QN$>h0~#>2xyuvjJyHuh;WegZJA4o6Ux6 zZLwJF*RfnKQHEkkm+3(@^jH!M26MUG!@~pY6?QU diff --git a/Telegram/Resources/icons/default_checkbox_check.png b/Telegram/Resources/icons/default_checkbox_check.png deleted file mode 100644 index 574f57123bd038d32c8e3bf99a69d06c028444c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^d_c^_!3HE-Zss2cQvRMUjv*Qor(QV7d%%E$*UC`R$QFGvXLkEW?ufrslO=)(kHYK<-q*XX@dhQcr_PW8$=5?c)tt#)& lA@(iy=|`C_h5pLFyK&;vvwQ2e6$6cC@O1TaS?83{1OOVvFtq>x diff --git a/Telegram/Resources/icons/default_checkbox_check@2x.png b/Telegram/Resources/icons/default_checkbox_check@2x.png deleted file mode 100644 index 6d3859c84cb809ea2feda32901477b47b31768a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmVIfkOX0+BC-g_ve z?q~d%F$Otj06>iKj?Tl5^4`w@Ygo=1T5Eo=g&AY`-|z)C4!*=nkcc3qoQa*_vMdN8 zVB0pV>pBbDK_Ws3p&0C;1K18qDXRfBbPzkQv=wwk8L;xx^iU@3xfZQpChQ5eP$sMn z_RvAB4Yp8g?FiNfuV_jM&bd+SHz>rur$oeo;d7|}sxPY`gfDM-%B%nY002ovPDHLk FV1iQwXb=DZ diff --git a/Telegram/Resources/icons/default_checkbox_check@3x.png b/Telegram/Resources/icons/default_checkbox_check@3x.png deleted file mode 100644 index 46f67cbde14f5759ef7642d2948e2dd5085ff29a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296 zcmV+@0oVSCP)uw)PlR^a}`#KOxVCh&8GUr7SbTS5rR#&OhIGsX!1IOpE`7^8D;7=||X zG1P*Keasls%!ZSBo?rX5i*A~xzq8>y_ld6S)^!y^G_d==-}fD^g%Ffds5Yh4T6BftRh!^^9CLZemy0000(e~XaU5fTDy7Jd{{$ESh7e67RLjuE diff --git a/Telegram/Resources/icons/fade_horizontal.png b/Telegram/Resources/icons/fade_horizontal.png deleted file mode 100644 index f10b4f924644713a9cc976b8405651cbce2ca3ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^8bHj*!3HGP#J-sfq(VJi978x}`kt}oV^-i{xp=T4 zvVO<^!pT}1M=xj0mVNhcqKk_{=c-3@4(}EBEHMvtO}lhA<=JifNk9H~aD2PB!}{I1 sd$YG*OHMmuX=r?=$M6{Q9;O<`Yp0q28NX8!1X{r0>FVdQ&MBb@02OIAv;Y7A diff --git a/Telegram/Resources/icons/fade_horizontal@2x.png b/Telegram/Resources/icons/fade_horizontal@2x.png deleted file mode 100644 index ed229a5b7903aa815c482fd391469ed046add8b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 190 zcmV;v073tWP)F|38d-s1jwi&Y_kNwrsH&Nx3^nf+VU65pmIDjK!-8nTavR!OP4;X3x!8 z)1TSQyzHtfkX*TWPeiODiFvoXBO+EevkTw*UI1?Ojqyzu;%-KKdfARLm2fhs?{hwP+O#lD@07*qoM6N<$g7qI!xBvhE diff --git a/Telegram/Resources/icons/fade_horizontal@3x.png b/Telegram/Resources/icons/fade_horizontal@3x.png deleted file mode 100644 index 5f7f76e800bc9d39b8790cdf31f42ac2d277a906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^6+q0)!2~3)KaOb!Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JirhV2978H@y}97c#h}2!a^Yk7e{FSEBaXMqHQbU-DFF;G z($dn6%IzmRcX9A-49)oY?S|d9v#r?$+h5IbVaX5fvHapcje(h=O@Wy?@>#<~ppguo Lu6{1-oD!MmdKI;Vst06-=ey#N3J diff --git a/Telegram/Resources/icons/round_shadow_bottom@2x.png b/Telegram/Resources/icons/round_shadow_bottom@2x.png deleted file mode 100644 index a64ca5c8e364c18a98e98d764e312fda9f8e57e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-R!3HER>*_fHDI-r8#}JO0s>gQnGAQt{9CTfz z^gVv6^8Tg=YO|8l_#f=DW-wQpawcSz*UP-rv?+I4A26$EiK)$5@5~O=&EVeT=H zlh{yP_y1pS^XJ#s^^+_tcoR~RlA5AVoj7oS;kkjK0wcrkozm9p=lMPdYG?3t^>bP0 Hl+XkKBbFlW diff --git a/Telegram/Resources/icons/round_shadow_bottom_left.png b/Telegram/Resources/icons/round_shadow_bottom_left.png deleted file mode 100644 index 8beb9a7e12a1c878d326750a2237db2179a71ec3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^96-#;!3HGxgLCzORF$WTV~E7m)_zB>W(NV5)#+g$ zHyr=_Uw-e(H@7TRxSX7%eq9IpARTRHE`y?W`K zVj1?=yNr%i+&E;G%=4{w`S)|%a``KP)o5kv&c3=u(V4I+Zq>xJVu z^7q(7L;#TAyWq7KU{*tEPa6Dje6$oZ{o_b9M$qL|!hYpK8Nwy#wSK)SLC7qgx{L(WWuE zQw0Egmqi^i0NbpWZ8~N?Mzrb-egz-^zS?kA&Bv2mW94&g*t>gy)pj z&H95jCJin@g;P`S2V7+lC}(##({tU$#n86aF-^=ObJKNub6xgE6Q3(TE*=7!z~JfX K=d#Wzp$Pyf-XWs^ diff --git a/Telegram/Resources/icons/round_shadow_box_bottom@3x.png b/Telegram/Resources/icons/round_shadow_box_bottom@3x.png deleted file mode 100644 index 5bd51530921a811fd14f1646d9c0852fcd5dd001..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^%s?#1!3HE>CM@{{q-;E09780gCWnNE{yopYs`LN< z;hQ`<_5c657X17ByWN@1mA}jA&N9K3?Av)lry3e6Fxs#^xZ=EoamQB%2JM@kd7b9= R-9U2~JYD@<);T3K0RZ4QCTjoy diff --git a/Telegram/Resources/icons/round_shadow_box_bottom_left.png b/Telegram/Resources/icons/round_shadow_box_bottom_left.png deleted file mode 100644 index 3bb8de35632d70c6025be26dc8b874732a14aad5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmV+<0o(qGP)=DSWm(oS5kakWcO%B2Pmf~X z=O!p3E3`c`@S*cwC=)QV)$tJA-9a)9t_4bR2aeGG`>^LxG;`X6GUo)av6?No1KiAy q@NCf;@a!r`;iJy3>Ju$p!424g~-v5!~{TZ3-8s7pqK= zKid)YG5N(Ac)yWaXr?#R2mICgfWKP*8=Qz{43pB|a@=z84tF(`9DfIR6I=pnz;^{v z&hfil|B7}bqNhzyhbN-*@?>qCGIAaFVD550>^Rpb*S#QSxeXM+d&+Q7dDk=(8c|k>i=g=!#NHzE=M3fCh?nT;3({$`t8x_v0fcHSps&7UTY^>^N_C=+>HBR%gkr zD!iI-^_mXg0lK@+kJX6i;W$qoyH$?hLXKNoTK?PvIF929tdEfaxAXum9hNJlRV^em zZ%2X4;Fcl*c+ebJgB^iu?^CHF%bgcMCN&}fguzD@f(PIoWsDroIId~K&%yq~%fZ*J z-K`Xe0B0&m89W<=*0#pX1P}txz(-)C>0$n+foI^Ofp}v?flJ^~h?T%+Ukt!A6GQ>4 zAEL?t1Kb-t3s%i%wLcv2%7j-_;Zly52Ak-pI~RiB6@aU-rE++6;MM=X1U?faJr^c| dhbNs7;5Y0phJ{}GN3H+>002ovPDHLkV1iP0_>lkr diff --git a/Telegram/Resources/icons/round_shadow_box_bottom_left@3x.png b/Telegram/Resources/icons/round_shadow_box_bottom_left@3x.png deleted file mode 100644 index a8b2b8d0b8976c72ac26d6631163a5dad9f64b2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 887 zcmV--1Bm>IP)867kEwDw7h?=5rO!F)xct6e zuTQz0s|7U=JWsBj6{&i5{}_i90*4d=hZF*b6at470*4d=hZF*b6at470#iuIIp2O) z$59G_?xeIV_Ey1O>tXyd&g5T-P!dB5|7FbfWWDQ=k)Z4*q3Wg+aI6{^3bzNq2crxi5zV`8A1x4MI zLPZS;P!UxAf)hB{W@R*+4@U+*w)&P8)=E|818=O?O{}CKvr?!@bLyao`ZviNx8Bj# zL0q_&2wA~w3YwF*)+fKBa=PRxR7zittx8!5&7@!gu;0)~S;=e)%;uwoTn{{ZGEcz$ z-iIRru4t*G4!2#EkPteXkA|}L;WFS=P(=!t%2U@%9o&9pr|VAMq=b#yXo9e+s$7R3 zBI6H=VV}J}F0N7vB#4@j<>gpP>2nOfqNaRP4|w{jCZ)y~a1NLw0q(y@>L>)`deqn! ze32q;0Z^S=2IG8)Wj#dAC#7^d|9-!3V_D`Sp~=y5J8T-D$%OE&2=31a1+adNYPETG z-;Trsb1UX@&FXY9LGb)_UF2A`PPf_7Qe~>H)Q@*uZY=DiYjdEckj&MA_28Pw+P}rQ zTINuTA?9PvR=Fpyg>-FCSnEdvOA7aNrELPv=36GH0gp@h7q1z0-MID;A^9ba^a86x zrN*;hSoF~#ks__cVO>lAxUB_UqE9)enH877No5Tq7MC%Ep!O)S%!Nj->^-*h>6x1a zQ_RP!tO99ELNhU`j)q}x2rUaU&tC4#{TGw@gwQ6}7IWyaKjOvI=p!XpT26%PQ^0-H zs>`etZeVY{k&5W09ycl8!M%~IWvpIpA4}dadb}v><&3M8@@ZW0B%Uw_FPccXb<}tR zqX`(E=Ffs;4kJnIrj)Oa7kcvUzQZ6=*a08%6CRu|t_e(6N(*MG>=(j7a#|JuwWt69 N002ovPDHLkV1h*~n;!rG diff --git a/Telegram/Resources/icons/round_shadow_box_left.png b/Telegram/Resources/icons/round_shadow_box_left.png deleted file mode 100644 index 7f5e183c9902c54fccb225c6df948c4fbccb5c51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^AT}ch8<2ed)xHi$sd&0LhH%VeJ=n;|V8GFAu;J=| q`+Gu-%NH3Lt0;2jW}bdmR9>U`j4|Ti#kM}6dInEdKbLh*2~7a}3>wY= diff --git a/Telegram/Resources/icons/round_shadow_box_left@2x.png b/Telegram/Resources/icons/round_shadow_box_left@2x.png deleted file mode 100644 index 1e28073414f034c5cc32b4a23db4487bf87d548d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^B0$WA4$1cCB`|25eEARC4*rOecv1u0dH-VZMJYD@<);T3K0RY`;A87yp diff --git a/Telegram/Resources/icons/round_shadow_box_left@3x.png b/Telegram/Resources/icons/round_shadow_box_left@3x.png deleted file mode 100644 index f52dac8931eb6ee42e01ba3a736d6de981668ffe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^azMTNj==t=#aQJc|!MnaYZH#AECwpce7eDXC7_Z>B`VmkwgQu&X%Q~lo FCIBAOB9#CD diff --git a/Telegram/Resources/icons/round_shadow_box_top.png b/Telegram/Resources/icons/round_shadow_box_top.png deleted file mode 100644 index f2ff11208bc527027f58ab6d23d1f2f9e6ddc9f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrA!3HEtFPV4&DHTr_#}JO0x<@uLG8ph2GH`zI sfAVcsUZx7R4NFp`@89*>bn6qN@m7P$PqCM@{{qzpY>9780gCZ{AM{5a3R>civF zAj8M4tgO89l*YB@+YBM8K0ZDcDJxdmdKI;Vst E06xha*Z=?k diff --git a/Telegram/Resources/icons/round_shadow_box_top_left.png b/Telegram/Resources/icons/round_shadow_box_top_left.png deleted file mode 100644 index a5736889a34945c4c1a01ccfa925c4134f274d70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^;vmey1|%P7U0DF67J0fjhIn-DopO=yr~-$x_OJi@ z_xyh1+inf{YjGMOM+L1-?T-7Z2^tZ5t_f@|?1J!o}MD+*H)@w(nt+nYkmV&PU zLNx_G3p(?>8{E^`odbix5eTi$YBz$wy+BfZ6dtK(f_tF7X|#-!3XWQCOiTAj+9MGh zfwu0^0a^K+Tu}uDm9E;o)Q=<<+?sF*s0NL|TV?L3g2JMMkN_>Khg?Mi)Y94&1s1`j zhEV9}06fqvxHgC}--5OVqCqcNfX0T#PBrM>yZa4jz+8T4uVMjb=;I3r30)PSi6%B# z5`326$*KA4=+jvWeMop*eE z&DPYRXQ|1FYL}%?so>VKFXd$@WbxhBCKA9kunN#b5B^=a1boL^_-%{t&>M3D3igpo R#8?0T002ovPDHLkV1kPm!KnZM diff --git a/Telegram/Resources/icons/round_shadow_box_top_left@3x.png b/Telegram/Resources/icons/round_shadow_box_top_left@3x.png deleted file mode 100644 index d8e5c7fb185890ba21719c768d9f4caa2b47214c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmV7DP>4M!$}!6u?UWQ2z2w5-X77j{WMadgQV`i@4RB$GQOn)?X36{BE>Uk%RO6> zk~OjV8YdM?U$_k~smsVG!z*wb!${F~#Mxz?kJ?I5lcMRcHL=>gODRA9G4A_jB}Uof zbKdMVR6bVsmh2-BsRb1&o&ji{WNVV17?B~Nu%~DyLtU$-!5!BUE2Zp1iUuv>@G*65 zq}Pa~WNBWK2}gqFUdhnm)OSX|fV^pJby!moi`S0_!o? zKSv5x?^uzNeI(DICX98jCB;itYy22f)@UgbwkPFrO(_;g$JT5x8b731bG}vryAT54 z{O;qO)DDCIBm`RO4R?1>38iFe;`q8II0EE;q5*v&>)`P9d+%svu0&*2D(kTN3Xgg5Ps ze(KX>3ipmfNv4M6A+M5;wosGmklHQ_J>-;H=FdQnzEc*iC9jzwpQ!hIkzySzmxghB zR7P4Ep+%-u5gl9lawH`Y{O+?dp#TCYtx{Z5>T^p6tK;mrCeUqjG380GE8a9{Rk^jz z$`~S_6&8*xP?KWaW>wp+?im>2ZRp$z*>znXo7#2;E+P#kV4=e X;<}47-El{100000NkvXXu0mjfT?J7x diff --git a/Telegram/Resources/icons/round_shadow_left.png b/Telegram/Resources/icons/round_shadow_left.png deleted file mode 100644 index e7df24dc12ec19205ecf3c84be4ad43a18583729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y@!3HFyABb!LQlg$Njv*W~lT#8Cf1W>h@SuW@ b&KU*Km`n*u6{1-oD!M> q?fy2ugd&x-Dmjx|W-ip7-p_7R!I;zk_0xBtdInEdKbLh*2~7a)F&hp5 diff --git a/Telegram/Resources/icons/round_shadow_left@3x.png b/Telegram/Resources/icons/round_shadow_left@3x.png deleted file mode 100644 index 2431067e2372d1fa43deeed9eee760f87ec5c8d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRX!3HE9xm3%5l$NK9V+hC0-cuU|84Ng%7}!74 xxKV#Ug*8O`m3Na@=)y~PSG>_Pb}lUNlAqAUnD@`!>@83igQu&X%Q~loCID@W9EJb@ diff --git a/Telegram/Resources/icons/round_shadow_top.png b/Telegram/Resources/icons/round_shadow_top.png deleted file mode 100644 index 39edc4a4ebe14f516bca0a1b64bdba296d74fe57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75 zcmeAS@N?(olHy`uVBq!ia0vp^j6lr9!3HE}Hf~b~QbL|Cjv*W~lM@mXf1X#-&^W=s Yz|PL-=FVdQ&MBb@00%!1LI3~& diff --git a/Telegram/Resources/icons/round_shadow_top@2x.png b/Telegram/Resources/icons/round_shadow_top@2x.png deleted file mode 100644 index af421f2a05bde2ce515e850112f9fc42d65ddd73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-S!3HFkynkm2q!c|}978x}svg_O%b>u(tl+cz o+yCOy37wOlI8AQY_V*^6oXQi%py!?vEzopr0L2>_&Hw-a diff --git a/Telegram/Resources/icons/round_shadow_top@3x.png b/Telegram/Resources/icons/round_shadow_top@3x.png deleted file mode 100644 index 5e3c7b6ac0882527d77c0dd50738492180001899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y>!3H9qs_+0QHBT4E5RRG2DQRi{&NHy;^z`)b r6z~}v8#9+l`uX{>wDR|A6^>bP0l+XkKaHWbP0l+XkKQtwIl diff --git a/Telegram/Resources/icons/round_shadow_top_left@3x.png b/Telegram/Resources/icons/round_shadow_top_left@3x.png deleted file mode 100644 index 16d3a6862b5005cc3d00f9485c5d51b796dcfa7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmV+o0rLKdP)A3atQeG!l`=^=dz4HQ3#!>m6_lSQ&`Oa@_)+ zf@Y@f{vI$h%X=|MLRF!v?*WlbH3l$@R260hGXu%7_hb$L)*a-`L}Zu-pi^yz+YPE| zNL6pfBtfz@nyKxg{NC04Cx9>^iR-#XQKKGPfZ4|spy-;IZv(PnEp7#Lb=n5h9?!`-T=A Tc8Rq600000NkvXXu0mjfxvXwE diff --git a/Telegram/Resources/icons/simple_close.png b/Telegram/Resources/icons/simple_close.png deleted file mode 100644 index 10f8b030e8cd105c9c3ac7af355ac80689fada49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4IeWS|hDc1EI>C^O!GMF=xxQxq z+5AfjXKzhl6)zSLzR4%x`Ic#;ETiY8L$15|WJ?Zc*~H|nem8Y#|IFzALp`r&itS@e XW7--y?Rs%L&^QK9S3j3^P62i8i!3(nixPixIk2@h aGyfsu%hQhzXx{)jfx*+&&t;ucLK6TR1Vk_Z diff --git a/Telegram/Resources/icons/simple_close@3x.png b/Telegram/Resources/icons/simple_close@3x.png deleted file mode 100644 index 5a5f817033b6165696a4693f51a60b7778cd58b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264 zcmV+j0r&oiP)X=WQZ|}h(JV;Qi7R5Rng{l4ONAiA?MuB z3v$k=wE_T2DO=&Hic$&ypw{|<_t2(Y@E+RqhMO7AvU9auT@ndewFCHye={)`L@ngr1tGel)Ida4$fM@3U7UL=IjIj(14RaU|%(-58 Q2xtt0r>mdKI;Vst0L_gj`~Uy| diff --git a/Telegram/Resources/icons/slide_shadow@3x.png b/Telegram/Resources/icons/slide_shadow@3x.png deleted file mode 100644 index a67d341e0e3ccecaba0c4a8bd8fa8a17994f0e55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^6M&eRgAGX5Y-tMxQl6eJjv*GkZzmaYF&J<#pa1j! z{M)FmD90C1d}p~7UTAP_nweT=DP1|QE<0WR>csfDUpGzlaXtU-&pJjG+t1(MPSk5# fc+lp;g(T*kEsTfke4FbP0l+XkKq>3>8 diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 50d25deb7..9faae34d5 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -10,8 +10,26 @@ using "ui/basic.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; -boxDuration: 200; -boxRadius: 3px; +UserpicButton { + size: size; + photoSize: pixels; + photoPosition: point; + changeButton: RoundButton; + changeIcon: icon; + changeIconPosition: point; + duration: int; + uploadHeight: pixels; + uploadBg: color; + uploadIcon: icon; + uploadIconPosition: point; +} + +FeedUserpicButton { + size: size; + innerSize: pixels; + innerPosition: point; + innerPart: UserpicButton; +} ServiceCheck { margin: margins; @@ -26,6 +44,9 @@ ServiceCheck { duration: int; } +boxDuration: 200; +boxRadius: 3px; + boxButtonFont: font(boxFontSize semibold); defaultBoxButton: RoundButton(defaultLightButton) { width: -24px; @@ -161,6 +182,28 @@ boxPhotoCompressedSkip: 20px; boxPhotoCaptionSkip: 8px; boxPhotoTextFg: windowSubTextFg; +defaultChangeUserpicIcon: icon {{ "new_chat_photo", activeButtonFg }}; +defaultUploadUserpicIcon: icon {{ "upload_chat_photo", msgDateImgFg }}; +defaultUserpicButton: UserpicButton { + size: size(76px, 76px); + photoSize: 76px; + photoPosition: point(-1px, -1px); + changeButton: defaultActiveButton; + changeIcon: defaultChangeUserpicIcon; + changeIconPosition: point(23px, 25px); + duration: 500; + uploadHeight: 24px; + uploadBg: msgDateImgBgOver; + uploadIcon: defaultUploadUserpicIcon; + uploadIconPosition: point(-1px, 1px); +} +defaultFeedUserpicButton: FeedUserpicButton { + size: size(76px, 76px); + innerSize: 76px; + innerPosition: point(-1px, -1px); + innerPart: defaultUserpicButton; +} + boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { color: windowSubTextFg; thickness: 2px; @@ -224,24 +267,6 @@ contactUserIcon: icon {{ "settings_name", menuIconFg }}; contactPhoneIcon: icon {{ "settings_phone_number", menuIconFg }}; contactIconPosition: point(-5px, 23px); -contactsAddIconAbove: icon {{ "contacts_add", activeButtonFg, point(18px, 18px) }}; -contactsAdd: TwoIconButton { - width: 52px; - height: 52px; - - iconBelow: contactsAddIconBelow; - iconBelowOver: contactsAddIconBelowOver; - iconAbove: contactsAddIconAbove; - iconAboveOver: contactsAddIconAbove; - - rippleAreaPosition: point(5px, 5px); - rippleAreaSize: 42px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: activeButtonBgRipple; - } -} -contactsAddPosition: point(14px, 8px); - contactPadding: margins(49px, 2px, 0px, 12px); contactSkip: 6px; contactPhoneSkip: 30px; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index eebecd866..d090907c8 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -47,6 +47,14 @@ historyToDownPosition: point(12px, 10px); historyToDownAbove: icon {{ "history_down_arrow", historyToDownFg, point(17px, 23px) }}; historyToDownAboveOver: icon {{ "history_down_arrow", historyToDownFgOver, point(17px, 23px) }}; historyToDownPaddingTop: 10px; +historyToDownBelow: icon { + { "history_down_shadow", historyToDownShadow }, + { "history_down_circle", historyToDownBg, point(4px, 4px) }, +}; +historyToDownBelowOver: icon { + { "history_down_shadow", historyToDownShadow }, + { "history_down_circle", historyToDownBgOver, point(4px, 4px) }, +}; historyToDown: TwoIconButton { width: 52px; height: 62px; diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 0f3aa8934..bd953a0d5 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "info/info_memento.h" #include "lang/lang_keys.h" -#include "styles/style_info.h" #include "ui/widgets/labels.h" #include "ui/effects/ripple_animation.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper @@ -29,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "main/main_session.h" #include "apiwrap.h" +#include "styles/style_boxes.h" +#include "styles/style_info.h" namespace Info { namespace Profile { diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 1a561099c..9257c530c 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL using "ui/basic.style"; using "ui/widgets/widgets.style"; using "history/history.style"; +using "boxes/boxes.style"; // UserpicButton windowMinWidth: 380px; windowMinHeight: 480px; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 342fad864..946c33ef2 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 342fad864a30acc92f6cfc2cd5ad631c7133ee8f +Subproject commit 946c33ef251ea5a46340e72ae903487576baf9b4 From 860353824b273308eb2053fc98cf2c3508ed8738 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 18 Sep 2019 11:36:04 +0300 Subject: [PATCH 07/59] Fix build in Xcode. --- Telegram/SourceFiles/platform/mac/file_utilities_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/launcher_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/mac_touchbar.mm | 2 +- Telegram/SourceFiles/platform/mac/main_window_mac.mm | 2 +- .../SourceFiles/platform/mac/notifications_manager_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/specific_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/specific_mac_p.mm | 2 +- Telegram/gyp/telegram/mac.gypi | 6 +++--- docs/building-cmake.md | 2 +- docs/building-xcode.md | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm index 885e141b8..000f3db4f 100644 --- a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm +++ b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/file_utilities_mac.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "lang/lang_keys.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/platform/mac/launcher_mac.mm b/Telegram/SourceFiles/platform/mac/launcher_mac.mm index 69d421024..2182e97ca 100644 --- a/Telegram/SourceFiles/platform/mac/launcher_mac.mm +++ b/Telegram/SourceFiles/platform/mac/launcher_mac.mm @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" #include "base/platform/base_platform_info.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "platform/platform_specific.h" #include diff --git a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm index 796af5936..f8869f488 100644 --- a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm +++ b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm @@ -28,7 +28,7 @@ #include "mainwidget.h" #include "mainwindow.h" #include "observer_peer.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "stickers.h" #include "styles/style_dialogs.h" #include "styles/style_media_player.h" diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index c949052d5..487970e34 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/about_box.h" #include "lang/lang_keys.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "ui/widgets/input_fields.h" #include "facades.h" #include "app.h" diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 8d89dcdc4..99c1c7669 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "history/history.h" #include "ui/empty_userpic.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index d95d3a116..bcc0132dc 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "mainwindow.h" #include "history/history_location_manager.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "facades.h" #include diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index ef6540016..3b4c3ee24 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" #include "platform/mac/mac_touchbar.h" -#include "base/platform/mac/base_platform_mac_utilities.h" +#include "base/platform/mac/base_platform_utilities_mac.h" #include "base/platform/base_platform_info.h" #include "lang/lang_keys.h" #include "base/timer.h" diff --git a/Telegram/gyp/telegram/mac.gypi b/Telegram/gyp/telegram/mac.gypi index 1e97c5706..6ac51ec4a 100644 --- a/Telegram/gyp/telegram/mac.gypi +++ b/Telegram/gyp/telegram/mac.gypi @@ -10,7 +10,7 @@ 'mac_bundle': '1', 'mac_bundle_resources': [ ' Date: Wed, 18 Sep 2019 14:19:05 +0300 Subject: [PATCH 08/59] Use layers and boxes from lib_ui. --- .../icons/profile_divider_bottom.png | Bin 81 -> 0 bytes .../icons/profile_divider_bottom@2x.png | Bin 85 -> 0 bytes .../icons/profile_divider_bottom@3x.png | Bin 85 -> 0 bytes .../Resources/icons/profile_divider_left.png | Bin 106 -> 0 bytes .../icons/profile_divider_left@2x.png | Bin 130 -> 0 bytes .../icons/profile_divider_left@3x.png | Bin 148 -> 0 bytes .../Resources/icons/profile_divider_top.png | Bin 88 -> 0 bytes .../icons/profile_divider_top@2x.png | Bin 106 -> 0 bytes .../icons/profile_divider_top@3x.png | Bin 104 -> 0 bytes Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/boxes/about_box.cpp | 1 + Telegram/SourceFiles/boxes/about_box.h | 2 +- Telegram/SourceFiles/boxes/abstract_box.cpp | 605 +------------ Telegram/SourceFiles/boxes/abstract_box.h | 428 +-------- .../SourceFiles/boxes/add_contact_box.cpp | 25 +- Telegram/SourceFiles/boxes/add_contact_box.h | 10 +- .../SourceFiles/boxes/auto_download_box.cpp | 1 + .../SourceFiles/boxes/auto_download_box.h | 2 +- Telegram/SourceFiles/boxes/auto_lock_box.cpp | 1 + Telegram/SourceFiles/boxes/auto_lock_box.h | 2 +- Telegram/SourceFiles/boxes/background_box.cpp | 7 +- Telegram/SourceFiles/boxes/background_box.h | 2 +- .../boxes/background_preview_box.cpp | 1 + .../boxes/background_preview_box.h | 2 +- Telegram/SourceFiles/boxes/boxes.style | 138 +-- Telegram/SourceFiles/boxes/calendar_box.h | 2 +- .../SourceFiles/boxes/change_phone_box.cpp | 7 +- Telegram/SourceFiles/boxes/change_phone_box.h | 2 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 3 +- Telegram/SourceFiles/boxes/confirm_box.h | 12 +- .../SourceFiles/boxes/confirm_phone_box.cpp | 5 +- .../SourceFiles/boxes/confirm_phone_box.h | 2 +- Telegram/SourceFiles/boxes/connection_box.cpp | 15 +- Telegram/SourceFiles/boxes/connection_box.h | 11 +- .../SourceFiles/boxes/create_poll_box.cpp | 3 +- Telegram/SourceFiles/boxes/create_poll_box.h | 2 +- .../SourceFiles/boxes/download_path_box.cpp | 1 + .../SourceFiles/boxes/download_path_box.h | 2 +- .../SourceFiles/boxes/edit_caption_box.cpp | 15 +- Telegram/SourceFiles/boxes/edit_caption_box.h | 2 +- Telegram/SourceFiles/boxes/edit_color_box.h | 2 +- .../SourceFiles/boxes/edit_privacy_box.cpp | 4 +- Telegram/SourceFiles/boxes/edit_privacy_box.h | 6 +- Telegram/SourceFiles/boxes/generic_box.cpp | 25 - Telegram/SourceFiles/boxes/generic_box.h | 154 ---- Telegram/SourceFiles/boxes/language_box.cpp | 6 +- Telegram/SourceFiles/boxes/language_box.h | 2 +- .../SourceFiles/boxes/local_storage_box.cpp | 1 + .../SourceFiles/boxes/local_storage_box.h | 2 +- .../SourceFiles/boxes/mute_settings_box.cpp | 1 + .../SourceFiles/boxes/mute_settings_box.h | 2 +- Telegram/SourceFiles/boxes/passcode_box.cpp | 21 +- Telegram/SourceFiles/boxes/passcode_box.h | 8 +- Telegram/SourceFiles/boxes/peer_list_box.cpp | 10 +- Telegram/SourceFiles/boxes/peer_list_box.h | 2 +- .../boxes/peer_list_controllers.cpp | 6 +- .../boxes/peers/add_participants_box.cpp | 36 +- .../boxes/peers/add_participants_box.h | 4 +- .../boxes/peers/edit_contact_box.cpp | 12 +- .../boxes/peers/edit_contact_box.h | 5 +- .../boxes/peers/edit_linked_chat_box.cpp | 17 +- .../boxes/peers/edit_linked_chat_box.h | 10 +- .../boxes/peers/edit_participant_box.cpp | 20 +- .../boxes/peers/edit_participant_box.h | 2 +- .../boxes/peers/edit_participants_box.cpp | 14 +- .../boxes/peers/edit_participants_box.h | 6 +- .../edit_peer_history_visibility_box.cpp | 4 +- .../peers/edit_peer_history_visibility_box.h | 2 +- .../boxes/peers/edit_peer_info_box.cpp | 32 +- .../boxes/peers/edit_peer_info_box.h | 2 +- .../boxes/peers/edit_peer_permissions_box.cpp | 4 +- .../boxes/peers/edit_peer_permissions_box.h | 2 +- .../boxes/peers/edit_peer_type_box.cpp | 15 +- .../boxes/peers/edit_peer_type_box.h | 2 +- Telegram/SourceFiles/boxes/photo_crop_box.cpp | 1 + Telegram/SourceFiles/boxes/photo_crop_box.h | 2 +- Telegram/SourceFiles/boxes/rate_call_box.cpp | 4 +- Telegram/SourceFiles/boxes/rate_call_box.h | 2 +- Telegram/SourceFiles/boxes/report_box.cpp | 1 + Telegram/SourceFiles/boxes/report_box.h | 2 +- .../boxes/self_destruction_box.cpp | 1 + .../SourceFiles/boxes/self_destruction_box.h | 2 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 3 +- Telegram/SourceFiles/boxes/send_files_box.h | 2 +- Telegram/SourceFiles/boxes/sessions_box.cpp | 5 +- Telegram/SourceFiles/boxes/sessions_box.h | 2 +- Telegram/SourceFiles/boxes/share_box.cpp | 3 +- Telegram/SourceFiles/boxes/share_box.h | 2 +- .../SourceFiles/boxes/single_choice_box.cpp | 1 + .../SourceFiles/boxes/single_choice_box.h | 2 +- .../SourceFiles/boxes/sticker_set_box.cpp | 8 +- Telegram/SourceFiles/boxes/sticker_set_box.h | 4 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 3 +- Telegram/SourceFiles/boxes/stickers_box.h | 5 +- Telegram/SourceFiles/boxes/url_auth_box.cpp | 5 +- Telegram/SourceFiles/boxes/url_auth_box.h | 2 +- Telegram/SourceFiles/boxes/username_box.cpp | 1 + Telegram/SourceFiles/boxes/username_box.h | 2 +- Telegram/SourceFiles/calls/calls_top_bar.cpp | 4 +- .../chat_helpers/emoji_sets_manager.cpp | 1 + .../chat_helpers/emoji_sets_manager.h | 2 +- .../chat_helpers/message_field.cpp | 5 +- .../SourceFiles/chat_helpers/stickers.cpp | 4 +- .../chat_helpers/stickers_list_widget.cpp | 6 +- .../chat_helpers/stickers_list_widget.h | 5 +- Telegram/SourceFiles/core/application.h | 4 +- .../SourceFiles/core/click_handler_types.cpp | 2 +- .../SourceFiles/core/local_url_handlers.cpp | 2 +- Telegram/SourceFiles/core/update_checker.cpp | 1 + Telegram/SourceFiles/data/data_session.h | 7 +- .../dialogs_search_from_controllers.cpp | 2 +- .../view/export_view_panel_controller.cpp | 16 +- .../view/export_view_panel_controller.h | 7 +- .../export/view/export_view_settings.cpp | 2 +- .../export/view/export_view_settings.h | 7 +- Telegram/SourceFiles/facades.cpp | 2 +- .../admin_log/history_admin_log_filter.cpp | 1 + .../admin_log/history_admin_log_filter.h | 9 +- .../admin_log/history_admin_log_inner.cpp | 2 +- .../SourceFiles/history/history_drag_area.cpp | 2 +- .../SourceFiles/history/history_message.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 6 +- .../view/history_view_contact_status.cpp | 8 +- .../view/history_view_schedule_box.cpp | 4 +- .../history/view/history_view_schedule_box.h | 6 +- .../view/history_view_scheduled_section.cpp | 16 +- .../SourceFiles/info/info_layer_widget.cpp | 2 +- Telegram/SourceFiles/info/info_layer_widget.h | 4 +- Telegram/SourceFiles/info/info_memento.cpp | 4 +- Telegram/SourceFiles/info/info_memento.h | 4 +- .../SourceFiles/info/info_section_widget.cpp | 2 +- .../SourceFiles/info/info_section_widget.h | 2 +- .../info/media/info_media_inner_widget.cpp | 3 +- .../info/profile/info_profile_actions.cpp | 9 +- .../profile/info_profile_inner_widget.cpp | 5 +- Telegram/SourceFiles/intro/introcode.cpp | 2 +- Telegram/SourceFiles/intro/intropwdcheck.cpp | 2 +- Telegram/SourceFiles/intro/introwidget.cpp | 2 +- .../SourceFiles/lang/lang_cloud_manager.cpp | 14 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 26 +- Telegram/SourceFiles/mainwindow.h | 15 +- .../passport/passport_form_controller.h | 1 - .../passport/passport_form_view_controller.h | 6 +- .../SourceFiles/passport/passport_panel.cpp | 4 +- .../SourceFiles/passport/passport_panel.h | 9 +- .../passport/passport_panel_controller.cpp | 51 +- .../passport/passport_panel_controller.h | 36 +- .../passport/passport_panel_details_row.cpp | 2 +- .../passport/passport_panel_edit_contact.cpp | 11 +- .../passport/passport_panel_edit_contact.h | 7 +- .../passport/passport_panel_edit_document.cpp | 12 +- .../passport/passport_panel_edit_document.h | 9 +- .../passport/passport_panel_edit_scans.cpp | 9 +- .../passport/passport_panel_edit_scans.h | 5 +- .../passport/passport_panel_form.cpp | 4 +- .../passport/passport_panel_form.h | 3 +- .../passport/passport_panel_password.cpp | 2 +- Telegram/SourceFiles/profile/profile.style | 6 - .../SourceFiles/settings/settings_chat.cpp | 4 +- .../SourceFiles/settings/settings_common.cpp | 5 +- .../settings/settings_information.cpp | 5 +- .../SourceFiles/settings/settings_intro.cpp | 2 +- .../SourceFiles/settings/settings_intro.h | 4 +- .../settings/settings_privacy_controllers.cpp | 4 +- .../settings/settings_privacy_security.cpp | 8 +- .../settings/settings_privacy_security.h | 7 +- .../SourceFiles/storage/localimageloader.cpp | 4 +- .../support/support_autocomplete.cpp | 2 +- .../support/support_autocomplete.h | 2 +- .../SourceFiles/support/support_helper.cpp | 5 +- Telegram/SourceFiles/ui/countryinput.cpp | 1 + Telegram/SourceFiles/ui/countryinput.h | 2 +- .../ui/effects/radial_animation.cpp | 299 ------- .../SourceFiles/ui/effects/radial_animation.h | 109 --- Telegram/SourceFiles/ui/special_buttons.cpp | 4 +- .../SourceFiles/ui/widgets/separate_panel.cpp | 8 +- .../SourceFiles/ui/widgets/separate_panel.h | 14 +- Telegram/SourceFiles/window/layer_widget.cpp | 847 ------------------ Telegram/SourceFiles/window/layer_widget.h | 213 ----- Telegram/SourceFiles/window/main_window.cpp | 8 +- Telegram/SourceFiles/window/main_window.h | 8 +- .../window/notifications_manager_default.cpp | 6 +- Telegram/SourceFiles/window/section_memento.h | 7 +- Telegram/SourceFiles/window/section_widget.h | 7 +- .../window/themes/window_theme_editor.cpp | 3 +- .../window/themes/window_theme_editor_box.cpp | 7 +- .../window/themes/window_theme_editor_box.h | 8 +- .../window/themes/window_theme_warning.cpp | 3 +- .../themes/window_themes_cloud_list.cpp | 2 +- .../window/themes/window_themes_cloud_list.h | 2 +- .../window/window_connecting_widget.cpp | 1 + .../SourceFiles/window/window_controller.cpp | 7 +- .../SourceFiles/window/window_controller.h | 7 +- .../window/window_history_hider.cpp | 2 +- .../window/window_lock_widgets.cpp | 3 +- .../SourceFiles/window/window_lock_widgets.h | 2 +- .../SourceFiles/window/window_main_menu.cpp | 8 +- .../SourceFiles/window/window_main_menu.h | 11 +- .../window/window_media_preview.cpp | 2 +- .../SourceFiles/window/window_peer_menu.cpp | 25 +- .../SourceFiles/window/window_peer_menu.h | 4 +- .../window/window_session_controller.cpp | 2 +- .../window/window_session_controller.h | 7 +- Telegram/gyp/Telegram.gyp | 1 + Telegram/gyp/telegram/sources.txt | 6 - Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 209 files changed, 586 insertions(+), 3349 deletions(-) delete mode 100644 Telegram/Resources/icons/profile_divider_bottom.png delete mode 100644 Telegram/Resources/icons/profile_divider_bottom@2x.png delete mode 100644 Telegram/Resources/icons/profile_divider_bottom@3x.png delete mode 100644 Telegram/Resources/icons/profile_divider_left.png delete mode 100644 Telegram/Resources/icons/profile_divider_left@2x.png delete mode 100644 Telegram/Resources/icons/profile_divider_left@3x.png delete mode 100644 Telegram/Resources/icons/profile_divider_top.png delete mode 100644 Telegram/Resources/icons/profile_divider_top@2x.png delete mode 100644 Telegram/Resources/icons/profile_divider_top@3x.png delete mode 100644 Telegram/SourceFiles/boxes/generic_box.cpp delete mode 100644 Telegram/SourceFiles/boxes/generic_box.h delete mode 100644 Telegram/SourceFiles/ui/effects/radial_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/radial_animation.h delete mode 100644 Telegram/SourceFiles/window/layer_widget.cpp delete mode 100644 Telegram/SourceFiles/window/layer_widget.h diff --git a/Telegram/Resources/icons/profile_divider_bottom.png b/Telegram/Resources/icons/profile_divider_bottom.png deleted file mode 100644 index 31fee84f91d4bfe79b4cb01529cee5544c0554a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrF!3HE-TH59VDG5&(#}JO0$q5MwKh7UHaNt0L ei-EDRF$05WGo#?P74Zr{H4L7velF{r5}E+O7!*zb diff --git a/Telegram/Resources/icons/profile_divider_bottom@2x.png b/Telegram/Resources/icons/profile_divider_bottom@2x.png deleted file mode 100644 index 87fb6562126db7292842b7b319e1c7e04a399bde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-V!3HGfJ?-}dQZk+{jv*W~lT#8Bew;r51V=FAxv4Q7TjJd7Fzehd77>KHs-{an^LB{Ts5|A!ST diff --git a/Telegram/Resources/icons/profile_divider_left.png b/Telegram/Resources/icons/profile_divider_left.png deleted file mode 100644 index 9bcfd455e14a23b180fe2c8f298cdd8b5bafbdb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-V!3HGfJ?-}dQih%`jv*W~dyj48J>bA`z~Ol! z!?*h)oDcOUF&1654PEM0X0Byv{6^2ErJ?=&{3n+0^^H#nsapwe30lak|Lc7Rj9o5Rydh(X-_M^yK!Dn;X&+Hs-MDz*j}cfYWnufLzw+?c`Y ech0vXEz&*`=1NnyI`;q#W$<+Mb6Mw<&;$TbjVT`h diff --git a/Telegram/Resources/icons/profile_divider_left@3x.png b/Telegram/Resources/icons/profile_divider_left@3x.png deleted file mode 100644 index ccf2312a9ce60293aef95fcfacbdab74dbee3d25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol>0V4T5^Y;O%Xipc%5RHj%`wjUHDDW_!50yLc z|G(EA&Z$8TvAeED7j0X-VoDOj?HkKpCM-;T#@2CWPIJ_2DUU;DGb0~Qn<^3V?b*dJYD@<);T3K0RYJjHbDRY diff --git a/Telegram/Resources/icons/profile_divider_top.png b/Telegram/Resources/icons/profile_divider_top.png deleted file mode 100644 index 6dcdc143c0c16a25c8744dfc0d33722afc934ceb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrF!3HE-TH59VDS1y9#}JO0tOp$h85lU270$74 ms1J!3ev|k7ok!4%1I+Q-Y|4jbJ0AopW$<+Mb6Mw<&;$S>>lTIp diff --git a/Telegram/Resources/icons/profile_divider_top@2x.png b/Telegram/Resources/icons/profile_divider_top@2x.png deleted file mode 100644 index 5bc32a739b61ee044003537da63f62bbacc2358f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-V!3HGfJ?-}dQih%`jv*W~Q%@W6H5l-)9JF+K z|9}70iOzyjN5U@Wtjaq4^zFNM&0DV>Jky|ZQ=n$Oz1)RB9+id<2H%0289ZJ6T-G@y GGywoN=Omo~ diff --git a/Telegram/Resources/icons/profile_divider_top@3x.png b/Telegram/Resources/icons/profile_divider_top@3x.png deleted file mode 100644 index 938e1e1c20d574ca14962d819f812b269c98ed3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y~!3HE1UzHvKQu>}Qjv*W~lb<|){{K7!s}GMy z!_)u&|8tzXzrVio;er=GzrU9+lXPQaV|z1g(qTr1<*&tcCU0zA4b;lu>FVdQ&MBb@ E0ANle`v3p{ diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e5ad1c9a6..f2f8bd648 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2810,7 +2810,7 @@ void ApiWrap::requestAttachedStickerSets(not_null photo) { : MTP_inputStickerSetShortName(setData->vshort_name()); Ui::show( Box(App::wnd()->sessionController(), setId), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }).fail([=](const RPCError &error) { Ui::show(Box(tr::lng_stickers_not_found(tr::now))); }).send(); @@ -4903,7 +4903,7 @@ void ApiWrap::editUploadedFile( _session->data().sendHistoryChangeNotifications(); Ui::show( Box(tr::lng_edit_media_invalid_file(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } else { sendMessageFail(error, peer); } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 916b0ed02..e783d4a62 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -45,7 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_mediaview.h" #include "styles/style_chat_helpers.h" #include "styles/style_history.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include #include diff --git a/Telegram/SourceFiles/boxes/about_box.cpp b/Telegram/SourceFiles/boxes/about_box.cpp index 38595e765..c39830251 100644 --- a/Telegram/SourceFiles/boxes/about_box.cpp +++ b/Telegram/SourceFiles/boxes/about_box.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "core/click_handler_types.h" #include "core/update_checker.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include diff --git a/Telegram/SourceFiles/boxes/about_box.h b/Telegram/SourceFiles/boxes/about_box.h index 4010000bb..3ec637de2 100644 --- a/Telegram/SourceFiles/boxes/about_box.h +++ b/Telegram/SourceFiles/boxes/about_box.h @@ -14,7 +14,7 @@ class LinkButton; class FlatLabel; } // namespace Ui -class AboutBox : public BoxContent { +class AboutBox : public Ui::BoxContent { public: AboutBox(QWidget*); diff --git a/Telegram/SourceFiles/boxes/abstract_box.cpp b/Telegram/SourceFiles/boxes/abstract_box.cpp index 76bfe2582..d9d7b7761 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.cpp +++ b/Telegram/SourceFiles/boxes/abstract_box.cpp @@ -7,606 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/abstract_box.h" -#include "styles/style_boxes.h" -#include "styles/style_profile.h" -#include "storage/localstorage.h" -#include "lang/lang_keys.h" -#include "ui/effects/radial_animation.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/scroll_area.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/shadow.h" -#include "ui/wrap/fade_wrap.h" -#include "ui/text/text_utilities.h" -#include "ui/painter.h" -#include "base/timer.h" #include "mainwidget.h" #include "mainwindow.h" #include "app.h" -struct AbstractBox::LoadingProgress { - LoadingProgress( - Fn &&callback, - const style::InfiniteRadialAnimation &st); - - Ui::InfiniteRadialAnimation animation; - base::Timer removeTimer; -}; - -AbstractBox::LoadingProgress::LoadingProgress( - Fn &&callback, - const style::InfiniteRadialAnimation &st) -: animation(std::move(callback), st) { -} - -void BoxContent::setTitle(rpl::producer title) { - getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities()); -} - -QPointer BoxContent::addButton( - rpl::producer text, - Fn clickCallback) { - return addButton( - std::move(text), - std::move(clickCallback), - st::defaultBoxButton); -} - -QPointer BoxContent::addLeftButton( - rpl::producer text, - Fn clickCallback) { - return getDelegate()->addLeftButton( - std::move(text), - std::move(clickCallback), - st::defaultBoxButton); -} - -void BoxContent::setInner(object_ptr inner) { - setInner(std::move(inner), st::boxLayerScroll); -} - -void BoxContent::setInner(object_ptr inner, const style::ScrollArea &st) { - if (inner) { - getDelegate()->setLayerType(true); - _scroll.create(this, st); - _scroll->setGeometryToLeft(0, _innerTopSkip, width(), 0); - _scroll->setOwnedWidget(std::move(inner)); - if (_topShadow) { - _topShadow->raise(); - _bottomShadow->raise(); - } else { - _topShadow.create(this); - _bottomShadow.create(this); - } - if (!_preparing) { - // We didn't set dimensions yet, this will be called from finishPrepare(); - finishScrollCreate(); - } - } else { - getDelegate()->setLayerType(false); - _scroll.destroyDelayed(); - _topShadow.destroyDelayed(); - _bottomShadow.destroyDelayed(); - } -} - -void BoxContent::finishPrepare() { - _preparing = false; - if (_scroll) { - finishScrollCreate(); - } - setInnerFocus(); -} - -void BoxContent::finishScrollCreate() { - Expects(_scroll != nullptr); - - if (!_scroll->isHidden()) { - _scroll->show(); - } - updateScrollAreaGeometry(); - connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize())); -} - -void BoxContent::scrollToWidget(not_null widget) { - if (_scroll) { - _scroll->scrollToWidget(widget); - } -} - -void BoxContent::onScrollToY(int top, int bottom) { - if (_scroll) { - _scroll->scrollToY(top, bottom); - } -} - -void BoxContent::onDraggingScrollDelta(int delta) { - _draggingScrollDelta = _scroll ? delta : 0; - if (_draggingScrollDelta) { - if (!_draggingScrollTimer) { - _draggingScrollTimer.create(this); - _draggingScrollTimer->setSingleShot(false); - connect(_draggingScrollTimer, SIGNAL(timeout()), this, SLOT(onDraggingScrollTimer())); - } - _draggingScrollTimer->start(15); - } else { - _draggingScrollTimer.destroy(); - } -} - -void BoxContent::onDraggingScrollTimer() { - auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed)); - _scroll->scrollToY(_scroll->scrollTop() + delta); -} - -void BoxContent::updateInnerVisibleTopBottom() { - if (auto widget = static_cast(_scroll ? _scroll->widget() : nullptr)) { - auto top = _scroll->scrollTop(); - widget->setVisibleTopBottom(top, top + _scroll->height()); - } -} - -void BoxContent::updateShadowsVisibility() { - if (!_scroll) return; - - auto top = _scroll->scrollTop(); - _topShadow->toggle( - (top > 0 || _innerTopSkip > 0), - anim::type::normal); - _bottomShadow->toggle( - (top < _scroll->scrollTopMax() || _innerBottomSkip > 0), - anim::type::normal); -} - -void BoxContent::onScroll() { - updateInnerVisibleTopBottom(); - updateShadowsVisibility(); -} - -void BoxContent::onInnerResize() { - updateInnerVisibleTopBottom(); - updateShadowsVisibility(); -} - -void BoxContent::setDimensionsToContent( - int newWidth, - not_null content) { - content->resizeToWidth(newWidth); - content->heightValue( - ) | rpl::start_with_next([=](int height) { - setDimensions(newWidth, height); - }, content->lifetime()); -} - -void BoxContent::setInnerTopSkip(int innerTopSkip, bool scrollBottomFixed) { - if (_innerTopSkip != innerTopSkip) { - auto delta = innerTopSkip - _innerTopSkip; - _innerTopSkip = innerTopSkip; - if (_scroll && width() > 0) { - auto scrollTopWas = _scroll->scrollTop(); - updateScrollAreaGeometry(); - if (scrollBottomFixed) { - _scroll->scrollToY(scrollTopWas + delta); - } - } - } -} - -void BoxContent::setInnerBottomSkip(int innerBottomSkip) { - if (_innerBottomSkip != innerBottomSkip) { - auto delta = innerBottomSkip - _innerBottomSkip; - _innerBottomSkip = innerBottomSkip; - if (_scroll && width() > 0) { - updateScrollAreaGeometry(); - } - } -} - -void BoxContent::setInnerVisible(bool scrollAreaVisible) { - if (_scroll) { - _scroll->setVisible(scrollAreaVisible); - } -} - -QPixmap BoxContent::grabInnerCache() { - auto isTopShadowVisible = !_topShadow->isHidden(); - auto isBottomShadowVisible = !_bottomShadow->isHidden(); - if (isTopShadowVisible) _topShadow->setVisible(false); - if (isBottomShadowVisible) _bottomShadow->setVisible(false); - auto result = Ui::GrabWidget(this, _scroll->geometry()); - if (isTopShadowVisible) _topShadow->setVisible(true); - if (isBottomShadowVisible) _bottomShadow->setVisible(true); - return result; -} - -void BoxContent::resizeEvent(QResizeEvent *e) { - if (_scroll) { - updateScrollAreaGeometry(); - } -} - -void BoxContent::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Escape && !_closeByEscape) { - e->accept(); - } else { - RpWidget::keyPressEvent(e); - } -} - -void BoxContent::updateScrollAreaGeometry() { - auto newScrollHeight = height() - _innerTopSkip - _innerBottomSkip; - auto changed = (_scroll->height() != newScrollHeight); - _scroll->setGeometryToLeft(0, _innerTopSkip, width(), newScrollHeight); - _topShadow->entity()->resize(width(), st::lineWidth); - _topShadow->moveToLeft(0, _innerTopSkip); - _bottomShadow->entity()->resize(width(), st::lineWidth); - _bottomShadow->moveToLeft( - 0, - height() - _innerBottomSkip - st::lineWidth); - if (changed) { - updateInnerVisibleTopBottom(); - - auto top = _scroll->scrollTop(); - _topShadow->toggle( - (top > 0 || _innerTopSkip > 0), - anim::type::instant); - _bottomShadow->toggle( - (top < _scroll->scrollTopMax() || _innerBottomSkip > 0), - anim::type::instant); - } -} - -object_ptr BoxContent::doTakeInnerWidget() { - return _scroll->takeWidget(); -} - -void BoxContent::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (testAttribute(Qt::WA_OpaquePaintEvent)) { - for (auto rect : e->region().rects()) { - p.fillRect(rect, st::boxBg); - } - } -} - -AbstractBox::AbstractBox( - not_null layer, - object_ptr content) -: LayerWidget(layer) -, _layer(layer) -, _content(std::move(content)) { - _content->setParent(this); - _content->setDelegate(this); - - _additionalTitle.changes( - ) | rpl::start_with_next([=] { - updateSize(); - update(); - }, lifetime()); -} - -AbstractBox::~AbstractBox() = default; - -void AbstractBox::setLayerType(bool layerType) { - _layerType = layerType; - updateTitlePosition(); -} - -int AbstractBox::titleHeight() const { - return _layerType ? st::boxLayerTitleHeight : st::boxTitleHeight; -} - -int AbstractBox::buttonsHeight() const { - const auto padding = _layerType - ? st::boxLayerButtonPadding - : st::boxButtonPadding; - return padding.top() + st::defaultBoxButton.height + padding.bottom(); -} - -int AbstractBox::buttonsTop() const { - const auto padding = _layerType - ? st::boxLayerButtonPadding - : st::boxButtonPadding; - return height() - padding.bottom() - st::defaultBoxButton.height; -} - -QRect AbstractBox::loadingRect() const { - const auto padding = _layerType - ? st::boxLayerButtonPadding - : st::boxButtonPadding; - const auto size = st::boxLoadingSize; - const auto skipx = _layerType - ? st::boxLayerTitlePosition.x() - : st::boxTitlePosition.x(); - const auto skipy = (st::defaultBoxButton.height - size) / 2; - return QRect( - skipx, - height() - padding.bottom() - skipy - size, - size, - size); -} - -void AbstractBox::paintEvent(QPaintEvent *e) { - Painter p(this); - auto clip = e->rect(); - auto paintTopRounded = clip.intersects(QRect(0, 0, width(), st::boxRadius)); - auto paintBottomRounded = clip.intersects(QRect(0, height() - st::boxRadius, width(), st::boxRadius)); - if (paintTopRounded || paintBottomRounded) { - auto parts = RectPart::None | 0; - if (paintTopRounded) parts |= RectPart::FullTop; - if (paintBottomRounded) parts |= RectPart::FullBottom; - App::roundRect(p, rect(), st::boxBg, BoxCorners, nullptr, parts); - } - auto other = e->region().intersected(QRect(0, st::boxRadius, width(), height() - 2 * st::boxRadius)); - if (!other.isEmpty()) { - for (auto rect : other.rects()) { - p.fillRect(rect, st::boxBg); - } - } - if (!_additionalTitle.current().isEmpty() - && clip.intersects(QRect(0, 0, width(), titleHeight()))) { - paintAdditionalTitle(p); - } - if (_loadingProgress) { - const auto rect = loadingRect(); - _loadingProgress->animation.draw( - p, - rect.topLeft(), - rect.size(), - width()); - } -} - -void AbstractBox::paintAdditionalTitle(Painter &p) { - p.setFont(st::boxLayerTitleAdditionalFont); - p.setPen(st::boxTitleAdditionalFg); - p.drawTextLeft(_titleLeft + (_title ? _title->width() : 0) + st::boxLayerTitleAdditionalSkip, _titleTop + st::boxTitleFont->ascent - st::boxLayerTitleAdditionalFont->ascent, width(), _additionalTitle.current()); -} - -void AbstractBox::parentResized() { - auto newHeight = countRealHeight(); - auto parentSize = parentWidget()->size(); - setGeometry((parentSize.width() - width()) / 2, (parentSize.height() - newHeight) / 2, width(), newHeight); - update(); -} - -void AbstractBox::setTitle(rpl::producer title) { - const auto wasTitle = hasTitle(); - if (title) { - _title.create(this, std::move(title), st::boxTitle); - _title->show(); - updateTitlePosition(); - } else { - _title.destroy(); - } - if (wasTitle != hasTitle()) { - updateSize(); - } -} - -void AbstractBox::setAdditionalTitle(rpl::producer additional) { - _additionalTitle = std::move(additional); -} - -void AbstractBox::setCloseByOutsideClick(bool close) { - _closeByOutsideClick = close; -} - -bool AbstractBox::closeByOutsideClick() const { - return _closeByOutsideClick; -} - -bool AbstractBox::hasTitle() const { - return (_title != nullptr) || !_additionalTitle.current().isEmpty(); -} - -void AbstractBox::showBox( - object_ptr box, - LayerOptions options, - anim::type animated) { - _layer->showBox(std::move(box), options, animated); -} - -void AbstractBox::updateSize() { - setDimensions(width(), _maxContentHeight); -} - -void AbstractBox::updateButtonsPositions() { - if (!_buttons.empty() || _leftButton) { - auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding; - auto right = padding.right(); - auto top = buttonsTop(); - if (_leftButton) { - _leftButton->moveToLeft(right, top); - } - for (const auto &button : _buttons) { - button->moveToRight(right, top); - right += button->width() + padding.left(); - } - } - if (_topButton) { - _topButton->moveToRight(0, 0); - } -} - -QPointer AbstractBox::outerContainer() { - return parentWidget(); -} - -void AbstractBox::updateTitlePosition() { - _titleLeft = _layerType ? st::boxLayerTitlePosition.x() : st::boxTitlePosition.x(); - _titleTop = _layerType ? st::boxLayerTitlePosition.y() : st::boxTitlePosition.y(); - if (_title) { - _title->resizeToWidth(qMin(_title->naturalWidth(), width() - _titleLeft * 2)); - _title->moveToLeft(_titleLeft, _titleTop); - } -} - -void AbstractBox::clearButtons() { - for (auto &button : base::take(_buttons)) { - button.destroy(); - } - _leftButton.destroy(); - _topButton = nullptr; -} - -QPointer AbstractBox::addButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) { - _buttons.emplace_back(this, std::move(text), st); - auto result = QPointer(_buttons.back()); - result->setClickedCallback(std::move(clickCallback)); - result->show(); - result->widthValue( - ) | rpl::start_with_next([=] { - updateButtonsPositions(); - }, result->lifetime()); - return result; -} - -QPointer AbstractBox::addLeftButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) { - _leftButton = object_ptr(this, std::move(text), st); - auto result = QPointer(_leftButton); - result->setClickedCallback(std::move(clickCallback)); - result->show(); - result->widthValue( - ) | rpl::start_with_next([=] { - updateButtonsPositions(); - }, result->lifetime()); - return result; -} - -QPointer AbstractBox::addTopButton(const style::IconButton &st, Fn clickCallback) { - _topButton = base::make_unique_q(this, st); - auto result = QPointer(_topButton.get()); - result->setClickedCallback(std::move(clickCallback)); - result->show(); - updateButtonsPositions(); - return result; -} - -void AbstractBox::showLoading(bool show) { - const auto &st = st::boxLoadingAnimation; - if (!show) { - if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) { - _loadingProgress->removeTimer.callOnce( - st.sineDuration + st.sinePeriod); - _loadingProgress->animation.stop(); - } - return; - } - if (!_loadingProgress) { - const auto callback = [=] { - if (!anim::Disabled()) { - const auto t = st::boxLoadingAnimation.thickness; - update(loadingRect().marginsAdded({ t, t, t, t })); - } - }; - _loadingProgress = std::make_unique( - callback, - st::boxLoadingAnimation); - _loadingProgress->removeTimer.setCallback([=] { - _loadingProgress = nullptr; - }); - } else { - _loadingProgress->removeTimer.cancel(); - } - _loadingProgress->animation.start(); -} - - -void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) { - _maxContentHeight = maxHeight; - - auto fullHeight = countFullHeight(); - if (width() != newWidth || _fullHeight != fullHeight) { - _fullHeight = fullHeight; - if (parentWidget()) { - auto oldGeometry = geometry(); - resize(newWidth, countRealHeight()); - auto newGeometry = geometry(); - auto parentHeight = parentWidget()->height(); - if (newGeometry.top() + newGeometry.height() + st::boxVerticalMargin > parentHeight - || forceCenterPosition) { - const auto top1 = parentHeight - int(st::boxVerticalMargin) - newGeometry.height(); - const auto top2 = (parentHeight - newGeometry.height()) / 2; - const auto newTop = forceCenterPosition - ? std::min(top1, top2) - : std::max(top1, top2); - if (newTop != newGeometry.top()) { - move(newGeometry.left(), newTop); - resizeEvent(0); - } - } - parentWidget()->update(oldGeometry.united(geometry()).marginsAdded(st::boxRoundShadow.extend)); - } else { - resize(newWidth, 0); - } - } -} - -int AbstractBox::countRealHeight() const { - return qMin(_fullHeight, parentWidget()->height() - 2 * st::boxVerticalMargin); -} - -int AbstractBox::countFullHeight() const { - return contentTop() + _maxContentHeight + buttonsHeight(); -} - -int AbstractBox::contentTop() const { - return hasTitle() ? titleHeight() : (_noContentMargin ? 0 : st::boxTopMargin); -} - -void AbstractBox::resizeEvent(QResizeEvent *e) { - updateButtonsPositions(); - updateTitlePosition(); - - auto top = contentTop(); - _content->resize(width(), height() - top - buttonsHeight()); - _content->moveToLeft(0, top); - - LayerWidget::resizeEvent(e); -} - -void AbstractBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Escape) { - closeBox(); - } else { - LayerWidget::keyPressEvent(e); - } -} - -BoxContentDivider::BoxContentDivider(QWidget *parent) -: BoxContentDivider(parent, st::rightsDividerHeight) { -} - -BoxContentDivider::BoxContentDivider(QWidget *parent, int height) -: RpWidget(parent) { - resize(width(), height); -} - -void BoxContentDivider::paintEvent(QPaintEvent *e) { - Painter p(this); - p.fillRect(e->rect(), st::contactsAboutBg); - auto dividerFillTop = myrtlrect(0, 0, width(), st::profileDividerTop.height()); - st::profileDividerTop.fill(p, dividerFillTop); - auto dividerFillBottom = myrtlrect(0, height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height()); - st::profileDividerBottom.fill(p, dividerFillBottom); -} - namespace Ui { namespace internal { void showBox( - object_ptr content, - LayerOptions options, - anim::type animated) { + object_ptr content, + LayerOptions options, + anim::type animated) { if (auto w = App::wnd()) { w->ui_showBox(std::move(content), options, animated); } @@ -634,14 +45,4 @@ bool isLayerShown() { return false; } -int DividerLabel::naturalWidth() const { - return -1; -} - -void DividerLabel::resizeEvent(QResizeEvent *e) { - _background->lower(); - _background->setGeometry(rect()); - return PaddingWrap::resizeEvent(e); -} - } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h index d08b97c22..95d15f038 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.h +++ b/Telegram/SourceFiles/boxes/abstract_box.h @@ -7,14 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "window/layer_widget.h" -#include "base/unique_qptr.h" -#include "base/flags.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/widgets/labels.h" -#include "ui/effects/animation_value.h" -#include "ui/text/text_entity.h" -#include "ui/rp_widget.h" +#include "ui/layers/box_layer_widget.h" class Painter; @@ -32,413 +25,13 @@ class FlatLabel; class FadeShadow; } // namespace Ui -class BoxContent; - -class BoxContentDelegate { -public: - virtual void setLayerType(bool layerType) = 0; - virtual void setTitle(rpl::producer title) = 0; - virtual void setAdditionalTitle(rpl::producer additional) = 0; - virtual void setCloseByOutsideClick(bool close) = 0; - - virtual void clearButtons() = 0; - virtual QPointer addButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) = 0; - virtual QPointer addLeftButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) = 0; - virtual QPointer addTopButton( - const style::IconButton &st, - Fn clickCallback) = 0; - virtual void showLoading(bool show) = 0; - virtual void updateButtonsPositions() = 0; - - virtual void showBox( - object_ptr box, - LayerOptions options, - anim::type animated) = 0; - virtual void setDimensions( - int newWidth, - int maxHeight, - bool forceCenterPosition = false) = 0; - virtual void setNoContentMargin(bool noContentMargin) = 0; - virtual bool isBoxShown() const = 0; - virtual void closeBox() = 0; - - template - QPointer show( - object_ptr content, - LayerOptions options = LayerOption::KeepOther, - anim::type animated = anim::type::normal) { - auto result = QPointer(content.data()); - showBox(std::move(content), options, animated); - return result; - } - - virtual QPointer outerContainer() = 0; - -}; - -class BoxContent : public Ui::RpWidget { - Q_OBJECT - -public: - BoxContent() { - setAttribute(Qt::WA_OpaquePaintEvent); - } - - bool isBoxShown() const { - return getDelegate()->isBoxShown(); - } - void closeBox() { - getDelegate()->closeBox(); - } - - void setTitle(rpl::producer title); - void setTitle(rpl::producer title) { - getDelegate()->setTitle(std::move(title)); - } - void setAdditionalTitle(rpl::producer additional) { - getDelegate()->setAdditionalTitle(std::move(additional)); - } - void setCloseByEscape(bool close) { - _closeByEscape = close; - } - void setCloseByOutsideClick(bool close) { - getDelegate()->setCloseByOutsideClick(close); - } - - void scrollToWidget(not_null widget); - - void clearButtons() { - getDelegate()->clearButtons(); - } - QPointer addButton( - rpl::producer text, - Fn clickCallback = nullptr); - QPointer addLeftButton( - rpl::producer text, - Fn clickCallback = nullptr); - QPointer addTopButton( - const style::IconButton &st, - Fn clickCallback = nullptr) { - return getDelegate()->addTopButton(st, std::move(clickCallback)); - } - QPointer addButton( - rpl::producer text, - const style::RoundButton &st) { - return getDelegate()->addButton(std::move(text), nullptr, st); - } - QPointer addButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) { - return getDelegate()->addButton( - std::move(text), - std::move(clickCallback), - st); - } - void showLoading(bool show) { - getDelegate()->showLoading(show); - } - void updateButtonsGeometry() { - getDelegate()->updateButtonsPositions(); - } - - virtual void setInnerFocus() { - setFocus(); - } - - rpl::producer<> boxClosing() const { - return _boxClosingStream.events(); - } - void notifyBoxClosing() { - _boxClosingStream.fire({}); - } - - void setDelegate(not_null newDelegate) { - _delegate = newDelegate; - _preparing = true; - prepare(); - finishPrepare(); - } - not_null getDelegate() const { - return _delegate; - } - -public slots: - void onScrollToY(int top, int bottom = -1); - - void onDraggingScrollDelta(int delta); - -protected: - virtual void prepare() = 0; - - void setLayerType(bool layerType) { - getDelegate()->setLayerType(layerType); - } - - void setNoContentMargin(bool noContentMargin) { - if (_noContentMargin != noContentMargin) { - _noContentMargin = noContentMargin; - setAttribute(Qt::WA_OpaquePaintEvent, !_noContentMargin); - } - getDelegate()->setNoContentMargin(noContentMargin); - } - void setDimensions( - int newWidth, - int maxHeight, - bool forceCenterPosition = false) { - getDelegate()->setDimensions( - newWidth, - maxHeight, - forceCenterPosition); - } - void setDimensionsToContent( - int newWidth, - not_null content); - void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false); - void setInnerBottomSkip(int bottomSkip); - - template - QPointer setInnerWidget( - object_ptr inner, - const style::ScrollArea &st, - int topSkip = 0, - int bottomSkip = 0) { - auto result = QPointer(inner.data()); - setInnerTopSkip(topSkip); - setInnerBottomSkip(bottomSkip); - setInner(std::move(inner), st); - return result; - } - - template - QPointer setInnerWidget( - object_ptr inner, - int topSkip = 0, - int bottomSkip = 0) { - auto result = QPointer(inner.data()); - setInnerTopSkip(topSkip); - setInnerBottomSkip(bottomSkip); - setInner(std::move(inner)); - return result; - } - - template - object_ptr takeInnerWidget() { - return object_ptr::fromRaw( - static_cast(doTakeInnerWidget().release())); - } - - void setInnerVisible(bool scrollAreaVisible); - QPixmap grabInnerCache(); - - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - -private slots: - void onScroll(); - void onInnerResize(); - - void onDraggingScrollTimer(); - -private: - void finishPrepare(); - void finishScrollCreate(); - void setInner(object_ptr inner); - void setInner(object_ptr inner, const style::ScrollArea &st); - void updateScrollAreaGeometry(); - void updateInnerVisibleTopBottom(); - void updateShadowsVisibility(); - object_ptr doTakeInnerWidget(); - - BoxContentDelegate *_delegate = nullptr; - - bool _preparing = false; - bool _noContentMargin = false; - bool _closeByEscape = true; - int _innerTopSkip = 0; - int _innerBottomSkip = 0; - object_ptr _scroll = { nullptr }; - object_ptr _topShadow = { nullptr }; - object_ptr _bottomShadow = { nullptr }; - - object_ptr _draggingScrollTimer = { nullptr }; - int _draggingScrollDelta = 0; - - rpl::event_stream<> _boxClosingStream; - -}; - -class AbstractBox : public Window::LayerWidget, public BoxContentDelegate { -public: - AbstractBox( - not_null layer, - object_ptr content); - ~AbstractBox(); - - void parentResized() override; - - void setLayerType(bool layerType) override; - void setTitle(rpl::producer title) override; - void setAdditionalTitle(rpl::producer additional) override; - void showBox( - object_ptr box, - LayerOptions options, - anim::type animated) override; - - void clearButtons() override; - QPointer addButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) override; - QPointer addLeftButton( - rpl::producer text, - Fn clickCallback, - const style::RoundButton &st) override; - QPointer addTopButton( - const style::IconButton &st, - Fn clickCallback) override; - void showLoading(bool show) override; - void updateButtonsPositions() override; - QPointer outerContainer() override; - - void setDimensions( - int newWidth, - int maxHeight, - bool forceCenterPosition = false) override; - - void setNoContentMargin(bool noContentMargin) override { - if (_noContentMargin != noContentMargin) { - _noContentMargin = noContentMargin; - updateSize(); - } - } - - bool isBoxShown() const override { - return !isHidden(); - } - void closeBox() override { - closeLayer(); - } - - void setCloseByOutsideClick(bool close) override; - bool closeByOutsideClick() const override; - -protected: - void keyPressEvent(QKeyEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - - void doSetInnerFocus() override { - _content->setInnerFocus(); - } - void closeHook() override { - _content->notifyBoxClosing(); - } - -private: - struct LoadingProgress; - - void paintAdditionalTitle(Painter &p); - void updateTitlePosition(); - - [[nodiscard]] bool hasTitle() const; - [[nodiscard]] int titleHeight() const; - [[nodiscard]] int buttonsHeight() const; - [[nodiscard]] int buttonsTop() const; - [[nodiscard]] int contentTop() const; - [[nodiscard]] int countFullHeight() const; - [[nodiscard]] int countRealHeight() const; - [[nodiscard]] QRect loadingRect() const; - void updateSize(); - - not_null _layer; - int _fullHeight = 0; - - bool _noContentMargin = false; - int _maxContentHeight = 0; - object_ptr _content; - - object_ptr _title = { nullptr }; - Fn _titleFactory; - rpl::variable _additionalTitle; - int _titleLeft = 0; - int _titleTop = 0; - bool _layerType = false; - bool _closeByOutsideClick = true; - - std::vector> _buttons; - object_ptr _leftButton = { nullptr }; - base::unique_qptr _topButton = { nullptr }; - std::unique_ptr _loadingProgress; - -}; - -class BoxContentDivider : public Ui::RpWidget { -public: - BoxContentDivider(QWidget *parent); - BoxContentDivider(QWidget *parent, int height); - -protected: - void paintEvent(QPaintEvent *e) override; - -}; - -class BoxPointer { -public: - BoxPointer() = default; - BoxPointer(const BoxPointer &other) = default; - BoxPointer(BoxPointer &&other) : _value(base::take(other._value)) { - } - BoxPointer &operator=(const BoxPointer &other) { - if (_value != other._value) { - destroy(); - _value = other._value; - } - return *this; - } - BoxPointer &operator=(BoxPointer &&other) { - if (_value != other._value) { - destroy(); - _value = base::take(other._value); - } - return *this; - } - BoxPointer &operator=(BoxContent *other) { - if (_value != other) { - destroy(); - _value = other; - } - return *this; - } - ~BoxPointer() { - destroy(); - } - -private: - void destroy() { - if (const auto value = base::take(_value)) { - value->closeBox(); - } - } - - QPointer _value; - -}; - // Legacy global method. namespace Ui { namespace internal { void showBox( object_ptr content, - LayerOptions options, + Ui::LayerOptions options, anim::type animated); } // namespace internal @@ -446,7 +39,7 @@ void showBox( template QPointer show( object_ptr content, - LayerOptions options = LayerOption::CloseOther, + Ui::LayerOptions options = Ui::LayerOption::CloseOther, anim::type animated = anim::type::normal) { auto result = QPointer(content.data()); internal::showBox(std::move(content), options, animated); @@ -457,19 +50,4 @@ void hideLayer(anim::type animated = anim::type::normal); void hideSettingsAndLayer(anim::type animated = anim::type::normal); bool isLayerShown(); -class DividerLabel : public PaddingWrap { -public: - using PaddingWrap::PaddingWrap; - - int naturalWidth() const override; - -protected: - void resizeEvent(QResizeEvent *e) override; - -private: - object_ptr _background - = object_ptr(this); - -}; - } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 728afe6ed..9d5356464 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/add_contact_box.h" -#include "styles/style_boxes.h" -#include "styles/style_dialogs.h" #include "lang/lang_keys.h" #include "mtproto/sender.h" #include "base/flat_set.h" @@ -42,6 +40,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "observer_peer.h" #include "main/main_session.h" #include "facades.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_dialogs.h" #include #include @@ -164,7 +165,7 @@ void ShowAddParticipantsError( tr::lng_cant_invite_make_admin(tr::now), tr::lng_cancel(tr::now), makeAdmin), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -199,7 +200,7 @@ void ShowAddParticipantsError( } return tr::lng_failed_add_participant(tr::now); }(); - Ui::show(Box(text), LayerOption::KeepOther); + Ui::show(Box(text), Ui::LayerOption::KeepOther); } class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender { @@ -588,16 +589,16 @@ void GroupInfoBox::createGroup( } else if (error.type() == qstr("USERS_TOO_FEW")) { Ui::show( Box(tr::lng_cant_invite_privacy(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } else if (error.type() == qstr("PEER_FLOOD")) { Ui::show( Box( PeerFloodErrorText(PeerFloodType::InviteGroup)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } else if (error.type() == qstr("USER_RESTRICTED")) { Ui::show( Box(tr::lng_cant_do_this(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } }).send(); } @@ -636,7 +637,7 @@ void GroupInfoBox::submit() { Box( std::make_unique(_navigation), std::move(initBox)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } @@ -1031,7 +1032,7 @@ void SetupChannelBox::privacyChanged(Privacy value) { Box( &_channel->session(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } _link->show(); @@ -1126,14 +1127,14 @@ void SetupChannelBox::showRevokePublicLinkBoxForEdit() { const auto callback = [=] { Ui::show( Box(navigation, channel, existing), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }; closeBox(); Ui::show( Box( &channel->session(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool SetupChannelBox::onFirstCheckFail(const RPCError &error) { @@ -1412,7 +1413,7 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) { callback(); } }).send(); - })), LayerOption::KeepOther); + })), Ui::LayerOption::KeepOther); } } diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index 76231b4be..e97eff55e 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -54,7 +54,7 @@ void ShowAddParticipantsError( not_null chat, const std::vector> &users); -class AddContactBox : public BoxContent { +class AddContactBox : public Ui::BoxContent { public: AddContactBox(QWidget*, not_null session); AddContactBox( @@ -94,7 +94,7 @@ private: }; -class GroupInfoBox : public BoxContent, private MTP::Sender { +class GroupInfoBox : public Ui::BoxContent, private MTP::Sender { public: enum class Type { Group, @@ -140,7 +140,7 @@ private: }; class SetupChannelBox - : public BoxContent + : public Ui::BoxContent , public RPCSender , private base::Subscriber { public: @@ -209,7 +209,7 @@ private: }; -class EditNameBox : public BoxContent, public RPCSender { +class EditNameBox : public Ui::BoxContent, public RPCSender { public: EditNameBox(QWidget*, not_null user); @@ -238,7 +238,7 @@ private: }; class RevokePublicLinkBox - : public BoxContent + : public Ui::BoxContent , public RPCSender , private base::Subscriber { public: diff --git a/Telegram/SourceFiles/boxes/auto_download_box.cpp b/Telegram/SourceFiles/boxes/auto_download_box.cpp index 7ba178507..bc4351e33 100644 --- a/Telegram/SourceFiles/boxes/auto_download_box.cpp +++ b/Telegram/SourceFiles/boxes/auto_download_box.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "settings/settings_common.h" #include "export/view/export_view_settings.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_settings.h" diff --git a/Telegram/SourceFiles/boxes/auto_download_box.h b/Telegram/SourceFiles/boxes/auto_download_box.h index 50fefad68..617a4e203 100644 --- a/Telegram/SourceFiles/boxes/auto_download_box.h +++ b/Telegram/SourceFiles/boxes/auto_download_box.h @@ -19,7 +19,7 @@ enum class Source; } // namespace AutoDownload } // namespace Data -class AutoDownloadBox : public BoxContent { +class AutoDownloadBox : public Ui::BoxContent { public: AutoDownloadBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/auto_lock_box.cpp b/Telegram/SourceFiles/boxes/auto_lock_box.cpp index cc04ccb77..9cbfbbf25 100644 --- a/Telegram/SourceFiles/boxes/auto_lock_box.cpp +++ b/Telegram/SourceFiles/boxes/auto_lock_box.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "ui/widgets/checkbox.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" AutoLockBox::AutoLockBox(QWidget*, not_null session) diff --git a/Telegram/SourceFiles/boxes/auto_lock_box.h b/Telegram/SourceFiles/boxes/auto_lock_box.h index 903510236..13cd573d5 100644 --- a/Telegram/SourceFiles/boxes/auto_lock_box.h +++ b/Telegram/SourceFiles/boxes/auto_lock_box.h @@ -17,7 +17,7 @@ namespace Ui { class Radiobutton; } // namespace Ui -class AutoLockBox : public BoxContent { +class AutoLockBox : public Ui::BoxContent { public: AutoLockBox(QWidget*, not_null session); diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 7dd55753b..9d91c177a 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "app.h" #include "styles/style_overview.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" @@ -144,7 +145,7 @@ void BackgroundBox::prepare() { ) | rpl::start_with_next([=](const Data::WallPaper &paper) { Ui::show( Box(_session, paper), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }, _inner->lifetime()); _inner->removeRequests( @@ -154,7 +155,7 @@ void BackgroundBox::prepare() { } void BackgroundBox::removePaper(const Data::WallPaper &paper) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto session = _session; const auto remove = [=, weak = Ui::MakeWeak(this)]{ if (*box) { @@ -176,7 +177,7 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) { tr::lng_selected_delete(tr::now), tr::lng_cancel(tr::now), remove), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } BackgroundBox::Inner::Inner( diff --git a/Telegram/SourceFiles/boxes/background_box.h b/Telegram/SourceFiles/boxes/background_box.h index 4dbfae467..dbfd8439e 100644 --- a/Telegram/SourceFiles/boxes/background_box.h +++ b/Telegram/SourceFiles/boxes/background_box.h @@ -17,7 +17,7 @@ namespace Data { class WallPaper; } // namespace Data -class BackgroundBox : public BoxContent { +class BackgroundBox : public Ui::BoxContent { public: BackgroundBox(QWidget*, not_null session); diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index 908efa695..61c3a1c05 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/background_preview_box.h" #include "app.h" #include "styles/style_history.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h index 879cc0d02..f0c929de1 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.h +++ b/Telegram/SourceFiles/boxes/background_preview_box.h @@ -24,7 +24,7 @@ class Checkbox; } // namespace Ui class BackgroundPreviewBox - : public BoxContent + : public Ui::BoxContent , private HistoryView::SimpleElementDelegate , private base::Subscriber { public: diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 9faae34d5..69b46a1d7 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ using "ui/basic.style"; +using "ui/layers/layers.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; @@ -31,135 +32,6 @@ FeedUserpicButton { innerPart: UserpicButton; } -ServiceCheck { - margin: margins; - diameter: pixels; - shift: pixels; - thickness: pixels; - tip: point; - small: pixels; - large: pixels; - stroke: pixels; - color: color; - duration: int; -} - -boxDuration: 200; -boxRadius: 3px; - -boxButtonFont: font(boxFontSize semibold); -defaultBoxButton: RoundButton(defaultLightButton) { - width: -24px; - height: 36px; - font: boxButtonFont; -} - -boxTextStyle: TextStyle(defaultTextStyle) { - font: font(boxFontSize); - linkFont: font(boxFontSize); - linkFontOver: font(boxFontSize underline); -} - -boxLabelStyle: TextStyle(boxTextStyle) { - lineHeight: 22px; -} - -attentionBoxButton: RoundButton(defaultBoxButton) { - textFg: attentionButtonFg; - textFgOver: attentionButtonFgOver; - textBgOver: attentionButtonBgOver; - - ripple: RippleAnimation(defaultRippleAnimation) { - color: attentionButtonBgRipple; - } -} - -defaultBoxCheckbox: Checkbox(defaultCheckbox) { - width: -46px; - textPosition: point(12px, 1px); - style: boxTextStyle; -} - -boxRoundShadow: Shadow { - left: icon {{ "round_shadow_box_left", windowShadowFg }}; - topLeft: icon {{ "round_shadow_box_top_left", windowShadowFg }}; - top: icon {{ "round_shadow_box_top", windowShadowFg }}; - topRight: icon {{ "round_shadow_box_top_left-flip_horizontal", windowShadowFg }}; - right: icon {{ "round_shadow_box_left-flip_horizontal", windowShadowFg }}; - bottomRight: icon {{ "round_shadow_box_bottom_left-flip_horizontal", windowShadowFg }}; - bottom: icon {{ "round_shadow_box_bottom", windowShadowFg }}; - bottomLeft: icon {{ "round_shadow_box_bottom_left", windowShadowFg }}; - extend: margins(10px, 10px, 10px, 10px); - fallback: windowShadowFgFallback; -} - -boxTitleFont: font(17px semibold); -boxTitle: FlatLabel(defaultFlatLabel) { - textFg: boxTitleFg; - maxHeight: 24px; - style: TextStyle(defaultTextStyle) { - font: boxTitleFont; - linkFont: boxTitleFont; - linkFontOver: font(17px semibold underline); - } -} -boxTitlePosition: point(23px, 16px); -boxTitleHeight: 56px; -boxLayerTitlePosition: point(23px, 16px); -boxLayerTitleHeight: 56px; -boxLayerTitleAdditionalSkip: 9px; -boxLayerTitleAdditionalFont: normalFont; -boxLayerScroll: defaultSolidScroll; - -boxRowPadding: margins(23px, 0px, 23px, 0px); - -boxTopMargin: 6px; - -boxTitleClose: IconButton(defaultIconButton) { - width: boxTitleHeight; - height: boxTitleHeight; - - icon: boxTitleCloseIcon; - iconOver: boxTitleCloseIconOver; - - rippleAreaPosition: point(6px, 6px); - rippleAreaSize: 44px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: windowBgOver; - } -} - -boxLinkButton: LinkButton(defaultLinkButton) { - font: boxTextFont; - overFont: font(boxFontSize underline); -} - -boxOptionListPadding: margins(0px, 0px, 0px, 0px); -boxOptionListSkip: 20px; -boxOptionInputSkip: 6px; - -boxVerticalMargin: 10px; -boxWidth: 320px; -boxWideWidth: 364px; -boxPadding: margins(23px, 30px, 23px, 8px); -boxMaxListHeight: 492px; -boxLittleSkip: 10px; -boxMediumSkip: 20px; - -boxButtonPadding: margins(8px, 12px, 13px, 12px); -boxLayerButtonPadding: margins(8px, 8px, 8px, 8px); -boxLabel: FlatLabel(defaultFlatLabel) { - minWidth: 274px; - align: align(topleft); - style: boxLabelStyle; -} -boxDividerLabel: FlatLabel(boxLabel) { - minWidth: 245px; - align: align(topleft); - textFg: windowSubTextFg; - style: defaultTextStyle; -} - countryRowHeight: 36px; countryRowNameFont: semiboldFont; countryRowNameFg: boxTextFg; @@ -204,12 +76,6 @@ defaultFeedUserpicButton: FeedUserpicButton { innerPart: defaultUserpicButton; } -boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { - color: windowSubTextFg; - thickness: 2px; -} -boxLoadingSize: 20px; - cropPointSize: 10px; cropSkip: 13px; cropMinSize: 20px; @@ -768,7 +634,7 @@ rightsToggle: Toggle(defaultToggle) { stroke: 2px; duration: 120; } -rightsDividerHeight: 10px; +rightsDividerHeight: boxDividerHeight; rightsDividerMargin: margins(0px, 0px, 0px, 20px); rightsHeaderMargin: margins(23px, 0px, 23px, 8px); rightsToggleMargin: margins(23px, 8px, 23px, 8px); diff --git a/Telegram/SourceFiles/boxes/calendar_box.h b/Telegram/SourceFiles/boxes/calendar_box.h index b718fcf0a..066d0425f 100644 --- a/Telegram/SourceFiles/boxes/calendar_box.h +++ b/Telegram/SourceFiles/boxes/calendar_box.h @@ -17,7 +17,7 @@ namespace Ui { class IconButton; } // namespace Ui -class CalendarBox : public BoxContent, private base::Subscriber { +class CalendarBox : public Ui::BoxContent, private base::Subscriber { public: CalendarBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp index 6b50813b7..9240fbf64 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace { @@ -61,7 +62,7 @@ void createErrorLabel( } // namespace -class ChangePhoneBox::EnterPhone : public BoxContent { +class ChangePhoneBox::EnterPhone : public Ui::BoxContent { public: EnterPhone(QWidget*, not_null session); @@ -89,7 +90,7 @@ private: }; -class ChangePhoneBox::EnterCode : public BoxContent { +class ChangePhoneBox::EnterCode : public Ui::BoxContent { public: EnterCode( QWidget*, @@ -214,7 +215,7 @@ void ChangePhoneBox::EnterPhone::sendPhoneDone(const QString &phoneNumber, const phoneCodeHash, codeLength, callTimeout), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool ChangePhoneBox::EnterPhone::sendPhoneFail(const QString &phoneNumber, const RPCError &error) { diff --git a/Telegram/SourceFiles/boxes/change_phone_box.h b/Telegram/SourceFiles/boxes/change_phone_box.h index 9eb6a8479..23a2feca1 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.h +++ b/Telegram/SourceFiles/boxes/change_phone_box.h @@ -13,7 +13,7 @@ namespace Main { class Session; } // namespace Main -class ChangePhoneBox : public BoxContent { +class ChangePhoneBox : public Ui::BoxContent { public: ChangePhoneBox(QWidget*, not_null session); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 1e6532f69..c7db08c40 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/confirm_box.h" -#include "styles/style_boxes.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" @@ -36,6 +35,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "observer_peer.h" #include "facades.h" #include "app.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" #include #include diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index ce31a2078..07c4971c0 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -20,7 +20,7 @@ class EmptyUserpic; } // namespace Ui class InformBox; -class ConfirmBox : public BoxContent, public ClickHandlerHost { +class ConfirmBox : public Ui::BoxContent, public ClickHandlerHost { public: ConfirmBox(QWidget*, const QString &text, FnMut confirmedCallback = FnMut(), FnMut cancelledCallback = FnMut()); ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut confirmedCallback = FnMut(), FnMut cancelledCallback = FnMut()); @@ -95,7 +95,7 @@ public: }; -class MaxInviteBox : public BoxContent, private base::Subscriber { +class MaxInviteBox : public Ui::BoxContent, private base::Subscriber { public: MaxInviteBox(QWidget*, not_null channel); @@ -123,7 +123,7 @@ private: }; -class PinMessageBox : public BoxContent, public RPCSender { +class PinMessageBox : public Ui::BoxContent, public RPCSender { public: PinMessageBox(QWidget*, not_null peer, MsgId msgId); @@ -148,7 +148,7 @@ private: }; -class DeleteMessagesBox : public BoxContent, public RPCSender { +class DeleteMessagesBox : public Ui::BoxContent, public RPCSender { public: DeleteMessagesBox( QWidget*, @@ -202,7 +202,7 @@ private: }; class ConfirmInviteBox - : public BoxContent + : public Ui::BoxContent , public RPCSender , private base::Subscriber { public: @@ -238,7 +238,7 @@ private: }; -class ConfirmDontWarnBox : public BoxContent { +class ConfirmDontWarnBox : public Ui::BoxContent { public: ConfirmDontWarnBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp index 7d7782478..8e4b8bf48 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/confirm_phone_box.h" -#include "styles/style_boxes.h" #include "boxes/confirm_box.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" @@ -20,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "numbers.h" #include "app.h" #include "lang/lang_keys.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" namespace { @@ -54,7 +55,7 @@ Locale: ") + Platform::SystemLanguage(); } // namespace void ShowPhoneBannedError(const QString &phone) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto close = [=] { if (*box) { (*box)->closeBox(); diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.h b/Telegram/SourceFiles/boxes/confirm_phone_box.h index 17aedd96c..d2963f960 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.h +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.h @@ -85,7 +85,7 @@ private: }; -class ConfirmPhoneBox : public BoxContent, public RPCSender { +class ConfirmPhoneBox : public Ui::BoxContent, public RPCSender { public: static void start(const QString &phone, const QString &hash); diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 7aa97edcd..316caefd3 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/radial_animation.h" #include "ui/text_options.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" #include "styles/style_info.h" @@ -134,7 +135,7 @@ private: }; -class ProxiesBox : public BoxContent { +class ProxiesBox : public Ui::BoxContent { public: using View = ProxiesBoxController::ItemView; @@ -166,7 +167,7 @@ private: }; -class ProxyBox : public BoxContent { +class ProxyBox : public Ui::BoxContent { public: ProxyBox( QWidget*, @@ -1029,7 +1030,7 @@ void ProxiesBoxController::ShowApplyConfirmation( if (const auto strong = box->data()) { strong->closeBox(); } - }), LayerOption::KeepOther); + }), Ui::LayerOption::KeepOther); } else { Ui::show(Box( (proxy.status() == ProxyData::Status::Unsupported @@ -1134,14 +1135,14 @@ void ProxiesBoxController::setupChecker(int id, const Checker &checker) { pointer->connect(pointer, &Connection::error, failed); } -object_ptr ProxiesBoxController::CreateOwningBox() { +object_ptr ProxiesBoxController::CreateOwningBox() { auto controller = std::make_unique(); auto box = controller->create(); Ui::AttachAsChild(box, std::move(controller)); return box; } -object_ptr ProxiesBoxController::create() { +object_ptr ProxiesBoxController::create() { auto result = Box(this); for (const auto &item : _list) { updateView(item); @@ -1249,7 +1250,7 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) { updateView(*item); } -object_ptr ProxiesBoxController::editItemBox(int id) { +object_ptr ProxiesBoxController::editItemBox(int id) { return Box(findById(id)->data, [=](const ProxyData &result) { auto i = findById(id); auto j = ranges::find( @@ -1300,7 +1301,7 @@ void ProxiesBoxController::replaceItemValue( saveDelayed(); } -object_ptr ProxiesBoxController::addNewItemBox() { +object_ptr ProxiesBoxController::addNewItemBox() { return Box(ProxyData(), [=](const ProxyData &result) { auto j = ranges::find( _list, diff --git a/Telegram/SourceFiles/boxes/connection_box.h b/Telegram/SourceFiles/boxes/connection_box.h index 66b0eaa80..37e7040f2 100644 --- a/Telegram/SourceFiles/boxes/connection_box.h +++ b/Telegram/SourceFiles/boxes/connection_box.h @@ -7,11 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/abstract_box.h" #include "base/timer.h" +#include "base/object_ptr.h" #include "mtproto/connection_abstract.h" namespace Ui { +class BoxContent; class InputField; class PortInput; class PasswordInput; @@ -32,8 +33,8 @@ public: Type type, const QMap &fields); - static object_ptr CreateOwningBox(); - object_ptr create(); + static object_ptr CreateOwningBox(); + object_ptr create(); enum class ItemState { Connecting, @@ -60,8 +61,8 @@ public: void restoreItem(int id); void shareItem(int id); void applyItem(int id); - object_ptr editItemBox(int id); - object_ptr addNewItemBox(); + object_ptr editItemBox(int id); + object_ptr addNewItemBox(); bool setProxySettings(ProxyData::Settings value); void setProxyForCalls(bool enabled); void setTryIPv6(bool enabled); diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 617388bdc..eb46cc606 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "base/unique_qptr.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_settings.h" @@ -724,7 +725,7 @@ object_ptr CreatePollBox::setupContent() { this, SendMenuType::Scheduled, send), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }; const auto updateValid = [=] { valid->fire(isValidQuestion() && options->isValid()); diff --git a/Telegram/SourceFiles/boxes/create_poll_box.h b/Telegram/SourceFiles/boxes/create_poll_box.h index cc38e5af3..57b7fa789 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.h +++ b/Telegram/SourceFiles/boxes/create_poll_box.h @@ -21,7 +21,7 @@ namespace Main { class Session; } // namespace Main -class CreatePollBox : public BoxContent { +class CreatePollBox : public Ui::BoxContent { public: struct Result { PollData poll; diff --git a/Telegram/SourceFiles/boxes/download_path_box.cpp b/Telegram/SourceFiles/boxes/download_path_box.cpp index 18ca38bc6..ecd00ff84 100644 --- a/Telegram/SourceFiles/boxes/download_path_box.cpp +++ b/Telegram/SourceFiles/boxes/download_path_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "platform/platform_specific.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" DownloadPathBox::DownloadPathBox(QWidget *parent) diff --git a/Telegram/SourceFiles/boxes/download_path_box.h b/Telegram/SourceFiles/boxes/download_path_box.h index e092ff0fb..4ff63efce 100644 --- a/Telegram/SourceFiles/boxes/download_path_box.h +++ b/Telegram/SourceFiles/boxes/download_path_box.h @@ -18,7 +18,7 @@ class Radioenum; class LinkButton; } // namespace Ui -class DownloadPathBox : public BoxContent { +class DownloadPathBox : public Ui::BoxContent { public: DownloadPathBox(QWidget *parent); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 36868c467..47f80eb78 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -29,9 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "layout.h" #include "media/clip/media_clip_reader.h" #include "storage/storage_media_prepare.h" -#include "styles/style_boxes.h" -#include "styles/style_chat_helpers.h" -#include "styles/style_history.h" #include "ui/image/image.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/checkbox.h" @@ -42,6 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "confirm_box.h" #include "facades.h" #include "app.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_chat_helpers.h" +#include "styles/style_history.h" #include @@ -493,7 +494,7 @@ void EditCaptionBox::createEditMediaButton() { if (mimeType == qstr("image/webp")) { Ui::show( Box(tr::lng_edit_media_invalid_file(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return false; } return true; @@ -521,7 +522,7 @@ void EditCaptionBox::createEditMediaButton() { || file->type == Storage::PreparedFile::AlbumType::None) { Ui::show( Box(tr::lng_edit_media_album_error(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -551,7 +552,7 @@ void EditCaptionBox::createEditMediaButton() { if (!valid) { Ui::show( Box(tr::lng_edit_media_album_error(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -687,7 +688,7 @@ bool EditCaptionBox::fileFromClipboard(not_null data) { && _isAlbum) { Ui::show( Box(tr::lng_edit_media_album_error(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return false; } diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 5dde6f010..14e7cd5c3 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -36,7 +36,7 @@ class SessionController; } // namespace Window class EditCaptionBox - : public BoxContent + : public Ui::BoxContent , public RPCSender , private base::Subscriber { public: diff --git a/Telegram/SourceFiles/boxes/edit_color_box.h b/Telegram/SourceFiles/boxes/edit_color_box.h index 46b8ccf20..28ad8cc5a 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.h +++ b/Telegram/SourceFiles/boxes/edit_color_box.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" -class EditColorBox : public BoxContent, private base::Subscriber { +class EditColorBox : public Ui::BoxContent, private base::Subscriber { public: enum class Mode { RGBA, diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 1578716bd..d693492a1 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "window/window_session_controller.h" #include "styles/style_settings.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace { @@ -170,7 +170,7 @@ void EditPrivacyBox::editExceptions( }; Ui::show( Box(std::move(controller), std::move(initBox)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } QVector EditPrivacyBox::collectResult() { diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index 80efbdbc0..1060157fd 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -23,6 +23,10 @@ template class SlideWrap; } // namespace Ui +namespace Window { +class SessionController; +} // namespace Window + class EditPrivacyBox; class EditPrivacyController { @@ -96,7 +100,7 @@ private: }; -class EditPrivacyBox : public BoxContent, private MTP::Sender { +class EditPrivacyBox : public Ui::BoxContent, private MTP::Sender { public: using Value = ApiWrap::Privacy; using Option = Value::Option; diff --git a/Telegram/SourceFiles/boxes/generic_box.cpp b/Telegram/SourceFiles/boxes/generic_box.cpp deleted file mode 100644 index 0e49abcbb..000000000 --- a/Telegram/SourceFiles/boxes/generic_box.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* -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 "boxes/generic_box.h" - -#include "ui/wrap/vertical_layout.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/wrap/wrap.h" -#include "styles/style_boxes.h" - -void GenericBox::prepare() { - _init(this); - - auto wrap = object_ptr(this, std::move(_content)); - setDimensionsToContent(_width ? _width : st::boxWidth, wrap.data()); - setInnerWidget(std::move(wrap)); -} - -void GenericBox::addSkip(int height) { - addRow(object_ptr(this, height)); -} diff --git a/Telegram/SourceFiles/boxes/generic_box.h b/Telegram/SourceFiles/boxes/generic_box.h deleted file mode 100644 index 8c02ccca6..000000000 --- a/Telegram/SourceFiles/boxes/generic_box.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "boxes/abstract_box.h" -#include "ui/wrap/vertical_layout.h" - -#include - -namespace st { -extern const style::margins &boxRowPadding; -} // namespace st - -class GenericBox : public BoxContent { -public: - // InitMethod::operator()(not_null box, InitArgs...) - // init(box, args...) - template < - typename InitMethod, - typename ...InitArgs, - typename = decltype(std::declval>()( - std::declval>(), - std::declval>()...))> - GenericBox( - QWidget*, - InitMethod &&init, - InitArgs &&...args); - - void setWidth(int width) { - _width = width; - } - void setFocusCallback(Fn callback) { - _focus = callback; - } - - int rowsCount() const { - return _content->count(); - } - - template < - typename Widget, - typename = std::enable_if_t< - std::is_base_of_v>> - Widget *insertRow( - int atPosition, - object_ptr &&child, - const style::margins &margin = st::boxRowPadding) { - return _content->insert( - atPosition, - std::move(child), - margin); - } - - template < - typename Widget, - typename = std::enable_if_t< - std::is_base_of_v>> - Widget *addRow( - object_ptr &&child, - const style::margins &margin = st::boxRowPadding) { - return _content->add(std::move(child), margin); - } - - void addSkip(int height); - - void setInnerFocus() override { - if (_focus) { - _focus(); - } - } - -protected: - void prepare() override; - -private: - template - struct Initer { - template < - typename OtherMethod, - typename ...OtherArgs, - typename = std::enable_if_t< - std::is_constructible_v>> - Initer(OtherMethod &&method, OtherArgs &&...args); - - void operator()(not_null box); - - template - void call( - not_null box, - std::index_sequence); - - InitMethod method; - std::tuple args; - }; - - template - auto MakeIniter(InitMethod &&method, InitArgs &&...args) - -> Initer, std::decay_t...>; - - FnMut)> _init; - Fn _focus; - object_ptr _content; - int _width = 0; - -}; - -template -template -GenericBox::Initer::Initer( - OtherMethod &&method, - OtherArgs &&...args) -: method(std::forward(method)) -, args(std::forward(args)...) { -} - -template -inline void GenericBox::Initer::operator()( - not_null box) { - call(box, std::make_index_sequence()); -} - -template -template -inline void GenericBox::Initer::call( - not_null box, - std::index_sequence) { - std::invoke(method, box, std::get(std::move(args))...); -} - -template -inline auto GenericBox::MakeIniter(InitMethod &&method, InitArgs &&...args) --> Initer, std::decay_t...> { - return { - std::forward(method), - std::forward(args)... - }; -} - -template -inline GenericBox::GenericBox( - QWidget*, - InitMethod &&init, - InitArgs &&...args) -: _init( - MakeIniter( - std::forward(init), - std::forward(args)...)) -, _content(this) { -} diff --git a/Telegram/SourceFiles/boxes/language_box.cpp b/Telegram/SourceFiles/boxes/language_box.cpp index c85399cbf..a169f7a1f 100644 --- a/Telegram/SourceFiles/boxes/language_box.cpp +++ b/Telegram/SourceFiles/boxes/language_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/multi_select.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/box_content_divider.h" #include "ui/text/text_entity.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" @@ -27,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "lang/lang_instance.h" #include "lang/lang_cloud_manager.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" #include "styles/style_passport.h" @@ -896,9 +898,9 @@ void Content::setupContent( }; const auto main = add(recent, false); const auto divider = content->add( - object_ptr>( + object_ptr>( content, - object_ptr(content))); + object_ptr(content))); const auto other = add(official, true); Ui::ResizeFitChild(this, content); diff --git a/Telegram/SourceFiles/boxes/language_box.h b/Telegram/SourceFiles/boxes/language_box.h index 4cc3dd56b..92eeedd40 100644 --- a/Telegram/SourceFiles/boxes/language_box.h +++ b/Telegram/SourceFiles/boxes/language_box.h @@ -16,7 +16,7 @@ class MultiSelect; struct ScrollToRequest; } // namespace Ui -class LanguageBox : public BoxContent { +class LanguageBox : public Ui::BoxContent { public: LanguageBox(QWidget*) { } diff --git a/Telegram/SourceFiles/boxes/local_storage_box.cpp b/Telegram/SourceFiles/boxes/local_storage_box.cpp index 047dcc2cb..618c1035a 100644 --- a/Telegram/SourceFiles/boxes/local_storage_box.cpp +++ b/Telegram/SourceFiles/boxes/local_storage_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "main/main_session.h" #include "layout.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace { diff --git a/Telegram/SourceFiles/boxes/local_storage_box.h b/Telegram/SourceFiles/boxes/local_storage_box.h index f00d83958..8f1d2401f 100644 --- a/Telegram/SourceFiles/boxes/local_storage_box.h +++ b/Telegram/SourceFiles/boxes/local_storage_box.h @@ -28,7 +28,7 @@ class LabelSimple; class MediaSlider; } // namespace Ui -class LocalStorageBox : public BoxContent { +class LocalStorageBox : public Ui::BoxContent { struct CreateTag { }; diff --git a/Telegram/SourceFiles/boxes/mute_settings_box.cpp b/Telegram/SourceFiles/boxes/mute_settings_box.cpp index cd0d46cf9..fb8b67b09 100644 --- a/Telegram/SourceFiles/boxes/mute_settings_box.cpp +++ b/Telegram/SourceFiles/boxes/mute_settings_box.cpp @@ -15,6 +15,7 @@ Copyright (C) 2017, Nicholas Guriev #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace { diff --git a/Telegram/SourceFiles/boxes/mute_settings_box.h b/Telegram/SourceFiles/boxes/mute_settings_box.h index fe5c1efc6..d91692410 100644 --- a/Telegram/SourceFiles/boxes/mute_settings_box.h +++ b/Telegram/SourceFiles/boxes/mute_settings_box.h @@ -12,7 +12,7 @@ Copyright (C) 2017, Nicholas Guriev /* This class implements a dialog-box with radio-buttons for pick duration of * turning off notifications from a chat. The widget is opened by a context menu * in the left list of dialogues. */ -class MuteSettingsBox : public BoxContent { +class MuteSettingsBox : public Ui::BoxContent { public: MuteSettingsBox(QWidget *parent, not_null peer); diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 1391df364..26a073c50 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -22,8 +22,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_encryption.h" #include "passport/passport_panel_edit_contact.h" #include "facades.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_passport.h" +#include "styles/style_boxes.h" namespace { @@ -375,7 +376,7 @@ void PasscodeBox::validateEmail( Lang::Hard::EmailConfirmationExpired()); weak->getDelegate()->show( std::move(box), - LayerOption::CloseOther); + Ui::LayerOption::CloseOther); } } else { errors->fire(Lang::Hard::ServerError()); @@ -531,7 +532,7 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) { if (_cloudFields.turningOff && _cloudFields.notEmptyPassport) { Assert(!_cloudFields.customCheckCallback); - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto confirmed = [=] { send(); if (*box) { @@ -738,7 +739,7 @@ void PasscodeBox::changeCloudPassword( } void PasscodeBox::suggestSecretReset(const QString &newPassword) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto resetSecretAndSave = [=] { checkPasswordHash([=](const Core::CloudPasswordResult &check) { resetSecret(check, newPassword, [=] { @@ -990,7 +991,7 @@ void RecoverBox::submit() { rpcFail(&RecoverBox::codeSubmitFail)); }); if (_notEmptyPassport) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto confirmed = [=] { send(); if (*box) { @@ -1019,7 +1020,7 @@ void RecoverBox::codeSubmitDone( _passwordCleared.fire({}); getDelegate()->show( Box(tr::lng_cloud_password_removed(tr::now)), - LayerOption::CloseOther); + Ui::LayerOption::CloseOther); } bool RecoverBox::codeSubmitFail(const RPCError &error) { @@ -1039,7 +1040,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) { _passwordCleared.fire({}); getDelegate()->show( Box(tr::lng_cloud_password_removed(tr::now)), - LayerOption::CloseOther); + Ui::LayerOption::CloseOther); return true; } else if (err == qstr("PASSWORD_RECOVERY_NA")) { closeBox(); @@ -1070,7 +1071,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern) { const auto errors = std::make_shared>(); const auto resent = std::make_shared>(); const auto requestId = std::make_shared(0); - const auto weak = std::make_shared>(); + const auto weak = std::make_shared>(); const auto reloads = std::make_shared>(); const auto cancels = std::make_shared>(); @@ -1084,7 +1085,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern) { if (*weak) { (*weak)->getDelegate()->show( Box(tr::lng_cloud_password_was_set(tr::now)), - LayerOption::CloseOther); + Ui::LayerOption::CloseOther); } }; const auto fail = [=](const RPCError &error) { @@ -1105,7 +1106,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern) { Lang::Hard::EmailConfirmationExpired()); (*weak)->getDelegate()->show( std::move(box), - LayerOption::CloseOther); + Ui::LayerOption::CloseOther); } } else { errors->fire(Lang::Hard::ServerError()); diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 7c907d523..a8ad5ea8c 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -25,7 +25,7 @@ namespace Core { struct CloudPasswordState; } // namespace Core -class PasscodeBox : public BoxContent, private MTP::Sender { +class PasscodeBox : public Ui::BoxContent, private MTP::Sender { public: PasscodeBox(QWidget*, not_null session, bool turningOff); @@ -133,7 +133,7 @@ private: QString _pattern; - QPointer _replacedBy; + QPointer _replacedBy; bool _turningOff = false; bool _cloudPwd = false; CloudFields _cloudFields; @@ -163,7 +163,7 @@ private: }; -class RecoverBox : public BoxContent, public RPCSender { +class RecoverBox : public Ui::BoxContent, public RPCSender { public: RecoverBox(QWidget*, const QString &pattern, bool notEmptyPassport); @@ -201,7 +201,7 @@ private: }; struct RecoveryEmailValidation { - object_ptr box; + object_ptr box; rpl::producer<> reloadRequests; rpl::producer<> cancelRequests; }; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 4835bf039..0542e0e34 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -7,10 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peer_list_box.h" -#include -#include "styles/style_boxes.h" -#include "styles/style_dialogs.h" -#include "styles/style_widgets.h" #include "main/main_session.h" #include "mainwidget.h" #include "ui/widgets/multi_select.h" @@ -30,6 +26,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "base/unixtime.h" #include "window/themes/window_theme.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_dialogs.h" +#include "styles/style_widgets.h" + +#include auto PaintUserpicCallback( not_null peer, diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index ceced9760..3c6b84f6d 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -752,7 +752,7 @@ private: }; class PeerListBox - : public BoxContent + : public Ui::BoxContent , public PeerListContentDelegate { public: PeerListBox( diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index bad2a093e..190c2f372 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -464,7 +464,7 @@ void AddBotToGroupBoxController::shareBotGame(not_null chat) { }(); Ui::show( Box(confirmText, std::move(send)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void AddBotToGroupBoxController::addBotToGroup(not_null chat) { @@ -472,7 +472,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null chat) { if (!megagroup->canAddMembers()) { Ui::show( Box(tr::lng_error_cant_add_member(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -482,7 +482,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null chat) { auto confirmText = tr::lng_bot_sure_invite(tr::now, lt_group, chat->name); Ui::show( Box(confirmText, send), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } auto AddBotToGroupBoxController::createRow(not_null history) diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 0ae8fadbf..08c95bc72 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -98,13 +98,13 @@ void AddParticipantsBoxController::rowClicked(not_null row) { if (!_peer->isMegagroup()) { Ui::show( Box(_peer->asChannel()), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) { Ui::show( Box(tr::lng_profile_add_more_after_create(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } @@ -210,7 +210,7 @@ void AddParticipantsBoxController::Start( Box( std::move(controller), std::move(initBox)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void AddParticipantsBoxController::Start( @@ -251,7 +251,7 @@ void AddParticipantsBoxController::Start( Box( std::move(controller), std::move(initBox)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void AddParticipantsBoxController::Start( @@ -517,19 +517,19 @@ void AddSpecialBoxController::showAdmin( Box( tr::lng_sure_add_admin_unremove(tr::now), showAdminSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show(Box( tr::lng_error_cant_add_admin_unban(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show(Box( tr::lng_error_cant_add_admin_invite(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else if (_additional.restrictedRights(user).has_value()) { @@ -540,13 +540,13 @@ void AddSpecialBoxController::showAdmin( Box( tr::lng_sure_add_admin_unremove(tr::now), showAdminSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show(Box( tr::lng_error_cant_add_admin_unban(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else if (_additional.isExternal(user)) { @@ -560,13 +560,13 @@ void AddSpecialBoxController::showAdmin( Box( text, showAdminSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show( Box(tr::lng_error_cant_add_admin_invite(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -597,7 +597,7 @@ void AddSpecialBoxController::showAdmin( }); box->setSaveCallback(SaveAdminCallback(_peer, user, done, fail)); } - _editParticipantBox = Ui::show(std::move(box), LayerOption::KeepOther); + _editParticipantBox = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } void AddSpecialBoxController::editAdminDone( @@ -669,13 +669,13 @@ void AddSpecialBoxController::showRestricted( Box( tr::lng_sure_ban_admin(tr::now), showRestrictedSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show( Box(tr::lng_error_cant_ban_admin(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -704,7 +704,7 @@ void AddSpecialBoxController::showRestricted( box->setSaveCallback( SaveRestrictedCallback(_peer, user, done, fail)); } - _editParticipantBox = Ui::show(std::move(box), LayerOption::KeepOther); + _editParticipantBox = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } void AddSpecialBoxController::editRestrictedDone( @@ -759,13 +759,13 @@ void AddSpecialBoxController::kickUser( Box( tr::lng_sure_ban_admin(tr::now), kickUserSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } else { Ui::show( Box(tr::lng_error_cant_ban_admin(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } } @@ -780,7 +780,7 @@ void AddSpecialBoxController::kickUser( user->name); _editBox = Ui::show( Box(text, kickUserSure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.h b/Telegram/SourceFiles/boxes/peers/add_participants_box.h index b1fab5a43..d15f474e8 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.h @@ -126,8 +126,8 @@ private: bool _allLoaded = false; ParticipantsAdditionalData _additional; std::unique_ptr _onlineSorter; - BoxPointer _editBox; - QPointer _editParticipantBox; + Ui::BoxPointer _editBox; + QPointer _editParticipantBox; AdminDoneCallback _adminDoneCallback; BannedDoneCallback _bannedDoneCallback; diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp index 5d2ce7e03..c7138e4af 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/edit_contact_box.h" -#include "boxes/generic_box.h" #include "data/data_user.h" #include "data/data_session.h" #include "ui/wrap/vertical_layout.h" @@ -21,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "apiwrap.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" @@ -36,7 +36,7 @@ QString UserPhone(not_null user) { } void SendRequest( - QPointer box, + QPointer box, not_null user, bool sharePhone, const QString &first, @@ -82,7 +82,7 @@ void SendRequest( class Controller { public: Controller( - not_null box, + not_null box, not_null window, not_null user); @@ -99,7 +99,7 @@ private: not_null last, bool inverted); - not_null _box; + not_null _box; not_null _window; not_null _user; Ui::Checkbox *_sharePhone = nullptr; @@ -110,7 +110,7 @@ private: }; Controller::Controller( - not_null box, + not_null box, not_null window, not_null user) : _box(box) @@ -265,7 +265,7 @@ void Controller::setupSharePhoneNumber() { } // namespace void EditContactBox( - not_null box, + not_null box, not_null window, not_null user) { box->lifetime().make_state(box, window, user)->prepare(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.h b/Telegram/SourceFiles/boxes/peers/edit_contact_box.h index da19edcf4..3f3e2c776 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.h @@ -7,14 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/layers/generic_box.h" + class UserData; -class GenericBox; namespace Window { class Controller; } // namespace Window void EditContactBox( - not_null box, + not_null box, not_null window, not_null user); diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp index 2b059460c..e813bb3f1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "facades.h" #include "main/main_session.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" @@ -145,7 +146,7 @@ void Controller::choose(not_null chat) { Ui::Text::RichLangValue)); } } - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto sure = [=] { if (*box) { (*box)->closeBox(); @@ -158,7 +159,7 @@ void Controller::choose(not_null chat) { text, tr::lng_manage_discussion_group_link(tr::now), sure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void Controller::choose(not_null chat) { @@ -177,7 +178,7 @@ void Controller::choose(not_null chat) { text.append(tr::lng_manage_discussion_group_warning( tr::now, Ui::Text::RichLangValue)); - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto sure = [=] { if (*box) { (*box)->closeBox(); @@ -193,7 +194,7 @@ void Controller::choose(not_null chat) { text, tr::lng_manage_discussion_group_link(tr::now), sure), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } object_ptr SetupAbout( @@ -256,7 +257,7 @@ object_ptr SetupCreateGroup( GroupInfoBox::Type::Megagroup, channel->name + " Chat", guarded), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }); return result; } @@ -277,7 +278,7 @@ object_ptr SetupUnlink( return result; } -object_ptr EditLinkedChatBox( +object_ptr EditLinkedChatBox( not_null navigation, not_null channel, ChannelData *chat, @@ -324,7 +325,7 @@ object_ptr EditLinkedChatBox( } // namespace -object_ptr EditLinkedChatBox( +object_ptr EditLinkedChatBox( not_null navigation, not_null channel, std::vector> &&chats, @@ -338,7 +339,7 @@ object_ptr EditLinkedChatBox( callback); } -object_ptr EditLinkedChatBox( +object_ptr EditLinkedChatBox( not_null navigation, not_null channel, not_null chat, diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h index e40864935..cf36d107b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h @@ -7,20 +7,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/abstract_box.h" +#include "base/object_ptr.h" + +namespace Ui { +class BoxContent; +} // namespace Ui namespace Window { class SessionNavigation; } // namespace Window -object_ptr EditLinkedChatBox( +object_ptr EditLinkedChatBox( not_null navigation, not_null channel, not_null chat, bool canEdit, Fn callback); -object_ptr EditLinkedChatBox( +object_ptr EditLinkedChatBox( not_null navigation, not_null channel, std::vector> &&chats, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index ff10e04ba..289708e36 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/box_content_divider.h" +#include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/text_options.h" @@ -23,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/profile/info_profile_button.h" #include "settings/settings_privacy_security.h" #include "boxes/calendar_box.h" -#include "boxes/generic_box.h" #include "boxes/confirm_box.h" #include "boxes/passcode_box.h" #include "boxes/peers/edit_peer_permissions_box.h" @@ -37,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "facades.h" #include "main/main_session.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" @@ -53,7 +55,7 @@ enum class PasswordErrorType { Later, }; -void SetCloudPassword(not_null box, not_null user) { +void SetCloudPassword(not_null box, not_null user) { user->session().api().passwordState( ) | rpl::start_with_next([=] { using namespace Settings; @@ -71,7 +73,7 @@ void SetCloudPassword(not_null box, not_null user) { } void TransferPasswordError( - not_null box, + not_null box, not_null user, PasswordErrorType error) { box->setTitle(tr::lng_rights_transfer_check()); @@ -299,7 +301,7 @@ void EditAdminBox::prepare() { : tr::lng_channel_add_admin()); addControl( - object_ptr(this), + object_ptr(this), st::rightsDividerMargin); const auto chat = peer()->asChat(); @@ -404,7 +406,7 @@ void EditAdminBox::prepare() { not_null EditAdminBox::addRankInput() { addControl( - object_ptr(this), + object_ptr(this), st::rightsRankMargin); addControl( @@ -474,7 +476,7 @@ not_null*> EditAdminBox::setupTransferButton( const auto container = wrap->entity(); container->add( - object_ptr(container), + object_ptr(container), { 0, st::infoProfileSkip, 0, st::infoProfileSkip }); container->add(EditPeerInfoBox::CreateButton( this, @@ -664,7 +666,7 @@ void EditRestrictedBox::prepare() { setTitle(tr::lng_rights_user_restrictions()); addControl( - object_ptr(this), + object_ptr(this), st::rightsDividerMargin); const auto chat = peer()->asChat(); @@ -708,7 +710,7 @@ void EditRestrictedBox::prepare() { addControl(std::move(checkboxes), QMargins()); _until = prepareRights.c_chatBannedRights().vuntil_date().v; - addControl(object_ptr(this), st::rightsUntilMargin); + addControl(object_ptr(this), st::rightsUntilMargin); addControl( object_ptr( this, @@ -759,7 +761,7 @@ void EditRestrictedBox::showRestrictUntil() { setRestrictUntil( static_cast(QDateTime(date).toTime_t())); }), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); _restrictUntilBox->setMaxDate( QDate::currentDate().addDays(kMaxRestrictDelayDays)); _restrictUntilBox->setMinDate(tomorrow); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 1804aae46..7ef9e877e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -27,7 +27,7 @@ struct CloudPasswordResult; class CalendarBox; class PasscodeBox; -class EditParticipantBox : public BoxContent { +class EditParticipantBox : public Ui::BoxContent { public: EditParticipantBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 17d408d34..09a0960f6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -874,7 +874,7 @@ void ParticipantsBoxController::Start( }; Ui::show( Box(std::move(controller), initBox), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ParticipantsBoxController::addNewItem() { @@ -907,7 +907,7 @@ void ParticipantsBoxController::addNewItem() { adminDone, restrictedDone), initBox), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ParticipantsBoxController::addNewParticipants() { @@ -929,7 +929,7 @@ void ParticipantsBoxController::addNewParticipants() { channel, { already.begin(), already.end() }); } else { - Ui::show(Box(channel), LayerOption::KeepOther); + Ui::show(Box(channel), Ui::LayerOption::KeepOther); } } @@ -1474,7 +1474,7 @@ void ParticipantsBoxController::showAdmin(not_null user) { }); box->setSaveCallback(SaveAdminCallback(_peer, user, done, fail)); } - _editParticipantBox = Ui::show(std::move(box), LayerOption::KeepOther); + _editParticipantBox = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } void ParticipantsBoxController::editAdminDone( @@ -1552,7 +1552,7 @@ void ParticipantsBoxController::showRestricted(not_null user) { box->setSaveCallback( SaveRestrictedCallback(_peer, user, done, fail)); } - _editParticipantBox = Ui::show(std::move(box), LayerOption::KeepOther); + _editParticipantBox = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } void ParticipantsBoxController::editRestrictedDone( @@ -1618,7 +1618,7 @@ void ParticipantsBoxController::kickMember(not_null user) { text, tr::lng_box_remove(tr::now), crl::guard(this, [=] { kickMemberSure(user); })), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ParticipantsBoxController::unkickMember(not_null user) { @@ -1661,7 +1661,7 @@ void ParticipantsBoxController::removeAdmin(not_null user) { user->firstName), tr::lng_box_remove(tr::now), crl::guard(this, [=] { removeAdminSure(user); })), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ParticipantsBoxController::removeAdminSure(not_null user) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index bf6772502..af0b8decc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -244,9 +244,9 @@ private: bool _allLoaded = false; ParticipantsAdditionalData _additional; std::unique_ptr _onlineSorter; - BoxPointer _editBox; - BoxPointer _addBox; - QPointer _editParticipantBox; + Ui::BoxPointer _editBox; + Ui::BoxPointer _addBox; + QPointer _editParticipantBox; }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp index f82e4f5d4..fbf4de9bf 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp @@ -13,13 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_peer.h" #include "lang/lang_keys.h" -#include "styles/style_boxes.h" -#include "styles/style_info.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "styles/style_layers.h" +#include "styles/style_info.h" namespace { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.h index 984a061e8..a66065340 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.h @@ -15,7 +15,7 @@ enum class HistoryVisibility { Hidden, }; -class EditPeerHistoryVisibilityBox : public BoxContent { +class EditPeerHistoryVisibilityBox : public Ui::BoxContent { public: EditPeerHistoryVisibilityBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index dc7b15475..56bf1dfac 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -31,14 +31,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "mtproto/sender.h" #include "observer_peer.h" -#include "styles/style_boxes.h" -#include "styles/style_info.h" #include "ui/rp_widget.h" #include "ui/special_buttons.h" #include "ui/toast/toast.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" @@ -46,6 +45,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/profile/info_profile_icon.h" #include "app.h" #include "facades.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_info.h" namespace { @@ -70,7 +72,7 @@ void AddSkip( container->add(object_ptr( container, top)); - container->add(object_ptr(container)); + container->add(object_ptr(container)); container->add(object_ptr( container, bottom)); @@ -193,7 +195,7 @@ void SaveSlowmodeSeconds( void ShowEditPermissions(not_null peer) { const auto box = Ui::show( Box(peer), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); const auto saving = box->lifetime().make_state(0); const auto save = [=]( not_null peer, @@ -244,7 +246,7 @@ class Controller public: Controller( not_null navigation, - not_null box, + not_null box, not_null peer); object_ptr createContent(); @@ -330,7 +332,7 @@ private: std::optional _signaturesSavedValue; const not_null _navigation; - const not_null _box; + const not_null _box; not_null _peer; const bool _isGroup = false; @@ -351,7 +353,7 @@ private: Controller::Controller( not_null navigation, - not_null box, + not_null box, not_null peer) : _navigation(navigation) , _box(box) @@ -559,7 +561,7 @@ object_ptr Controller::createStickersEdit() { tr::lng_group_stickers_add(tr::now), st::editPeerInviteLinkButton) )->addClickHandler([=] { - Ui::show(Box(channel), LayerOption::KeepOther); + Ui::show(Box(channel), Ui::LayerOption::KeepOther); }); return std::move(result); @@ -602,13 +604,13 @@ void Controller::showEditPeerTypeBox( _privacySavedValue, _usernameSavedValue, error), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void Controller::showEditLinkedChatBox() { Expects(_peer->isChannel()); - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto channel = _peer->asChannel(); const auto callback = [=](ChannelData *result) { if (*box) { @@ -633,7 +635,7 @@ void Controller::showEditLinkedChatBox() { chat, canEdit, callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } else if (!canEdit || _linkedChatsRequestId) { return; @@ -660,7 +662,7 @@ void Controller::showEditLinkedChatBox() { channel, std::move(chats), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }).fail([=](const RPCError &error) { _linkedChatsRequestId = 0; }).send(); @@ -749,7 +751,7 @@ void Controller::fillInviteLinkButton() { Expects(_controls.buttonsLayout != nullptr); const auto buttonCallback = [=] { - Ui::show(Box(_peer), LayerOption::KeepOther); + Ui::show(Box(_peer), Ui::LayerOption::KeepOther); }; AddButtonWithText( @@ -807,7 +809,7 @@ void Controller::fillHistoryVisibilityButton() { _peer, boxCallback, *_historyVisibilitySavedValue), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }; AddButtonWithText( container, @@ -1429,7 +1431,7 @@ void Controller::deleteWithConfirmation() { tr::lng_box_delete(tr::now), st::attentionBoxButton, deleteCallback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void Controller::deleteChannel() { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 2346277ed..59c85a48a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -28,7 +28,7 @@ class Button; } // namespace Profile } // namespace Info -class EditPeerInfoBox : public BoxContent { +class EditPeerInfoBox : public Ui::BoxContent { public: EditPeerInfoBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 740033b46..0bbe227c7 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/continuous_sliders.h" +#include "ui/widgets/box_content_divider.h" #include "ui/toast/toast.h" #include "info/profile/info_profile_button.h" #include "info/profile/info_profile_icon.h" @@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "mainwindow.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" @@ -387,7 +389,7 @@ Fn EditPeerPermissionsBox::addSlowmodeSlider( channel ? channel->slowmodeSeconds() : 0); container->add( - object_ptr(container), + object_ptr(container), { 0, st::infoProfileSkip, 0, st::infoProfileSkip }); container->add( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index c62a28a56..4faf5e468 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -15,7 +15,7 @@ class RoundButton; class VerticalLayout; } // namespace Ui -class EditPeerPermissionsBox : public BoxContent { +class EditPeerPermissionsBox : public Ui::BoxContent { public: EditPeerPermissionsBox(QWidget*, not_null peer); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index b00af3ac3..7c67aba61 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -24,8 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "mtproto/sender.h" #include "observer_peer.h" -#include "styles/style_boxes.h" -#include "styles/style_info.h" #include "ui/rp_widget.h" #include "ui/special_buttons.h" #include "ui/toast/toast.h" @@ -33,16 +31,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/special_fields.h" #include "window/window_session_controller.h" -#include +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_info.h" #include #include +#include + namespace { constexpr auto kUsernameCheckTimeout = crl::time(200); @@ -189,7 +192,7 @@ void Controller::createContent() { fillPrivaciesButtons(_wrap, _privacySavedValue); // Skip. - _wrap->add(object_ptr(_wrap)); + _wrap->add(object_ptr(_wrap)); // _wrap->add(createInviteLinkCreate()); _wrap->add(createInviteLinkEdit()); @@ -471,7 +474,7 @@ void Controller::askUsernameRevoke() { Box( &_peer->session(), std::move(revokeCallback)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void Controller::usernameChanged() { @@ -553,7 +556,7 @@ void Controller::exportInviteLink(const QString &confirmation) { auto box = Box( confirmation, std::move(callback)); - *boxPointer = Ui::show(std::move(box), LayerOption::KeepOther); + *boxPointer = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } bool Controller::canEditInviteLink() const { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 2d4dcf1cf..5e5ba890a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -35,7 +35,7 @@ enum class UsernameState { NotAvailable, }; -class EditPeerTypeBox : public BoxContent { +class EditPeerTypeBox : public Ui::BoxContent { public: // Edit just the invite link. EditPeerTypeBox(QWidget*, not_null peer); diff --git a/Telegram/SourceFiles/boxes/photo_crop_box.cpp b/Telegram/SourceFiles/boxes/photo_crop_box.cpp index 81b5347fa..918b9a41b 100644 --- a/Telegram/SourceFiles/boxes/photo_crop_box.cpp +++ b/Telegram/SourceFiles/boxes/photo_crop_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/ui_utility.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" PhotoCropBox::PhotoCropBox( diff --git a/Telegram/SourceFiles/boxes/photo_crop_box.h b/Telegram/SourceFiles/boxes/photo_crop_box.h index 2db2cd792..32ee55629 100644 --- a/Telegram/SourceFiles/boxes/photo_crop_box.h +++ b/Telegram/SourceFiles/boxes/photo_crop_box.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" -class PhotoCropBox : public BoxContent { +class PhotoCropBox : public Ui::BoxContent { public: PhotoCropBox(QWidget*, const QImage &img, const QString &title); diff --git a/Telegram/SourceFiles/boxes/rate_call_box.cpp b/Telegram/SourceFiles/boxes/rate_call_box.cpp index 30e7371b1..be102184b 100644 --- a/Telegram/SourceFiles/boxes/rate_call_box.cpp +++ b/Telegram/SourceFiles/boxes/rate_call_box.cpp @@ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/rate_call_box.h" #include "lang/lang_keys.h" -#include "styles/style_boxes.h" -#include "styles/style_calls.h" #include "boxes/confirm_box.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" @@ -17,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "main/main_session.h" #include "apiwrap.h" +#include "styles/style_layers.h" +#include "styles/style_calls.h" namespace { diff --git a/Telegram/SourceFiles/boxes/rate_call_box.h b/Telegram/SourceFiles/boxes/rate_call_box.h index 641afe177..9efaa4d53 100644 --- a/Telegram/SourceFiles/boxes/rate_call_box.h +++ b/Telegram/SourceFiles/boxes/rate_call_box.h @@ -20,7 +20,7 @@ namespace Main { class Session; } // namespace Main -class RateCallBox : public BoxContent, private MTP::Sender { +class RateCallBox : public Ui::BoxContent, private MTP::Sender { public: RateCallBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index e94be583e..5ad271db2 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/toast/toast.h" #include "mainwindow.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_profile.h" diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index 3b48cd03d..d7afc3761 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -17,7 +17,7 @@ class Radioenum; class InputField; } // namespace Ui -class ReportBox : public BoxContent, public RPCSender { +class ReportBox : public Ui::BoxContent, public RPCSender { public: ReportBox(QWidget*, not_null peer); ReportBox(QWidget*, not_null peer, MessageIdsList ids); diff --git a/Telegram/SourceFiles/boxes/self_destruction_box.cpp b/Telegram/SourceFiles/boxes/self_destruction_box.cpp index 04b02d49d..79b5cf517 100644 --- a/Telegram/SourceFiles/boxes/self_destruction_box.cpp +++ b/Telegram/SourceFiles/boxes/self_destruction_box.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "apiwrap.h" #include "main/main_session.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" SelfDestructionBox::SelfDestructionBox( diff --git a/Telegram/SourceFiles/boxes/self_destruction_box.h b/Telegram/SourceFiles/boxes/self_destruction_box.h index f4128e821..475f59749 100644 --- a/Telegram/SourceFiles/boxes/self_destruction_box.h +++ b/Telegram/SourceFiles/boxes/self_destruction_box.h @@ -20,7 +20,7 @@ namespace Main { class Session; } // namespace Main -class SelfDestructionBox : public BoxContent, private MTP::Sender { +class SelfDestructionBox : public Ui::BoxContent, private MTP::Sender { public: SelfDestructionBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index f4b3b1a4b..afc842773 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "app.h" #include "styles/style_history.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" @@ -1981,7 +1982,7 @@ void SendFilesBox::sendScheduled() { const auto callback = [=](Api::SendOptions options) { send(options); }; Ui::show( HistoryView::PrepareScheduleBox(this, _sendMenuType, callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } SendFilesBox::~SendFilesBox() = default; diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index a3e6beda2..23f741295 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -48,7 +48,7 @@ enum class SendFilesWay { Files, }; -class SendFilesBox : public BoxContent { +class SendFilesBox : public Ui::BoxContent { public: enum class SendLimit { One, diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 1ff16bdb9..a2888695f 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" #include "styles/style_settings.h" @@ -320,7 +321,7 @@ void SessionsBox::terminateOne(uint64 hash) { tr::lng_settings_reset_button(tr::now), st::attentionBoxButton, callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void SessionsBox::terminateAll() { @@ -346,7 +347,7 @@ void SessionsBox::terminateAll() { tr::lng_settings_reset_button(tr::now), st::attentionBoxButton, callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } SessionsBox::Inner::Inner(QWidget *parent) diff --git a/Telegram/SourceFiles/boxes/sessions_box.h b/Telegram/SourceFiles/boxes/sessions_box.h index 4eaf6e6a6..b27ec5dd4 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.h +++ b/Telegram/SourceFiles/boxes/sessions_box.h @@ -22,7 +22,7 @@ namespace Main { class Session; } // namespace Main -class SessionsBox : public BoxContent, private MTP::Sender { +class SessionsBox : public Ui::BoxContent, private MTP::Sender { public: SessionsBox(QWidget*, not_null session); diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 20b4ac99f..82fea368f 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_folder.h" #include "main/main_session.h" #include "core/application.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_history.h" @@ -479,7 +480,7 @@ void ShareBox::submitScheduled() { const auto callback = [=](Api::SendOptions options) { submit(options); }; Ui::show( HistoryView::PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ShareBox::copyLink() { diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 69dc63b9c..55d223c04 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -52,7 +52,7 @@ void ShareGameScoreByHash( not_null session, const QString &hash); -class ShareBox : public BoxContent, public RPCSender { +class ShareBox : public Ui::BoxContent, public RPCSender { public: using CopyCallback = Fn; using SubmitCallback = Fn @@ -136,14 +136,14 @@ StickerSetBox::StickerSetBox( , _set(set) { } -QPointer StickerSetBox::Show( +QPointer StickerSetBox::Show( not_null controller, not_null document) { if (const auto sticker = document->sticker()) { if (sticker->set.type() != mtpc_inputStickerSetEmpty) { return Ui::show( Box(controller, sticker->set), - LayerOption::KeepOther).data(); + Ui::LayerOption::KeepOther).data(); } } return nullptr; @@ -695,7 +695,7 @@ void StickerSetBox::Inner::install() { if (isMasksSet()) { Ui::show( Box(tr::lng_stickers_masks_pack(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } else if (_installRequest) { return; diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.h b/Telegram/SourceFiles/boxes/sticker_set_box.h index 4c5cc61fe..ae01ba020 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.h +++ b/Telegram/SourceFiles/boxes/sticker_set_box.h @@ -21,14 +21,14 @@ namespace Ui { class PlainShadow; } // namespace Ui -class StickerSetBox : public BoxContent, public RPCSender { +class StickerSetBox : public Ui::BoxContent, public RPCSender { public: StickerSetBox( QWidget*, not_null controller, const MTPInputStickerSet &set); - static QPointer Show( + static QPointer Show( not_null controller, not_null document); diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index a44cca08c..0d3048172 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "facades.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" @@ -1302,7 +1303,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { Box( App::wnd()->sessionController(), Stickers::inputSetId(*set)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } }; if (selectedIndex >= 0 && !_inDragArea) { diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index 1436e3b5a..d5aba39ed 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -26,6 +26,7 @@ class RippleAnimation; class SettingsSlider; class SlideAnimation; class CrossButton; +class BoxContentDivider; } // namespace Ui namespace Main { @@ -33,7 +34,7 @@ class Session; } // namespace Main class StickersBox final - : public BoxContent + : public Ui::BoxContent , public RPCSender , private base::Subscriber { public: @@ -365,7 +366,7 @@ private: object_ptr _megagroupSetField = { nullptr }; object_ptr _megagroupSelectedShadow = { nullptr }; object_ptr _megagroupSelectedRemove = { nullptr }; - object_ptr _megagroupDivider = { nullptr }; + object_ptr _megagroupDivider = { nullptr }; object_ptr _megagroupSubTitle = { nullptr }; base::Timer _megagroupSetAddressChangedTimer; mtpRequestId _megagroupSetRequestId = 0; diff --git a/Telegram/SourceFiles/boxes/url_auth_box.cpp b/Telegram/SourceFiles/boxes/url_auth_box.cpp index 29c0d6aa8..65b504ce2 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box.cpp +++ b/Telegram/SourceFiles/boxes/url_auth_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "apiwrap.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" void UrlAuthBox::Activate( @@ -85,7 +86,7 @@ void UrlAuthBox::Request( const auto bot = request.is_request_write_access() ? session->data().processUser(request.vbot()).get() : nullptr; - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto finishWithUrl = [=](const QString &url) { if (*box) { (*box)->closeBox(); @@ -122,7 +123,7 @@ void UrlAuthBox::Request( }; *box = Ui::show( Box(session, url, qs(request.vdomain()), bot, callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } UrlAuthBox::UrlAuthBox( diff --git a/Telegram/SourceFiles/boxes/url_auth_box.h b/Telegram/SourceFiles/boxes/url_auth_box.h index ddad3204f..4ac461326 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box.h +++ b/Telegram/SourceFiles/boxes/url_auth_box.h @@ -16,7 +16,7 @@ namespace Main { class Session; } // namespace Main -class UrlAuthBox : public BoxContent { +class UrlAuthBox : public Ui::BoxContent { public: static void Activate( not_null message, diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp index 55db8b294..f90b17391 100644 --- a/Telegram/SourceFiles/boxes/username_box.cpp +++ b/Telegram/SourceFiles/boxes/username_box.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "data/data_session.h" #include "data/data_user.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include diff --git a/Telegram/SourceFiles/boxes/username_box.h b/Telegram/SourceFiles/boxes/username_box.h index c1b1e8b65..2cc31dd52 100644 --- a/Telegram/SourceFiles/boxes/username_box.h +++ b/Telegram/SourceFiles/boxes/username_box.h @@ -18,7 +18,7 @@ namespace Main { class Session; } // namespace Main -class UsernameBox : public BoxContent, public RPCSender { +class UsernameBox : public Ui::BoxContent, public RPCSender { public: UsernameBox(QWidget*, not_null session); diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index bea980c4b..f4e0d76aa 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -22,14 +22,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "layout.h" #include "app.h" #include "styles/style_calls.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Calls { namespace { constexpr auto kUpdateDebugTimeoutMs = crl::time(500); -class DebugInfoBox : public BoxContent { +class DebugInfoBox : public Ui::BoxContent { public: DebugInfoBox(QWidget*, base::weak_ptr call); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp index f3b6635e2..ad1f896fc 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "mainwidget.h" #include "app.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" diff --git a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.h b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.h index 13dfdf6e5..316b06100 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace Emoji { -class ManageSetsBox : public BoxContent { +class ManageSetsBox : public Ui::BoxContent { public: explicit ManageSetsBox(QWidget*); diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 93e4400e7..a3b1f94b4 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mainwindow.h" #include "main/main_session.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_history.h" @@ -57,7 +58,7 @@ public: }; -class EditLinkBox : public BoxContent { +class EditLinkBox : public Ui::BoxContent { public: EditLinkBox( QWidget*, @@ -247,7 +248,7 @@ FncommitMarkdownLinkEdit(selection, text, link); } - }), LayerOption::KeepOther); + }), Ui::LayerOption::KeepOther); return true; }; } diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index 3fd8c702f..6cebd226f 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -82,7 +82,7 @@ void ApplyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { toast.multiline = true; toast.padding = st::stickersToastPadding; Ui::Toast::Show(toast); -// Ui::show(Box(archived, &Auth()), LayerOption::KeepOther); +// Ui::show(Box(archived, &Auth()), Ui::LayerOption::KeepOther); Auth().data().notifyStickersUpdated(); } @@ -187,7 +187,7 @@ void UndoInstallLocally(uint64 setId) { Ui::show( Box(tr::lng_stickers_not_found(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool IsFaved(not_null document) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index f88aebbac..908cb45a2 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -2775,7 +2775,7 @@ void StickersListWidget::displaySet(uint64 setId) { _displayingSet = true; checkHideWithBox(Ui::show( Box(_megagroupSet), - LayerOption::KeepOther).data()); + Ui::LayerOption::KeepOther).data()); return; } else if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) { setId = _megagroupSet->mgInfo->stickerSet.c_inputStickerSetID().vid().v; @@ -2789,11 +2789,11 @@ void StickersListWidget::displaySet(uint64 setId) { _displayingSet = true; checkHideWithBox(Ui::show( Box(controller(), Stickers::inputSetId(*it)), - LayerOption::KeepOther).data()); + Ui::LayerOption::KeepOther).data()); } } -void StickersListWidget::checkHideWithBox(QPointer box) { +void StickersListWidget::checkHideWithBox(QPointer box) { if (!box) { return; } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index bfe030dbd..53c11c794 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -12,8 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/variant.h" #include "base/timer.h" -class BoxContent; - namespace Main { class Session; } // namespace Main @@ -25,6 +23,7 @@ class SessionController; namespace Ui { class LinkButton; class RippleAnimation; +class BoxContent; } // namespace Ui namespace Lottie { @@ -203,7 +202,7 @@ private: void setSection(Section section); void displaySet(uint64 setId); - void checkHideWithBox(QPointer box); + void checkHideWithBox(QPointer box); void installSet(uint64 setId); void removeMegagroupSet(bool locally); void removeSet(uint64 setId); diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 8ee6961c3..e9c284414 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -16,7 +16,6 @@ class MainWindow; class MainWidget; class FileUploader; class Translator; -class BoxContent; namespace Storage { class Databases; @@ -43,6 +42,7 @@ namespace Ui { namespace Animations { class Manager; } // namespace Animations +class BoxContent; } // namespace Ui namespace MTP { @@ -273,7 +273,7 @@ private: const std::unique_ptr _emojiKeywords; std::unique_ptr _translator; base::Observable _passcodedChanged; - QPointer _badProxyDisableBox; + QPointer _badProxyDisableBox; const std::unique_ptr _audio; const QImage _logo; diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 32828252a..e98d6a5e6 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -58,7 +58,7 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) { tr::lng_open_this_link(tr::now) + qsl("\n\n") + displayUrl, tr::lng_open_link(tr::now), [=] { Ui::hideLayer(); open(); }), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } else { open(); } diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 0799098dc..f19f54a24 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -341,7 +341,7 @@ bool HandleUnknown( Api::EntitiesFromMTP(result.ventities().value_or_empty()) }; if (result.is_update_app()) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto callback = [=] { Core::UpdateApplication(); if (*box) (*box)->closeBox(); diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp index efe892b81..709ae2234 100644 --- a/Telegram/SourceFiles/core/update_checker.cpp +++ b/Telegram/SourceFiles/core/update_checker.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/settings/info_settings_widget.h" #include "window/window_session_controller.h" #include "settings/settings_intro.h" +#include "ui/layers/box_content.h" #include "app.h" #include diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 776af0db9..39d22c4fd 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -23,7 +23,6 @@ class Image; class HistoryItem; class HistoryMessage; class HistoryService; -class BoxContent; struct WebPageCollage; enum class WebPageType; enum class NewMessageType; @@ -54,6 +53,10 @@ class PanelController; } // namespace View } // namespace Export +namespace Ui { +class BoxContent; +} // namespace Ui + namespace Passport { struct SavedCredentials; } // namespace Passport @@ -840,7 +843,7 @@ private: std::unique_ptr _exportPanel; rpl::event_stream _exportViewChanges; TimeId _exportAvailableAt = 0; - QPointer _exportSuggestion; + QPointer _exportSuggestion; rpl::variable _contactsLoaded = false; rpl::event_stream _chatsListLoadedEvents; diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp index 9e11fa53e..7f0206ace 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp @@ -39,7 +39,7 @@ void ShowSearchFromBox( box->addButton(tr::lng_cancel(), [box, subscription] { box->closeBox(); }); - }), LayerOption::KeepOther); + }), Ui::LayerOption::KeepOther); box->boxClosing() | rpl::start_with_next( std::move(closedCallback), *subscription); diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index d40907a8a..dec220ed0 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "facades.h" #include "styles/style_export.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Export { namespace View { @@ -30,7 +30,7 @@ namespace { constexpr auto kSaveSettingsTimeout = crl::time(1000); -class SuggestBox : public BoxContent { +class SuggestBox : public Ui::BoxContent { public: SuggestBox(QWidget*); @@ -85,9 +85,9 @@ Environment PrepareEnvironment() { return result; } -QPointer SuggestStart() { +QPointer SuggestStart() { ClearSuggestStart(); - return Ui::show(Box(), LayerOption::KeepOther).data(); + return Ui::show(Box(), Ui::LayerOption::KeepOther).data(); } void ClearSuggestStart() { @@ -166,10 +166,10 @@ void PanelController::showSettings() { auto settings = base::make_unique_q( _panel, *_settings); - settings->setShowBoxCallback([=](object_ptr box) { + settings->setShowBoxCallback([=](object_ptr box) { _panel->showBox( std::move(box), - LayerOption::KeepOther, + Ui::LayerOption::KeepOther, anim::type::normal); }); @@ -256,7 +256,7 @@ void PanelController::showError(const QString &text) { const auto hidden = _panel->isHidden(); _panel->showBox( std::move(box), - LayerOption::CloseOther, + Ui::LayerOption::CloseOther, hidden ? anim::type::instant : anim::type::normal); weak->setCloseByEscape(false); weak->setCloseByOutsideClick(false); @@ -330,7 +330,7 @@ void PanelController::stopWithConfirmation(FnMut callback) { _confirmStopBox = box.data(); _panel->showBox( std::move(box), - LayerOption::CloseOther, + Ui::LayerOption::CloseOther, hidden ? anim::type::instant : anim::type::normal); if (hidden) { _panel->showAndActivate(); diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.h b/Telegram/SourceFiles/export/view/export_view_panel_controller.h index 106fb1348..3bcebb513 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.h +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.h @@ -12,10 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "base/timer.h" -class BoxContent; - namespace Ui { class SeparatePanel; +class BoxContent; } // namespace Ui namespace Export { @@ -25,7 +24,7 @@ struct Environment; namespace View { Environment PrepareEnvironment(); -QPointer SuggestStart(); +QPointer SuggestStart(); void ClearSuggestStart(); bool IsDefaultPath(const QString &path); void ResolveSettings(Settings &settings); @@ -71,7 +70,7 @@ private: base::unique_qptr _panel; State _state; - QPointer _confirmStopBox; + QPointer _confirmStopBox; rpl::event_stream> _panelCloseEvents; bool _stopRequested = false; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index e964877d7..772211254 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "styles/style_widgets.h" #include "styles/style_export.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Export { namespace View { diff --git a/Telegram/SourceFiles/export/view/export_view_settings.h b/Telegram/SourceFiles/export/view/export_view_settings.h index 68fae7695..facc3efe8 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.h +++ b/Telegram/SourceFiles/export/view/export_view_settings.h @@ -11,12 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" -class BoxContent; - namespace Ui { class VerticalLayout; class Checkbox; class ScrollArea; +class BoxContent; } // namespace Ui namespace Export { @@ -34,7 +33,7 @@ public: rpl::producer<> startClicks() const; rpl::producer<> cancelClicks() const; - void setShowBoxCallback(Fn)> callback) { + void setShowBoxCallback(Fn)> callback) { _showBoxCallback = std::move(callback); } @@ -97,7 +96,7 @@ private: void changeData(Callback &&callback); PeerId _singlePeerId = 0; - Fn)> _showBoxCallback; + Fn)> _showBoxCallback; // Use through readData / changeData wrappers. Settings _internal_data; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index a1b1e6fcf..cb8cd9262 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/confirm_box.h" #include "boxes/url_auth_box.h" -#include "window/layer_widget.h" +#include "ui/layers/layer_widget.h" #include "lang/lang_keys.h" #include "base/observer.h" #include "history/history.h" diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index 9354b05c5..5b7578ab5 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_user.h" #include "base/unixtime.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace AdminLog { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.h index 0d21cc755..18741954e 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.h @@ -12,9 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace AdminLog { -class FilterBox : public BoxContent { +class FilterBox : public Ui::BoxContent { public: - FilterBox(QWidget*, not_null channel, const std::vector> &admins, const FilterValue &filter, Fn saveCallback); + FilterBox( + QWidget*, + not_null channel, + const std::vector> &admins, + const FilterValue &filter, + Fn saveCallback); protected: void prepare() override; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 183a3e0e5..555364637 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1212,7 +1212,7 @@ void InnerWidget::suggestRestrictUser(not_null user) { }); *weakBox = Ui::show( std::move(box), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }; if (base::contains(_admins, user)) { editRestrictions(true, MTP_chatBannedRights(MTP_flags(0), MTP_int(0))); diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index 9b9b4069f..d786ad5c9 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "app.h" #include "styles/style_chat_helpers.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" DragArea::DragArea(QWidget *parent) : TWidget(parent) { setMouseTracking(true); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index e6f756616..077f7477a 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -260,7 +260,7 @@ void FastShareMessage(not_null item) { text.append(error.first); Ui::show( Box(text), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index fd549bf9a..c1bd89f43 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3003,7 +3003,7 @@ void HistoryWidget::sendScheduled() { const auto callback = [=](Api::SendOptions options) { send(options); }; Ui::show( HistoryView::PrepareScheduleBox(_list, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } SendMenuType HistoryWidget::sendMenuType() const { @@ -5725,7 +5725,7 @@ bool HistoryWidget::sendExistingDocument(not_null document) { ? Data::RestrictionError(_peer, ChatRestriction::f_send_stickers) : std::nullopt; if (error) { - Ui::show(Box(*error), LayerOption::KeepOther); + Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; } else if (!_peer || !_peer->canWrite()) { return false; @@ -5756,7 +5756,7 @@ bool HistoryWidget::sendExistingPhoto(not_null photo) { ? Data::RestrictionError(_peer, ChatRestriction::f_send_media) : std::nullopt; if (error) { - Ui::show(Box(*error), LayerOption::KeepOther); + Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; } else if (!_peer || !_peer->canWrite()) { return false; diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index 6404bccd4..94b2113cd 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" +#include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "data/data_peer.h" @@ -23,11 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" #include "boxes/confirm_box.h" -#include "boxes/generic_box.h" // window->show(Box(InitMethod())) #include "boxes/peers/edit_contact_box.h" #include "app.h" #include "styles/style_history.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace HistoryView { namespace { @@ -313,7 +313,7 @@ void ContactStatus::setupBlockHandler(not_null user) { void ContactStatus::setupShareHandler(not_null user) { _bar.entity()->shareClicks( ) | rpl::start_with_next([=] { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto share = [=] { user->setSettings(0); user->session().api().request(MTPcontacts_AcceptContact( @@ -349,7 +349,7 @@ void ContactStatus::setupReportHandler(not_null peer) { ) | rpl::start_with_next([=] { Expects(!peer->isUser()); - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto callback = crl::guard(&_bar, [=] { if (*box) { (*box)->closeBox(); diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index 20b6dd9e5..64ef33a53 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/wrap/padding_wrap.h" #include "chat_helpers/message_field.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_history.h" namespace HistoryView { @@ -542,7 +542,7 @@ TimeId DefaultScheduleTime() { } void ScheduleBox( - not_null box, + not_null box, SendMenuType type, FnMut done, TimeId time) { diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.h b/Telegram/SourceFiles/history/view/history_view_schedule_box.h index a0269a5fc..387ee8c3c 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.h +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/generic_box.h" +#include "ui/layers/generic_box.h" namespace Api { struct SendOptions; @@ -19,13 +19,13 @@ namespace HistoryView { [[nodiscard]] TimeId DefaultScheduleTime(); void ScheduleBox( - not_null box, + not_null box, SendMenuType type, FnMut done, TimeId time); template -[[nodiscard]] object_ptr PrepareScheduleBox( +[[nodiscard]] object_ptr PrepareScheduleBox( Guard &&guard, SendMenuType type, Submit &&submit) { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index f2813348f..e1409ad36 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" // SendMenuType. #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" +#include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/special_buttons.h" #include "ui/ui_utility.h" @@ -24,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "boxes/confirm_box.h" #include "boxes/send_files_box.h" -#include "boxes/generic_box.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "core/event_filter.h" @@ -344,7 +344,7 @@ void ScheduledWidget::uploadFile( }; Ui::show( PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool ScheduledWidget::showSendingFilesError( @@ -387,7 +387,7 @@ void ScheduledWidget::send() { const auto callback = [=](Api::SendOptions options) { send(options); }; Ui::show( PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ScheduledWidget::send(Api::SendOptions options) { @@ -432,7 +432,7 @@ void ScheduledWidget::sendExistingDocument( }; Ui::show( PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool ScheduledWidget::sendExistingDocument( @@ -442,7 +442,7 @@ bool ScheduledWidget::sendExistingDocument( _history->peer, ChatRestriction::f_send_stickers); if (error) { - Ui::show(Box(*error), LayerOption::KeepOther); + Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; } @@ -470,7 +470,7 @@ void ScheduledWidget::sendExistingPhoto(not_null photo) { }; Ui::show( PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool ScheduledWidget::sendExistingPhoto( @@ -480,7 +480,7 @@ bool ScheduledWidget::sendExistingPhoto( _history->peer, ChatRestriction::f_send_media); if (error) { - Ui::show(Box(*error), LayerOption::KeepOther); + Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; } @@ -507,7 +507,7 @@ void ScheduledWidget::sendInlineResult( }; Ui::show( PrepareScheduleBox(this, sendMenuType(), callback), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void ScheduledWidget::sendInlineResult( diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp index 2b745796c..ca4b0317f 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.cpp +++ b/Telegram/SourceFiles/info/info_layer_widget.cpp @@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "app.h" #include "styles/style_info.h" #include "styles/style_window.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Info { diff --git a/Telegram/SourceFiles/info/info_layer_widget.h b/Telegram/SourceFiles/info/info_layer_widget.h index 13241cab7..db6edc1ac 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.h +++ b/Telegram/SourceFiles/info/info_layer_widget.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "window/layer_widget.h" +#include "ui/layers/layer_widget.h" #include "media/player/media_player_float.h" namespace Window { @@ -22,7 +22,7 @@ class WrapWidget; class TopBar; class LayerWidget - : public Window::LayerWidget + : public Ui::LayerWidget , private ::Media::Player::FloatDelegate { public: LayerWidget( diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index e20f7b471..a4495b677 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -163,7 +163,7 @@ object_ptr Memento::createWidget( return std::move(result); } -object_ptr Memento::createLayer( +object_ptr Memento::createLayer( not_null controller, const QRect &geometry) { if (geometry.width() >= LayerWidget::MinimalSupportedWidth()) { @@ -201,7 +201,7 @@ object_ptr MoveMemento::createWidget( return std::move(result); } -object_ptr MoveMemento::createLayer( +object_ptr MoveMemento::createLayer( not_null controller, const QRect &geometry) { if (geometry.width() < LayerWidget::MinimalSupportedWidth()) { diff --git a/Telegram/SourceFiles/info/info_memento.h b/Telegram/SourceFiles/info/info_memento.h index 73ec0da59..45e41dff4 100644 --- a/Telegram/SourceFiles/info/info_memento.h +++ b/Telegram/SourceFiles/info/info_memento.h @@ -44,7 +44,7 @@ public: Window::Column column, const QRect &geometry) override; - object_ptr createLayer( + object_ptr createLayer( not_null controller, const QRect &geometry) override; @@ -98,7 +98,7 @@ public: Window::Column column, const QRect &geometry) override; - object_ptr createLayer( + object_ptr createLayer( not_null controller, const QRect &geometry) override; diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp index 4050b0d40..cc0141ddf 100644 --- a/Telegram/SourceFiles/info/info_section_widget.cpp +++ b/Telegram/SourceFiles/info/info_section_widget.cpp @@ -93,7 +93,7 @@ std::unique_ptr SectionWidget::createMemento() { return _content->createMemento(); } -object_ptr SectionWidget::moveContentToLayer( +object_ptr SectionWidget::moveContentToLayer( QRect bodyGeometry) { if (_content->controller()->wrap() != Wrap::Narrow || width() < LayerWidget::MinimalSupportedWidth()) { diff --git a/Telegram/SourceFiles/info/info_section_widget.h b/Telegram/SourceFiles/info/info_section_widget.h index 7f26ca57e..1ca552569 100644 --- a/Telegram/SourceFiles/info/info_section_widget.h +++ b/Telegram/SourceFiles/info/info_section_widget.h @@ -50,7 +50,7 @@ public: const Window::SectionShow ¶ms) override; std::unique_ptr createMemento() override; - object_ptr moveContentToLayer( + object_ptr moveContentToLayer( QRect bodyGeometry) override; // Float player interface. diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 0ccd361aa..d94f87651 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/vertical_layout.h" #include "ui/search_field_controller.h" #include "styles/style_info.h" @@ -66,7 +67,7 @@ void InnerWidget::createOtherTypes() { _otherTypes->show(); createTypeButtons(); - _otherTypes->add(object_ptr(_otherTypes)); + _otherTypes->add(object_ptr(_otherTypes)); //createTabs(); _otherTypes->resizeToWidth(width()); diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index d40976216..859e3d68a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/widgets/shadow.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" +#include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "history/history_location_manager.h" // LocationClickHandler. @@ -26,7 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/add_contact_box.h" #include "boxes/report_box.h" -#include "boxes/generic_box.h" // window->show(Box(InitMethod())) #include "boxes/peers/edit_contact_box.h" #include "lang/lang_keys.h" #include "info/info_controller.h" @@ -465,7 +466,7 @@ Ui::MultiSlideTracker DetailsFiller::fillChannelButtons( } object_ptr DetailsFiller::fill() { - add(object_ptr(_wrap)); + add(object_ptr(_wrap)); add(CreateSkipWidget(_wrap)); add(setupInfo()); if (!_peer->isSelf()) { @@ -746,7 +747,7 @@ object_ptr ActionsFiller::fill() { //} // //object_ptr FeedDetailsFiller::fill() { -// add(object_ptr(_wrap)); +// add(object_ptr(_wrap)); // add(CreateSkipWidget(_wrap)); // add(setupDefaultToggle()); // add(CreateSkipWidget(_wrap)); @@ -853,7 +854,7 @@ object_ptr SetupChannelMembers( ); auto members = result->entity(); - members->add(object_ptr(members)); + members->add(object_ptr(members)); members->add(CreateSkipWidget(members)); auto button = AddActionButton( members, diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index adf1ef84f..8145c18b3 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "data/data_shared_media.h" @@ -102,7 +103,7 @@ object_ptr InnerWidget::setupContent( if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) { result->add(std::move(members)); } - result->add(object_ptr(result)); + result->add(object_ptr(result)); if (auto actions = SetupActions(_controller, result.data(), _peer)) { result->add(std::move(actions)); } @@ -215,7 +216,7 @@ object_ptr InnerWidget::setupSharedMedia( auto layout = result->entity(); - layout->add(object_ptr(layout)); + layout->add(object_ptr(layout)); layout->add(object_ptr( layout, st::infoSharedMediaBottomSkip) diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 2558f7969..26736e1a8 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -333,7 +333,7 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) { _code->setFocus(); return; } else if (!getData()->pwdRequest) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto callback = [=] { Core::UpdateApplication(); if (*box) (*box)->closeBox(); diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 801f207cb..b8d31d8ab 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -360,7 +360,7 @@ void PwdCheckWidget::submit() { }); if (_notEmptyPassport) { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto confirmed = [=] { send(); if (*box) { diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index a64998fd0..6ffe8b060 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "facades.h" #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_intro.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp index aade61114..c025588fc 100644 --- a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp +++ b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp @@ -22,12 +22,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "core/click_handler_types.h" #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Lang { namespace { -class ConfirmSwitchBox : public BoxContent { +class ConfirmSwitchBox : public Ui::BoxContent { public: ConfirmSwitchBox( QWidget*, @@ -46,7 +46,7 @@ private: }; -class NotReadyBox : public BoxContent { +class NotReadyBox : public Ui::BoxContent { public: NotReadyBox( QWidget*, @@ -349,7 +349,7 @@ bool CloudManager::showOfferSwitchBox() { tr::lng_cancel(tr::now), confirm, cancel), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return true; } @@ -471,7 +471,7 @@ void CloudManager::switchToLanguage(const Language &data) { tr::lng_box_ok(tr::now), tr::lng_cancel(tr::now), [=] { performSwitchAndRestart(data); }), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }).fail([=](const RPCError &error) { _switchingToLanguageRequest = 0; }).send(); @@ -515,12 +515,12 @@ void CloudManager::performSwitchToCustom() { tr::lng_box_ok(tr::now), tr::lng_cancel(tr::now), change), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } else { Ui::show( Box("Custom lang failed :(\n\nError: " + loader.errors()), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } }); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 423acba13..ee09ab726 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -608,7 +608,7 @@ bool MainWidget::setForwardDraft(PeerId peerId, MessageIdsList &&items) { session().data().idsToItems(items), true); if (!error.isEmpty()) { - Ui::show(Box(error), LayerOption::KeepOther); + Ui::show(Box(error), Ui::LayerOption::KeepOther); return false; } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 0f89d2468..ae1af98e0 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -10,14 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "dialogs/dialogs_layout.h" -#include "styles/style_dialogs.h" -#include "styles/style_window.h" -#include "styles/style_boxes.h" #include "history/history.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/widgets/tooltip.h" +#include "ui/layers/layer_widget.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" #include "lang/lang_cloud_manager.h" @@ -39,7 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_intro.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" -#include "window/layer_widget.h" #include "window/notifications_manager.h" #include "window/themes/window_theme.h" #include "window/themes/window_theme_warning.h" @@ -50,6 +47,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_media_preview.h" #include "facades.h" #include "app.h" +#include "styles/style_dialogs.h" +#include "styles/style_layers.h" +#include "styles/style_window.h" #include #include @@ -274,7 +274,7 @@ void MainWindow::showSettings() { } void MainWindow::showSpecialLayer( - object_ptr layer, + object_ptr layer, anim::type animated) { if (_passcodeLock) { return; @@ -303,14 +303,16 @@ void MainWindow::showMainMenu() { if (isHidden()) showFromTray(); ensureLayerCreated(); - _layer->showMainMenu(sessionController(), anim::type::normal); + _layer->showMainMenu( + object_ptr(this, sessionController()), + anim::type::normal); } void MainWindow::ensureLayerCreated() { if (_layer) { return; } - _layer = base::make_unique_q( + _layer = base::make_unique_q( bodyWidget()); _layer->hideFinishEvents( @@ -367,8 +369,8 @@ MainWidget *MainWindow::mainWidget() { } void MainWindow::ui_showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) { if (box) { ensureLayerCreated(); @@ -600,7 +602,7 @@ void MainWindow::onShowAddContact() { if (account().sessionExists()) { Ui::show( Box(&account().session()), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } @@ -612,7 +614,7 @@ void MainWindow::onShowNewGroup() { Box( sessionController(), GroupInfoBox::Type::Group), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } @@ -624,7 +626,7 @@ void MainWindow::onShowNewChannel() { Box( sessionController(), GroupInfoBox::Type::Channel), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index ffc1604b3..11ba00c6d 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -10,11 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "platform/platform_main_window.h" #include "base/unique_qptr.h" -#include "window/layer_widget.h" +#include "ui/layers/layer_widget.h" #include "ui/effects/animation_value.h" class MainWidget; -class BoxContent; namespace Intro { class Widget; @@ -26,8 +25,6 @@ class ClearManager; namespace Window { class MediaPreviewWidget; -class LayerWidget; -class LayerStackWidget; class SectionMemento; struct SectionShow; class PasscodeLockWidget; @@ -39,6 +36,8 @@ class WarningWidget; namespace Ui { class LinkButton; +class BoxContent; +class LayerStackWidget; } // namespace Ui class MediaPreviewWidget; @@ -100,14 +99,14 @@ public: void updateTrayMenu(bool force = false) override; void showSpecialLayer( - object_ptr layer, + object_ptr layer, anim::type animated); bool showSectionInExistingLayer( not_null memento, const Window::SectionShow ¶ms); void ui_showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated); void ui_hideSettingsAndLayer(anim::type animated); void ui_removeLayerBlackout(); @@ -171,7 +170,7 @@ private: object_ptr _passcodeLock = { nullptr }; object_ptr _intro = { nullptr }; object_ptr _main = { nullptr }; - base::unique_qptr _layer; + base::unique_qptr _layer; object_ptr _mediaPreview = { nullptr }; object_ptr _testingThemeWarning = { nullptr }; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index aadb0cd90..b7f56d467 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" #include "core/core_cloud_password.h" -class BoxContent; class mtpFileLoader; namespace Storage { diff --git a/Telegram/SourceFiles/passport/passport_form_view_controller.h b/Telegram/SourceFiles/passport/passport_form_view_controller.h index 795fb84f1..50d5b04e1 100644 --- a/Telegram/SourceFiles/passport/passport_form_view_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_view_controller.h @@ -50,8 +50,8 @@ public: virtual void editScope(int index) = 0; virtual void showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) = 0; virtual void showToast(const QString &text) = 0; virtual void suggestReset(Fn callback) = 0; @@ -64,7 +64,7 @@ public: template QPointer show( object_ptr box, - LayerOptions options = LayerOption::KeepOther, + Ui::LayerOptions options = Ui::LayerOption::KeepOther, anim::type animated = anim::type::normal) { auto result = QPointer(box.data()); showBox(std::move(box), options, animated); diff --git a/Telegram/SourceFiles/passport/passport_panel.cpp b/Telegram/SourceFiles/passport/passport_panel.cpp index b1618189d..0779886c7 100644 --- a/Telegram/SourceFiles/passport/passport_panel.cpp +++ b/Telegram/SourceFiles/passport/passport_panel.cpp @@ -93,8 +93,8 @@ void Panel::showEditValue(object_ptr from) { } void Panel::showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) { _widget->showBox(std::move(box), options, animated); _widget->showAndActivate(); diff --git a/Telegram/SourceFiles/passport/passport_panel.h b/Telegram/SourceFiles/passport/passport_panel.h index aa4ccb723..ef40205c4 100644 --- a/Telegram/SourceFiles/passport/passport_panel.h +++ b/Telegram/SourceFiles/passport/passport_panel.h @@ -7,13 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "window/layer_widget.h" - -class BoxContent; +#include "ui/layers/layer_widget.h" namespace Ui { class RpWidget; class SeparatePanel; +class BoxContent; } // namespace Ui namespace Passport { @@ -32,8 +31,8 @@ public: void showCriticalError(const QString &error); void showEditValue(object_ptr form); void showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated); void showToast(const QString &text); diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 051c876b1..c67cd04bb 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_countries.h" #include "layout.h" #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Passport { namespace { @@ -568,41 +568,6 @@ ScanInfo::ScanInfo( , error(error) { } -BoxPointer::BoxPointer(QPointer value) -: _value(value) { -} - -BoxPointer::BoxPointer(BoxPointer &&other) -: _value(base::take(other._value)) { -} - -BoxPointer &BoxPointer::operator=(BoxPointer &&other) { - std::swap(_value, other._value); - return *this; -} - -BoxPointer::~BoxPointer() { - if (const auto strong = get()) { - strong->closeBox(); - } -} - -BoxContent *BoxPointer::get() const { - return _value.data(); -} - -BoxPointer::operator BoxContent*() const { - return get(); -} - -BoxPointer::operator bool() const { - return get(); -} - -BoxContent *BoxPointer::operator->() const { - return get(); -} - PanelController::PanelController(not_null form) : _form(form) , _scopes(ComputeScopes(_form->form())) { @@ -750,7 +715,7 @@ void PanelController::setupPassword() { } void PanelController::cancelPasswordSubmit() { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); *box = show(Box( tr::lng_passport_stop_password_sure(tr::now), tr::lng_passport_stop(tr::now), @@ -926,7 +891,7 @@ void PanelController::deleteValueSure(bool withDetails) { } void PanelController::suggestReset(Fn callback) { - _resetBox = BoxPointer(show(Box( + _resetBox = Ui::BoxPointer(show(Box( Lang::Hard::PassportCorrupted(), Lang::Hard::PassportCorruptedReset(), [=] { resetPassport(callback); }, @@ -940,7 +905,7 @@ void PanelController::resetPassport(Fn callback) { st::attentionBoxButton, [=] { base::take(_resetBox); callback(); }, [=] { suggestReset(callback); })); - _resetBox = BoxPointer(box.data()); + _resetBox = Ui::BoxPointer(box.data()); } void PanelController::cancelReset() { @@ -986,7 +951,7 @@ void PanelController::showUpdateAppBox() { tr::lng_menu_update(tr::now), callback, [=] { _form->cancelSure(); }), - LayerOption::KeepOther, + Ui::LayerOption::KeepOther, anim::type::instant); } @@ -1104,7 +1069,7 @@ void PanelController::editWithUpload(int index, int documentIndex) { const auto widget = _panel->widget(); EditScans::ChooseScan(widget.get(), type, [=](QByteArray &&content) { if (_scopeDocumentTypeBox) { - _scopeDocumentTypeBox = BoxPointer(); + _scopeDocumentTypeBox = Ui::BoxPointer(); } if (!_editScope || !_editDocument) { startScopeEdit(index, documentIndex); @@ -1462,8 +1427,8 @@ void PanelController::cancelAuthSure() { } void PanelController::showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) { _panel->showBox(std::move(box), options, animated); } diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index 7f0e2ce41..beb3b103f 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -9,6 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_form_view_controller.h" #include "passport/passport_form_controller.h" +#include "ui/layers/layer_widget.h" + +namespace Ui { +class BoxContent; +} // namespace Ui namespace Passport { @@ -63,23 +68,6 @@ struct ScopeError { QString text; }; -class BoxPointer { -public: - BoxPointer(QPointer value = nullptr); - BoxPointer(BoxPointer &&other); - BoxPointer &operator=(BoxPointer &&other); - ~BoxPointer(); - - BoxContent *get() const; - operator BoxContent*() const; - explicit operator bool() const; - BoxContent *operator->() const; - -private: - QPointer _value; - -}; - class PanelController : public ViewController { public: PanelController(not_null form); @@ -132,8 +120,8 @@ public: void cancelEditScope(); void showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) override; void showToast(const QString &text) override; void suggestReset(Fn callback) override; @@ -182,15 +170,15 @@ private: std::unique_ptr _panel; Fn _panelHasUnsavedChanges; - QPointer _confirmForgetChangesBox; - std::vector _editScopeBoxes; + QPointer _confirmForgetChangesBox; + std::vector _editScopeBoxes; Scope *_editScope = nullptr; const Value *_editValue = nullptr; const Value *_editDocument = nullptr; - BoxPointer _scopeDocumentTypeBox; - std::map, BoxPointer> _verificationBoxes; + Ui::BoxPointer _scopeDocumentTypeBox; + std::map, Ui::BoxPointer> _verificationBoxes; - BoxPointer _resetBox; + Ui::BoxPointer _resetBox; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp index 2a42af852..eb00b371f 100644 --- a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "data/data_user.h" #include "data/data_countries.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_passport.h" namespace Passport { diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp index d02e76c84..f328dbb62 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/fade_wrap.h" @@ -26,12 +27,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "app.h" #include "styles/style_passport.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Passport { namespace { -class VerifyBox : public BoxContent { +class VerifyBox : public Ui::BoxContent { public: VerifyBox( QWidget*, @@ -231,7 +232,7 @@ void PanelEditContact::setupControls( _content->resizeToWidth(width); }, _content->lifetime()); - _content->add(object_ptr( + _content->add(object_ptr( _content, st::passportFormDividerHeight)); if (!existing.isEmpty()) { @@ -389,7 +390,7 @@ void PanelEditContact::save(const QString &value) { _controller->saveScope(std::move(data), {}); } -object_ptr VerifyPhoneBox( +object_ptr VerifyPhoneBox( const QString &phone, int codeLength, Fn submit, @@ -409,7 +410,7 @@ object_ptr VerifyPhoneBox( nullptr); } -object_ptr VerifyEmailBox( +object_ptr VerifyEmailBox( const QString &email, int codeLength, Fn submit, diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h index cd58420b2..f911dac89 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h @@ -10,13 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" -class BoxContent; - namespace Ui { class MaskedInputField; class PlainShadow; class RoundButton; class VerticalLayout; +class BoxContent; } // namespace Ui namespace Passport { @@ -76,13 +75,13 @@ private: }; -object_ptr VerifyPhoneBox( +object_ptr VerifyPhoneBox( const QString &phone, int codeLength, Fn submit, rpl::producer call, rpl::producer error); -object_ptr VerifyEmailBox( +object_ptr VerifyEmailBox( const QString &email, int codeLength, Fn submit, diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp index bcdbcdfee..4d54bafaf 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp @@ -25,13 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "lang/lang_keys.h" #include "styles/style_widgets.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_passport.h" namespace Passport { namespace { -class RequestTypeBox : public BoxContent { +class RequestTypeBox : public Ui::BoxContent { public: RequestTypeBox( QWidget*, @@ -55,7 +55,7 @@ private: }; -class DeleteDocumentBox : public BoxContent { +class DeleteDocumentBox : public Ui::BoxContent { public: DeleteDocumentBox( QWidget*, @@ -676,7 +676,7 @@ void PanelEditDocument::save() { std::move(result.filesData)); } -object_ptr RequestIdentityType( +object_ptr RequestIdentityType( Fn submit, std::vector labels) { return Box( @@ -686,7 +686,7 @@ object_ptr RequestIdentityType( submit); } -object_ptr RequestAddressType( +object_ptr RequestAddressType( Fn submit, std::vector labels) { return Box( @@ -696,7 +696,7 @@ object_ptr RequestAddressType( submit); } -object_ptr ConfirmDeleteDocument( +object_ptr ConfirmDeleteDocument( Fn submit, const QString &text, const QString &detailsCheckbox) { diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.h b/Telegram/SourceFiles/passport/passport_panel_edit_document.h index fcf9aa287..ce4ade6d2 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.h @@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" -class BoxContent; - namespace Ui { class InputField; class ScrollArea; @@ -20,6 +18,7 @@ class PlainShadow; class FlatLabel; class RoundButton; class VerticalLayout; +class BoxContent; template class SlideWrap; } // namespace Ui @@ -167,14 +166,14 @@ private: }; -object_ptr RequestIdentityType( +object_ptr RequestIdentityType( Fn submit, std::vector labels); -object_ptr RequestAddressType( +object_ptr RequestAddressType( Fn submit, std::vector labels); -object_ptr ConfirmDeleteDocument( +object_ptr ConfirmDeleteDocument( Fn submit, const QString &text, const QString &detailsCheckbox = QString()); diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp index eed994090..bc57ac7a0 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/profile/info_profile_button.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/fade_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" @@ -23,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_media_prepare.h" #include "storage/file_upload.h" // For Storage::kUseBigFilesFrom. #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_passport.h" #include @@ -544,9 +545,9 @@ void EditScans::setupList( if (type == FileType::Scan) { list.divider = container->add( - object_ptr>( + object_ptr>( container, - object_ptr( + object_ptr( container, st::passportFormDividerHeight))); list.divider->toggle(list.files.empty(), anim::type::instant); @@ -591,7 +592,7 @@ void EditScans::setupList( chooseScan(type); }); - container->add(object_ptr( + container->add(object_ptr( container, st::passportFormDividerHeight)); } diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.h b/Telegram/SourceFiles/passport/passport_panel_edit_scans.h index b529db963..6ec6783df 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.h @@ -11,9 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "base/object_ptr.h" -class BoxContentDivider; - namespace Ui { +class BoxContentDivider; class VerticalLayout; class FlatLabel; template @@ -96,7 +95,7 @@ private: std::vector files; std::optional initialCount; QString errorMissing; - QPointer> divider; + QPointer> divider; QPointer> header; QPointer> uploadMoreError; QPointer wrap; diff --git a/Telegram/SourceFiles/passport/passport_panel_form.cpp b/Telegram/SourceFiles/passport/passport_panel_form.cpp index 9db860a3c..826851c74 100644 --- a/Telegram/SourceFiles/passport/passport_panel_form.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_form.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/fade_wrap.h" #include "ui/wrap/padding_wrap.h" @@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text_options.h" #include "ui/special_buttons.h" #include "styles/style_passport.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace Passport { @@ -245,7 +247,7 @@ not_null PanelForm::setupContent() { st::passportPasswordLabel)), st::passportFormAbout2Padding)->entity(); - inner->add(object_ptr( + inner->add(object_ptr( inner, st::passportFormDividerHeight)); inner->add( diff --git a/Telegram/SourceFiles/passport/passport_panel_form.h b/Telegram/SourceFiles/passport/passport_panel_form.h index a7fdd9f96..badc773f0 100644 --- a/Telegram/SourceFiles/passport/passport_panel_form.h +++ b/Telegram/SourceFiles/passport/passport_panel_form.h @@ -10,9 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" -class BoxContentDivider; - namespace Ui { +class BoxContentDivider; class ScrollArea; class FadeShadow; class RoundButton; diff --git a/Telegram/SourceFiles/passport/passport_panel_password.cpp b/Telegram/SourceFiles/passport/passport_panel_password.cpp index 7b75f9f05..d0c9abacc 100644 --- a/Telegram/SourceFiles/passport/passport_panel_password.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_password.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "info/profile/info_profile_icon.h" #include "styles/style_passport.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Passport { diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 0869bbc6a..8546229f7 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -61,12 +61,6 @@ profileDropAreaBorderFg: profileDropAreaFg; profileDropAreaBorderWidth: 3px; profileDropAreaDuration: 200; -profileDividerBg: windowBgOver; -profileDividerFg: windowShadowFg; -profileDividerLeft: icon {{ "profile_divider_left", profileDividerFg }}; -profileDividerTop: icon {{ "profile_divider_top", profileDividerFg }}; -profileDividerBottom: icon {{ "profile_divider_bottom", profileDividerFg }}; - profileBlocksTop: 7px; profileBlocksBottom: 20px; profileBlockLeftMin: 8px; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 01e6b1702..29c5fbbed 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/stickers_box.h" #include "boxes/confirm_box.h" #include "boxes/background_box.h" -#include "boxes/generic_box.h" #include "boxes/background_preview_box.h" #include "boxes/download_path_box.h" #include "boxes/local_storage_box.h" @@ -23,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" +#include "ui/layers/generic_box.h" #include "ui/effects/radial_animation.h" #include "ui/toast/toast.h" #include "ui/image/image.h" @@ -50,7 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "app.h" #include "styles/style_settings.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Settings { namespace { diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp index 8fdd2cde5..124cb7c8a 100644 --- a/Telegram/SourceFiles/settings/settings_common.cpp +++ b/Telegram/SourceFiles/settings/settings_common.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/padding_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/widgets/labels.h" +#include "ui/widgets/box_content_divider.h" #include "info/profile/info_profile_button.h" #include "boxes/abstract_box.h" #include "window/themes/window_theme_editor_box.h" @@ -25,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mainwindow.h" #include "main/main_session.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_settings.h" namespace Settings { @@ -64,7 +65,7 @@ void AddSkip(not_null container, int skip) { } void AddDivider(not_null container) { - container->add(object_ptr(container)); + container->add(object_ptr(container)); } void AddDividerText( diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp index 6522ef459..dc8342008 100644 --- a/Telegram/SourceFiles/settings/settings_information.cpp +++ b/Telegram/SourceFiles/settings/settings_information.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/box_content_divider.h" #include "ui/special_buttons.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "boxes/add_contact_box.h" @@ -31,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "facades.h" #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_settings.h" #include @@ -46,7 +47,7 @@ void SetupPhoto( not_null container, not_null controller, not_null self) { - const auto wrap = container->add(object_ptr( + const auto wrap = container->add(object_ptr( container, st::settingsInfoPhotoHeight)); const auto photo = Ui::CreateChild( diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index d373256f5..276c998e0 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "app.h" #include "styles/style_settings.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_info.h" namespace Settings { diff --git a/Telegram/SourceFiles/settings/settings_intro.h b/Telegram/SourceFiles/settings/settings_intro.h index 7fccf37e2..1213be7a3 100644 --- a/Telegram/SourceFiles/settings/settings_intro.h +++ b/Telegram/SourceFiles/settings/settings_intro.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "window/layer_widget.h" +#include "ui/layers/layer_widget.h" namespace Ui { class VerticalLayout; @@ -21,7 +21,7 @@ namespace Settings { class IntroWidget; -class LayerWidget : public Window::LayerWidget { +class LayerWidget : public Ui::LayerWidget { public: LayerWidget(QWidget*); diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 5477c78e7..1aaae0be2 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -300,7 +300,7 @@ void BlockedBoxController::BlockNewUser( }; Ui::show( Box(std::move(controller), std::move(initBox)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool BlockedBoxController::appendRow(not_null user) { @@ -521,7 +521,7 @@ void LastSeenPrivacyController::confirmSave( tr::lng_continue(tr::now), tr::lng_cancel(tr::now), std::move(callback)); - *weakBox = Ui::show(std::move(box), LayerOption::KeepOther); + *weakBox = Ui::show(std::move(box), Ui::LayerOption::KeepOther); } else { saveCallback(); } diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 6ac954ede..284d588de 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -504,7 +504,7 @@ bool CheckEditCloudPassword(not_null<::Main::Session*> session) { return false; } -object_ptr EditCloudPasswordBox(not_null session) { +object_ptr EditCloudPasswordBox(not_null session) { const auto current = session->api().passwordStateCurrent(); Assert(current.has_value()); @@ -554,8 +554,8 @@ void RemoveCloudPassword(not_null<::Main::Session*> session) { }, box->lifetime()); } -object_ptr CloudPasswordAppOutdatedBox() { - auto box = std::make_shared>(); +object_ptr CloudPasswordAppOutdatedBox() { + auto box = std::make_shared>(); const auto callback = [=] { Core::UpdateApplication(); if (*box) (*box)->closeBox(); @@ -589,7 +589,7 @@ void AddPrivacyButton( ) | rpl::start_with_next([=](const Privacy &value) { Ui::show( Box(controller, controllerFactory(), value), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); }); }); } diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h index 4853e88bc..09ea6361b 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.h +++ b/Telegram/SourceFiles/settings/settings_privacy_security.h @@ -11,17 +11,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" class EditPrivacyController; + +namespace Ui { class BoxContent; +} // namespace Ui namespace Settings { int ExceptionUsersCount(const std::vector> &exceptions); bool CheckEditCloudPassword(not_null<::Main::Session*> session); -object_ptr EditCloudPasswordBox( +object_ptr EditCloudPasswordBox( not_null<::Main::Session*> session); void RemoveCloudPassword(not_null<::Main::Session*> session); -object_ptr CloudPasswordAppOutdatedBox(); +object_ptr CloudPasswordAppOutdatedBox(); void AddPrivacyButton( not_null controller, diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 67754d1fd..2eca96d02 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -953,13 +953,13 @@ void FileLoadTask::finish() { Ui::show( Box( tr::lng_send_image_empty(tr::now, lt_name, _filepath)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); removeFromAlbum(); } else if (_result->filesize > App::kFileSizeLimit) { Ui::show( Box( tr::lng_send_image_too_large(tr::now, lt_name, _filepath)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); removeFromAlbum(); } else if (App::main()) { const auto fullId = _msgIdToEdit diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp index 2e4e166d0..844b02593 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.cpp +++ b/Telegram/SourceFiles/support/support_autocomplete.cpp @@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "styles/style_chat_helpers.h" #include "styles/style_window.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" namespace Support { namespace { diff --git a/Telegram/SourceFiles/support/support_autocomplete.h b/Telegram/SourceFiles/support/support_autocomplete.h index 4f552d11f..903c46083 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.h +++ b/Telegram/SourceFiles/support/support_autocomplete.h @@ -60,7 +60,7 @@ private: }; class ConfirmContactBox - : public BoxContent + : public Ui::BoxContent , public HistoryView::SimpleElementDelegate { public: ConfirmContactBox( diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp index 4b2afed2d..56e2d717d 100644 --- a/Telegram/SourceFiles/support/support_helper.cpp +++ b/Telegram/SourceFiles/support/support_helper.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "observer_peer.h" #include "apiwrap.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace Main { @@ -43,7 +44,7 @@ constexpr auto kOccupyFor = TimeId(60); constexpr auto kReoccupyEach = 30 * crl::time(1000); constexpr auto kMaxSupportInfoLength = MaxMessageSize * 4; -class EditInfoBox : public BoxContent { +class EditInfoBox : public Ui::BoxContent { public: EditInfoBox( QWidget*, @@ -517,7 +518,7 @@ void Helper::showEditInfoBox(not_null user) { }; Ui::show( Box(&user->session(), editData, save), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } void Helper::saveInfo( diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index a95a70d21..d26302a99 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/multi_select.h" #include "ui/effects/ripple_animation.h" #include "data/data_countries.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_intro.h" diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index 33648c0c0..c8255115c 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -54,7 +54,7 @@ private: }; -class CountrySelectBox : public BoxContent { +class CountrySelectBox : public Ui::BoxContent { Q_OBJECT public: diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.cpp b/Telegram/SourceFiles/ui/effects/radial_animation.cpp deleted file mode 100644 index ca0e2729e..000000000 --- a/Telegram/SourceFiles/ui/effects/radial_animation.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* -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 "ui/effects/radial_animation.h" - -#include "styles/style_widgets.h" - -namespace Ui { - -void RadialAnimation::start(float64 prg) { - _firstStart = _lastStart = _lastTime = crl::now(); - const auto iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength); - const auto iprgstrict = qRound(prg * AlmostFullArcLength); - _arcEnd = anim::value(iprgstrict, iprg); - _animation.start(); -} - -bool RadialAnimation::update(float64 prg, bool finished, crl::time ms) { - const auto iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength); - const auto result = (iprg != qRound(_arcEnd.to())); - if (_finished != finished) { - _arcEnd.start(iprg); - _finished = finished; - _lastStart = _lastTime; - } else if (result) { - _arcEnd.start(iprg); - _lastStart = _lastTime; - } - _lastTime = ms; - - const auto dt = float64(ms - _lastStart); - const auto fulldt = float64(ms - _firstStart); - const auto opacitydt = _finished - ? (_lastStart - _firstStart) - : fulldt; - _opacity = qMin(opacitydt / st::radialDuration, 1.); - if (anim::Disabled()) { - _arcEnd.update(1., anim::linear); - if (finished) { - stop(); - } - } else if (!finished) { - _arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear); - } else if (dt >= st::radialDuration) { - _arcEnd.update(1., anim::linear); - stop(); - } else { - auto r = dt / st::radialDuration; - _arcEnd.update(r, anim::linear); - _opacity *= 1 - r; - } - auto fromstart = fulldt / st::radialPeriod; - _arcStart.update(fromstart - std::floor(fromstart), anim::linear); - return result; -} - -void RadialAnimation::stop() { - _firstStart = _lastStart = _lastTime = 0; - _arcEnd = anim::value(); - _animation.stop(); -} - -void RadialAnimation::draw( - Painter &p, - const QRect &inner, - int32 thickness, - style::color color) const { - const auto state = computeState(); - - auto o = p.opacity(); - p.setOpacity(o * state.shown); - - auto pen = color->p; - auto was = p.pen(); - pen.setWidth(thickness); - pen.setCapStyle(Qt::RoundCap); - p.setPen(pen); - - { - PainterHighQualityEnabler hq(p); - p.drawArc(inner, state.arcFrom, state.arcLength); - } - - p.setPen(was); - p.setOpacity(o); -} - -RadialState RadialAnimation::computeState() const { - auto length = MinArcLength + qRound(_arcEnd.current()); - auto from = QuarterArcLength - - length - - (anim::Disabled() ? 0 : qRound(_arcStart.current())); - if (rtl()) { - from = QuarterArcLength - (from - QuarterArcLength) - length; - if (from < 0) from += FullArcLength; - } - return { _opacity, from, length }; -} - -void InfiniteRadialAnimation::start(crl::time skip) { - const auto now = crl::now(); - if (_workFinished <= now && (_workFinished || !_workStarted)) { - _workStarted = std::max(now + _st.sineDuration - skip, crl::time(1)); - _workFinished = 0; - } - if (!_animation.animating()) { - _animation.start(); - } -} - -void InfiniteRadialAnimation::stop(anim::type animated) { - const auto now = crl::now(); - if (anim::Disabled() || animated == anim::type::instant) { - _workFinished = now; - } - if (!_workFinished) { - const auto zero = _workStarted - _st.sineDuration; - const auto index = (now - zero + _st.sinePeriod - _st.sineShift) - / _st.sinePeriod; - _workFinished = zero - + _st.sineShift - + (index * _st.sinePeriod) - + _st.sineDuration; - } else if (_workFinished <= now) { - _animation.stop(); - } -} - -void InfiniteRadialAnimation::draw( - Painter &p, - QPoint position, - int outerWidth) { - draw(p, position, _st.size, outerWidth); -} - -void InfiniteRadialAnimation::draw( - Painter &p, - QPoint position, - QSize size, - int outerWidth) { - const auto state = computeState(); - - auto o = p.opacity(); - p.setOpacity(o * state.shown); - - const auto rect = style::rtlrect( - position.x(), - position.y(), - size.width(), - size.height(), - outerWidth); - const auto was = p.pen(); - const auto brush = p.brush(); - if (anim::Disabled()) { - anim::DrawStaticLoading(p, rect, _st.thickness, _st.color); - } else { - auto pen = _st.color->p; - pen.setWidth(_st.thickness); - pen.setCapStyle(Qt::RoundCap); - p.setPen(pen); - - { - PainterHighQualityEnabler hq(p); - p.drawArc( - rect, - state.arcFrom, - state.arcLength); - } - } - p.setPen(was); - p.setBrush(brush); - p.setOpacity(o); -} - -RadialState InfiniteRadialAnimation::computeState() { - const auto now = crl::now(); - const auto linear = FullArcLength - - int(((now * FullArcLength) / _st.linearPeriod) % FullArcLength); - if (!_workStarted || (_workFinished && _workFinished <= now)) { - const auto shown = 0.; - _animation.stop(); - return { - shown, - linear, - FullArcLength }; - } - if (anim::Disabled()) { - const auto shown = 1.; - return { 1., 0, FullArcLength }; - } - const auto min = int(std::round(FullArcLength * _st.arcMin)); - const auto max = int(std::round(FullArcLength * _st.arcMax)); - if (now <= _workStarted) { - // zero .. _workStarted - const auto zero = _workStarted - _st.sineDuration; - const auto shown = (now - zero) / float64(_st.sineDuration); - const auto length = anim::interpolate( - FullArcLength, - min, - anim::sineInOut(1., snap(shown, 0., 1.))); - return { - shown, - linear, - length }; - } else if (!_workFinished || now <= _workFinished - _st.sineDuration) { - // _workStared .. _workFinished - _st.sineDuration - const auto shown = 1.; - const auto cycles = (now - _workStarted) / _st.sinePeriod; - const auto relative = (now - _workStarted) % _st.sinePeriod; - const auto smallDuration = _st.sineShift - _st.sineDuration; - const auto largeDuration = _st.sinePeriod - - _st.sineShift - - _st.sineDuration; - const auto basic = int((linear - + min - + (cycles * (FullArcLength + min - max))) % FullArcLength); - if (relative <= smallDuration) { - // localZero .. growStart - return { - shown, - basic - min, - min }; - } else if (relative <= smallDuration + _st.sineDuration) { - // growStart .. growEnd - const auto growLinear = (relative - smallDuration) / - float64(_st.sineDuration); - const auto growProgress = anim::sineInOut(1., growLinear); - const auto length = anim::interpolate(min, max, growProgress); - return { - shown, - basic - length, - length }; - } else if (relative <= _st.sinePeriod - _st.sineDuration) { - // growEnd .. shrinkStart - return { - shown, - basic - max, - max }; - } else { - // shrinkStart .. shrinkEnd - const auto shrinkLinear = (relative - - (_st.sinePeriod - _st.sineDuration)) - / float64(_st.sineDuration); - const auto shrinkProgress = anim::sineInOut(1., shrinkLinear); - const auto shrink = anim::interpolate( - 0, - max - min, - shrinkProgress); - return { - shown, - basic - max, - max - shrink }; // interpolate(max, min, shrinkProgress) - } - } else { - // _workFinished - _st.sineDuration .. _workFinished - const auto hidden = (now - (_workFinished - _st.sineDuration)) - / float64(_st.sineDuration); - const auto cycles = (_workFinished - _workStarted) / _st.sinePeriod; - const auto basic = int((linear - + min - + cycles * (FullArcLength + min - max)) % FullArcLength); - const auto length = anim::interpolate( - min, - FullArcLength, - anim::sineInOut(1., snap(hidden, 0., 1.))); - return { - 1. - hidden, - basic - length, - length }; - } - //const auto frontPeriods = time / st.sinePeriod; - //const auto frontCurrent = time % st.sinePeriod; - //const auto frontProgress = anim::sineInOut( - // st.arcMax - st.arcMin, - // std::min(frontCurrent, crl::time(st.sineDuration)) - // / float64(st.sineDuration)); - //const auto backTime = std::max(time - st.sineShift, 0LL); - //const auto backPeriods = backTime / st.sinePeriod; - //const auto backCurrent = backTime % st.sinePeriod; - //const auto backProgress = anim::sineInOut( - // st.arcMax - st.arcMin, - // std::min(backCurrent, crl::time(st.sineDuration)) - // / float64(st.sineDuration)); - //const auto front = linear + std::round((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * FullArcLength); - //const auto from = linear + std::round((backProgress + backPeriods * (st.arcMax - st.arcMin)) * FullArcLength); - //const auto length = (front - from); - - //return { - // _opacity, - // from, - // length - //}; -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.h b/Telegram/SourceFiles/ui/effects/radial_animation.h deleted file mode 100644 index be5b389e2..000000000 --- a/Telegram/SourceFiles/ui/effects/radial_animation.h +++ /dev/null @@ -1,109 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/effects/animations.h" - -namespace style { -struct InfiniteRadialAnimation; -} // namespace style - -namespace Ui { - -struct RadialState { - float64 shown = 0.; - int arcFrom = 0; - int arcLength = FullArcLength; -}; - -class RadialAnimation { -public: - template - RadialAnimation(Callback &&callback); - - float64 opacity() const { - return _opacity; - } - bool animating() const { - return _animation.animating(); - } - - void start(float64 prg); - bool update(float64 prg, bool finished, crl::time ms); - void stop(); - - void draw( - Painter &p, - const QRect &inner, - int32 thickness, - style::color color) const; - - RadialState computeState() const; - -private: - crl::time _firstStart = 0; - crl::time _lastStart = 0; - crl::time _lastTime = 0; - float64 _opacity = 0.; - anim::value _arcEnd; - anim::value _arcStart; - Ui::Animations::Basic _animation; - bool _finished = false; - -}; - -template -inline RadialAnimation::RadialAnimation(Callback &&callback) -: _arcStart(0, FullArcLength) -, _animation(std::forward(callback)) { -} - - -class InfiniteRadialAnimation { -public: - template - InfiniteRadialAnimation( - Callback &&callback, - const style::InfiniteRadialAnimation &st); - - bool animating() const { - return _animation.animating(); - } - - void start(crl::time skip = 0); - void stop(anim::type animated = anim::type::normal); - - void draw( - Painter &p, - QPoint position, - int outerWidth); - void draw( - Painter &p, - QPoint position, - QSize size, - int outerWidth); - - RadialState computeState(); - -private: - const style::InfiniteRadialAnimation &_st; - crl::time _workStarted = 0; - crl::time _workFinished = 0; - Ui::Animations::Basic _animation; - -}; - -template -inline InfiniteRadialAnimation::InfiniteRadialAnimation( - Callback &&callback, - const style::InfiniteRadialAnimation &st) -: _st(st) -, _animation(std::forward(callback)) { -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index 90da8b844..7a7737433 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -74,13 +74,13 @@ void SuggestPhoto( || badAspect(image.height(), image.width())) { Ui::show( Box(tr::lng_bad_photo(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } const auto box = Ui::show( Box(image, title), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); box->ready( ) | rpl::start_with_next( std::forward(callback), diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp index fab73dc97..77afdb0ff 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp @@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/widgets/tooltip.h" #include "ui/platform/ui_platform_utility.h" -#include "window/layer_widget.h" +#include "ui/layers/layer_widget.h" #include "window/themes/window_theme.h" #include "core/application.h" #include "app.h" @@ -265,8 +265,8 @@ int SeparatePanel::hideGetDuration() { } void SeparatePanel::showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated) { ensureLayerCreated(); _layer->showBox(std::move(box), options, animated); @@ -282,7 +282,7 @@ void SeparatePanel::ensureLayerCreated() { if (_layer) { return; } - _layer = base::make_unique_q(_body); + _layer = base::make_unique_q(_body); _layer->setHideByBackgroundClick(false); _layer->move(0, 0); _body->sizeValue( diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.h b/Telegram/SourceFiles/ui/widgets/separate_panel.h index cdf439ae5..92608fac1 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.h +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.h @@ -9,19 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" -#include "boxes/abstract_box.h" +#include "ui/layers/layer_widget.h" namespace Ui { +class BoxContent; class IconButton; +class LayerStackWidget; class FlatLabel; template class FadeWrapScaled; } // namespace Ui -namespace Window { -class LayerStackWidget; -} // namespace Window - namespace Ui { class SeparatePanel : public Ui::RpWidget, private base::Subscriber { @@ -37,8 +35,8 @@ public: void showInner(base::unique_qptr inner); void showBox( - object_ptr box, - LayerOptions options, + object_ptr box, + Ui::LayerOptions options, anim::type animated); void showToast(const QString &text); void destroyLayer(); @@ -86,7 +84,7 @@ private: object_ptr> _back; object_ptr _body; base::unique_qptr _inner; - base::unique_qptr _layer = { nullptr }; + base::unique_qptr _layer = { nullptr }; rpl::event_stream<> _synteticBackRequests; rpl::event_stream<> _userCloseRequests; rpl::event_stream<> _closeEvents; diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp deleted file mode 100644 index ff0d601d5..000000000 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ /dev/null @@ -1,847 +0,0 @@ -/* -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 "window/layer_widget.h" - -#include "boxes/abstract_box.h" -#include "ui/widgets/shadow.h" -#include "ui/ui_utility.h" -#include "window/window_main_menu.h" -#include "app.h" -#include "styles/style_boxes.h" -#include "styles/style_widgets.h" - -namespace Window { - -class LayerStackWidget::BackgroundWidget : public TWidget { -public: - BackgroundWidget(QWidget *parent) : TWidget(parent) { - } - - void setDoneCallback(Fn callback) { - _doneCallback = std::move(callback); - } - - void setLayerBoxes(const QRect &specialLayerBox, const QRect &layerBox); - void setCacheImages( - QPixmap &&bodyCache, - QPixmap &&mainMenuCache, - QPixmap &&specialLayerCache, - QPixmap &&layerCache); - void removeBodyCache(); - void startAnimation(Action action); - void skipAnimation(Action action); - void finishAnimating(); - - bool animating() const { - return _a_mainMenuShown.animating() || _a_specialLayerShown.animating() || _a_layerShown.animating(); - } - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - bool isShown() const { - return _mainMenuShown || _specialLayerShown || _layerShown; - } - void checkIfDone(); - void setMainMenuShown(bool shown); - void setSpecialLayerShown(bool shown); - void setLayerShown(bool shown); - void checkWasShown(bool wasShown); - void animationCallback(); - - QPixmap _bodyCache; - QPixmap _mainMenuCache; - int _mainMenuCacheWidth = 0; - QPixmap _specialLayerCache; - QPixmap _layerCache; - - Fn _doneCallback; - - bool _wasAnimating = false; - bool _inPaintEvent = false; - Ui::Animations::Simple _a_shown; - Ui::Animations::Simple _a_mainMenuShown; - Ui::Animations::Simple _a_specialLayerShown; - Ui::Animations::Simple _a_layerShown; - - QRect _specialLayerBox, _specialLayerCacheBox; - QRect _layerBox, _layerCacheBox; - int _mainMenuRight = 0; - - bool _mainMenuShown = false; - bool _specialLayerShown = false; - bool _layerShown = false; - -}; - -void LayerStackWidget::BackgroundWidget::setCacheImages( - QPixmap &&bodyCache, - QPixmap &&mainMenuCache, - QPixmap &&specialLayerCache, - QPixmap &&layerCache) { - _bodyCache = std::move(bodyCache); - _mainMenuCache = std::move(mainMenuCache); - _specialLayerCache = std::move(specialLayerCache); - _layerCache = std::move(layerCache); - _specialLayerCacheBox = _specialLayerBox; - _layerCacheBox = _layerBox; - setAttribute(Qt::WA_OpaquePaintEvent, !_bodyCache.isNull()); -} - -void LayerStackWidget::BackgroundWidget::removeBodyCache() { - if (!_bodyCache.isNull()) { - _bodyCache = {}; - setAttribute(Qt::WA_OpaquePaintEvent, false); - } -} - -void LayerStackWidget::BackgroundWidget::startAnimation(Action action) { - if (action == Action::ShowMainMenu) { - setMainMenuShown(true); - } else if (action != Action::HideLayer - && action != Action::HideSpecialLayer) { - setMainMenuShown(false); - } - if (action == Action::ShowSpecialLayer) { - setSpecialLayerShown(true); - } else if (action == Action::ShowMainMenu - || action == Action::HideAll - || action == Action::HideSpecialLayer) { - setSpecialLayerShown(false); - } - if (action == Action::ShowLayer) { - setLayerShown(true); - } else if (action != Action::ShowSpecialLayer - && action != Action::HideSpecialLayer) { - setLayerShown(false); - } - _wasAnimating = true; - checkIfDone(); -} - -void LayerStackWidget::BackgroundWidget::skipAnimation(Action action) { - startAnimation(action); - finishAnimating(); -} - -void LayerStackWidget::BackgroundWidget::checkIfDone() { - if (!_wasAnimating || _inPaintEvent || animating()) { - return; - } - _wasAnimating = false; - _mainMenuCache = _specialLayerCache = _layerCache = QPixmap(); - removeBodyCache(); - if (_doneCallback) { - _doneCallback(); - } -} - -void LayerStackWidget::BackgroundWidget::setMainMenuShown(bool shown) { - auto wasShown = isShown(); - if (_mainMenuShown != shown) { - _mainMenuShown = shown; - _a_mainMenuShown.start([this] { animationCallback(); }, _mainMenuShown ? 0. : 1., _mainMenuShown ? 1. : 0., st::boxDuration, anim::easeOutCirc); - } - _mainMenuCacheWidth = (_mainMenuCache.width() / cIntRetinaFactor()) - st::boxRoundShadow.extend.right(); - _mainMenuRight = _mainMenuShown ? _mainMenuCacheWidth : 0; - checkWasShown(wasShown); -} - -void LayerStackWidget::BackgroundWidget::setSpecialLayerShown(bool shown) { - auto wasShown = isShown(); - if (_specialLayerShown != shown) { - _specialLayerShown = shown; - _a_specialLayerShown.start([this] { animationCallback(); }, _specialLayerShown ? 0. : 1., _specialLayerShown ? 1. : 0., st::boxDuration); - } - checkWasShown(wasShown); -} - -void LayerStackWidget::BackgroundWidget::setLayerShown(bool shown) { - auto wasShown = isShown(); - if (_layerShown != shown) { - _layerShown = shown; - _a_layerShown.start([this] { animationCallback(); }, _layerShown ? 0. : 1., _layerShown ? 1. : 0., st::boxDuration); - } - checkWasShown(wasShown); -} - -void LayerStackWidget::BackgroundWidget::checkWasShown(bool wasShown) { - if (isShown() != wasShown) { - _a_shown.start([this] { animationCallback(); }, wasShown ? 1. : 0., wasShown ? 0. : 1., st::boxDuration, anim::easeOutCirc); - } -} - -void LayerStackWidget::BackgroundWidget::setLayerBoxes(const QRect &specialLayerBox, const QRect &layerBox) { - _specialLayerBox = specialLayerBox; - _layerBox = layerBox; - update(); -} - -void LayerStackWidget::BackgroundWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - - _inPaintEvent = true; - auto guard = gsl::finally([this] { - _inPaintEvent = false; - crl::on_main(this, [=] { checkIfDone(); }); - }); - - if (!_bodyCache.isNull()) { - p.drawPixmap(0, 0, _bodyCache); - } - - auto specialLayerBox = _specialLayerCache.isNull() ? _specialLayerBox : _specialLayerCacheBox; - auto layerBox = _layerCache.isNull() ? _layerBox : _layerCacheBox; - - auto mainMenuProgress = _a_mainMenuShown.value(-1); - auto mainMenuRight = (_mainMenuCache.isNull() || mainMenuProgress < 0) ? _mainMenuRight : (mainMenuProgress < 0) ? _mainMenuRight : anim::interpolate(0, _mainMenuCacheWidth, mainMenuProgress); - if (mainMenuRight) { - // Move showing boxes to the right while main menu is hiding. - if (!_specialLayerCache.isNull()) { - specialLayerBox.moveLeft(specialLayerBox.left() + mainMenuRight / 2); - } - if (!_layerCache.isNull()) { - layerBox.moveLeft(layerBox.left() + mainMenuRight / 2); - } - } - auto bgOpacity = _a_shown.value(isShown() ? 1. : 0.); - auto specialLayerOpacity = _a_specialLayerShown.value(_specialLayerShown ? 1. : 0.); - auto layerOpacity = _a_layerShown.value(_layerShown ? 1. : 0.); - if (bgOpacity == 0.) { - return; - } - - p.setOpacity(bgOpacity); - auto overSpecialOpacity = (layerOpacity * specialLayerOpacity); - auto bg = myrtlrect(mainMenuRight, 0, width() - mainMenuRight, height()); - - if (_mainMenuCache.isNull() && mainMenuRight > 0) { - // All cache images are taken together with their shadows, - // so we paint shadow only when there is no cache. - Ui::Shadow::paint(p, myrtlrect(0, 0, mainMenuRight, height()), width(), st::boxRoundShadow, RectPart::Right); - } - - if (_specialLayerCache.isNull() && !specialLayerBox.isEmpty()) { - // All cache images are taken together with their shadows, - // so we paint shadow only when there is no cache. - auto sides = RectPart::Left | RectPart::Right; - auto topCorners = (specialLayerBox.y() > 0); - auto bottomCorners = (specialLayerBox.y() + specialLayerBox.height() < height()); - if (topCorners) { - sides |= RectPart::Top; - } - if (bottomCorners) { - sides |= RectPart::Bottom; - } - if (topCorners || bottomCorners) { - p.setClipRegion(QRegion(rect()) - specialLayerBox.marginsRemoved(QMargins(st::boxRadius, 0, st::boxRadius, 0)) - specialLayerBox.marginsRemoved(QMargins(0, st::boxRadius, 0, st::boxRadius))); - } - Ui::Shadow::paint(p, specialLayerBox, width(), st::boxRoundShadow, sides); - - if (topCorners || bottomCorners) { - // In case of painting the shadow above the special layer we get - // glitches in the corners, so we need to paint the corners once more. - p.setClipping(false); - auto parts = (topCorners ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) - | (bottomCorners ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None); - App::roundRect(p, specialLayerBox, st::boxBg, BoxCorners, nullptr, parts); - } - } - - if (!layerBox.isEmpty() && !_specialLayerCache.isNull() && overSpecialOpacity < bgOpacity) { - // In case of moving special layer below the background while showing a box - // we need to fill special layer rect below its cache with a complex opacity - // (alpha_final - alpha_current) / (1 - alpha_current) so we won't get glitches - // in the transparent special layer cache corners after filling special layer - // rect above its cache with alpha_current opacity. - auto region = QRegion(bg) - specialLayerBox; - for (auto rect : region.rects()) { - p.fillRect(rect, st::layerBg); - } - p.setOpacity((bgOpacity - overSpecialOpacity) / (1. - (overSpecialOpacity * st::layerBg->c.alphaF()))); - p.fillRect(specialLayerBox, st::layerBg); - p.setOpacity(bgOpacity); - } else { - p.fillRect(bg, st::layerBg); - } - - if (!_specialLayerCache.isNull() && specialLayerOpacity > 0) { - p.setOpacity(specialLayerOpacity); - auto cacheLeft = specialLayerBox.x() - st::boxRoundShadow.extend.left(); - auto cacheTop = specialLayerBox.y() - (specialLayerBox.y() > 0 ? st::boxRoundShadow.extend.top() : 0); - p.drawPixmapLeft(cacheLeft, cacheTop, width(), _specialLayerCache); - } - if (!layerBox.isEmpty()) { - if (!_specialLayerCache.isNull()) { - p.setOpacity(overSpecialOpacity); - p.fillRect(specialLayerBox, st::layerBg); - } - if (_layerCache.isNull()) { - p.setOpacity(layerOpacity); - Ui::Shadow::paint(p, layerBox, width(), st::boxRoundShadow); - } - } - if (!_layerCache.isNull() && layerOpacity > 0) { - p.setOpacity(layerOpacity); - p.drawPixmapLeft(layerBox.topLeft() - QPoint(st::boxRoundShadow.extend.left(), st::boxRoundShadow.extend.top()), width(), _layerCache); - } - if (!_mainMenuCache.isNull() && mainMenuRight > 0) { - p.setOpacity(1.); - auto shownWidth = mainMenuRight + st::boxRoundShadow.extend.right(); - auto sourceWidth = shownWidth * cIntRetinaFactor(); - auto sourceRect = style::rtlrect(_mainMenuCache.width() - sourceWidth, 0, sourceWidth, _mainMenuCache.height(), _mainMenuCache.width()); - p.drawPixmapLeft(0, 0, shownWidth, height(), width(), _mainMenuCache, sourceRect); - } -} - -void LayerStackWidget::BackgroundWidget::finishAnimating() { - _a_shown.stop(); - _a_mainMenuShown.stop(); - _a_specialLayerShown.stop(); - _a_layerShown.stop(); - checkIfDone(); -} - -void LayerStackWidget::BackgroundWidget::animationCallback() { - update(); - checkIfDone(); -} - -LayerStackWidget::LayerStackWidget(QWidget *parent) -: RpWidget(parent) -, _background(this) { - setGeometry(parentWidget()->rect()); - hide(); - _background->setDoneCallback([this] { animationDone(); }); -} - -void LayerWidget::setInnerFocus() { - if (!isAncestorOf(window()->focusWidget())) { - doSetInnerFocus(); - } -} - -bool LayerWidget::overlaps(const QRect &globalRect) { - if (isHidden()) { - return false; - } - auto testRect = QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()); - if (testAttribute(Qt::WA_OpaquePaintEvent)) { - return rect().contains(testRect); - } - if (QRect(0, st::boxRadius, width(), height() - 2 * st::boxRadius).contains(testRect)) { - return true; - } - if (QRect(st::boxRadius, 0, width() - 2 * st::boxRadius, height()).contains(testRect)) { - return true; - } - return false; -} - -void LayerWidget::mousePressEvent(QMouseEvent *e) { - e->accept(); -} - -void LayerWidget::resizeEvent(QResizeEvent *e) { - if (_resizedCallback) { - _resizedCallback(); - } -} - -void LayerStackWidget::setHideByBackgroundClick(bool hide) { - _hideByBackgroundClick = hide; -} - -void LayerStackWidget::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Escape) { - hideCurrent(anim::type::normal); - } -} - -void LayerStackWidget::mousePressEvent(QMouseEvent *e) { - Ui::PostponeCall(this, [=] { backgroundClicked(); }); -} - -void LayerStackWidget::backgroundClicked() { - if (!_hideByBackgroundClick) { - return; - } - if (const auto layer = currentLayer()) { - if (!layer->closeByOutsideClick()) { - return; - } - } else if (const auto special = _specialLayer.data()) { - if (!special->closeByOutsideClick()) { - return; - } - } - hideCurrent(anim::type::normal); -} - -void LayerStackWidget::hideCurrent(anim::type animated) { - return currentLayer() ? hideLayers(animated) : hideAll(animated); -} - -void LayerStackWidget::hideLayers(anim::type animated) { - startAnimation([] {}, [&] { - clearLayers(); - }, Action::HideLayer, animated); -} - -void LayerStackWidget::hideAll(anim::type animated) { - startAnimation([] {}, [&] { - clearLayers(); - clearSpecialLayer(); - _mainMenu.destroy(); - }, Action::HideAll, animated); -} - -void LayerStackWidget::hideTopLayer(anim::type animated) { - if (_specialLayer || _mainMenu) { - hideLayers(animated); - } else { - hideAll(animated); - } -} - -void LayerStackWidget::removeBodyCache() { - _background->removeBodyCache(); - setAttribute(Qt::WA_OpaquePaintEvent, false); -} - -bool LayerStackWidget::layerShown() const { - return _specialLayer || currentLayer() || _mainMenu; -} - -void LayerStackWidget::setCacheImages() { - auto bodyCache = QPixmap(), mainMenuCache = QPixmap(); - auto specialLayerCache = QPixmap(); - if (_specialLayer) { - Ui::SendPendingMoveResizeEvents(_specialLayer); - auto sides = RectPart::Left | RectPart::Right; - if (_specialLayer->y() > 0) { - sides |= RectPart::Top; - } - if (_specialLayer->y() + _specialLayer->height() < height()) { - sides |= RectPart::Bottom; - } - specialLayerCache = Ui::Shadow::grab(_specialLayer, st::boxRoundShadow, sides); - } - auto layerCache = QPixmap(); - if (auto layer = currentLayer()) { - layerCache = Ui::Shadow::grab(layer, st::boxRoundShadow); - } - if (isAncestorOf(window()->focusWidget())) { - setFocus(); - } - if (_mainMenu) { - removeBodyCache(); - hideChildren(); - bodyCache = Ui::GrabWidget(parentWidget()); - showChildren(); - mainMenuCache = Ui::Shadow::grab(_mainMenu, st::boxRoundShadow, RectPart::Right); - } - setAttribute(Qt::WA_OpaquePaintEvent, !bodyCache.isNull()); - updateLayerBoxes(); - _background->setCacheImages(std::move(bodyCache), std::move(mainMenuCache), std::move(specialLayerCache), std::move(layerCache)); -} - -void LayerStackWidget::closeLayer(not_null layer) { - const auto weak = Ui::MakeWeak(layer.get()); - if (weak->inFocusChain()) { - setFocus(); - } - if (!weak || !weak->setClosing()) { - // This layer is already closing. - return; - } else if (!weak) { - // setClosing() could've killed the layer. - return; - } - - if (layer == _specialLayer) { - hideAll(anim::type::normal); - } else if (layer == currentLayer()) { - if (_layers.size() == 1) { - hideCurrent(anim::type::normal); - } else { - const auto taken = std::move(_layers.back()); - _layers.pop_back(); - - layer = currentLayer(); - layer->parentResized(); - if (!_background->animating()) { - layer->show(); - showFinished(); - } - } - } else { - for (auto i = _layers.begin(), e = _layers.end(); i != e; ++i) { - if (layer == i->get()) { - const auto taken = std::move(*i); - _layers.erase(i); - break; - } - } - } -} - -void LayerStackWidget::updateLayerBoxes() { - const auto layerBox = [&] { - if (const auto layer = currentLayer()) { - return layer->geometry(); - } - return QRect(); - }(); - const auto specialLayerBox = _specialLayer - ? _specialLayer->geometry() - : QRect(); - _background->setLayerBoxes(specialLayerBox, layerBox); - update(); -} - -void LayerStackWidget::finishAnimating() { - _background->finishAnimating(); -} - -bool LayerStackWidget::canSetFocus() const { - return (currentLayer() || _specialLayer || _mainMenu); -} - -void LayerStackWidget::setInnerFocus() { - if (_background->animating()) { - setFocus(); - } else if (auto l = currentLayer()) { - l->setInnerFocus(); - } else if (_specialLayer) { - _specialLayer->setInnerFocus(); - } else if (_mainMenu) { - _mainMenu->setInnerFocus(); - } -} - -bool LayerStackWidget::contentOverlapped(const QRect &globalRect) { - if (isHidden()) { - return false; - } - if (_specialLayer && _specialLayer->overlaps(globalRect)) { - return true; - } - if (auto layer = currentLayer()) { - return layer->overlaps(globalRect); - } - return false; -} - -template -void LayerStackWidget::startAnimation( - SetupNew setupNewWidgets, - ClearOld clearOldWidgets, - Action action, - anim::type animated) { - if (animated == anim::type::instant) { - setupNewWidgets(); - clearOldWidgets(); - prepareForAnimation(); - _background->skipAnimation(action); - } else { - setupNewWidgets(); - setCacheImages(); - const auto weak = Ui::MakeWeak(this); - clearOldWidgets(); - if (weak) { - prepareForAnimation(); - _background->startAnimation(action); - } - } -} - -void LayerStackWidget::resizeEvent(QResizeEvent *e) { - const auto weak = Ui::MakeWeak(this); - _background->setGeometry(rect()); - if (!weak) { - return; - } - if (_specialLayer) { - _specialLayer->parentResized(); - if (!weak) { - return; - } - } - if (const auto layer = currentLayer()) { - layer->parentResized(); - if (!weak) { - return; - } - } - if (_mainMenu) { - _mainMenu->resize(_mainMenu->width(), height()); - if (!weak) { - return; - } - } - updateLayerBoxes(); -} - -void LayerStackWidget::showBox( - object_ptr box, - LayerOptions options, - anim::type animated) { - if (options & LayerOption::KeepOther) { - if (options & LayerOption::ShowAfterOther) { - prependBox(std::move(box), animated); - } else { - appendBox(std::move(box), animated); - } - } else { - replaceBox(std::move(box), animated); - } -} - -void LayerStackWidget::replaceBox( - object_ptr box, - anim::type animated) { - const auto pointer = pushBox(std::move(box), animated); - const auto removeTill = ranges::find( - _layers, - pointer, - &std::unique_ptr::get); - _closingLayers.insert( - end(_closingLayers), - std::make_move_iterator(begin(_layers)), - std::make_move_iterator(removeTill)); - _layers.erase(begin(_layers), removeTill); - clearClosingLayers(); -} - -void LayerStackWidget::prepareForAnimation() { - if (isHidden()) { - show(); - } - if (_mainMenu) { - _mainMenu->hide(); - } - if (_specialLayer) { - _specialLayer->hide(); - } - if (const auto layer = currentLayer()) { - layer->hide(); - } -} - -void LayerStackWidget::animationDone() { - bool hidden = true; - if (_mainMenu) { - _mainMenu->show(); - hidden = false; - } - if (_specialLayer) { - _specialLayer->show(); - hidden = false; - } - if (auto layer = currentLayer()) { - layer->show(); - hidden = false; - } - setAttribute(Qt::WA_OpaquePaintEvent, false); - if (hidden) { - _hideFinishStream.fire({}); - } else { - showFinished(); - } -} - -rpl::producer<> LayerStackWidget::hideFinishEvents() const { - return _hideFinishStream.events(); -} - -void LayerStackWidget::showFinished() { - fixOrder(); - sendFakeMouseEvent(); - updateLayerBoxes(); - if (_specialLayer) { - _specialLayer->showFinished(); - } - if (auto layer = currentLayer()) { - layer->showFinished(); - } - if (canSetFocus()) { - setInnerFocus(); - } -} - -void LayerStackWidget::showSpecialLayer( - object_ptr layer, - anim::type animated) { - startAnimation([&] { - _specialLayer.destroy(); - _specialLayer = std::move(layer); - initChildLayer(_specialLayer); - }, [&] { - _mainMenu.destroy(); - }, Action::ShowSpecialLayer, animated); -} - -bool LayerStackWidget::showSectionInternal( - not_null memento, - const SectionShow ¶ms) { - if (_specialLayer) { - return _specialLayer->showSectionInternal(memento, params); - } - return false; -} - -void LayerStackWidget::hideSpecialLayer(anim::type animated) { - startAnimation([] {}, [&] { - clearSpecialLayer(); - _mainMenu.destroy(); - }, Action::HideSpecialLayer, animated); -} - -void LayerStackWidget::showMainMenu( - not_null controller, - anim::type animated) { - startAnimation([&] { - _mainMenu.create(this, controller); - _mainMenu->setGeometryToLeft(0, 0, _mainMenu->width(), height()); - _mainMenu->setParent(this); - }, [&] { - clearLayers(); - _specialLayer.destroy(); - }, Action::ShowMainMenu, animated); -} - -void LayerStackWidget::appendBox( - object_ptr box, - anim::type animated) { - pushBox(std::move(box), animated); -} - -LayerWidget *LayerStackWidget::pushBox( - object_ptr box, - anim::type animated) { - auto oldLayer = currentLayer(); - if (oldLayer) { - if (oldLayer->inFocusChain()) setFocus(); - oldLayer->hide(); - } - _layers.push_back(std::make_unique(this, std::move(box))); - const auto raw = _layers.back().get(); - initChildLayer(raw); - - if (_layers.size() > 1) { - if (!_background->animating()) { - raw->setVisible(true); - showFinished(); - } - } else { - startAnimation([] {}, [&] { - _mainMenu.destroy(); - }, Action::ShowLayer, animated); - } - - return raw; -} - -void LayerStackWidget::prependBox( - object_ptr box, - anim::type animated) { - if (_layers.empty()) { - replaceBox(std::move(box), animated); - return; - } - _layers.insert( - begin(_layers), - std::make_unique(this, std::move(box))); - const auto raw = _layers.front().get(); - raw->hide(); - initChildLayer(raw); -} - -bool LayerStackWidget::takeToThirdSection() { - return _specialLayer - ? _specialLayer->takeToThirdSection() - : false; -} - -void LayerStackWidget::clearLayers() { - _closingLayers.insert( - end(_closingLayers), - std::make_move_iterator(begin(_layers)), - std::make_move_iterator(end(_layers))); - _layers.clear(); - clearClosingLayers(); -} - -void LayerStackWidget::clearClosingLayers() { - const auto weak = Ui::MakeWeak(this); - while (!_closingLayers.empty()) { - const auto index = _closingLayers.size() - 1; - const auto layer = _closingLayers.back().get(); - if (layer->inFocusChain()) { - setFocus(); - } - - // This may destroy LayerStackWidget (by calling Ui::hideLayer). - // So each time we check a weak pointer (if we are still alive). - layer->setClosing(); - - // setClosing() could destroy 'this' or could call clearLayers(). - if (weak && !_closingLayers.empty()) { - // We could enqueue more closing layers, so we remove by index. - Assert(index < _closingLayers.size()); - Assert(_closingLayers[index].get() == layer); - _closingLayers.erase(begin(_closingLayers) + index); - } else { - // Everything was destroyed in clearLayers or ~LayerStackWidget. - break; - } - } -} - -void LayerStackWidget::clearSpecialLayer() { - if (_specialLayer) { - _specialLayer->setClosing(); - _specialLayer.destroy(); - } -} - -void LayerStackWidget::initChildLayer(LayerWidget *layer) { - layer->setParent(this); - layer->setClosedCallback([=] { closeLayer(layer); }); - layer->setResizedCallback([=] { updateLayerBoxes(); }); - Ui::SendPendingMoveResizeEvents(layer); - layer->parentResized(); -} - -void LayerStackWidget::fixOrder() { - if (auto layer = currentLayer()) { - _background->raise(); - layer->raise(); - } else if (_specialLayer) { - _specialLayer->raise(); - } - if (_mainMenu) { - _mainMenu->raise(); - } -} - -void LayerStackWidget::sendFakeMouseEvent() { - SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); -} - -LayerStackWidget::~LayerStackWidget() { - // Some layer destructors call back into LayerStackWidget. - while (!_layers.empty() || !_closingLayers.empty()) { - hideAll(anim::type::instant); - clearClosingLayers(); - } -} - -} // namespace Window diff --git a/Telegram/SourceFiles/window/layer_widget.h b/Telegram/SourceFiles/window/layer_widget.h deleted file mode 100644 index 3d41806f2..000000000 --- a/Telegram/SourceFiles/window/layer_widget.h +++ /dev/null @@ -1,213 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/rp_widget.h" -#include "ui/effects/animations.h" -#include "base/object_ptr.h" -#include "base/flags.h" - -namespace Lottie { -class SinglePlayer; -} // namespace Lottie - -class BoxContent; - -enum class LayerOption { - CloseOther = (1 << 0), - KeepOther = (1 << 1), - ShowAfterOther = (1 << 2), -}; -using LayerOptions = base::flags; -inline constexpr auto is_flag_type(LayerOption) { return true; }; - -namespace Window { - -class MainMenu; -class SessionController; -class SectionMemento; -struct SectionShow; - -class LayerWidget : public Ui::RpWidget { -public: - using RpWidget::RpWidget; - - virtual void parentResized() = 0; - virtual void showFinished() { - } - void setInnerFocus(); - bool setClosing() { - if (!_closing) { - _closing = true; - closeHook(); - return true; - } - return false; - } - - bool overlaps(const QRect &globalRect); - - void setClosedCallback(Fn callback) { - _closedCallback = std::move(callback); - } - void setResizedCallback(Fn callback) { - _resizedCallback = std::move(callback); - } - virtual bool takeToThirdSection() { - return false; - } - virtual bool showSectionInternal( - not_null memento, - const SectionShow ¶ms) { - return false; - } - virtual bool closeByOutsideClick() const { - return true; - } - -protected: - void closeLayer() { - if (const auto callback = base::take(_closedCallback)) { - callback(); - } - } - void mousePressEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - virtual void doSetInnerFocus() { - setFocus(); - } - virtual void closeHook() { - } - -private: - bool _closing = false; - Fn _closedCallback; - Fn _resizedCallback; - -}; - -class LayerStackWidget : public Ui::RpWidget { -public: - LayerStackWidget(QWidget *parent); - - void finishAnimating(); - rpl::producer<> hideFinishEvents() const; - - void showBox( - object_ptr box, - LayerOptions options, - anim::type animated); - void showSpecialLayer( - object_ptr layer, - anim::type animated); - void showMainMenu( - not_null controller, - anim::type animated); - bool takeToThirdSection(); - - bool canSetFocus() const; - void setInnerFocus(); - - bool contentOverlapped(const QRect &globalRect); - - void hideSpecialLayer(anim::type animated); - void hideLayers(anim::type animated); - void hideAll(anim::type animated); - void hideTopLayer(anim::type animated); - void setHideByBackgroundClick(bool hide); - void removeBodyCache(); - - bool showSectionInternal( - not_null memento, - const SectionShow ¶ms); - - bool layerShown() const; - - ~LayerStackWidget(); - -protected: - void keyPressEvent(QKeyEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - void appendBox( - object_ptr box, - anim::type animated); - void prependBox( - object_ptr box, - anim::type animated); - void replaceBox( - object_ptr box, - anim::type animated); - void backgroundClicked(); - - LayerWidget *pushBox( - object_ptr box, - anim::type animated); - void showFinished(); - void hideCurrent(anim::type animated); - void closeLayer(not_null layer); - - enum class Action { - ShowMainMenu, - ShowSpecialLayer, - ShowLayer, - HideSpecialLayer, - HideLayer, - HideAll, - }; - template - void startAnimation( - SetupNew setupNewWidgets, - ClearOld clearOldWidgets, - Action action, - anim::type animated); - - void prepareForAnimation(); - void animationDone(); - - void setCacheImages(); - void clearLayers(); - void clearSpecialLayer(); - void initChildLayer(LayerWidget *layer); - void updateLayerBoxes(); - void fixOrder(); - void sendFakeMouseEvent(); - void clearClosingLayers(); - - LayerWidget *currentLayer() { - return _layers.empty() ? nullptr : _layers.back().get(); - } - const LayerWidget *currentLayer() const { - return const_cast(this)->currentLayer(); - } - - std::vector> _layers; - std::vector> _closingLayers; - - object_ptr _specialLayer = { nullptr }; - object_ptr _mainMenu = { nullptr }; - - class BackgroundWidget; - object_ptr _background; - bool _hideByBackgroundClick = true; - - rpl::event_stream<> _hideFinishStream; - -}; - -} // namespace Window - -class GenericBox; - -template -inline object_ptr Box(Args&&... args) { - const auto parent = static_cast(nullptr); - return object_ptr(parent, std::forward(args)...); -} diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 583198ce4..58c9a1824 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "app.h" #include "styles/style_window.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include #include @@ -217,7 +217,7 @@ void MainWindow::showTermsDecline() { tr::lng_terms_decline_and_delete(), tr::lng_terms_back(), true), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); box->agreeClicks( ) | rpl::start_with_next([=] { @@ -236,7 +236,7 @@ void MainWindow::showTermsDecline() { } void MainWindow::showTermsDelete() { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto deleteByTerms = [=] { if (account().sessionExists()) { account().session().termsDeleteNow(); @@ -251,7 +251,7 @@ void MainWindow::showTermsDelete() { st::attentionBoxButton, deleteByTerms, [=] { if (*box) (*box)->closeBox(); }), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); } bool MainWindow::hideNoQuit() { diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 739520329..a8eedea57 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -14,12 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include -class BoxContent; - namespace Main { class Account; } // namespace Main +namespace Ui { +class BoxContent; +} // namespace Ui + namespace Window { class Controller; @@ -175,7 +177,7 @@ private: object_ptr _outdated; object_ptr _body; object_ptr _rightColumn = { nullptr }; - QPointer _termsBox; + QPointer _termsBox; QIcon _icon; bool _usingSupportIcon = false; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 54ddca477..e0ba399a1 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -19,9 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "dialogs/dialogs_layout.h" #include "window/themes/window_theme.h" -#include "styles/style_dialogs.h" -#include "styles/style_boxes.h" -#include "styles/style_window.h" #include "storage/file_download.h" #include "main/main_session.h" #include "history/history.h" @@ -29,6 +26,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "facades.h" #include "app.h" +#include "styles/style_dialogs.h" +#include "styles/style_layers.h" +#include "styles/style_window.h" #include diff --git a/Telegram/SourceFiles/window/section_memento.h b/Telegram/SourceFiles/window/section_memento.h index 3bf78f478..628c27c64 100644 --- a/Telegram/SourceFiles/window/section_memento.h +++ b/Telegram/SourceFiles/window/section_memento.h @@ -7,11 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Ui { +class LayerWidget; +} // namespace Ui + namespace Window { class SessionController; class SectionWidget; -class LayerWidget; enum class Column; class SectionMemento { @@ -22,7 +25,7 @@ public: Column column, const QRect &geometry) = 0; - virtual object_ptr createLayer( + virtual object_ptr createLayer( not_null controller, const QRect &geometry) { return nullptr; diff --git a/Telegram/SourceFiles/window/section_widget.h b/Telegram/SourceFiles/window/section_widget.h index 5962ef8ba..0c755a9ee 100644 --- a/Telegram/SourceFiles/window/section_widget.h +++ b/Telegram/SourceFiles/window/section_widget.h @@ -15,10 +15,13 @@ namespace Main { class Session; } // namespace Main +namespace Ui { +class LayerWidget; +} // namespace Ui + namespace Window { class SessionController; -class LayerWidget; class SlideAnimation; struct SectionShow; enum class SlideDirection; @@ -130,7 +133,7 @@ public: virtual rpl::producer desiredHeight() const; // Some sections convert to layers on some geometry sizes. - virtual object_ptr moveContentToLayer( + virtual object_ptr moveContentToLayer( QRect bodyGeometry) { return nullptr; } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 4fbf73e89..769698566 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "app.h" #include "styles/style_window.h" #include "styles/style_dialogs.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" namespace Window { @@ -888,7 +889,7 @@ void Editor::closeWithConfirmation() { closeEditor(); return; } - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto close = crl::guard(this, [=] { Background()->clearEditingTheme(ClearEditing::RevertChanges); closeEditor(); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 2b7c2b55c..60cf87441 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_widgets.h" #include "styles/style_window.h" #include "styles/style_settings.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include @@ -673,13 +674,13 @@ void StartEditor( } void CreateBox( - not_null box, + not_null box, not_null window) { CreateForExistingBox(box, window, Data::CloudTheme()); } void CreateForExistingBox( - not_null box, + not_null box, not_null window, const Data::CloudTheme &cloud) { const auto userId = window->account().sessionExists() @@ -807,7 +808,7 @@ QByteArray CollectForExport(const QByteArray &palette) { } void SaveThemeBox( - not_null box, + not_null box, not_null window, const Data::CloudTheme &cloud, const QByteArray &palette) { diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.h b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h index 6accd3005..5d526c905 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/generic_box.h" +#include "ui/layers/generic_box.h" namespace Data { struct CloudTheme; @@ -26,10 +26,10 @@ void StartEditor( not_null window, const Data::CloudTheme &cloud); void CreateBox( - not_null box, + not_null box, not_null window); void CreateForExistingBox( - not_null box, + not_null box, not_null window, const Data::CloudTheme &cloud); void SaveTheme( @@ -38,7 +38,7 @@ void SaveTheme( const QByteArray &palette, Fn unlock); void SaveThemeBox( - not_null box, + not_null box, not_null window, const Data::CloudTheme &cloud, const QByteArray &palette); diff --git a/Telegram/SourceFiles/window/themes/window_theme_warning.cpp b/Telegram/SourceFiles/window/themes/window_theme_warning.cpp index 84a4649e8..ccdfe12f5 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_warning.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_warning.cpp @@ -7,13 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/themes/window_theme_warning.h" -#include "styles/style_boxes.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/ui_utility.h" #include "window/themes/window_theme.h" #include "lang/lang_keys.h" #include "app.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" namespace Window { namespace Theme { diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp index c3abbecef..fb33bb12b 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp @@ -595,7 +595,7 @@ void CloudList::showMenu(Element &element) { } const auto id = cloud.id; _contextMenu->addAction(tr::lng_theme_delete(tr::now), [=] { - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); const auto remove = [=] { if (*box) { (*box)->closeBox(); diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h index b9b0cf137..10fd9ade3 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h @@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/generic_box.h" #include "data/data_cloud_themes.h" +#include "ui/layers/generic_box.h" #include "ui/widgets/checkbox.h" #include "base/unique_qptr.h" #include "base/binary_guard.h" diff --git a/Telegram/SourceFiles/window/window_connecting_widget.cpp b/Telegram/SourceFiles/window/window_connecting_widget.cpp index c15bc2b55..718b17ba5 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.cpp +++ b/Telegram/SourceFiles/window/window_connecting_widget.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "window/themes/window_theme.h" #include "boxes/connection_box.h" +#include "boxes/abstract_box.h" #include "lang/lang_keys.h" #include "facades.h" #include "app.h" diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 7ee034bd7..d281aff33 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -9,7 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "main/main_account.h" -#include "window/layer_widget.h" +#include "ui/layers/box_content.h" +#include "ui/layers/layer_widget.h" #include "window/window_session_controller.h" #include "window/themes/window_theme.h" #include "window/themes/window_theme_editor.h" @@ -75,8 +76,8 @@ void Controller::showSettings() { } void Controller::showBox( - object_ptr content, - LayerOptions options, + object_ptr content, + Ui::LayerOptions options, anim::type animated) { _widget.ui_showBox(std::move(content), options, animated); } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index eae0f4670..d8adfc2e6 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "mainwindow.h" +#include "ui/layers/layer_widget.h" namespace Main { class Account; @@ -45,7 +46,7 @@ public: template QPointer show( object_ptr content, - LayerOptions options = LayerOption::KeepOther, + Ui::LayerOptions options = Ui::LayerOption::KeepOther, anim::type animated = anim::type::normal) { const auto result = QPointer(content.data()); showBox(std::move(content), options, animated); @@ -66,8 +67,8 @@ public: private: void showBox( - object_ptr content, - LayerOptions options, + object_ptr content, + Ui::LayerOptions options, anim::type animated); void checkThemeEditor(); diff --git a/Telegram/SourceFiles/window/window_history_hider.cpp b/Telegram/SourceFiles/window/window_history_hider.cpp index 677e781cb..583541e42 100644 --- a/Telegram/SourceFiles/window/window_history_hider.cpp +++ b/Telegram/SourceFiles/window/window_history_hider.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "facades.h" #include "app.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_history.h" namespace Window { diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index 96109def0..8f3af9398 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -19,12 +19,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/wrap/vertical_layout.h" #include "ui/toast/toast.h" -#include "styles/style_boxes.h" #include "window/window_controller.h" #include "window/window_slide_animation.h" #include "window/window_session_controller.h" #include "main/main_account.h" #include "facades.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" namespace Window { diff --git a/Telegram/SourceFiles/window/window_lock_widgets.h b/Telegram/SourceFiles/window/window_lock_widgets.h index 9795e9fd2..9e6d83400 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.h +++ b/Telegram/SourceFiles/window/window_lock_widgets.h @@ -86,7 +86,7 @@ struct TermsLock { }; -class TermsBox : public BoxContent { +class TermsBox : public Ui::BoxContent { public: TermsBox( QWidget*, diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 9638ee822..1e5b6bb94 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -126,7 +126,7 @@ void MainMenu::ResetScaleButton::paintEvent(QPaintEvent *e) { MainMenu::MainMenu( QWidget *parent, not_null controller) -: RpWidget(parent) +: LayerWidget(parent) , _controller(controller) , _menu(this, st::mainMenu) , _telegram(this, st::mainMenuTelegramLabel) @@ -196,7 +196,7 @@ MainMenu::MainMenu( } }); - resize(st::mainMenuWidth, parentWidget()->height()); + parentResized(); _menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) { emit action->triggered(); }); @@ -237,6 +237,10 @@ MainMenu::MainMenu( initResetScaleButton(); } +void MainMenu::parentResized() { + resize(st::mainMenuWidth, parentWidget()->height()); +} + void MainMenu::refreshMenu() { _menu->clearActions(); if (!_controller->session().supportMode()) { diff --git a/Telegram/SourceFiles/window/window_main_menu.h b/Telegram/SourceFiles/window/window_main_menu.h index 44f43dcbf..cd6406ed7 100644 --- a/Telegram/SourceFiles/window/window_main_menu.h +++ b/Telegram/SourceFiles/window/window_main_menu.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "base/object_ptr.h" #include "ui/rp_widget.h" +#include "ui/layers/layer_widget.h" namespace Ui { class IconButton; @@ -23,18 +24,20 @@ namespace Window { class SessionController; -class MainMenu : public Ui::RpWidget, private base::Subscriber { +class MainMenu : public Ui::LayerWidget, private base::Subscriber { public: MainMenu(QWidget *parent, not_null controller); - void setInnerFocus() { - setFocus(); - } + void parentResized() override; protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void doSetInnerFocus() override { + setFocus(); + } + private: void updateControlsGeometry(); void updatePhone(); diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index 9270d61e0..c2affbe9e 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "chat_helpers/stickers.h" #include "window/window_session_controller.h" -#include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_chat_helpers.h" #include "styles/style_history.h" diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index ac4ece849..90e8ff906 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/mute_settings_box.h" #include "boxes/add_contact_box.h" #include "boxes/report_box.h" -#include "boxes/generic_box.h" #include "boxes/create_poll_box.h" #include "boxes/peers/add_participants_box.h" #include "boxes/peers/edit_contact_box.h" @@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" +#include "ui/layers/generic_box.h" #include "main/main_session.h" #include "apiwrap.h" #include "mainwidget.h" @@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_key.h" #include "boxes/peers/edit_peer_info_box.h" #include "facades.h" +#include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_window.h" // st::windowMinWidth @@ -669,7 +670,7 @@ void PeerMenuShareContactBox( if (!peer->canWrite()) { Ui::show(Box( tr::lng_forward_share_cant(tr::now)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } else if (peer->isSelf()) { auto action = Api::SendAction(peer->owner().history(peer)); @@ -693,7 +694,7 @@ void PeerMenuShareContactBox( auto action = Api::SendAction(history); action.clearDraft = false; user->session().api().shareContact(user, action); - }), LayerOption::KeepOther); + }), Ui::LayerOption::KeepOther); }; *weak = Ui::show(Box( std::make_unique( @@ -736,7 +737,7 @@ void PeerMenuCreatePoll(not_null peer) { } void PeerMenuBlockUserBox( - not_null box, + not_null box, not_null window, not_null user, bool suggestClearChat) { @@ -859,7 +860,7 @@ QPointer ShowForwardMessagesBox( std::make_unique( navigation, std::move(callback)), - std::move(initBox)), LayerOption::KeepOther); + std::move(initBox)), Ui::LayerOption::KeepOther); return weak->data(); } @@ -885,7 +886,7 @@ QPointer ShowSendNowMessagesBox( Ui::Toast::Show(config); return { nullptr }; } - const auto box = std::make_shared>(); + const auto box = std::make_shared>(); auto done = [ =, list = std::move(items), @@ -915,7 +916,7 @@ QPointer ShowSendNowMessagesBox( }; *box = Ui::show( Box(text, tr::lng_send_button(tr::now), std::move(done)), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return box->data(); } @@ -926,7 +927,7 @@ void PeerMenuAddChannelMembers( && channel->membersCount() >= Global::ChatSizeMax()) { Ui::show( Box(channel), - LayerOption::KeepOther); + Ui::LayerOption::KeepOther); return; } const auto api = &channel->session().api(); @@ -1008,13 +1009,17 @@ void ToggleHistoryArchived(not_null history, bool archived) { Fn ClearHistoryHandler(not_null peer) { return [=] { - Ui::show(Box(peer, true), LayerOption::KeepOther); + Ui::show( + Box(peer, true), + Ui::LayerOption::KeepOther); }; } Fn DeleteAndLeaveHandler(not_null peer) { return [=] { - Ui::show(Box(peer, false), LayerOption::KeepOther); + Ui::show( + Box(peer, false), + Ui::LayerOption::KeepOther); }; } diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index ffb99ace0..0d893ff07 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -class GenericBox; class History; namespace Ui { class RpWidget; +class GenericBox; } // namespace Ui namespace Data { @@ -60,7 +60,7 @@ void PeerMenuAddChannelMembers( //void PeerMenuUngroupFeed(not_null feed); // #feed void PeerMenuCreatePoll(not_null peer); void PeerMenuBlockUserBox( - not_null box, + not_null box, not_null window, not_null user, bool suggestClearChat); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 14146faa1..34ee9a7ad 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -660,7 +660,7 @@ void SessionController::showBackFromStack(const SectionShow ¶ms) { } void SessionController::showSpecialLayer( - object_ptr &&layer, + object_ptr &&layer, anim::type animated) { App::wnd()->showSpecialLayer(std::move(layer), animated); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 41790c8e7..2673dacec 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -47,9 +47,12 @@ struct FormRequest; class FormController; } // namespace Passport +namespace Ui { +class LayerWidget; +} // namespace Ui + namespace Window { -class LayerWidget; class MainWindow; class SectionMemento; class Controller; @@ -244,7 +247,7 @@ public: } void showSpecialLayer( - object_ptr &&layer, + object_ptr &&layer, anim::type animated = anim::type::normal); void hideSpecialLayer( anim::type animated = anim::type::normal) { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 4dedbfc4c..3ee5fbd0b 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -36,6 +36,7 @@ 'dependent_style_files': [ '<(submodules_loc)/lib_ui/ui/colors.palette', '<(submodules_loc)/lib_ui/ui/basic.style', + '<(submodules_loc)/lib_ui/ui/layers/layers.style', '<(submodules_loc)/lib_ui/ui/widgets/widgets.style', ], 'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp', diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index 5780b9171..1a7fecd2b 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -58,8 +58,6 @@ <(src_loc)/boxes/edit_color_box.h <(src_loc)/boxes/edit_privacy_box.cpp <(src_loc)/boxes/edit_privacy_box.h -<(src_loc)/boxes/generic_box.cpp -<(src_loc)/boxes/generic_box.h <(src_loc)/boxes/language_box.cpp <(src_loc)/boxes/language_box.h <(src_loc)/boxes/local_storage_box.cpp @@ -728,8 +726,6 @@ <(src_loc)/support/support_helper.h <(src_loc)/support/support_templates.cpp <(src_loc)/support/support_templates.h -<(src_loc)/ui/effects/radial_animation.cpp -<(src_loc)/ui/effects/radial_animation.h <(src_loc)/ui/effects/round_checkbox.cpp <(src_loc)/ui/effects/round_checkbox.h <(src_loc)/ui/effects/send_action_animations.cpp @@ -775,8 +771,6 @@ <(src_loc)/ui/text_options.h <(src_loc)/ui/unread_badge.cpp <(src_loc)/ui/unread_badge.h -<(src_loc)/window/layer_widget.cpp -<(src_loc)/window/layer_widget.h <(src_loc)/window/main_window.cpp <(src_loc)/window/main_window.h <(src_loc)/window/notifications_manager.cpp diff --git a/Telegram/lib_base b/Telegram/lib_base index c1b1450dc..ec7852a1c 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit c1b1450dc165bcdea96a99e3341ac29539022511 +Subproject commit ec7852a1cc36c45550074cd98f292f61f056ecf2 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 946c33ef2..f4904e5ec 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 946c33ef251ea5a46340e72ae903487576baf9b4 +Subproject commit f4904e5ec4f6596c445fbec295cedcdb69dd90b7 From a6b96662c4d23808e3fd4aecbb55893875f0106f Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 18 Sep 2019 16:01:58 +0300 Subject: [PATCH 09/59] Use some resources from lib_ui. --- Telegram/Resources/etc/qt_linux.conf | 2 - Telegram/Resources/etc/qt_win.conf | 2 - Telegram/Resources/fc-custom.conf | 57 ------------------ Telegram/Resources/fonts/OpenSans-Bold.ttf | Bin 224592 -> 0 bytes Telegram/Resources/fonts/OpenSans-Regular.ttf | Bin 217360 -> 0 bytes .../Resources/fonts/OpenSans-Semibold.ttf | Bin 221328 -> 0 bytes Telegram/Resources/qrc/fonts.qrc | 7 --- Telegram/Resources/qrc/linux.qrc | 8 --- Telegram/Resources/qrc/mac.qrc | 2 - Telegram/Resources/qrc/wnd.qrc | 5 -- Telegram/SourceFiles/history/history.style | 9 +++ Telegram/gyp/helpers | 2 +- Telegram/gyp/telegram/mac.gypi | 2 +- Telegram/gyp/telegram/qrc.gypi | 24 -------- Telegram/lib_ui | 2 +- 15 files changed, 12 insertions(+), 110 deletions(-) delete mode 100644 Telegram/Resources/etc/qt_linux.conf delete mode 100644 Telegram/Resources/etc/qt_win.conf delete mode 100644 Telegram/Resources/fc-custom.conf delete mode 100644 Telegram/Resources/fonts/OpenSans-Bold.ttf delete mode 100644 Telegram/Resources/fonts/OpenSans-Regular.ttf delete mode 100644 Telegram/Resources/fonts/OpenSans-Semibold.ttf delete mode 100644 Telegram/Resources/qrc/fonts.qrc delete mode 100644 Telegram/Resources/qrc/linux.qrc delete mode 100644 Telegram/Resources/qrc/mac.qrc delete mode 100644 Telegram/Resources/qrc/wnd.qrc diff --git a/Telegram/Resources/etc/qt_linux.conf b/Telegram/Resources/etc/qt_linux.conf deleted file mode 100644 index 6d80862b5..000000000 --- a/Telegram/Resources/etc/qt_linux.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Paths] -Libraries=:/gui/art diff --git a/Telegram/Resources/etc/qt_win.conf b/Telegram/Resources/etc/qt_win.conf deleted file mode 100644 index f26ffe77f..000000000 --- a/Telegram/Resources/etc/qt_win.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Platforms] -WindowsArguments = nomousefromtouch diff --git a/Telegram/Resources/fc-custom.conf b/Telegram/Resources/fc-custom.conf deleted file mode 100644 index ab5c18306..000000000 --- a/Telegram/Resources/fc-custom.conf +++ /dev/null @@ -1,57 +0,0 @@ - - - - /usr/share/fonts - /usr/local/share/fonts - ~/.fonts - ~/.local/share/fonts - /usr/X11R6/lib/X11/fonts - fonts - - - mono - - - monospace - - - - - sans serif - - - sans-serif - - - - - sans - - - sans-serif - - - /var/cache/fontconfig_11 - fontconfig_11 - ~/.fontconfig_11 - - - true - - - false - - - true - - - hintslight - - - lcddefault - - - rgb - - - diff --git a/Telegram/Resources/fonts/OpenSans-Bold.ttf b/Telegram/Resources/fonts/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43bea0293ac1b20e8aca1142627983d2c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224592 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 diff --git a/Telegram/Resources/fonts/OpenSans-Regular.ttf b/Telegram/Resources/fonts/OpenSans-Regular.ttf deleted file mode 100644 index db433349b7047f72f40072630c1bc110620bf09e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217360 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 !}#gv~NV!5|5YF|kE)J0y^zWbk4>=%|6F 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 diff --git a/Telegram/Resources/fonts/OpenSans-Semibold.ttf b/Telegram/Resources/fonts/OpenSans-Semibold.ttf deleted file mode 100644 index 1a7679e3949fb045f152f456bc4adad31e8b9f55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221328 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%0DJH?LFC28_mTUAZOar9j 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
-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

MAzfu-#(7W5VH7%hxxNU1% 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 - - ../fonts/OpenSans-Regular.ttf - ../fonts/OpenSans-Bold.ttf - ../fonts/OpenSans-Semibold.ttf - - diff --git a/Telegram/Resources/qrc/linux.qrc b/Telegram/Resources/qrc/linux.qrc deleted file mode 100644 index 164e8d4f2..000000000 --- a/Telegram/Resources/qrc/linux.qrc +++ /dev/null @@ -1,8 +0,0 @@ - - - ../etc/qt_linux.conf - - - ../fc-custom.conf - - diff --git a/Telegram/Resources/qrc/mac.qrc b/Telegram/Resources/qrc/mac.qrc deleted file mode 100644 index 03585ec03..000000000 --- a/Telegram/Resources/qrc/mac.qrc +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/Telegram/Resources/qrc/wnd.qrc b/Telegram/Resources/qrc/wnd.qrc deleted file mode 100644 index 447387bfb..000000000 --- a/Telegram/Resources/qrc/wnd.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - ../etc/qt_win.conf - - diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index d090907c8..da50b0ab2 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -639,3 +639,12 @@ scheduleTimeSeparator: FlatLabel(defaultFlatLabel) { } } scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px); + +youtubeIcon: icon { + { "media_youtube_play_bg", youtubePlayIconBg }, + { "media_youtube_play", youtubePlayIconFg, point(24px, 12px) }, +}; +videoIcon: icon { + { "media_video_play_bg", videoPlayIconBg }, + { "media_video_play", videoPlayIconFg, point(12px, 12px) }, +}; diff --git a/Telegram/gyp/helpers b/Telegram/gyp/helpers index 85d6a5afc..14890fb48 160000 --- a/Telegram/gyp/helpers +++ b/Telegram/gyp/helpers @@ -1 +1 @@ -Subproject commit 85d6a5afc029d398411aac27c02970f00132bc42 +Subproject commit 14890fb48acb3ee8151f98589847ffcfd3aa7826 diff --git a/Telegram/gyp/telegram/mac.gypi b/Telegram/gyp/telegram/mac.gypi index 6ac51ec4a..4a9f29c7a 100644 --- a/Telegram/gyp/telegram/mac.gypi +++ b/Telegram/gyp/telegram/mac.gypi @@ -14,7 +14,7 @@ ], 'xcode_settings': { 'INFOPLIST_FILE': '../Telegram.plist', - 'CURRENT_PROJECT_VERSION': ' Date: Thu, 19 Sep 2019 12:28:36 +0300 Subject: [PATCH 10/59] Use install_base_filter for lib_base. --- .../SourceFiles/boxes/create_poll_box.cpp | 10 ++--- .../SourceFiles/boxes/edit_caption_box.cpp | 6 +-- Telegram/SourceFiles/boxes/send_files_box.cpp | 6 +-- .../chat_helpers/emoji_suggestions_widget.cpp | 14 +++---- .../chat_helpers/message_field.cpp | 8 ++-- Telegram/SourceFiles/core/event_filter.cpp | 38 ----------------- Telegram/SourceFiles/core/event_filter.h | 41 ------------------- .../SourceFiles/dialogs/dialogs_widget.cpp | 10 ++--- .../SourceFiles/history/history_widget.cpp | 6 +-- .../view/history_view_compose_controls.cpp | 6 +-- .../view/history_view_scheduled_section.cpp | 10 ++--- .../window/themes/window_theme_editor_box.cpp | 8 ++-- Telegram/gyp/telegram/sources.txt | 2 - Telegram/lib_base | 2 +- 14 files changed, 43 insertions(+), 124 deletions(-) delete mode 100644 Telegram/SourceFiles/core/event_filter.cpp delete mode 100644 Telegram/SourceFiles/core/event_filter.h diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index eb46cc606..e93824ba2 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -17,12 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "main/main_session.h" -#include "core/event_filter.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" #include "history/view/history_view_schedule_box.h" #include "settings/settings_common.h" #include "base/unique_qptr.h" +#include "base/event_filter.h" #include "facades.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -522,14 +522,14 @@ void Options::addEmptyOption() { QObject::connect(field, &Ui::InputField::focused, [=] { _scrollToWidget.fire_copy(field); }); - Core::InstallEventFilter(field, [=](not_null event) { + base::install_event_filter(field, [=](not_null event) { if (event->type() != QEvent::KeyPress || !field->getLastText().isEmpty()) { - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; } const auto key = static_cast(event.get())->key(); if (key != Qt::Key_Backspace) { - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; } const auto index = findField(field); @@ -538,7 +538,7 @@ void Options::addEmptyOption() { } else { _backspaceInFront.fire({}); } - return Core::EventFilter::Result::Cancel; + return base::EventFilterResult::Cancel; }); _list.back().removeClicks( diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 47f80eb78..8870d947b 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" -#include "core/event_filter.h" +#include "base/event_filter.h" #include "core/file_utilities.h" #include "core/mime_type.h" #include "data/data_document.h" @@ -725,9 +725,9 @@ void EditCaptionBox::setupEmojiPanel() { const auto filterCallback = [=](not_null event) { emojiFilterForGeometry(event); - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }; - _emojiFilter.reset(Core::InstallEventFilter(container, filterCallback)); + _emojiFilter.reset(base::install_event_filter(container, filterCallback)); _emojiToggle.create(this, st::boxAttachEmoji); _emojiToggle->installEventFilter(_emojiPanel); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index afc842773..2a458900e 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_schedule_box.h" #include "core/file_utilities.h" #include "core/mime_type.h" -#include "core/event_filter.h" +#include "base/event_filter.h" #include "ui/effects/animations.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" @@ -1706,9 +1706,9 @@ void SendFilesBox::setupEmojiPanel() { const auto filterCallback = [=](not_null event) { emojiFilterForGeometry(event); - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }; - _emojiFilter.reset(Core::InstallEventFilter(container, filterCallback)); + _emojiFilter.reset(base::install_event_filter(container, filterCallback)); _emojiToggle.create(this, st::boxAttachEmoji); _emojiToggle->setVisible(!_caption->isHidden()); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index 394691007..f5bee20f8 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "platform/platform_specific.h" #include "core/application.h" -#include "core/event_filter.h" +#include "base/event_filter.h" #include "main/main_session.h" #include "app.h" #include "styles/style_chat_helpers.h" @@ -525,17 +525,17 @@ SuggestionsController::SuggestionsController( const auto fieldCallback = [=](not_null event) { return fieldFilter(event) - ? Core::EventFilter::Result::Cancel - : Core::EventFilter::Result::Continue; + ? base::EventFilterResult::Cancel + : base::EventFilterResult::Continue; }; - _fieldFilter.reset(Core::InstallEventFilter(_field, fieldCallback)); + _fieldFilter.reset(base::install_event_filter(_field, fieldCallback)); const auto outerCallback = [=](not_null event) { return outerFilter(event) - ? Core::EventFilter::Result::Cancel - : Core::EventFilter::Result::Continue; + ? base::EventFilterResult::Cancel + : base::EventFilterResult::Continue; }; - _outerFilter.reset(Core::InstallEventFilter(outer, outerCallback)); + _outerFilter.reset(base::install_event_filter(outer, outerCallback)); QObject::connect( _field, diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index a3b1f94b4..43fb29500 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -12,13 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" // HistoryItem::originalText #include "base/qthelp_regex.h" #include "base/qthelp_url.h" +#include "base/event_filter.h" #include "boxes/abstract_box.h" #include "ui/wrap/vertical_layout.h" #include "ui/widgets/popup_menu.h" #include "ui/ui_utility.h" #include "data/data_session.h" #include "data/data_user.h" -#include "core/event_filter.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" @@ -676,10 +676,10 @@ void SetupSendMenu( (*menu)->popup(QCursor::pos()); return true; }; - Core::InstallEventFilter(button, [=](not_null e) { + base::install_event_filter(button, [=](not_null e) { if (e->type() == QEvent::ContextMenu && showMenu()) { - return Core::EventFilter::Result::Cancel; + return base::EventFilterResult::Cancel; } - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }); } diff --git a/Telegram/SourceFiles/core/event_filter.cpp b/Telegram/SourceFiles/core/event_filter.cpp deleted file mode 100644 index 37e3e7cf3..000000000 --- a/Telegram/SourceFiles/core/event_filter.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -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 "core/event_filter.h" - -namespace Core { - -EventFilter::EventFilter( - not_null parent, - not_null object, - Fn)> filter) -: QObject(parent) -, _filter(std::move(filter)) { - object->installEventFilter(this); -} - -bool EventFilter::eventFilter(QObject *watched, QEvent *event) { - return (_filter(event) == Result::Cancel); -} - -not_null InstallEventFilter( - not_null object, - Fn)> filter) { - return InstallEventFilter(object, object, std::move(filter)); -} - -not_null InstallEventFilter( - not_null context, - not_null object, - Fn)> filter) { - return new EventFilter(context, object, std::move(filter)); -} - -} // namespace Core diff --git a/Telegram/SourceFiles/core/event_filter.h b/Telegram/SourceFiles/core/event_filter.h deleted file mode 100644 index d6c847af2..000000000 --- a/Telegram/SourceFiles/core/event_filter.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -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 -*/ -#pragma once - -namespace Core { - -class EventFilter : public QObject { -public: - enum Result { - Continue, - Cancel, - }; - - EventFilter( - not_null parent, - not_null object, - Fn)> filter); - -protected: - bool eventFilter(QObject *watched, QEvent *event); - -private: - Fn)> _filter; - -}; - -not_null InstallEventFilter( - not_null object, - Fn)> filter); - -not_null InstallEventFilter( - not_null context, - not_null object, - Fn)> filter); - -} // namespace Core diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 020dfed4c..86b305d83 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -23,8 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "main/main_session.h" #include "apiwrap.h" +#include "base/event_filter.h" #include "core/application.h" -#include "core/event_filter.h" #include "core/update_checker.h" #include "boxes/peer_list_box.h" #include "boxes/peers/edit_participants_box.h" @@ -314,13 +314,13 @@ void Widget::setupScrollUpButton() { } scrollToTop(); }); - Core::InstallEventFilter(_scrollToTop, [=](not_null event) { + base::install_event_filter(_scrollToTop, [=](not_null event) { if (event->type() != QEvent::Wheel) { - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; } return _scroll->viewportEvent(event) - ? Core::EventFilter::Result::Cancel - : Core::EventFilter::Result::Continue; + ? base::EventFilterResult::Cancel + : base::EventFilterResult::Continue; }); updateScrollUpVisibility(); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c1bd89f43..20fbd79a0 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/share_box.h" #include "boxes/edit_caption_box.h" #include "core/file_utilities.h" -#include "core/event_filter.h" #include "ui/toast/toast.h" #include "ui/special_buttons.h" #include "ui/emoji_config.h" @@ -28,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/special_buttons.h" #include "inline_bots/inline_bot_result.h" +#include "base/event_filter.h" #include "base/unixtime.h" #include "data/data_drafts.h" #include "data/data_session.h" @@ -680,11 +680,11 @@ void HistoryWidget::initTabbedSelector() { const auto selector = controller()->tabbedSelector(); - Core::InstallEventFilter(this, selector, [=](not_null e) { + base::install_event_filter(this, selector, [=](not_null e) { if (_tabbedPanel && e->type() == QEvent::ParentChange) { setTabbedPanel(nullptr); } - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }); selector->emojiChosen( diff --git a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp index 41e8e143e..7bb12d966 100644 --- a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/special_buttons.h" #include "ui/ui_utility.h" #include "lang/lang_keys.h" -#include "core/event_filter.h" +#include "base/event_filter.h" #include "base/qt_signal_producer.h" #include "history/history.h" #include "chat_helpers/tabbed_panel.h" @@ -228,11 +228,11 @@ void ComposeControls::initTabbedSelector() { const auto selector = _window->tabbedSelector(); const auto wrap = _wrap.get(); - Core::InstallEventFilter(wrap, selector, [=](not_null e) { + base::install_event_filter(wrap, selector, [=](not_null e) { if (_tabbedPanel && e->type() == QEvent::ParentChange) { setTabbedPanel(nullptr); } - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }); selector->emojiChosen( diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index e1409ad36..6b9d7fbd3 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/send_files_box.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" -#include "core/event_filter.h" +#include "base/event_filter.h" #include "core/file_utilities.h" #include "main/main_session.h" #include "data/data_session.h" @@ -551,13 +551,13 @@ void ScheduledWidget::setupScrollDownButton() { _scrollDown->setClickedCallback([=] { scrollDownClicked(); }); - Core::InstallEventFilter(_scrollDown, [=](not_null event) { + base::install_event_filter(_scrollDown, [=](not_null event) { if (event->type() != QEvent::Wheel) { - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; } return _scroll->viewportEvent(event) - ? Core::EventFilter::Result::Cancel - : Core::EventFilter::Result::Continue; + ? base::EventFilterResult::Cancel + : base::EventFilterResult::Continue; }); updateScrollDownVisibility(); } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 60cf87441..31d5af17f 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -26,8 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "core/file_utilities.h" #include "core/application.h" -#include "core/event_filter.h" #include "lang/lang_keys.h" +#include "base/event_filter.h" #include "base/zlib_help.h" #include "base/unixtime.h" #include "data/data_session.h" @@ -717,15 +717,15 @@ void CreateForExistingBox( box->closeBox(); StartEditor(window, cloud); }; - Core::InstallEventFilter(box, box, [=](not_null event) { + base::install_event_filter(box, box, [=](not_null event) { if (event->type() == QEvent::KeyPress) { const auto key = static_cast(event.get())->key(); if (key == Qt::Key_Enter || key == Qt::Key_Return) { done(); - return Core::EventFilter::Result::Cancel; + return base::EventFilterResult::Cancel; } } - return Core::EventFilter::Result::Continue; + return base::EventFilterResult::Continue; }); box->addButton(tr::lng_theme_editor_create(), done); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index 1a7fecd2b..effb6c0ed 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -150,8 +150,6 @@ <(src_loc)/core/crash_report_window.h <(src_loc)/core/crash_reports.cpp <(src_loc)/core/crash_reports.h -<(src_loc)/core/event_filter.cpp -<(src_loc)/core/event_filter.h <(src_loc)/core/file_utilities.cpp <(src_loc)/core/file_utilities.h <(src_loc)/core/launcher.cpp diff --git a/Telegram/lib_base b/Telegram/lib_base index ec7852a1c..be5560996 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit ec7852a1cc36c45550074cd98f292f61f056ecf2 +Subproject commit be556099605f1ee83e3ca444b75dd90b950b6a36 From 2d3f683003c9d4f561d8e86d3266b150b0467884 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 19 Sep 2019 12:34:37 +0300 Subject: [PATCH 11/59] Rename core_ui_integration to ui_integration. --- Telegram/SourceFiles/core/application.cpp | 2 +- .../core/{core_ui_integration.cpp => ui_integration.cpp} | 2 +- .../core/{core_ui_integration.h => ui_integration.h} | 2 +- Telegram/gyp/telegram/sources.txt | 4 ++-- Telegram/lib_ui | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename Telegram/SourceFiles/core/{core_ui_integration.cpp => ui_integration.cpp} (99%) rename Telegram/SourceFiles/core/{core_ui_integration.h => ui_integration.h} (98%) diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 1d0a23af7..bd74ebb48 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/sandbox.h" #include "core/local_url_handlers.h" #include "core/launcher.h" -#include "core/core_ui_integration.h" +#include "core/ui_integration.h" #include "chat_helpers/emoji_keywords.h" #include "storage/localstorage.h" #include "platform/platform_specific.h" diff --git a/Telegram/SourceFiles/core/core_ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp similarity index 99% rename from Telegram/SourceFiles/core/core_ui_integration.cpp rename to Telegram/SourceFiles/core/ui_integration.cpp index a8011bd4a..a878b9075 100644 --- a/Telegram/SourceFiles/core/core_ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -5,7 +5,7 @@ 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 "core/core_ui_integration.h" +#include "core/ui_integration.h" #include "core/local_url_handlers.h" #include "core/file_utilities.h" diff --git a/Telegram/SourceFiles/core/core_ui_integration.h b/Telegram/SourceFiles/core/ui_integration.h similarity index 98% rename from Telegram/SourceFiles/core/core_ui_integration.h rename to Telegram/SourceFiles/core/ui_integration.h index e52bbb2ed..b53c76aa7 100644 --- a/Telegram/SourceFiles/core/core_ui_integration.h +++ b/Telegram/SourceFiles/core/ui_integration.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/ui_integration.h" +#include "ui/integration.h" namespace Core { diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index effb6c0ed..cabd89a56 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -144,8 +144,6 @@ <(src_loc)/core/core_cloud_password.h <(src_loc)/core/core_settings.cpp <(src_loc)/core/core_settings.h -<(src_loc)/core/core_ui_integration.cpp -<(src_loc)/core/core_ui_integration.h <(src_loc)/core/crash_report_window.cpp <(src_loc)/core/crash_report_window.h <(src_loc)/core/crash_reports.cpp @@ -163,6 +161,8 @@ <(src_loc)/core/sandbox.h <(src_loc)/core/shortcuts.cpp <(src_loc)/core/shortcuts.h +<(src_loc)/core/ui_integration.cpp +<(src_loc)/core/ui_integration.h <(src_loc)/core/update_checker.cpp <(src_loc)/core/update_checker.h <(src_loc)/core/utils.cpp diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 5b7ca43ba..34b05c25f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 5b7ca43baf8c5ff3b1a65a09a458c599ac03e68d +Subproject commit 34b05c25fc5e0642506fc59f3d791973632d4248 From 959901d599bb5f0b2ae6559da05e6165d7680988 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 20 Sep 2019 11:06:00 +0300 Subject: [PATCH 12/59] Use toasts from lib_ui. --- Telegram/SourceFiles/ui/toast/toast.cpp | 63 --------- Telegram/SourceFiles/ui/toast/toast.h | 63 --------- .../SourceFiles/ui/toast/toast_manager.cpp | 130 ------------------ Telegram/SourceFiles/ui/toast/toast_manager.h | 54 -------- .../SourceFiles/ui/toast/toast_widget.cpp | 72 ---------- Telegram/SourceFiles/ui/toast/toast_widget.h | 49 ------- Telegram/SourceFiles/window/main_window.cpp | 3 + Telegram/gyp/telegram/sources.txt | 6 - Telegram/lib_ui | 2 +- 9 files changed, 4 insertions(+), 438 deletions(-) delete mode 100644 Telegram/SourceFiles/ui/toast/toast.cpp delete mode 100644 Telegram/SourceFiles/ui/toast/toast.h delete mode 100644 Telegram/SourceFiles/ui/toast/toast_manager.cpp delete mode 100644 Telegram/SourceFiles/ui/toast/toast_manager.h delete mode 100644 Telegram/SourceFiles/ui/toast/toast_widget.cpp delete mode 100644 Telegram/SourceFiles/ui/toast/toast_widget.h diff --git a/Telegram/SourceFiles/ui/toast/toast.cpp b/Telegram/SourceFiles/ui/toast/toast.cpp deleted file mode 100644 index aea5ac7fb..000000000 --- a/Telegram/SourceFiles/ui/toast/toast.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -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 "ui/toast/toast.h" - -#include "ui/toast/toast_manager.h" -#include "ui/toast/toast_widget.h" -#include "mainwindow.h" - -namespace Ui { -namespace Toast { - -Instance::Instance(const Config &config, QWidget *widgetParent, const Private &) -: _hideAtMs(crl::now() + config.durationMs) { - _widget = std::make_unique(widgetParent, config); - _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::toastFadeInDuration); -} - -void Show(QWidget *parent, const Config &config) { - if (auto manager = internal::Manager::instance(parent)) { - auto toast = std::make_unique(config, parent, Instance::Private()); - manager->addToast(std::move(toast)); - } -} - -void Show(const Config &config) { - if (auto window = App::wnd()) { - Show(window->bodyWidget(), config); - } -} - -void Show(const QString &text) { - Config toast; - toast.text = text; - Show(toast); -} - -void Instance::opacityAnimationCallback() { - _widget->setShownLevel(_a_opacity.value(_hiding ? 0. : 1.)); - _widget->update(); - if (!_a_opacity.animating()) { - if (_hiding) { - hide(); - } - } -} - -void Instance::hideAnimated() { - _hiding = true; - _a_opacity.start([this] { opacityAnimationCallback(); }, 1., 0., st::toastFadeOutDuration); -} - -void Instance::hide() { - _widget->hide(); - _widget->deleteLater(); -} - -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/toast/toast.h b/Telegram/SourceFiles/ui/toast/toast.h deleted file mode 100644 index 6274c9ab5..000000000 --- a/Telegram/SourceFiles/ui/toast/toast.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/effects/animations.h" - -namespace Ui { -namespace Toast { - -namespace internal { -class Manager; -class Widget; -} // namespace internal - -static constexpr const int DefaultDuration = 1500; -struct Config { - QString text; - QMargins padding; - int durationMs = DefaultDuration; - int minWidth = 0; - int maxWidth = 0; - int maxLines = 16; - bool multiline = false; -}; -void Show(QWidget *parent, const Config &config); -void Show(const Config &config); -void Show(const QString &text); - -class Instance { - struct Private { - }; - -public: - - Instance(const Config &config, QWidget *widgetParent, const Private &); - Instance(const Instance &other) = delete; - Instance &operator=(const Instance &other) = delete; - - void hideAnimated(); - void hide(); - -private: - void opacityAnimationCallback(); - - bool _hiding = false; - Ui::Animations::Simple _a_opacity; - - const crl::time _hideAtMs; - - // ToastManager should reset _widget pointer if _widget is destroyed. - friend class internal::Manager; - friend void Show(QWidget *parent, const Config &config); - std::unique_ptr _widget; - -}; - -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/toast/toast_manager.cpp b/Telegram/SourceFiles/ui/toast/toast_manager.cpp deleted file mode 100644 index 178ef2008..000000000 --- a/Telegram/SourceFiles/ui/toast/toast_manager.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* -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 "ui/toast/toast_manager.h" - -#include "ui/toast/toast_widget.h" - -namespace Ui { -namespace Toast { -namespace internal { - -namespace { - -NeverFreedPointer> _managers; - -} // namespace - -Manager::Manager(QWidget *parent) : QObject(parent) -, _hideTimer([=] { hideByTimer(); }) { -} - -bool Manager::eventFilter(QObject *o, QEvent *e) { - if (e->type() == QEvent::Resize) { - for (auto i = _toastByWidget.cbegin(), e = _toastByWidget.cend(); i != e; ++i) { - if (i.key()->parentWidget() == o) { - i.key()->onParentResized(); - } - } - } - return QObject::eventFilter(o, e); -} - -Manager *Manager::instance(QWidget *parent) { - if (!parent) { - return nullptr; - } - - _managers.createIfNull(); - auto i = _managers->constFind(parent); - if (i == _managers->cend()) { - i = _managers->insert(parent, new Manager(parent)); - } - return i.value(); -} - -void Manager::addToast(std::unique_ptr &&toast) { - _toasts.push_back(toast.release()); - Instance *t = _toasts.back(); - Widget *widget = t->_widget.get(); - - _toastByWidget.insert(widget, t); - connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onToastWidgetDestroyed(QObject*))); - if (auto parent = widget->parentWidget()) { - auto found = false; - for (auto i = _toastParents.begin(); i != _toastParents.cend();) { - if (*i == parent) { - found = true; - break; - } else if (!*i) { - i = _toastParents.erase(i); - } else { - ++i; - } - } - if (!found) { - _toastParents.insert(parent); - parent->installEventFilter(this); - } - } - - auto oldHideNearestMs = _toastByHideTime.isEmpty() ? 0LL : _toastByHideTime.firstKey(); - _toastByHideTime.insert(t->_hideAtMs, t); - if (!oldHideNearestMs || _toastByHideTime.firstKey() < oldHideNearestMs) { - startNextHideTimer(); - } -} - -void Manager::hideByTimer() { - auto now = crl::now(); - for (auto i = _toastByHideTime.begin(); i != _toastByHideTime.cend();) { - if (i.key() <= now) { - auto toast = i.value(); - i = _toastByHideTime.erase(i); - toast->hideAnimated(); - } else { - break; - } - } - startNextHideTimer(); -} - -void Manager::onToastWidgetDestroyed(QObject *widget) { - auto i = _toastByWidget.find(static_cast(widget)); - if (i != _toastByWidget.cend()) { - auto toast = i.value(); - _toastByWidget.erase(i); - toast->_widget.release(); - - int index = _toasts.indexOf(toast); - if (index >= 0) { - _toasts.removeAt(index); - delete toast; - } - } -} - -void Manager::startNextHideTimer() { - if (_toastByHideTime.isEmpty()) return; - - auto ms = crl::now(); - if (ms >= _toastByHideTime.firstKey()) { - crl::on_main(this, [=] { - hideByTimer(); - }); - } else { - _hideTimer.callOnce(_toastByHideTime.firstKey() - ms); - } -} - -Manager::~Manager() { - _managers->remove(parent()); -} - -} // namespace internal -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/toast/toast_manager.h b/Telegram/SourceFiles/ui/toast/toast_manager.h deleted file mode 100644 index 09b64478b..000000000 --- a/Telegram/SourceFiles/ui/toast/toast_manager.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/toast/toast.h" -#include "base/timer.h" - -namespace Ui { -namespace Toast { -namespace internal { - -class Widget; -class Manager : public QObject { - Q_OBJECT - -public: - Manager(const Manager &other) = delete; - Manager &operator=(const Manager &other) = delete; - - static Manager *instance(QWidget *parent); - - void addToast(std::unique_ptr &&toast); - - ~Manager(); - -protected: - bool eventFilter(QObject *o, QEvent *e); - -private slots: - void onToastWidgetDestroyed(QObject *widget); - -private: - Manager(QWidget *parent); - void startNextHideTimer(); - void hideByTimer(); - - base::Timer _hideTimer; - crl::time _nextHide = 0; - - QMultiMap _toastByHideTime; - QMap _toastByWidget; - QList _toasts; - OrderedSet> _toastParents; - -}; - -} // namespace internal -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/toast/toast_widget.cpp b/Telegram/SourceFiles/ui/toast/toast_widget.cpp deleted file mode 100644 index 175bf2fda..000000000 --- a/Telegram/SourceFiles/ui/toast/toast_widget.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -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 "ui/toast/toast_widget.h" - -#include "ui/image/image_prepare.h" -#include "app.h" - -namespace Ui { -namespace Toast { -namespace internal { - -Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) -, _multiline(config.multiline) -, _maxWidth((config.maxWidth > 0) ? config.maxWidth : st::toastMaxWidth) -, _padding((config.padding.left() > 0) ? config.padding : st::toastPadding) -, _maxTextWidth(widthWithoutPadding(_maxWidth)) -, _maxTextHeight( - st::toastTextStyle.font->height * (_multiline ? config.maxLines : 1)) -, _text(_multiline ? widthWithoutPadding(config.minWidth) : QFIXED_MAX) { - const auto toastOptions = TextParseOptions{ - TextParseMultiline, - _maxTextWidth, - _maxTextHeight, - Qt::LayoutDirectionAuto - }; - _text.setText( - st::toastTextStyle, - _multiline ? config.text : TextUtilities::SingleLine(config.text), - toastOptions); - - setAttribute(Qt::WA_TransparentForMouseEvents); - - onParentResized(); - show(); -} - -void Widget::onParentResized() { - auto newWidth = _maxWidth; - accumulate_min(newWidth, _padding.left() + _text.maxWidth() + _padding.right()); - accumulate_min(newWidth, parentWidget()->width() - 2 * st::toastMinMargin); - _textWidth = widthWithoutPadding(newWidth); - const auto textHeight = _multiline - ? qMin(_text.countHeight(_textWidth), _maxTextHeight) - : _text.minHeight(); - const auto newHeight = _padding.top() + textHeight + _padding.bottom(); - setGeometry((parentWidget()->width() - newWidth) / 2, (parentWidget()->height() - newHeight) / 2, newWidth, newHeight); -} - -void Widget::setShownLevel(float64 shownLevel) { - _shownLevel = shownLevel; -} - -void Widget::paintEvent(QPaintEvent *e) { - Painter p(this); - PainterHighQualityEnabler hq(p); - - p.setOpacity(_shownLevel); - App::roundRect(p, rect(), st::toastBg, ImageRoundRadius::Large); - - const auto lines = _maxTextHeight / st::toastTextStyle.font->height; - p.setPen(st::toastFg); - _text.drawElided(p, _padding.left(), _padding.top(), _textWidth + 1, lines); -} - -} // namespace internal -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/toast/toast_widget.h b/Telegram/SourceFiles/ui/toast/toast_widget.h deleted file mode 100644 index d4c16619c..000000000 --- a/Telegram/SourceFiles/ui/toast/toast_widget.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/toast/toast.h" -#include "ui/rp_widget.h" -#include "ui/text/text.h" - -namespace Ui { -namespace Toast { -namespace internal { - -class Widget : public TWidget { -public: - Widget(QWidget *parent, const Config &config); - - // shownLevel=1 completely visible, shownLevel=0 completely invisible - void setShownLevel(float64 shownLevel); - - void onParentResized(); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - inline int widthWithoutPadding(int w) { - return w - _padding.left() - _padding.right(); - } - - float64 _shownLevel = 0; - bool _multiline = false; - int _maxWidth = 0; - QMargins _padding; - - int _maxTextWidth = 0; - int _maxTextHeight = 0; - int _textWidth = 0; - Text::String _text; - -}; - -} // namespace internal -} // namespace Toast -} // namespace Ui diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 58c9a1824..a984b487a 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "base/crc32hash.h" +#include "ui/toast/toast.h" #include "ui/ui_utility.h" #include "apiwrap.h" #include "mainwindow.h" @@ -145,6 +146,8 @@ MainWindow::MainWindow(not_null controller) checkLockByTerms(); }, lifetime()); + Ui::Toast::SetDefaultParent(_body.data()); + if (_outdated) { _outdated->heightValue( ) | rpl::filter([=] { diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index cabd89a56..e8017f382 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -736,12 +736,6 @@ <(src_loc)/ui/image/image_location.h <(src_loc)/ui/image/image_source.cpp <(src_loc)/ui/image/image_source.h -<(src_loc)/ui/toast/toast.cpp -<(src_loc)/ui/toast/toast.h -<(src_loc)/ui/toast/toast_manager.cpp -<(src_loc)/ui/toast/toast_manager.h -<(src_loc)/ui/toast/toast_widget.cpp -<(src_loc)/ui/toast/toast_widget.h <(src_loc)/ui/widgets/continuous_sliders.cpp <(src_loc)/ui/widgets/continuous_sliders.h <(src_loc)/ui/widgets/discrete_sliders.cpp diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 34b05c25f..d22f5f405 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 34b05c25fc5e0642506fc59f3d791973632d4248 +Subproject commit d22f5f405f18b6983b7ae19c5c8caff1c88c8098 From dad73c0e7b32893ae34e443d129a4e69473061ba Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 23 Sep 2019 10:14:27 +0300 Subject: [PATCH 13/59] Build with updated submodules. --- Telegram/Resources/icons/box_button_back.png | Bin 218 -> 0 bytes Telegram/Resources/icons/box_button_back@2x.png | Bin 282 -> 0 bytes Telegram/Resources/icons/box_button_back@3x.png | Bin 251 -> 0 bytes Telegram/Resources/icons/title_menu_dots.png | Bin 130 -> 0 bytes Telegram/Resources/icons/title_menu_dots@2x.png | Bin 212 -> 0 bytes Telegram/Resources/icons/title_menu_dots@3x.png | Bin 311 -> 0 bytes .../passport/passport_form_controller.cpp | 4 +++- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 9 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 Telegram/Resources/icons/box_button_back.png delete mode 100644 Telegram/Resources/icons/box_button_back@2x.png delete mode 100644 Telegram/Resources/icons/box_button_back@3x.png delete mode 100644 Telegram/Resources/icons/title_menu_dots.png delete mode 100644 Telegram/Resources/icons/title_menu_dots@2x.png delete mode 100644 Telegram/Resources/icons/title_menu_dots@3x.png diff --git a/Telegram/Resources/icons/box_button_back.png b/Telegram/Resources/icons/box_button_back.png deleted file mode 100644 index 9db1870f6b3b21d9a479cab250c00a689506c791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3q4&NLo9mNPTI+P*nr1%fAfLX zK1Q1s(@v8nh3&VRvNwE5ifh$uQeaB@)#2MP^@p579aBTb*)vAxH%DY&mCWFGQJK%B zI7fI(fu`%Ob=GcZ9)4V z%(?ycs_L>S=X5_#lhF6r=3hB$S@H5cYLdqTMQyfbbZ{ItSn$4D=ky}CA56v@>K5rc SRXPJ5%i!ti=d#Wzp$Pz!Z&t)@H9Xn{2HJX9tS9f zp8?9@-+&I`A3&$@Hy{?SRp=Sc%xuuP?#r?~jWMv+Mh8L&U}muf5|?n>Hl0h@8Ik`G z2_w!Al@aj;Ma1=@5+XgIfXELCBgzH^5#_@|h`J2|i26-2#5j(6fM0}juK5)_&{)SW gc?bdXJhvW$7owCe`{ii@%K!iX07*qoM6N<$f7l2gda-DUgVdC-9EVD>6nwz-%*((>eNoB175&srf z+UuOta;(`#^`s3uY?JxAm{l+a~5~1Nd~{4t2Rr;SH6bZ veufi=gSGK7jxDvpUJlhZ&vlBfsvTg+U`#H!Shwm6(4h>Tu6{1-oD!M<4X!3HEZNY`WoDNj!q#}JK)Z~G5&9xxDb@_qhW z;m==of$1BTvlMy0Unuu=p#=jAyY2D<=3Ua;5~WqlH5ihtgC`mNJ>PlN!Dy0_X6RO_ eWB(s564!UGXcGMu@Te4MD1)b~pUXO@geCx6A}XT* diff --git a/Telegram/Resources/icons/title_menu_dots@2x.png b/Telegram/Resources/icons/title_menu_dots@2x.png deleted file mode 100644 index 0020d07f11493d55e521effaba2075a58de38e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmV;_04x8AP)MxB;LObS>nZhhI%8{WgOcIzg><_}JY)-oN#vs>30c zQoawdNdSbqhr0)WQE^<&@-LgN^0MG$VUCl9ZC80&aI!GR$-=g)Ji`;3%+|&vp`VTb O0000Q<@% diff --git a/Telegram/Resources/icons/title_menu_dots@3x.png b/Telegram/Resources/icons/title_menu_dots@3x.png deleted file mode 100644 index 029087a43b10280f2845ef46f0e4adeaef16295b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmV-70m%M|P){cxTp$G^uj~5Wn!>=L<(%I?v>u4aAq0*wa;=qXtsGS$f+XhXH?XzFMRcp9LNCIZyay42BzYSq!y(G+mAz5!&7gvdt3C`$kU002ov JPDHLkV1gvBhgARo diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index f93469df8..1cb9206e4 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -757,7 +757,9 @@ std::vector> FormController::submitGetErrors() { _view->showToast(tr::lng_passport_success(tr::now)); App::CallDelayed( - Ui::Toast::DefaultDuration + st::toastFadeOutDuration, + (st::toastFadeInDuration + + Ui::Toast::kDefaultDuration + + st::toastFadeOutDuration), this, [=] { cancel(); }); }).fail([=](const RPCError &error) { diff --git a/Telegram/lib_base b/Telegram/lib_base index be5560996..f86bc73cf 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit be556099605f1ee83e3ca444b75dd90b950b6a36 +Subproject commit f86bc73cfe871b3ac8cd91ffebfa6ddf5cf480a3 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index d22f5f405..00393b7b3 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit d22f5f405f18b6983b7ae19c5c8caff1c88c8098 +Subproject commit 00393b7b3ba113a216d3704d47e0ca1cb97d0e6e From 250b7240f6b94eb958319e19f3e04c286b8fb859 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 23 Sep 2019 13:41:02 +0300 Subject: [PATCH 14/59] Add lib_[r]lottie as submodules. --- .gitmodules | 8 +- .../SourceFiles/lottie/lottie_animation.cpp | 264 -------- .../SourceFiles/lottie/lottie_animation.h | 79 --- Telegram/SourceFiles/lottie/lottie_cache.cpp | 626 ------------------ Telegram/SourceFiles/lottie/lottie_cache.h | 126 ---- Telegram/SourceFiles/lottie/lottie_common.cpp | 41 -- Telegram/SourceFiles/lottie/lottie_common.h | 66 -- .../lottie/lottie_frame_renderer.cpp | 612 ----------------- .../lottie/lottie_frame_renderer.h | 160 ----- .../lottie/lottie_multi_player.cpp | 389 ----------- .../SourceFiles/lottie/lottie_multi_player.h | 114 ---- Telegram/SourceFiles/lottie/lottie_player.h | 35 - .../lottie/lottie_single_player.cpp | 158 ----- .../SourceFiles/lottie/lottie_single_player.h | 79 --- Telegram/ThirdParty/rlottie | 2 +- Telegram/gyp/Telegram.gyp | 2 +- Telegram/gyp/generate.py | 1 + Telegram/gyp/lib_ffmpeg.gyp | 1 + Telegram/gyp/lib_lottie.gyp | 65 -- Telegram/gyp/lib_rlottie.gyp | 134 ---- Telegram/lib_lottie | 1 + Telegram/lib_rlottie | 1 + 22 files changed, 13 insertions(+), 2951 deletions(-) delete mode 100644 Telegram/SourceFiles/lottie/lottie_animation.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_animation.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_cache.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_cache.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_common.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_common.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_frame_renderer.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_multi_player.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_multi_player.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_player.h delete mode 100644 Telegram/SourceFiles/lottie/lottie_single_player.cpp delete mode 100644 Telegram/SourceFiles/lottie/lottie_single_player.h delete mode 100644 Telegram/gyp/lib_lottie.gyp delete mode 100644 Telegram/gyp/lib_rlottie.gyp create mode 160000 Telegram/lib_lottie create mode 160000 Telegram/lib_rlottie diff --git a/.gitmodules b/.gitmodules index 1c097c1d6..2e49012be 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,7 +15,7 @@ url = https://github.com/Cyan4973/xxHash.git [submodule "Telegram/ThirdParty/rlottie"] path = Telegram/ThirdParty/rlottie - url = https://github.com/john-preston/rlottie + url = https://github.com/desktop-app/rlottie.git [submodule "Telegram/ThirdParty/lz4"] path = Telegram/ThirdParty/lz4 url = https://github.com/lz4/lz4.git @@ -37,3 +37,9 @@ [submodule "Telegram/lib_ui"] path = Telegram/lib_ui url = https://github.com/desktop-app/lib_ui.git +[submodule "Telegram/lib_rlottie"] + path = Telegram/lib_rlottie + url = https://github.com/desktop-app/lib_rlottie.git +[submodule "Telegram/lib_lottie"] + path = Telegram/lib_lottie + url = https://github.com/desktop-app/lib_lottie.git diff --git a/Telegram/SourceFiles/lottie/lottie_animation.cpp b/Telegram/SourceFiles/lottie/lottie_animation.cpp deleted file mode 100644 index f681c9220..000000000 --- a/Telegram/SourceFiles/lottie/lottie_animation.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* -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 "lottie/lottie_animation.h" - -#include "lottie/lottie_frame_renderer.h" -#include "lottie/lottie_cache.h" -#include "lottie/lottie_player.h" -#include "base/algorithm.h" -#include "zlib.h" -#include "logs.h" - -#include -#include -#include -#include - -namespace Lottie { -namespace { - -const auto kIdealSize = QSize(512, 512); - -std::string UnpackGzip(const QByteArray &bytes) { - const auto original = [&] { - return std::string(bytes.constData(), bytes.size()); - }; - z_stream stream; - stream.zalloc = nullptr; - stream.zfree = nullptr; - stream.opaque = nullptr; - stream.avail_in = 0; - stream.next_in = nullptr; - int res = inflateInit2(&stream, 16 + MAX_WBITS); - if (res != Z_OK) { - return original(); - } - const auto guard = gsl::finally([&] { inflateEnd(&stream); }); - - auto result = std::string(kMaxFileSize + 1, char(0)); - stream.avail_in = bytes.size(); - stream.next_in = reinterpret_cast(const_cast(bytes.data())); - stream.avail_out = 0; - while (!stream.avail_out) { - stream.avail_out = result.size(); - stream.next_out = reinterpret_cast(result.data()); - int res = inflate(&stream, Z_NO_FLUSH); - if (res != Z_OK && res != Z_STREAM_END) { - return original(); - } else if (!stream.avail_out) { - return original(); - } - } - result.resize(result.size() - stream.avail_out); - return result; -} - -std::optional ContentError(const QByteArray &content) { - if (content.size() > kMaxFileSize) { - LOG(("Lottie Error: Too large file: %1").arg(content.size())); - return Error::ParseFailed; - } - return std::nullopt; -} - -details::InitData CheckSharedState(std::unique_ptr state) { - Expects(state != nullptr); - - auto information = state->information(); - if (!information.frameRate - || information.framesCount <= 0 - || information.size.isEmpty()) { - return Error::NotSupported; - } - return state; -} - -details::InitData Init( - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements) { - if (const auto error = ContentError(content)) { - return *error; - } - auto animation = details::CreateFromContent(content, replacements); - return animation - ? CheckSharedState(std::make_unique( - std::move(animation), - request.empty() ? FrameRequest{ kIdealSize } : request, - quality)) - : Error::ParseFailed; -} - -details::InitData Init( - const QByteArray &content, - FnMut put, - const QByteArray &cached, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements) { - Expects(!request.empty()); - - if (const auto error = ContentError(content)) { - return *error; - } - auto cache = std::make_unique(cached, request, std::move(put)); - const auto prepare = !cache->framesCount() - || (cache->framesReady() < cache->framesCount()); - auto animation = prepare - ? details::CreateFromContent(content, replacements) - : nullptr; - return (!prepare || animation) - ? CheckSharedState(std::make_unique( - content, - replacements, - std::move(animation), - std::move(cache), - request, - quality)) - : Error::ParseFailed; -} - -} // namespace - -namespace details { - -std::unique_ptr CreateFromContent( - const QByteArray &content, - const ColorReplacements *replacements) { - const auto string = UnpackGzip(content); - Assert(string.size() <= kMaxFileSize); - - auto result = rlottie::Animation::loadFromData( - string, - std::string(), - std::string(), - false, - (replacements - ? replacements->replacements - : std::vector>())); - if (!result) { - LOG(("Lottie Error: Parse failed.")); - } - return result; -} - -} // namespace details - -std::shared_ptr MakeFrameRenderer() { - return FrameRenderer::CreateIndependent(); -} - -QImage ReadThumbnail(const QByteArray &content) { - return Init(content, FrameRequest(), Quality::High, nullptr).match([]( - const std::unique_ptr &state) { - return state->frameForPaint()->original; - }, [](Error) { - return QImage(); - }); -} - -Animation::Animation( - not_null player, - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements) -: _player(player) { - const auto weak = base::make_weak(this); - crl::async([=] { - auto result = Init(content, request, quality, replacements); - crl::on_main(weak, [=, data = std::move(result)]() mutable { - initDone(std::move(data)); - }); - }); -} - -Animation::Animation( - not_null player, - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements) -: _player(player) { - const auto weak = base::make_weak(this); - get([=, put = std::move(put)](QByteArray &&cached) mutable { - crl::async([=, put = std::move(put)]() mutable { - auto result = Init( - content, - std::move(put), - cached, - request, - quality, - replacements); - crl::on_main(weak, [=, data = std::move(result)]() mutable { - initDone(std::move(data)); - }); - }); - }); -} - -bool Animation::ready() const { - return (_state != nullptr); -} - -void Animation::initDone(details::InitData &&data) { - data.match([&](std::unique_ptr &state) { - parseDone(std::move(state)); - }, [&](Error error) { - parseFailed(error); - }); -} - -void Animation::parseDone(std::unique_ptr state) { - Expects(state != nullptr); - - _state = state.get(); - _player->start(this, std::move(state)); -} - -void Animation::parseFailed(Error error) { - _player->failed(this, error); -} - -QImage Animation::frame() const { - Expects(_state != nullptr); - - return PrepareFrameByRequest(_state->frameForPaint(), true); -} - -QImage Animation::frame(const FrameRequest &request) const { - Expects(_state != nullptr); - - const auto frame = _state->frameForPaint(); - const auto changed = (frame->request != request); - if (changed) { - frame->request = request; - _player->updateFrameRequest(this, request); - } - return PrepareFrameByRequest(frame, !changed); -} - -auto Animation::frameInfo(const FrameRequest &request) const -> FrameInfo { - Expects(_state != nullptr); - - const auto frame = _state->frameForPaint(); - const auto changed = (frame->request != request); - if (changed) { - frame->request = request; - _player->updateFrameRequest(this, request); - } - return { - PrepareFrameByRequest(frame, !changed), - frame->index % _state->framesCount() - }; -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h deleted file mode 100644 index 09d16f4ea..000000000 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "lottie/lottie_common.h" -#include "base/weak_ptr.h" - -#include - -class QString; -class QByteArray; - -namespace rlottie { -class Animation; -} // namespace rlottie - -namespace Lottie { - -class Player; -class SharedState; -class FrameRenderer; - -std::shared_ptr MakeFrameRenderer(); - -QImage ReadThumbnail(const QByteArray &content); - -namespace details { - -using InitData = base::variant, Error>; - -std::unique_ptr CreateFromContent( - const QByteArray &content, - const ColorReplacements *replacements); - -} // namespace details - -class Animation final : public base::has_weak_ptr { -public: - struct FrameInfo { - QImage image; - int index = 0; - }; - - Animation( - not_null player, - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements = nullptr); - Animation( - not_null player, - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements = nullptr); - - [[nodiscard]] bool ready() const; - [[nodiscard]] QImage frame() const; - [[nodiscard]] QImage frame(const FrameRequest &request) const; - [[nodiscard]] FrameInfo frameInfo(const FrameRequest &request) const; - -private: - void initDone(details::InitData &&data); - void parseDone(std::unique_ptr state); - void parseFailed(Error error); - - not_null _player; - SharedState *_state = nullptr; - -}; - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_cache.cpp b/Telegram/SourceFiles/lottie/lottie_cache.cpp deleted file mode 100644 index 13d272381..000000000 --- a/Telegram/SourceFiles/lottie/lottie_cache.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* -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 "lottie/lottie_cache.h" - -#include "lottie/lottie_frame_renderer.h" -#include "ffmpeg/ffmpeg_utility.h" -#include "base/bytes.h" - -#include -#include -#include -#include - -namespace Lottie { -namespace { - -constexpr auto kAlignStorage = 16; - -// Must not exceed max database allowed entry size. -constexpr auto kMaxCacheSize = 10 * 1024 * 1024; - -void Xor(EncodedStorage &to, const EncodedStorage &from) { - Expects(to.size() == from.size()); - - using Block = std::conditional_t< - sizeof(void*) == sizeof(uint64), - uint64, - uint32>; - constexpr auto kBlockSize = sizeof(Block); - const auto amount = from.size(); - const auto fromBytes = reinterpret_cast(from.data()); - const auto toBytes = reinterpret_cast(to.data()); - const auto blocks = amount / kBlockSize; - const auto fromBlocks = reinterpret_cast(fromBytes); - const auto toBlocks = reinterpret_cast(toBytes); - for (auto i = 0; i != blocks; ++i) { - toBlocks[i] ^= fromBlocks[i]; - } - const auto left = amount - (blocks * kBlockSize); - for (auto i = amount - left; i != amount; ++i) { - toBytes[i] ^= fromBytes[i]; - } -} - -bool UncompressToRaw(EncodedStorage &to, bytes::const_span from) { - if (from.empty() || from.size() > to.size()) { - return false; - } else if (from.size() == to.size()) { - memcpy(to.data(), from.data(), from.size()); - return true; - } - const auto result = LZ4_decompress_safe( - reinterpret_cast(from.data()), - to.data(), - from.size(), - to.size()); - return (result == to.size()); -} - -void CompressFromRaw(QByteArray &to, const EncodedStorage &from) { - const auto size = from.size(); - const auto max = sizeof(qint32) + LZ4_compressBound(size); - to.reserve(max); - to.resize(max); - const auto compressed = LZ4_compress_default( - from.data(), - to.data() + sizeof(qint32), - size, - to.size() - sizeof(qint32)); - Assert(compressed > 0); - if (compressed >= size + sizeof(qint32)) { - to.resize(size + sizeof(qint32)); - memcpy(to.data() + sizeof(qint32), from.data(), size); - } else { - to.resize(compressed + sizeof(qint32)); - } - const auto length = qint32(to.size() - sizeof(qint32)); - bytes::copy( - bytes::make_detached_span(to), - bytes::object_as_span(&length)); -} - -void CompressAndSwapFrame( - QByteArray &to, - QByteArray *additional, - EncodedStorage &frame, - EncodedStorage &previous) { - CompressFromRaw(to, frame); - std::swap(frame, previous); - if (!additional) { - return; - } - - // Check if XOR-d delta compresses better. - Xor(frame, previous); - CompressFromRaw(*additional, frame); - if (additional->size() >= to.size()) { - return; - } - std::swap(to, *additional); - - // Negative length means we XOR-d with the previous frame. - const auto negativeLength = -qint32(to.size() - sizeof(qint32)); - bytes::copy( - bytes::make_detached_span(to), - bytes::object_as_span(&negativeLength)); -} - -void DecodeYUV2RGB( - QImage &to, - const EncodedStorage &from, - FFmpeg::SwscalePointer &context) { - context = FFmpeg::MakeSwscalePointer( - to.size(), - AV_PIX_FMT_YUV420P, - to.size(), - AV_PIX_FMT_BGRA, - &context); - Assert(context != nullptr); - - // AV_NUM_DATA_POINTERS defined in AVFrame struct - const uint8_t *src[AV_NUM_DATA_POINTERS] = { - from.yData(), - from.uData(), - from.vData(), - nullptr - }; - int srcLineSize[AV_NUM_DATA_POINTERS] = { - from.yBytesPerLine(), - from.uBytesPerLine(), - from.vBytesPerLine(), - 0 - }; - uint8_t *dst[AV_NUM_DATA_POINTERS] = { to.bits(), nullptr }; - int dstLineSize[AV_NUM_DATA_POINTERS] = { to.bytesPerLine(), 0 }; - - const auto lines = sws_scale( - context.get(), - src, - srcLineSize, - 0, - to.height(), - dst, - dstLineSize); - - Ensures(lines == to.height()); -} - -void DecodeAlpha(QImage &to, const EncodedStorage &from) { - auto bytes = to.bits(); - auto alpha = from.aData(); - const auto perLine = to.bytesPerLine(); - const auto width = to.width(); - const auto height = to.height(); - for (auto i = 0; i != height; ++i) { - auto ints = reinterpret_cast(bytes); - const auto till = ints + width; - while (ints != till) { - const auto value = uint32(*alpha++); - *ints = (*ints & 0x00FFFFFFU) - | ((value & 0xF0U) << 24) - | ((value & 0xF0U) << 20); - ++ints; - *ints = (*ints & 0x00FFFFFFU) - | (value << 28) - | ((value & 0x0FU) << 24); - ++ints; - } - bytes += perLine; - } -} - -void Decode( - QImage &to, - const EncodedStorage &from, - const QSize &fromSize, - FFmpeg::SwscalePointer &context) { - if (!FFmpeg::GoodStorageForFrame(to, fromSize)) { - to = FFmpeg::CreateFrameStorage(fromSize); - } - DecodeYUV2RGB(to, from, context); - DecodeAlpha(to, from); - FFmpeg::PremultiplyInplace(to); -} - -void EncodeRGB2YUV( - EncodedStorage &to, - const QImage &from, - FFmpeg::SwscalePointer &context) { - context = FFmpeg::MakeSwscalePointer( - from.size(), - AV_PIX_FMT_BGRA, - from.size(), - AV_PIX_FMT_YUV420P, - &context); - Assert(context != nullptr); - - // AV_NUM_DATA_POINTERS defined in AVFrame struct - const uint8_t *src[AV_NUM_DATA_POINTERS] = { from.bits(), nullptr }; - int srcLineSize[AV_NUM_DATA_POINTERS] = { from.bytesPerLine(), 0 }; - uint8_t *dst[AV_NUM_DATA_POINTERS] = { - to.yData(), - to.uData(), - to.vData(), - nullptr - }; - int dstLineSize[AV_NUM_DATA_POINTERS] = { - to.yBytesPerLine(), - to.uBytesPerLine(), - to.vBytesPerLine(), - 0 - }; - - const auto lines = sws_scale( - context.get(), - src, - srcLineSize, - 0, - from.height(), - dst, - dstLineSize); - - Ensures(lines == from.height()); -} - -void EncodeAlpha(EncodedStorage &to, const QImage &from) { - auto bytes = from.bits(); - auto alpha = to.aData(); - const auto perLine = from.bytesPerLine(); - const auto width = from.width(); - const auto height = from.height(); - for (auto i = 0; i != height; ++i) { - auto ints = reinterpret_cast(bytes); - const auto till = ints + width; - for (; ints != till; ints += 2) { - *alpha++ = (((*ints) >> 24) & 0xF0U) | ((*(ints + 1)) >> 28); - } - bytes += perLine; - } -} - -void Encode( - EncodedStorage &to, - const QImage &from, - QImage &cache, - FFmpeg::SwscalePointer &context) { - FFmpeg::UnPremultiply(cache, from); - EncodeRGB2YUV(to, cache, context); - EncodeAlpha(to, cache); -} - -int YLineSize(int width) { - return ((width + kAlignStorage - 1) / kAlignStorage) * kAlignStorage; -} - -int UVLineSize(int width) { - return (((width / 2) + kAlignStorage - 1) / kAlignStorage) * kAlignStorage; -} - -int YSize(int width, int height) { - return YLineSize(width) * height; -} - -int UVSize(int width, int height) { - return UVLineSize(width) * (height / 2); -} - -int ASize(int width, int height) { - return (width * height) / 2; -} - -} // namespace - -void EncodedStorage::allocate(int width, int height) { - Expects((width % 2) == 0 && (height % 2) == 0); - - if (YSize(width, height) != YSize(_width, _height) - || UVSize(width, height) != UVSize(_width, _height) - || ASize(width, height) != ASize(_width, _height)) { - _width = width; - _height = height; - reallocate(); - } -} - -void EncodedStorage::reallocate() { - const auto total = YSize(_width, _height) - + 2 * UVSize(_width, _height) - + ASize(_width, _height); - _data = QByteArray(total + kAlignStorage - 1, 0); -} - -int EncodedStorage::width() const { - return _width; -} - -int EncodedStorage::height() const { - return _height; -} - -int EncodedStorage::size() const { - return YSize(_width, _height) - + 2 * UVSize(_width, _height) - + ASize(_width, _height); -} - -char *EncodedStorage::data() { - const auto result = reinterpret_cast(_data.data()); - return reinterpret_cast(kAlignStorage - * ((result + kAlignStorage - 1) / kAlignStorage)); -} - -const char *EncodedStorage::data() const { - const auto result = reinterpret_cast(_data.data()); - return reinterpret_cast(kAlignStorage - * ((result + kAlignStorage - 1) / kAlignStorage)); -} - -uint8_t *EncodedStorage::yData() { - return reinterpret_cast(data()); -} - -const uint8_t *EncodedStorage::yData() const { - return reinterpret_cast(data()); -} - -int EncodedStorage::yBytesPerLine() const { - return YLineSize(_width); -} - -uint8_t *EncodedStorage::uData() { - return yData() + YSize(_width, _height); -} - -const uint8_t *EncodedStorage::uData() const { - return yData() + YSize(_width, _height); -} - -int EncodedStorage::uBytesPerLine() const { - return UVLineSize(_width); -} - -uint8_t *EncodedStorage::vData() { - return uData() + UVSize(_width, _height); -} - -const uint8_t *EncodedStorage::vData() const { - return uData() + UVSize(_width, _height); -} - -int EncodedStorage::vBytesPerLine() const { - return UVLineSize(_width); -} - -uint8_t *EncodedStorage::aData() { - return uData() + 2 * UVSize(_width, _height); -} - -const uint8_t *EncodedStorage::aData() const { - return uData() + 2 * UVSize(_width, _height); -} - -int EncodedStorage::aBytesPerLine() const { - return _width / 2; -} - -Cache::Cache( - const QByteArray &data, - const FrameRequest &request, - FnMut put) -: _data(data) -, _put(std::move(put)) { - if (!readHeader(request)) { - _framesReady = 0; - _data = QByteArray(); - } -} - -void Cache::init( - QSize original, - int frameRate, - int framesCount, - const FrameRequest &request) { - _size = request.size(original); - _original = original; - _frameRate = frameRate; - _framesCount = framesCount; - _framesReady = 0; - prepareBuffers(); -} - -int Cache::frameRate() const { - return _frameRate; -} - -int Cache::framesReady() const { - return _framesReady; -} - -int Cache::framesCount() const { - return _framesCount; -} - -QSize Cache::originalSize() const { - return _original; -} - -bool Cache::readHeader(const FrameRequest &request) { - if (_data.isEmpty()) { - return false; - - } - QDataStream stream(&_data, QIODevice::ReadOnly); - - auto encoder = qint32(0); - stream >> encoder; - if (static_cast(encoder) != Encoder::YUV420A4_LZ4) { - return false; - } - auto size = QSize(); - auto original = QSize(); - auto frameRate = qint32(0); - auto framesCount = qint32(0); - auto framesReady = qint32(0); - stream - >> size - >> original - >> frameRate - >> framesCount - >> framesReady; - if (stream.status() != QDataStream::Ok - || original.isEmpty() - || (original.width() > kMaxSize) - || (original.height() > kMaxSize) - || (frameRate <= 0) - || (frameRate > kNormalFrameRate && frameRate != kMaxFrameRate) - || (framesCount <= 0) - || (framesCount > kMaxFramesCount) - || (framesReady <= 0) - || (framesReady > framesCount) - || request.size(original) != size) { - return false; - } - _encoder = static_cast(encoder); - _size = size; - _original = original; - _frameRate = frameRate; - _framesCount = framesCount; - _framesReady = framesReady; - prepareBuffers(); - return renderFrame(_firstFrame, request, 0); -} - -QImage Cache::takeFirstFrame() { - return std::move(_firstFrame); -} - -bool Cache::renderFrame( - QImage &to, - const FrameRequest &request, - int index) { - Expects(index >= _framesReady - || index == _offsetFrameIndex - || index == 0); - - if (index >= _framesReady) { - return false; - } else if (request.size(_original) != _size) { - return false; - } else if (index == 0) { - _offset = headerSize(); - _offsetFrameIndex = 0; - } - const auto [ok, xored] = readCompressedFrame(); - if (!ok || (xored && index == 0)) { - _framesReady = 0; - _data = QByteArray(); - return false; - } else if (index + 1 == _framesReady && _data.size() > _offset) { - _data.resize(_offset); - } - if (xored) { - Xor(_previous, _uncompressed); - } else { - std::swap(_uncompressed, _previous); - } - Decode(to, _previous, _size, _decodeContext); - return true; -} - -void Cache::appendFrame( - const QImage &frame, - const FrameRequest &request, - int index) { - if (request.size(_original) != _size) { - _framesReady = 0; - _data = QByteArray(); - } - if (index != _framesReady) { - return; - } - if (index == 0) { - _size = request.size(_original); - _encode = EncodeFields(); - _encode.compressedFrames.reserve(_framesCount); - prepareBuffers(); - } - Assert(frame.size() == _size); - Encode(_uncompressed, frame, _encode.cache, _encode.context); - CompressAndSwapFrame( - _encode.compressBuffer, - (index != 0) ? &_encode.xorCompressBuffer : nullptr, - _uncompressed, - _previous); - const auto compressed = _encode.compressBuffer; - const auto nowSize = (_data.isEmpty() ? headerSize() : _data.size()) - + _encode.totalSize; - const auto totalSize = nowSize + compressed.size(); - if (nowSize <= kMaxCacheSize && totalSize > kMaxCacheSize) { - // Write to cache while we still can. - finalizeEncoding(); - } - _encode.totalSize += compressed.size(); - _encode.compressedFrames.push_back(compressed); - _encode.compressedFrames.back().detach(); - if (++_framesReady == _framesCount) { - finalizeEncoding(); - } -} - -void Cache::finalizeEncoding() { - if (_encode.compressedFrames.empty()) { - return; - } - const auto size = (_data.isEmpty() ? headerSize() : _data.size()) - + _encode.totalSize; - if (_data.isEmpty()) { - _data.reserve(size); - writeHeader(); - } else { - updateFramesReadyCount(); - } - const auto offset = _data.size(); - _data.resize(size); - auto to = _data.data() + offset; - for (const auto &block : _encode.compressedFrames) { - const auto amount = qint32(block.size()); - memcpy(to, block.data(), amount); - to += amount; - } - if (_data.size() <= kMaxCacheSize) { - _put(QByteArray(_data)); - } - _encode = EncodeFields(); -} - -int Cache::headerSize() const { - return 8 * sizeof(qint32); -} - -void Cache::writeHeader() { - Expects(_data.isEmpty()); - - QDataStream stream(&_data, QIODevice::WriteOnly); - - stream - << static_cast(_encoder) - << _size - << _original - << qint32(_frameRate) - << qint32(_framesCount) - << qint32(_framesReady); -} - -void Cache::updateFramesReadyCount() { - Expects(_data.size() >= headerSize()); - - QDataStream stream(&_data, QIODevice::ReadWrite); - stream.device()->seek(headerSize() - sizeof(qint32)); - stream << qint32(_framesReady); -} - -void Cache::prepareBuffers() { - // 12 bit per pixel in YUV420P. - const auto bytesPerLine = _size.width(); - - _uncompressed.allocate(bytesPerLine, _size.height()); - _previous.allocate(bytesPerLine, _size.height()); -} - -Cache::ReadResult Cache::readCompressedFrame() { - if (_data.size() < _offset) { - return { false }; - } - auto length = qint32(0); - const auto part = bytes::make_span(_data).subspan(_offset); - if (part.size() < sizeof(length)) { - return { false }; - } - bytes::copy( - bytes::object_as_span(&length), - part.subspan(0, sizeof(length))); - const auto bytes = part.subspan(sizeof(length)); - - const auto xored = (length < 0); - if (xored) { - length = -length; - } - _offset += sizeof(length) + length; - ++_offsetFrameIndex; - const auto ok = (length <= bytes.size()) - ? UncompressToRaw(_uncompressed, bytes.subspan(0, length)) - : false; - return { ok, xored }; -} - -Cache::~Cache() { - finalizeEncoding(); -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_cache.h b/Telegram/SourceFiles/lottie/lottie_cache.h deleted file mode 100644 index bb8cc2bb2..000000000 --- a/Telegram/SourceFiles/lottie/lottie_cache.h +++ /dev/null @@ -1,126 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ffmpeg/ffmpeg_utility.h" - -#include -#include -#include - -namespace Lottie { - -struct FrameRequest; - -class EncodedStorage { -public: - void allocate(int width, int height); - - int width() const; - int height() const; - - char *data(); - const char *data() const; - int size() const; - - uint8_t *yData(); - const uint8_t *yData() const; - int yBytesPerLine() const; - uint8_t *uData(); - const uint8_t *uData() const; - int uBytesPerLine() const; - uint8_t *vData(); - const uint8_t *vData() const; - int vBytesPerLine() const; - uint8_t *aData(); - const uint8_t *aData() const; - int aBytesPerLine() const; - -private: - void reallocate(); - - int _width = 0; - int _height = 0; - QByteArray _data; - -}; - -class Cache { -public: - enum class Encoder : qint8 { - YUV420A4_LZ4, - }; - - Cache( - const QByteArray &data, - const FrameRequest &request, - FnMut put); - - void init( - QSize original, - int frameRate, - int framesCount, - const FrameRequest &request); - [[nodiscard]] int frameRate() const; - [[nodiscard]] int framesReady() const; - [[nodiscard]] int framesCount() const; - [[nodiscard]] QSize originalSize() const; - [[nodiscard]] QImage takeFirstFrame(); - - [[nodiscard]] bool renderFrame( - QImage &to, - const FrameRequest &request, - int index); - void appendFrame( - const QImage &frame, - const FrameRequest &request, - int index); - - ~Cache(); - -private: - struct ReadResult { - bool ok = false; - bool xored = false; - }; - struct EncodeFields { - std::vector compressedFrames; - QByteArray compressBuffer; - QByteArray xorCompressBuffer; - QImage cache; - FFmpeg::SwscalePointer context; - int totalSize = 0; - }; - int headerSize() const; - void prepareBuffers(); - void finalizeEncoding(); - - void writeHeader(); - void updateFramesReadyCount(); - [[nodiscard]] bool readHeader(const FrameRequest &request); - [[nodiscard]] ReadResult readCompressedFrame(); - - QByteArray _data; - EncodeFields _encode; - QSize _size; - QSize _original; - EncodedStorage _uncompressed; - EncodedStorage _previous; - FFmpeg::SwscalePointer _decodeContext; - QImage _firstFrame; - int _frameRate = 0; - int _framesCount = 0; - int _framesReady = 0; - int _offset = 0; - int _offsetFrameIndex = 0; - Encoder _encoder = Encoder::YUV420A4_LZ4; - FnMut _put; - -}; - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_common.cpp b/Telegram/SourceFiles/lottie/lottie_common.cpp deleted file mode 100644 index 32068634d..000000000 --- a/Telegram/SourceFiles/lottie/lottie_common.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* -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 "lottie/lottie_common.h" - -#include "base/algorithm.h" - -#include - -namespace Lottie { -namespace { - -QByteArray ReadFile(const QString &filepath) { - auto f = QFile(filepath); - return (f.size() <= kMaxFileSize && f.open(QIODevice::ReadOnly)) - ? f.readAll() - : QByteArray(); -} - -} // namespace - -QSize FrameRequest::size(const QSize &original) const { - Expects(!empty()); - - const auto result = original.scaled(box, Qt::KeepAspectRatio); - const auto skipw = result.width() % 8; - const auto skiph = result.height() % 8; - return QSize( - std::max(result.width() - skipw, 8), - std::max(result.height() - skiph, 8)); -} - -QByteArray ReadContent(const QByteArray &data, const QString &filepath) { - return data.isEmpty() ? ReadFile(filepath) : base::duplicate(data); -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_common.h b/Telegram/SourceFiles/lottie/lottie_common.h deleted file mode 100644 index bfa875cf9..000000000 --- a/Telegram/SourceFiles/lottie/lottie_common.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" -#include "base/variant.h" - -#include -#include -#include -#include - -namespace Lottie { - -inline constexpr auto kTimeUnknown = std::numeric_limits::min(); -inline constexpr auto kMaxFileSize = 2 * 1024 * 1024; - -class Animation; - -struct Information { - int frameRate = 0; - int framesCount = 0; - QSize size; -}; - -enum class Error { - ParseFailed, - NotSupported, -}; - -struct FrameRequest { - QSize box; - std::optional colored; - - [[nodiscard]] bool empty() const { - return box.isEmpty(); - } - [[nodiscard]] QSize size(const QSize &original) const; - - [[nodiscard]] bool operator==(const FrameRequest &other) const { - return (box == other.box) - && (colored == other.colored); - } - [[nodiscard]] bool operator!=(const FrameRequest &other) const { - return !(*this == other); - } -}; - -enum class Quality : char { - Default, - High, -}; - -struct ColorReplacements { - std::vector> replacements; - uint8 tag = 0; -}; - -QByteArray ReadContent(const QByteArray &data, const QString &filepath); - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp deleted file mode 100644 index caae93a9d..000000000 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ /dev/null @@ -1,612 +0,0 @@ -/* -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 "lottie/lottie_frame_renderer.h" - -#include "lottie/lottie_player.h" -#include "lottie/lottie_animation.h" -#include "lottie/lottie_cache.h" -#include "base/flat_map.h" -#include "logs.h" - -#include -#include -#include -#include - -namespace Images { -QImage prepareColored(QColor add, QImage image); -} // namespace Images - -namespace Lottie { -namespace { - -std::weak_ptr GlobalInstance; - -constexpr auto kImageFormat = QImage::Format_ARGB32_Premultiplied; - -bool GoodStorageForFrame(const QImage &storage, QSize size) { - return !storage.isNull() - && (storage.format() == kImageFormat) - && (storage.size() == size) - && storage.isDetached(); -} - -QImage CreateFrameStorage(QSize size) { - return QImage(size, kImageFormat); -} - -int GetLottieFrameRate(not_null animation, Quality quality) { - const auto rate = int(qRound(animation->frameRate())); - return (quality == Quality::Default && rate == 60) ? (rate / 2) : rate; -} - -int GetLottieFramesCount(not_null animation, Quality quality) { - const auto rate = int(qRound(animation->frameRate())); - const auto count = int(animation->totalFrame()); - return (quality == Quality::Default && rate == 60) ? (count / 2) : count; -} - -int GetLottieFrameIndex(not_null animation, Quality quality, int index) { - const auto rate = int(qRound(animation->frameRate())); - return (quality == Quality::Default && rate == 60) ? (index * 2) : index; -} - -} // namespace - -class FrameRendererObject final { -public: - explicit FrameRendererObject( - crl::weak_on_queue weak); - - void append( - std::unique_ptr entry, - const FrameRequest &request); - void frameShown(); - void updateFrameRequest( - not_null entry, - const FrameRequest &request); - void remove(not_null entry); - -private: - struct Entry { - std::unique_ptr state; - FrameRequest request; - }; - - static not_null StateFromEntry(const Entry &entry) { - return entry.state.get(); - } - - void queueGenerateFrames(); - void generateFrames(); - - crl::weak_on_queue _weak; - std::vector _entries; - bool _queued = false; - -}; - -[[nodiscard]] bool GoodForRequest( - const QImage &image, - const FrameRequest &request) { - if (request.box.isEmpty()) { - return true; - } else if (request.colored.has_value()) { - return false; - } - const auto size = image.size(); - return (request.box.width() == size.width()) - || (request.box.height() == size.height()); -} - -[[nodiscard]] QImage PrepareByRequest( - const QImage &original, - const FrameRequest &request, - QImage storage) { - Expects(!request.box.isEmpty()); - - const auto size = request.size(original.size()); - if (!GoodStorageForFrame(storage, size)) { - storage = CreateFrameStorage(size); - } - storage.fill(Qt::transparent); - - { - QPainter p(&storage); - p.setRenderHint(QPainter::Antialiasing); - p.setRenderHint(QPainter::SmoothPixmapTransform); - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.drawImage(QRect(QPoint(), size), original); - } - if (request.colored.has_value()) { - storage = Images::prepareColored(*request.colored, std::move(storage)); - } - return storage; -} - -QImage PrepareFrameByRequest( - not_null frame, - bool useExistingPrepared = false) { - Expects(!frame->original.isNull()); - - if (GoodForRequest(frame->original, frame->request)) { - return frame->original; - } else if (frame->prepared.isNull() || !useExistingPrepared) { - frame->prepared = PrepareByRequest( - frame->original, - frame->request, - std::move(frame->prepared)); - } - return frame->prepared; -} - -FrameRendererObject::FrameRendererObject( - crl::weak_on_queue weak) -: _weak(std::move(weak)) { -} - -void FrameRendererObject::append( - std::unique_ptr state, - const FrameRequest &request) { - _entries.push_back({ std::move(state), request }); - queueGenerateFrames(); -} - -void FrameRendererObject::frameShown() { - queueGenerateFrames(); -} - -void FrameRendererObject::updateFrameRequest( - not_null entry, - const FrameRequest &request) { - const auto i = ranges::find(_entries, entry, &StateFromEntry); - Assert(i != end(_entries)); - i->request = request; -} - -void FrameRendererObject::remove(not_null entry) { - const auto i = ranges::find(_entries, entry, &StateFromEntry); - Assert(i != end(_entries)); - _entries.erase(i); -} - -void FrameRendererObject::generateFrames() { - auto players = base::flat_map>(); - const auto renderOne = [&](const Entry &entry) { - const auto result = entry.state->renderNextFrame(entry.request); - if (const auto player = result.notify.get()) { - players.emplace(player, result.notify); - } - return result.rendered; - }; - const auto rendered = ranges::count_if(_entries, renderOne); - if (rendered) { - if (!players.empty()) { - crl::on_main([players = std::move(players)] { - for (const auto &[player, weak] : players) { - if (weak) { - weak->checkStep(); - } - } - }); - } - queueGenerateFrames(); - } -} - -void FrameRendererObject::queueGenerateFrames() { - if (_queued) { - return; - } - _queued = true; - _weak.with([](FrameRendererObject &that) { - that._queued = false; - that.generateFrames(); - }); -} - - -Information SharedState::CalculateInformation( - Quality quality, - rlottie::Animation *animation, - Cache *cache) { - Expects(animation != nullptr || cache != nullptr); - - auto width = size_t(0); - auto height = size_t(0); - if (animation) { - animation->size(width, height); - } else { - width = cache->originalSize().width(); - height = cache->originalSize().height(); - } - const auto rate = animation - ? GetLottieFrameRate(animation, quality) - : cache->frameRate(); - const auto count = animation - ? GetLottieFramesCount(animation, quality) - : cache->framesCount(); - - auto result = Information(); - result.size = QSize( - (width > 0 && width <= kMaxSize) ? int(width) : 0, - (height > 0 && height <= kMaxSize) ? int(height) : 0); - result.frameRate = (rate > 0 && rate <= kMaxFrameRate) ? int(rate) : 0; - result.framesCount = (count > 0 && count <= kMaxFramesCount) - ? int(count) - : 0; - return result; -} - -SharedState::SharedState( - std::unique_ptr animation, - const FrameRequest &request, - Quality quality) -: _info(CalculateInformation(quality, animation.get(), nullptr)) -, _quality(quality) -, _animation(std::move(animation)) { - construct(request); -} - -SharedState::SharedState( - const QByteArray &content, - const ColorReplacements *replacements, - std::unique_ptr animation, - std::unique_ptr cache, - const FrameRequest &request, - Quality quality) -: _info(CalculateInformation(quality, animation.get(), cache.get())) -, _quality(quality) -, _cache(std::move(cache)) -, _animation(std::move(animation)) -, _content(content) -, _replacements(replacements) { - construct(request); -} - -void SharedState::construct(const FrameRequest &request) { - if (!isValid()) { - return; - } - auto cover = _cache ? _cache->takeFirstFrame() : QImage(); - if (!cover.isNull()) { - init(std::move(cover), request); - return; - } - if (_cache) { - _cache->init( - _info.size, - _info.frameRate, - _info.framesCount, - request); - } - renderFrame(cover, request, 0); - init(std::move(cover), request); -} - -bool SharedState::isValid() const { - return (_info.framesCount > 0) - && (_info.frameRate > 0) - && !_info.size.isEmpty(); -} - -void SharedState::renderFrame( - QImage &image, - const FrameRequest &request, - int index) { - if (!isValid()) { - return; - } - - const auto size = request.box.isEmpty() - ? _info.size - : request.size(_info.size); - if (!GoodStorageForFrame(image, size)) { - image = CreateFrameStorage(size); - } - if (_cache && _cache->renderFrame(image, request, index)) { - return; - } else if (!_animation) { - _animation = details::CreateFromContent(_content, _replacements); - } - - image.fill(Qt::transparent); - auto surface = rlottie::Surface( - reinterpret_cast(image.bits()), - image.width(), - image.height(), - image.bytesPerLine()); - _animation->renderSync( - GetLottieFrameIndex(_animation.get(), _quality, index), - surface); - if (_cache) { - _cache->appendFrame(image, request, index); - if (_cache->framesReady() == _cache->framesCount()) { - _animation = nullptr; - } - } -} - -void SharedState::init(QImage cover, const FrameRequest &request) { - Expects(!initialized()); - - _frames[0].request = request; - _frames[0].original = std::move(cover); -} - -void SharedState::start( - not_null owner, - crl::time started, - crl::time delay, - int skippedFrames) { - _owner = owner; - _started = started; - _delay = delay; - _skippedFrames = skippedFrames; - _counter.store(0, std::memory_order_release); -} - -bool IsRendered(not_null frame) { - return (frame->displayed == kTimeUnknown); -} - -void SharedState::renderNextFrame( - not_null frame, - const FrameRequest &request) { - Expects(_info.framesCount > 0); - - renderFrame( - frame->original, - request, - (++_frameIndex) % _info.framesCount); - frame->request = request; - PrepareFrameByRequest(frame); - frame->index = _frameIndex; - frame->displayed = kTimeUnknown; -} - -auto SharedState::renderNextFrame(const FrameRequest &request) --> RenderResult { - const auto prerender = [&](int index) -> RenderResult { - const auto frame = getFrame(index); - const auto next = getFrame((index + 1) % kFramesCount); - if (!IsRendered(frame)) { - renderNextFrame(frame, request); - return { true }; - } else if (!IsRendered(next)) { - renderNextFrame(next, request); - return { true }; - } - return { false }; - }; - const auto present = [&](int counter, int index) -> RenderResult { - const auto frame = getFrame(index); - if (!IsRendered(frame)) { - renderNextFrame(frame, request); - } - frame->display = countFrameDisplayTime(frame->index); - - // Release this frame to the main thread for rendering. - _counter.store( - (counter + 1) % (2 * kFramesCount), - std::memory_order_release); - return { true, _owner }; - }; - - switch (counter()) { - case 0: return present(0, 1); - case 1: return prerender(2); - case 2: return present(2, 2); - case 3: return prerender(3); - case 4: return present(4, 3); - case 5: return prerender(0); - case 6: return present(6, 0); - case 7: return prerender(1); - } - Unexpected("Counter value in Lottie::SharedState::renderNextFrame."); -} - -crl::time SharedState::countFrameDisplayTime(int index) const { - return _started - + _delay - + crl::time(1000) * (_skippedFrames + index) / _info.frameRate; -} - -int SharedState::counter() const { - return _counter.load(std::memory_order_acquire); -} - -bool SharedState::initialized() const { - return (counter() != kCounterUninitialized); -} - -not_null SharedState::getFrame(int index) { - Expects(index >= 0 && index < kFramesCount); - - return &_frames[index]; -} - -not_null SharedState::getFrame(int index) const { - Expects(index >= 0 && index < kFramesCount); - - return &_frames[index]; -} - -Information SharedState::information() const { - return isValid() ? _info : Information(); -} - -not_null SharedState::frameForPaint() { - const auto result = getFrame(counter() / 2); - Assert(!result->original.isNull()); - Assert(result->displayed != kTimeUnknown); - - return result; -} - -int SharedState::framesCount() const { - return _info.framesCount; -} - -crl::time SharedState::nextFrameDisplayTime() const { - const auto frameDisplayTime = [&](int counter) { - const auto next = (counter + 1) % (2 * kFramesCount); - const auto index = next / 2; - const auto frame = getFrame(index); - if (frame->displayed != kTimeUnknown) { - // Frame already displayed, but not yet shown. - return kFrameDisplayTimeAlreadyDone; - } - Assert(IsRendered(frame)); - Assert(frame->display != kTimeUnknown); - - return frame->display; - }; - - switch (counter()) { - case 0: return kTimeUnknown; - case 1: return frameDisplayTime(1); - case 2: return kTimeUnknown; - case 3: return frameDisplayTime(3); - case 4: return kTimeUnknown; - case 5: return frameDisplayTime(5); - case 6: return kTimeUnknown; - case 7: return frameDisplayTime(7); - } - Unexpected("Counter value in VideoTrack::Shared::nextFrameDisplayTime."); -} - -void SharedState::addTimelineDelay(crl::time delayed, int skippedFrames) { - if (!delayed && !skippedFrames) { - return; - } - - const auto recountCurrentFrame = [&](int counter) { - _delay += delayed; - _skippedFrames += skippedFrames; - - const auto next = (counter + 1) % (2 * kFramesCount); - const auto index = next / 2; - const auto frame = getFrame(index); - if (frame->displayed != kTimeUnknown) { - // Frame already displayed. - return; - } - Assert(IsRendered(frame)); - Assert(frame->display != kTimeUnknown); - frame->display = countFrameDisplayTime(frame->index); - }; - - switch (counter()) { - case 0: Unexpected("Value 0 in SharedState::addTimelineDelay."); - case 1: return recountCurrentFrame(1); - case 2: Unexpected("Value 2 in SharedState::addTimelineDelay."); - case 3: return recountCurrentFrame(3); - case 4: Unexpected("Value 4 in SharedState::addTimelineDelay."); - case 5: return recountCurrentFrame(5); - case 6: Unexpected("Value 6 in SharedState::addTimelineDelay."); - case 7: return recountCurrentFrame(7); - } - Unexpected("Counter value in VideoTrack::Shared::nextFrameDisplayTime."); -} - -void SharedState::markFrameDisplayed(crl::time now) { - const auto mark = [&](int counter) { - const auto next = (counter + 1) % (2 * kFramesCount); - const auto index = next / 2; - const auto frame = getFrame(index); - if (frame->displayed == kTimeUnknown) { - frame->displayed = now; - } - }; - - switch (counter()) { - case 0: Unexpected("Value 0 in SharedState::markFrameDisplayed."); - case 1: return mark(1); - case 2: Unexpected("Value 2 in SharedState::markFrameDisplayed."); - case 3: return mark(3); - case 4: Unexpected("Value 4 in SharedState::markFrameDisplayed."); - case 5: return mark(5); - case 6: Unexpected("Value 6 in SharedState::markFrameDisplayed."); - case 7: return mark(7); - } - Unexpected("Counter value in Lottie::SharedState::markFrameDisplayed."); -} - -bool SharedState::markFrameShown() { - const auto jump = [&](int counter) { - const auto next = (counter + 1) % (2 * kFramesCount); - const auto index = next / 2; - const auto frame = getFrame(index); - if (frame->displayed == kTimeUnknown) { - return false; - } - _counter.store( - next, - std::memory_order_release); - return true; - }; - - switch (counter()) { - case 0: return false; - case 1: return jump(1); - case 2: return false; - case 3: return jump(3); - case 4: return false; - case 5: return jump(5); - case 6: return false; - case 7: return jump(7); - } - Unexpected("Counter value in Lottie::SharedState::markFrameShown."); -} - -SharedState::~SharedState() = default; - -std::shared_ptr FrameRenderer::CreateIndependent() { - return std::make_shared(); -} - -std::shared_ptr FrameRenderer::Instance() { - if (auto result = GlobalInstance.lock()) { - return result; - } - auto result = CreateIndependent(); - GlobalInstance = result; - return result; -} - -void FrameRenderer::append( - std::unique_ptr entry, - const FrameRequest &request) { - _wrapped.with([=, entry = std::move(entry)]( - FrameRendererObject &unwrapped) mutable { - unwrapped.append(std::move(entry), request); - }); -} - -void FrameRenderer::frameShown() { - _wrapped.with([=](FrameRendererObject &unwrapped) { - unwrapped.frameShown(); - }); -} - -void FrameRenderer::updateFrameRequest( - not_null entry, - const FrameRequest &request) { - _wrapped.with([=](FrameRendererObject &unwrapped) { - unwrapped.updateFrameRequest(entry, request); - }); -} - -void FrameRenderer::remove(not_null entry) { - _wrapped.with([=](FrameRendererObject &unwrapped) { - unwrapped.remove(entry); - }); -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h deleted file mode 100644 index 60d0336c1..000000000 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h +++ /dev/null @@ -1,160 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "base/basic_types.h" -#include "base/weak_ptr.h" -#include "lottie/lottie_common.h" - -#include -#include -#include -#include -#include - -namespace rlottie { -class Animation; -} // namespace rlottie - -namespace Lottie { - -// Frame rate can be 1, 2, ... , 29, 30 or 60. -inline constexpr auto kNormalFrameRate = 30; -inline constexpr auto kMaxFrameRate = 60; -inline constexpr auto kMaxSize = 4096; -inline constexpr auto kMaxFramesCount = 210; -inline constexpr auto kFrameDisplayTimeAlreadyDone - = std::numeric_limits::max(); -inline constexpr auto kDisplayedInitial = crl::time(-1); - -class Player; -class Cache; - -struct Frame { - QImage original; - crl::time displayed = kDisplayedInitial; - crl::time display = kTimeUnknown; - int index = 0; - - FrameRequest request; - QImage prepared; -}; - -QImage PrepareFrameByRequest( - not_null frame, - bool useExistingPrepared); - -class SharedState { -public: - SharedState( - std::unique_ptr animation, - const FrameRequest &request, - Quality quality); - SharedState( - const QByteArray &content, - const ColorReplacements *replacements, - std::unique_ptr animation, - std::unique_ptr cache, - const FrameRequest &request, - Quality quality); - - void start( - not_null owner, - crl::time now, - crl::time delay = 0, - int skippedFrames = 0); - - [[nodiscard]] Information information() const; - [[nodiscard]] bool initialized() const; - - [[nodiscard]] not_null frameForPaint(); - [[nodiscard]] int framesCount() const; - [[nodiscard]] crl::time nextFrameDisplayTime() const; - void addTimelineDelay(crl::time delayed, int skippedFrames = 0); - void markFrameDisplayed(crl::time now); - bool markFrameShown(); - - void renderFrame(QImage &image, const FrameRequest &request, int index); - - struct RenderResult { - bool rendered = false; - base::weak_ptr notify; - }; - [[nodiscard]] RenderResult renderNextFrame(const FrameRequest &request); - - ~SharedState(); - -private: - static Information CalculateInformation( - Quality quality, - rlottie::Animation *animation, - Cache *cache); - - void construct(const FrameRequest &request); - bool isValid() const; - void init(QImage cover, const FrameRequest &request); - void renderNextFrame( - not_null frame, - const FrameRequest &request); - [[nodiscard]] crl::time countFrameDisplayTime(int index) const; - [[nodiscard]] not_null getFrame(int index); - [[nodiscard]] not_null getFrame(int index) const; - [[nodiscard]] int counter() const; - - // crl::queue changes 0,2,4,6 to 1,3,5,7. - // main thread changes 1,3,5,7 to 2,4,6,0. - static constexpr auto kCounterUninitialized = -1; - std::atomic _counter = kCounterUninitialized; - - static constexpr auto kFramesCount = 4; - std::array _frames; - - base::weak_ptr _owner; - crl::time _started = kTimeUnknown; - - // (_counter % 2) == 1 main thread can write _delay. - // (_counter % 2) == 0 crl::queue can read _delay. - crl::time _delay = kTimeUnknown; - - int _frameIndex = 0; - int _skippedFrames = 0; - const Information _info; - const Quality _quality = Quality::Default; - - const std::unique_ptr _cache; - - std::unique_ptr _animation; - const QByteArray _content; - const ColorReplacements *_replacements = nullptr; - -}; - -class FrameRendererObject; - -class FrameRenderer final { -public: - static std::shared_ptr CreateIndependent(); - static std::shared_ptr Instance(); - - void append( - std::unique_ptr entry, - const FrameRequest &request); - - void updateFrameRequest( - not_null entry, - const FrameRequest &request); - void frameShown(); - void remove(not_null state); - -private: - using Implementation = FrameRendererObject; - crl::object_on_queue _wrapped; - -}; - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.cpp b/Telegram/SourceFiles/lottie/lottie_multi_player.cpp deleted file mode 100644 index 8067b6c4b..000000000 --- a/Telegram/SourceFiles/lottie/lottie_multi_player.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* -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 "lottie/lottie_multi_player.h" - -#include "lottie/lottie_frame_renderer.h" -#include "lottie/lottie_animation.h" -#include "logs.h" - -#include - -namespace Lottie { - -MultiPlayer::MultiPlayer( - Quality quality, - std::shared_ptr renderer) -: _quality(quality) -, _timer([=] { checkNextFrameRender(); }) -, _renderer(renderer ? std::move(renderer) : FrameRenderer::Instance()) { - crl::on_main_update_requests( - ) | rpl::start_with_next([=] { - checkStep(); - }, _lifetime); -} - -MultiPlayer::~MultiPlayer() { - for (const auto &[animation, state] : _active) { - _renderer->remove(state); - } - for (const auto &[animation, info] : _paused) { - _renderer->remove(info.state); - } -} - -not_null MultiPlayer::append( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request) { - _animations.push_back(std::make_unique( - this, - std::move(get), - std::move(put), - content, - request, - _quality)); - return _animations.back().get(); -} - -not_null MultiPlayer::append( - const QByteArray &content, - const FrameRequest &request) { - _animations.push_back(std::make_unique( - this, - content, - request, - _quality)); - return _animations.back().get(); -} - -void MultiPlayer::startAtRightTime(std::unique_ptr state) { - if (_started == kTimeUnknown) { - _started = crl::now(); - _lastSyncTime = kTimeUnknown; - _delay = 0; - } - const auto lastSyncTime = (_lastSyncTime != kTimeUnknown) - ? _lastSyncTime - : _started; - const auto frameIndex = countFrameIndex( - state.get(), - lastSyncTime, - _delay); - state->start(this, _started, _delay, frameIndex); - const auto request = state->frameForPaint()->request; - _renderer->append(std::move(state), request); -} - -int MultiPlayer::countFrameIndex( - not_null state, - crl::time time, - crl::time delay) const { - Expects(time != kTimeUnknown); - - const auto rate = state->information().frameRate; - Assert(rate != 0); - - const auto framesTime = time - _started - delay; - return ((framesTime + 1) * rate - 1) / 1000; -} - -void MultiPlayer::start( - not_null animation, - std::unique_ptr state) { - Expects(state != nullptr); - - const auto paused = _pausedBeforeStart.remove(animation); - auto info = StartingInfo{ std::move(state), paused }; - if (_active.empty() - || (_lastSyncTime == kTimeUnknown - && _nextFrameTime == kTimeUnknown)) { - addNewToActive(animation, std::move(info)); - } else { - // We always try to mark as shown at the same time, so we start a new - // animation at the same time we mark all existing as shown. - _pendingToStart.emplace(animation, std::move(info)); - } - _updates.fire({}); -} - -void MultiPlayer::addNewToActive( - not_null animation, - StartingInfo &&info) { - _active.emplace(animation, info.state.get()); - startAtRightTime(std::move(info.state)); - if (info.paused) { - _pendingPause.emplace(animation); - } -} - -void MultiPlayer::processPending() { - Expects(_lastSyncTime != kTimeUnknown); - - for (const auto &animation : base::take(_pendingPause)) { - pauseAndSaveState(animation); - } - for (const auto &animation : base::take(_pendingUnpause)) { - unpauseAndKeepUp(animation); - } - for (auto &[animation, info] : base::take(_pendingToStart)) { - addNewToActive(animation, std::move(info)); - } - for (const auto &animation : base::take(_pendingRemove)) { - removeNow(animation); - } -} - -void MultiPlayer::remove(not_null animation) { - if (!_active.empty()) { - _pendingRemove.emplace(animation); - } else { - removeNow(animation); - } -} - -void MultiPlayer::removeNow(not_null animation) { - const auto i = _active.find(animation); - if (i != end(_active)) { - _renderer->remove(i->second); - _active.erase(i); - } - const auto j = _paused.find(animation); - if (j != end(_paused)) { - _renderer->remove(j->second.state); - _paused.erase(j); - } - - _pendingRemove.remove(animation); - _pendingToStart.remove(animation); - _pendingPause.remove(animation); - _pendingUnpause.remove(animation); - _pausedBeforeStart.remove(animation); - _animations.erase( - ranges::remove( - _animations, - animation.get(), - &std::unique_ptr::get), - end(_animations)); - - if (_active.empty()) { - _nextFrameTime = kTimeUnknown; - _timer.cancel(); - if (_paused.empty()) { - _started = kTimeUnknown; - _lastSyncTime = kTimeUnknown; - _delay = 0; - } - } -} - -void MultiPlayer::pause(not_null animation) { - if (_active.contains(animation)) { - _pendingPause.emplace(animation); - } else if (_paused.contains(animation)) { - _pendingUnpause.remove(animation); - } else if (const auto i = _pendingToStart.find(animation); i != end(_pendingToStart)) { - i->second.paused = true; - } else { - _pausedBeforeStart.emplace(animation); - } -} - -void MultiPlayer::unpause(not_null animation) { - if (const auto i = _paused.find(animation); i != end(_paused)) { - if (_active.empty()) { - unpauseFirst(animation, i->second.state); - _paused.erase(i); - } else { - _pendingUnpause.emplace(animation); - } - } else if (_pendingPause.contains(animation)) { - _pendingPause.remove(animation); - } else { - const auto i = _pendingToStart.find(animation); - if (i != end(_pendingToStart)) { - i->second.paused = false; - } else { - _pausedBeforeStart.remove(animation); - } - } -} - -void MultiPlayer::unpauseFirst( - not_null animation, - not_null state) { - Expects(_lastSyncTime != kTimeUnknown); - - _active.emplace(animation, state); - - const auto now = crl::now(); - addTimelineDelay(now - _lastSyncTime); - _lastSyncTime = now; - - markFrameShown(); -} - -void MultiPlayer::pauseAndSaveState(not_null animation) { - Expects(_lastSyncTime != kTimeUnknown); - - const auto i = _active.find(animation); - Assert(i != end(_active)); - _paused.emplace( - animation, - PausedInfo{ i->second, _lastSyncTime, _delay }); - _active.erase(i); -} - -void MultiPlayer::unpauseAndKeepUp(not_null animation) { - Expects(_lastSyncTime != kTimeUnknown); - - const auto i = _paused.find(animation); - Assert(i != end(_paused)); - const auto state = i->second.state; - const auto frameIndexAtPaused = countFrameIndex( - state, - i->second.pauseTime, - i->second.pauseDelay); - const auto frameIndexNow = countFrameIndex( - state, - _lastSyncTime, - _delay); - state->addTimelineDelay( - (_delay - i->second.pauseDelay), - frameIndexNow - frameIndexAtPaused); - _active.emplace(animation, state); - _paused.erase(i); -} - -void MultiPlayer::failed(not_null animation, Error error) { - //_updates.fire({ animation, error }); -} - -rpl::producer MultiPlayer::updates() const { - return _updates.events(); -} - -void MultiPlayer::checkStep() { - if (_active.empty() || _nextFrameTime == kFrameDisplayTimeAlreadyDone) { - return; - } else if (_nextFrameTime != kTimeUnknown) { - checkNextFrameRender(); - } else { - checkNextFrameAvailability(); - } -} - -void MultiPlayer::checkNextFrameAvailability() { - Expects(_nextFrameTime == kTimeUnknown); - - auto next = kTimeUnknown; - for (const auto &[animation, state] : _active) { - const auto time = state->nextFrameDisplayTime(); - if (time == kTimeUnknown) { - for (const auto &[animation, state] : _active) { - if (state->nextFrameDisplayTime() != kTimeUnknown) { - break; - } - } - return; - } else if (time == kFrameDisplayTimeAlreadyDone) { - continue; - } - if (next == kTimeUnknown || next > time) { - next = time; - } - } - if (next == kTimeUnknown) { - return; - } - _nextFrameTime = next; - checkNextFrameRender(); -} - -void MultiPlayer::checkNextFrameRender() { - Expects(_nextFrameTime != kTimeUnknown); - - const auto now = crl::now(); - if (now < _nextFrameTime) { - if (!_timer.isActive()) { - _timer.callOnce(_nextFrameTime - now); - } - } else { - _timer.cancel(); - - markFrameDisplayed(now); - addTimelineDelay(now - _nextFrameTime); - _lastSyncTime = now; - _nextFrameTime = kFrameDisplayTimeAlreadyDone; - processPending(); - _updates.fire({}); - } -} - -void MultiPlayer::updateFrameRequest( - not_null animation, - const FrameRequest &request) { - const auto state = [&]() -> Lottie::SharedState* { - const auto key = animation; - if (const auto i = _active.find(animation); i != end(_active)) { - return i->second; - } else if (const auto j = _paused.find(animation); - j != end(_paused)) { - return j->second.state; - } else if (const auto k = _pendingToStart.find(animation); - k != end(_pendingToStart)) { - return nullptr; - } - Unexpected("Animation in MultiPlayer::updateFrameRequest."); - }(); - if (state) { - _renderer->updateFrameRequest(state, request); - } -} - -void MultiPlayer::markFrameDisplayed(crl::time now) { - Expects(!_active.empty()); - - for (const auto &[animation, state] : _active) { - const auto time = state->nextFrameDisplayTime(); - Assert(time != kTimeUnknown); - if (time == kFrameDisplayTimeAlreadyDone) { - continue; - } else if (now >= time) { - state->markFrameDisplayed(now); - } - } -} - -void MultiPlayer::addTimelineDelay(crl::time delayed) { - Expects(!_active.empty()); - - for (const auto &[animation, state] : _active) { - state->addTimelineDelay(delayed); - } - _delay += delayed; -} - -bool MultiPlayer::markFrameShown() { - if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) { - _nextFrameTime = kTimeUnknown; - } - auto count = 0; - for (const auto &[animation, state] : _active) { - if (state->markFrameShown()) { - ++count; - } - } - if (count) { - _renderer->frameShown(); - return true; - } - return false; -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.h b/Telegram/SourceFiles/lottie/lottie_multi_player.h deleted file mode 100644 index 2cf9fbd59..000000000 --- a/Telegram/SourceFiles/lottie/lottie_multi_player.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "lottie/lottie_player.h" -#include "base/timer.h" -#include "base/algorithm.h" -#include "base/flat_set.h" -#include "base/flat_map.h" - -#include - -namespace Lottie { - -class Animation; -class FrameRenderer; - -struct MultiUpdate { - //base::variant< - // std::pair, - // DisplayMultiFrameRequest, - // std::pair> data; -}; - -class MultiPlayer final : public Player { -public: - MultiPlayer( - Quality quality = Quality::Default, - std::shared_ptr renderer = nullptr); - ~MultiPlayer(); - - void start( - not_null animation, - std::unique_ptr state) override; - void failed(not_null animation, Error error) override; - void updateFrameRequest( - not_null animation, - const FrameRequest &request) override; - bool markFrameShown() override; - void checkStep() override; - - not_null append( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request); - not_null append( - const QByteArray &content, - const FrameRequest &request); - - rpl::producer updates() const; - - void remove(not_null animation); - - void pause(not_null animation); - void unpause(not_null animation); - -private: - struct PausedInfo { - not_null state; - crl::time pauseTime = kTimeUnknown; - crl::time pauseDelay = kTimeUnknown; - }; - struct StartingInfo { - std::unique_ptr state; - bool paused = false; - }; - - void addNewToActive( - not_null animation, - StartingInfo &&info); - [[nodiscard]] int countFrameIndex( - not_null state, - crl::time time, - crl::time delay) const; - void startAtRightTime(std::unique_ptr state); - void processPending(); - void markFrameDisplayed(crl::time now); - void addTimelineDelay(crl::time delayed); - void checkNextFrameAvailability(); - void checkNextFrameRender(); - void unpauseFirst( - not_null animation, - not_null state); - void pauseAndSaveState(not_null animation); - void unpauseAndKeepUp(not_null animation); - void removeNow(not_null animation); - - Quality _quality = Quality::Default; - base::Timer _timer; - const std::shared_ptr _renderer; - std::vector> _animations; - base::flat_map, not_null> _active; - base::flat_map, PausedInfo> _paused; - base::flat_set> _pendingPause; - base::flat_set> _pendingUnpause; - base::flat_set> _pausedBeforeStart; - base::flat_set> _pendingRemove; - base::flat_map, StartingInfo> _pendingToStart; - crl::time _started = kTimeUnknown; - crl::time _lastSyncTime = kTimeUnknown; - crl::time _delay = 0; - crl::time _nextFrameTime = kTimeUnknown; - rpl::event_stream _updates; - rpl::lifetime _lifetime; - -}; - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_player.h b/Telegram/SourceFiles/lottie/lottie_player.h deleted file mode 100644 index 2216d6708..000000000 --- a/Telegram/SourceFiles/lottie/lottie_player.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "lottie/lottie_common.h" -#include "base/weak_ptr.h" - -#include - -namespace Lottie { - -class SharedState; - -class Player : public base::has_weak_ptr { -public: - virtual void start( - not_null animation, - std::unique_ptr state) = 0; - virtual void failed(not_null animation, Error error) = 0; - virtual void updateFrameRequest( - not_null animation, - const FrameRequest &request) = 0; - virtual bool markFrameShown() = 0; - virtual void checkStep() = 0; - - virtual ~Player() = default; - -}; - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.cpp b/Telegram/SourceFiles/lottie/lottie_single_player.cpp deleted file mode 100644 index d88a2cb59..000000000 --- a/Telegram/SourceFiles/lottie/lottie_single_player.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* -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 "lottie/lottie_single_player.h" - -#include "lottie/lottie_frame_renderer.h" - -namespace Lottie { - -SinglePlayer::SinglePlayer( - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements, - std::shared_ptr renderer) -: _animation(this, content, request, quality, replacements) -, _timer([=] { checkNextFrameRender(); }) -, _renderer(renderer ? renderer : FrameRenderer::Instance()) { -} - -SinglePlayer::SinglePlayer( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request, - Quality quality, - const ColorReplacements *replacements, - std::shared_ptr renderer) -: _animation( - this, - std::move(get), - std::move(put), - content, - request, - quality, - replacements) -, _timer([=] { checkNextFrameRender(); }) -, _renderer(renderer ? renderer : FrameRenderer::Instance()) { -} - -SinglePlayer::~SinglePlayer() { - if (_state) { - _renderer->remove(_state); - } -} - -void SinglePlayer::start( - not_null animation, - std::unique_ptr state) { - Expects(animation == &_animation); - - _state = state.get(); - auto information = state->information(); - state->start(this, crl::now()); - const auto request = state->frameForPaint()->request; - _renderer->append(std::move(state), request); - _updates.fire({ std::move(information) }); - - crl::on_main_update_requests( - ) | rpl::start_with_next([=] { - checkStep(); - }, _lifetime); -} - -void SinglePlayer::failed(not_null animation, Error error) { - Expects(animation == &_animation); - - _updates.fire_error(std::move(error)); -} - -rpl::producer SinglePlayer::updates() const { - return _updates.events(); -} - -bool SinglePlayer::ready() const { - return _animation.ready(); -} - -QImage SinglePlayer::frame() const { - return _animation.frame(); -} - -QImage SinglePlayer::frame(const FrameRequest &request) const { - return _animation.frame(request); -} - -Animation::FrameInfo SinglePlayer::frameInfo( - const FrameRequest &request) const { - return _animation.frameInfo(request); -} - -void SinglePlayer::checkStep() { - if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) { - return; - } else if (_nextFrameTime != kTimeUnknown) { - checkNextFrameRender(); - } else { - checkNextFrameAvailability(); - } -} - -void SinglePlayer::checkNextFrameAvailability() { - Expects(_state != nullptr); - Expects(_nextFrameTime == kTimeUnknown); - - _nextFrameTime = _state->nextFrameDisplayTime(); - Assert(_nextFrameTime != kFrameDisplayTimeAlreadyDone); - if (_nextFrameTime != kTimeUnknown) { - checkNextFrameRender(); - } -} - -void SinglePlayer::checkNextFrameRender() { - Expects(_nextFrameTime != kTimeUnknown); - - const auto now = crl::now(); - if (now < _nextFrameTime) { - if (!_timer.isActive()) { - _timer.callOnce(_nextFrameTime - now); - } - } else { - _timer.cancel(); - - _state->markFrameDisplayed(now); - _state->addTimelineDelay(now - _nextFrameTime); - - _nextFrameTime = kFrameDisplayTimeAlreadyDone; - _updates.fire({ DisplayFrameRequest() }); - } -} - -void SinglePlayer::updateFrameRequest( - not_null animation, - const FrameRequest &request) { - Expects(animation == &_animation); - Expects(_state != nullptr); - - _renderer->updateFrameRequest(_state, request); -} - -bool SinglePlayer::markFrameShown() { - Expects(_state != nullptr); - - if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) { - _nextFrameTime = kTimeUnknown; - } - if (_state->markFrameShown()) { - _renderer->frameShown(); - return true; - } - return false; -} - -} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.h b/Telegram/SourceFiles/lottie/lottie_single_player.h deleted file mode 100644 index 772e7743a..000000000 --- a/Telegram/SourceFiles/lottie/lottie_single_player.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "lottie/lottie_player.h" -#include "lottie/lottie_animation.h" -#include "base/timer.h" - -#include - -namespace Lottie { - -class FrameRenderer; - -struct DisplayFrameRequest { -}; - -struct Update { - base::variant< - Information, - DisplayFrameRequest> data; -}; - -class SinglePlayer final : public Player { -public: - SinglePlayer( - const QByteArray &content, - const FrameRequest &request, - Quality quality = Quality::Default, - const ColorReplacements *replacements = nullptr, - std::shared_ptr renderer = nullptr); - SinglePlayer( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &content, - const FrameRequest &request, - Quality quality = Quality::Default, - const ColorReplacements *replacements = nullptr, - std::shared_ptr renderer = nullptr); - ~SinglePlayer(); - - void start( - not_null animation, - std::unique_ptr state) override; - void failed(not_null animation, Error error) override; - void updateFrameRequest( - not_null animation, - const FrameRequest &request) override; - bool markFrameShown() override; - void checkStep() override; - - rpl::producer updates() const; - - [[nodiscard]] bool ready() const; - [[nodiscard]] QImage frame() const; - [[nodiscard]] QImage frame(const FrameRequest &request) const; - [[nodiscard]] Animation::FrameInfo frameInfo( - const FrameRequest &request) const; - -private: - void checkNextFrameAvailability(); - void checkNextFrameRender(); - - Animation _animation; - base::Timer _timer; - const std::shared_ptr _renderer; - SharedState *_state = nullptr; - crl::time _nextFrameTime = kTimeUnknown; - rpl::event_stream _updates; - rpl::lifetime _lifetime; - -}; - -} // namespace Lottie diff --git a/Telegram/ThirdParty/rlottie b/Telegram/ThirdParty/rlottie index 589db026e..fceedfb5e 160000 --- a/Telegram/ThirdParty/rlottie +++ b/Telegram/ThirdParty/rlottie @@ -1 +1 @@ -Subproject commit 589db026ec211bc4979e3bffe074f6e48ce7cedc +Subproject commit fceedfb5e1a226934771d146839518c6dc4780c0 diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 3ee5fbd0b..3cfba863d 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -77,11 +77,11 @@ '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', '<(submodules_loc)/lib_ui/lib_ui.gyp:lib_ui', '<(third_party_loc)/libtgvoip/libtgvoip.gyp:libtgvoip', + '<(submodules_loc)/lib_lottie/lib_lottie.gyp:lib_lottie', 'tests/tests.gyp:tests', 'utils.gyp:Updater', 'lib_export.gyp:lib_export', 'lib_storage.gyp:lib_storage', - 'lib_lottie.gyp:lib_lottie', 'lib_ffmpeg.gyp:lib_ffmpeg', 'lib_mtproto.gyp:lib_mtproto', ], diff --git a/Telegram/gyp/generate.py b/Telegram/gyp/generate.py index 4194e1666..b6c6df7b4 100644 --- a/Telegram/gyp/generate.py +++ b/Telegram/gyp/generate.py @@ -84,6 +84,7 @@ gypArguments.append('--generator-output=..') gypArguments.append('-Goutput_dir=../out') gypArguments.append('-Dapi_id=' + apiId) gypArguments.append('-Dapi_hash=' + apiHash) +gypArguments.append('-Dlottie_use_cache=1') gypArguments.append('-Dofficial_build_target=' + officialTarget) if 'TDESKTOP_BUILD_DEFINES' in os.environ: buildDefines = os.environ['TDESKTOP_BUILD_DEFINES'] diff --git a/Telegram/gyp/lib_ffmpeg.gyp b/Telegram/gyp/lib_ffmpeg.gyp index 47e00d237..851707640 100644 --- a/Telegram/gyp/lib_ffmpeg.gyp +++ b/Telegram/gyp/lib_ffmpeg.gyp @@ -32,6 +32,7 @@ ], 'direct_dependent_settings': { 'include_dirs': [ + '<(src_loc)', '<(libs_loc)/ffmpeg', ], }, diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp deleted file mode 100644 index ca4158a06..000000000 --- a/Telegram/gyp/lib_lottie.gyp +++ /dev/null @@ -1,65 +0,0 @@ -# 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 - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_lottie', - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/openssl.gypi', - 'helpers/modules/qt.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - }, - 'dependencies': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - 'lib_rlottie.gyp:lib_rlottie', - 'lib_ffmpeg.gyp:lib_ffmpeg', - 'lib_lz4.gyp:lib_lz4', - ], - 'export_dependent_settings': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - 'lib_rlottie.gyp:lib_rlottie', - 'lib_ffmpeg.gyp:lib_ffmpeg', - 'lib_lz4.gyp:lib_lz4', - ], - 'defines': [ - 'LOT_BUILD', - ], - 'include_dirs': [ - '<(src_loc)', - '<(libs_loc)/zlib', - ], - 'sources': [ - '<(src_loc)/lottie/lottie_animation.cpp', - '<(src_loc)/lottie/lottie_animation.h', - '<(src_loc)/lottie/lottie_cache.cpp', - '<(src_loc)/lottie/lottie_cache.h', - '<(src_loc)/lottie/lottie_common.cpp', - '<(src_loc)/lottie/lottie_common.h', - '<(src_loc)/lottie/lottie_frame_renderer.cpp', - '<(src_loc)/lottie/lottie_frame_renderer.h', - '<(src_loc)/lottie/lottie_multi_player.cpp', - '<(src_loc)/lottie/lottie_multi_player.h', - '<(src_loc)/lottie/lottie_player.h', - '<(src_loc)/lottie/lottie_single_player.cpp', - '<(src_loc)/lottie/lottie_single_player.h', - ], - 'conditions': [[ 'build_macold', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], - }, - 'include_dirs': [ - '/usr/local/macold/include/c++/v1', - ], - }]], - }], -} diff --git a/Telegram/gyp/lib_rlottie.gyp b/Telegram/gyp/lib_rlottie.gyp deleted file mode 100644 index 44634c7be..000000000 --- a/Telegram/gyp/lib_rlottie.gyp +++ /dev/null @@ -1,134 +0,0 @@ -# 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 - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'variables': { - 'build_standard_win': 'c++14', - }, - 'targets': [{ - 'target_name': 'lib_rlottie', - 'includes': [ - 'helpers/common/library.gypi', - ], - 'variables': { - 'build_standard_win': 'c++14', - 'rlottie_loc': '<(third_party_loc)/rlottie', - 'rlottie_src': '<(rlottie_loc)/src', - }, - 'defines': [ - '_USE_MATH_DEFINES', - 'RAPIDJSON_ASSERT=(void)', - 'LOT_BUILD', - ], - 'include_dirs': [ - '<(rlottie_loc)/inc', - '<(rlottie_src)/lottie', - '<(rlottie_src)/vector', - '<(rlottie_src)/vector/pixman', - '<(rlottie_src)/vector/freetype', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(rlottie_loc)/inc', - ], - }, - 'sources': [ - '<(rlottie_loc)/inc/rlottie.h', - '<(rlottie_loc)/inc/rlottie_capi.h', - '<(rlottie_loc)/inc/rlottiecommon.h', - - '<(rlottie_src)/lottie/lottieanimation.cpp', - '<(rlottie_src)/lottie/lottieitem.cpp', - '<(rlottie_src)/lottie/lottieitem.h', - '<(rlottie_src)/lottie/lottiekeypath.cpp', - '<(rlottie_src)/lottie/lottiekeypath.h', - '<(rlottie_src)/lottie/lottieloader.cpp', - '<(rlottie_src)/lottie/lottieloader.h', - '<(rlottie_src)/lottie/lottiemodel.cpp', - '<(rlottie_src)/lottie/lottiemodel.h', - '<(rlottie_src)/lottie/lottieparser.cpp', - '<(rlottie_src)/lottie/lottieparser.h', - '<(rlottie_src)/lottie/lottieproxymodel.cpp', - '<(rlottie_src)/lottie/lottieproxymodel.h', - - '<(rlottie_src)/vector/freetype/v_ft_math.cpp', - '<(rlottie_src)/vector/freetype/v_ft_math.h', - '<(rlottie_src)/vector/freetype/v_ft_raster.cpp', - '<(rlottie_src)/vector/freetype/v_ft_raster.h', - '<(rlottie_src)/vector/freetype/v_ft_stroker.cpp', - '<(rlottie_src)/vector/freetype/v_ft_stroker.h', - '<(rlottie_src)/vector/freetype/v_ft_types.h', - - #'<(rlottie_src)/vector/pixman/pixman-arm-neon-asm.h', - #'<(rlottie_src)/vector/pixman/pixman-arm-neon-asm.S', - '<(rlottie_src)/vector/pixman/vregion.cpp', - '<(rlottie_src)/vector/pixman/vregion.h', - - '<(rlottie_src)/vector/config.h', - '<(rlottie_src)/vector/vbezier.cpp', - '<(rlottie_src)/vector/vbezier.h', - '<(rlottie_src)/vector/vbitmap.cpp', - '<(rlottie_src)/vector/vbitmap.h', - '<(rlottie_src)/vector/vbrush.cpp', - '<(rlottie_src)/vector/vbrush.h', - '<(rlottie_src)/vector/vcompositionfunctions.cpp', - '<(rlottie_src)/vector/vcowptr.h', - '<(rlottie_src)/vector/vdasher.cpp', - '<(rlottie_src)/vector/vdasher.h', - '<(rlottie_src)/vector/vdebug.cpp', - '<(rlottie_src)/vector/vdebug.h', - '<(rlottie_src)/vector/vdrawable.cpp', - '<(rlottie_src)/vector/vdrawable.h', - '<(rlottie_src)/vector/vdrawhelper.cpp', - '<(rlottie_src)/vector/vdrawhelper.h', - '<(rlottie_src)/vector/vdrawhelper_neon.cpp', - '<(rlottie_src)/vector/vdrawhelper_sse2.cpp', - '<(rlottie_src)/vector/velapsedtimer.cpp', - '<(rlottie_src)/vector/velapsedtimer.h', - '<(rlottie_src)/vector/vglobal.h', - '<(rlottie_src)/vector/vimageloader.cpp', - '<(rlottie_src)/vector/vimageloader.h', - '<(rlottie_src)/vector/vinterpolator.cpp', - '<(rlottie_src)/vector/vinterpolator.h', - '<(rlottie_src)/vector/vline.h', - '<(rlottie_src)/vector/vmatrix.cpp', - '<(rlottie_src)/vector/vmatrix.h', - '<(rlottie_src)/vector/vpainter.cpp', - '<(rlottie_src)/vector/vpainter.h', - '<(rlottie_src)/vector/vpath.cpp', - '<(rlottie_src)/vector/vpath.h', - '<(rlottie_src)/vector/vpathmesure.cpp', - '<(rlottie_src)/vector/vpathmesure.h', - '<(rlottie_src)/vector/vpoint.h', - '<(rlottie_src)/vector/vraster.cpp', - '<(rlottie_src)/vector/vraster.h', - '<(rlottie_src)/vector/vrect.cpp', - '<(rlottie_src)/vector/vrect.h', - '<(rlottie_src)/vector/vrle.cpp', - '<(rlottie_src)/vector/vrle.h', - '<(rlottie_src)/vector/vstackallocator.h', - '<(rlottie_src)/vector/vtaskqueue.h', - ], - 'conditions': [[ 'build_macold', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], - }, - 'include_dirs': [ - '/usr/local/macold/include/c++/v1', - ], - }]], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': [ - '/w44244', # 'initializing': conversion from 'double' to 'float' - ], - }, - }, - }], -} diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie new file mode 160000 index 000000000..5c820ee0a --- /dev/null +++ b/Telegram/lib_lottie @@ -0,0 +1 @@ +Subproject commit 5c820ee0a0902e2511f7c1c040801c133900496d diff --git a/Telegram/lib_rlottie b/Telegram/lib_rlottie new file mode 160000 index 000000000..b52e0c2cc --- /dev/null +++ b/Telegram/lib_rlottie @@ -0,0 +1 @@ +Subproject commit b52e0c2cc1daa8cba6a0cec278ee9914020aabe1 From fcb2950ce82b5027a60223b59ed9a3893004bf9d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 24 Sep 2019 09:53:35 +0300 Subject: [PATCH 15/59] Use slide animation from lib_ui. --- .../ui/effects/slide_animation.cpp | 53 ------------------ .../SourceFiles/ui/effects/slide_animation.h | 55 ------------------- Telegram/gyp/telegram/sources.txt | 2 - Telegram/lib_ui | 2 +- 4 files changed, 1 insertion(+), 111 deletions(-) delete mode 100644 Telegram/SourceFiles/ui/effects/slide_animation.cpp delete mode 100644 Telegram/SourceFiles/ui/effects/slide_animation.h diff --git a/Telegram/SourceFiles/ui/effects/slide_animation.cpp b/Telegram/SourceFiles/ui/effects/slide_animation.cpp deleted file mode 100644 index 1dd099d75..000000000 --- a/Telegram/SourceFiles/ui/effects/slide_animation.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -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 "ui/effects/slide_animation.h" - -namespace Ui { - -void SlideAnimation::setSnapshots(QPixmap leftSnapshot, QPixmap rightSnapshot) { - _leftSnapshot = std::move(leftSnapshot); - _rightSnapshot = std::move(rightSnapshot); - Assert(!_leftSnapshot.isNull()); - Assert(!_rightSnapshot.isNull()); - _leftSnapshot.setDevicePixelRatio(cRetinaFactor()); - _rightSnapshot.setDevicePixelRatio(cRetinaFactor()); -} - -void SlideAnimation::paintFrame(Painter &p, int x, int y, int outerWidth) { - auto dt = _animation.value(1.); - if (!animating()) return; - - auto easeOut = anim::easeOutCirc(1., dt); - auto easeIn = anim::easeInCirc(1., dt); - auto arrivingAlpha = easeIn; - auto departingAlpha = 1. - easeOut; - auto leftCoord = (_slideLeft ? anim::interpolate(-_leftSnapshotWidth, 0, easeOut) : anim::interpolate(0, -_leftSnapshotWidth, easeIn)); - auto leftAlpha = (_slideLeft ? arrivingAlpha : departingAlpha); - auto rightCoord = (_slideLeft ? anim::interpolate(0, _rightSnapshotWidth, easeIn) : anim::interpolate(_rightSnapshotWidth, 0, easeOut)); - auto rightAlpha = (_slideLeft ? departingAlpha : arrivingAlpha); - - if (_overflowHidden) { - auto leftWidth = (_leftSnapshotWidth + leftCoord); - if (leftWidth > 0) { - p.setOpacity(leftAlpha); - p.drawPixmap(x, y, leftWidth, _leftSnapshotHeight, _leftSnapshot, (_leftSnapshot.width() - leftWidth * cIntRetinaFactor()), 0, leftWidth * cIntRetinaFactor(), _leftSnapshot.height()); - } - auto rightWidth = _rightSnapshotWidth - rightCoord; - if (rightWidth > 0) { - p.setOpacity(rightAlpha); - p.drawPixmap(x + rightCoord, y, _rightSnapshot, 0, 0, rightWidth * cIntRetinaFactor(), _rightSnapshot.height()); - } - } else { - p.setOpacity(leftAlpha); - p.drawPixmap(x + leftCoord, y, _leftSnapshot); - p.setOpacity(rightAlpha); - p.drawPixmap(x + rightCoord, y, _rightSnapshot); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/slide_animation.h b/Telegram/SourceFiles/ui/effects/slide_animation.h deleted file mode 100644 index 055434fca..000000000 --- a/Telegram/SourceFiles/ui/effects/slide_animation.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -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 -*/ -#pragma once - -#include "ui/effects/animations.h" - -namespace Ui { - -class SlideAnimation { -public: - void setSnapshots(QPixmap leftSnapshot, QPixmap rightSnapshot); - - void setOverflowHidden(bool hidden) { - _overflowHidden = hidden; - } - - template - void start(bool slideLeft, Lambda &&updateCallback, float64 duration); - - void paintFrame(Painter &p, int x, int y, int outerWidth); - - bool animating() const { - return _animation.animating(); - } - -private: - Ui::Animations::Simple _animation; - QPixmap _leftSnapshot; - QPixmap _rightSnapshot; - bool _slideLeft = false; - bool _overflowHidden = true; - int _leftSnapshotWidth = 0; - int _leftSnapshotHeight = 0; - int _rightSnapshotWidth = 0; - -}; - -template -void SlideAnimation::start(bool slideLeft, Lambda &&updateCallback, float64 duration) { - _slideLeft = slideLeft; - if (_slideLeft) { - std::swap(_leftSnapshot, _rightSnapshot); - } - _leftSnapshotWidth = _leftSnapshot.width() / cIntRetinaFactor(); - _leftSnapshotHeight = _leftSnapshot.height() / cIntRetinaFactor(); - _rightSnapshotWidth = _rightSnapshot.width() / cIntRetinaFactor(); - _animation.start(std::forward(updateCallback), 0., 1., duration); -} - -} // namespace Ui diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt index e8017f382..79e7a42b1 100644 --- a/Telegram/gyp/telegram/sources.txt +++ b/Telegram/gyp/telegram/sources.txt @@ -728,8 +728,6 @@ <(src_loc)/ui/effects/round_checkbox.h <(src_loc)/ui/effects/send_action_animations.cpp <(src_loc)/ui/effects/send_action_animations.h -<(src_loc)/ui/effects/slide_animation.cpp -<(src_loc)/ui/effects/slide_animation.h <(src_loc)/ui/image/image.cpp <(src_loc)/ui/image/image.h <(src_loc)/ui/image/image_location.cpp diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 00393b7b3..f7c3e22cc 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 00393b7b3ba113a216d3704d47e0ca1cb97d0e6e +Subproject commit f7c3e22cc9d51e4bce0fe82e9eaa10cedc7bf5d5 From c87f9d0074ec0bdad91ef235a0acfa1e8ee5c154 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 25 Sep 2019 17:27:39 +0300 Subject: [PATCH 16/59] Fix build for Linux. --- Telegram/SourceFiles/window/window.style | 82 ----------- Telegram/gyp/PrecompiledHeader.cmake | 173 ----------------------- Telegram/gyp/helpers | 2 +- Telegram/gyp/linux_glibc_wraps.gyp | 27 ---- Telegram/gyp/telegram/linux.gypi | 9 -- Telegram/gyp/tests/tests.gyp | 5 + Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 8 files changed, 8 insertions(+), 294 deletions(-) delete mode 100644 Telegram/gyp/PrecompiledHeader.cmake delete mode 100644 Telegram/gyp/linux_glibc_wraps.gyp diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 9257c530c..3f6ac32ee 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -14,8 +14,6 @@ windowMinWidth: 380px; windowMinHeight: 480px; windowDefaultWidth: 800px; windowDefaultHeight: 600px; -windowShadow: icon {{ "window_shadow", windowShadowFg }}; -windowShadowShift: 1px; columnMinimalWidthLeft: 260px; columnMaximalWidthLeft: 540px; @@ -164,86 +162,6 @@ mainMenuVersionLabel: FlatLabel(mainMenuTelegramLabel) { } mainMenuVersionBottom: 21px; -// Windows specific title - -titleHeight: 21px; -titleButtonMinimize: IconButton { - width: 24px; - height: 21px; - - icon: icon { - { size(24px, 21px), titleButtonBg }, - { "title_button_minimize", titleButtonFg, point(4px, 4px) }, - }; - iconOver: icon { - { size(24px, 21px), titleButtonBgOver }, - { "title_button_minimize", titleButtonFgOver, point(4px, 4px) }, - }; - iconPosition: point(0px, 0px); -} -titleButtonMinimizeIconActive: icon { - { size(24px, 21px), titleButtonBgActive }, - { "title_button_minimize", titleButtonFgActive, point(4px, 4px) }, -}; -titleButtonMinimizeIconActiveOver: icon { - { size(24px, 21px), titleButtonBgActiveOver }, - { "title_button_minimize", titleButtonFgActiveOver, point(4px, 4px) }, -}; -titleButtonMaximize: IconButton(titleButtonMinimize) { - icon: icon { - { size(24px, 21px), titleButtonBg }, - { "title_button_maximize", titleButtonFg, point(4px, 4px) }, - }; - iconOver: icon { - { size(24px, 21px), titleButtonBgOver }, - { "title_button_maximize", titleButtonFgOver, point(4px, 4px) }, - }; -} -titleButtonMaximizeIconActive: icon { - { size(24px, 21px), titleButtonBgActive }, - { "title_button_maximize", titleButtonFgActive, point(4px, 4px) }, -}; -titleButtonMaximizeIconActiveOver: icon { - { size(24px, 21px), titleButtonBgActiveOver }, - { "title_button_maximize", titleButtonFgActiveOver, point(4px, 4px) }, -}; -titleButtonRestoreIcon: icon { - { size(24px, 21px), titleButtonBg }, - { "title_button_restore", titleButtonFg, point(4px, 4px) }, -}; -titleButtonRestoreIconOver: icon { - { size(24px, 21px), titleButtonBgOver }, - { "title_button_restore", titleButtonFgOver, point(4px, 4px) }, -}; -titleButtonRestoreIconActive: icon { - { size(24px, 21px), titleButtonBgActive }, - { "title_button_restore", titleButtonFgActive, point(4px, 4px) }, -}; -titleButtonRestoreIconActiveOver: icon { - { size(24px, 21px), titleButtonBgActiveOver }, - { "title_button_restore", titleButtonFgActiveOver, point(4px, 4px) }, -}; -titleButtonClose: IconButton(titleButtonMinimize) { - width: 25px; - - icon: icon { - { size(25px, 21px), titleButtonCloseBg }, - { "title_button_close", titleButtonCloseFg, point(5px, 4px) }, - }; - iconOver: icon { - { size(25px, 21px), titleButtonCloseBgOver }, - { "title_button_close", titleButtonCloseFgOver, point(5px, 4px) }, - }; -} -titleButtonCloseIconActive: icon { - { size(25px, 21px), titleButtonCloseBgActive }, - { "title_button_close", titleButtonCloseFgActive, point(5px, 4px) }, -}; -titleButtonCloseIconActiveOver: icon { - { size(25px, 21px), titleButtonCloseBgActiveOver }, - { "title_button_close", titleButtonCloseFgActiveOver, point(5px, 4px) }, -}; - themeEditorSampleSize: size(90px, 51px); themeEditorMargin: margins(17px, 10px, 17px, 10px); themeEditorDescriptionSkip: 10px; diff --git a/Telegram/gyp/PrecompiledHeader.cmake b/Telegram/gyp/PrecompiledHeader.cmake deleted file mode 100644 index dfe1193be..000000000 --- a/Telegram/gyp/PrecompiledHeader.cmake +++ /dev/null @@ -1,173 +0,0 @@ -## -## Modified for Telegram Desktop project by Telegram Desktop authors. -## -# Function for setting up precompiled headers. Usage: -# -# add_library/executable(target -# pchheader.c pchheader.cpp pchheader.h) -# -# add_precompiled_header(target pchheader.h -# [FORCEINCLUDE] -# [SOURCE_C pchheader.c] -# [SOURCE_CXX pchheader.cpp]) -# -# Options: -# -# FORCEINCLUDE: Add compiler flags to automatically include the -# pchheader.h from every source file. Works with both GCC and -# MSVC. This is recommended. -# -# SOURCE_C/CXX: Specifies the .c/.cpp source file that includes -# pchheader.h for generating the pre-compiled header -# output. Defaults to pchheader.c. Only required for MSVC. -# -# Caveats: -# -# * Its not currently possible to use the same precompiled-header in -# more than a single target in the same directory (No way to set -# the source file properties differently for each target). -# -# * MSVC: A source file with the same name as the header must exist -# and be included in the target (E.g. header.cpp). Name of file -# can be changed using the SOURCE_CXX/SOURCE_C options. -# -# License: -# -# Copyright (C) 2009-2013 Lars Christensen -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the 'Software') deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -include(CMakeParseArguments) - -macro(combine_arguments _variable) - set(_result "") - foreach(_element ${${_variable}}) - set(_result "${_result} \"${_element}\"") - endforeach() - string(STRIP "${_result}" _result) - set(${_variable} "${_result}") -endmacro() - -function(export_all_flags _filename _source_name_for_flags) - set(_include_directories "$") - set(_compile_definitions "$") - get_source_file_property(_compile_file_flags "${_source_name_for_flags}" COMPILE_FLAGS) - set(_compile_flags "$") - set(_compile_options "$") - set(_include_directories "$<$:-I$\n>") - set(_compile_definitions "$<$:-D$\n>") - set(_compile_file_flags "$<$:$\n>") - set(_compile_flags "$<$:$\n>") - set(_compile_options "$<$:$\n>") - file(GENERATE OUTPUT "${_filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_file_flags}${_compile_flags}${_compile_options}\n") -endfunction() - -function(add_precompiled_header _target _input) - if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU|Clang") - get_filename_component(_name ${_input} NAME) - set(_pch_header "${CMAKE_CURRENT_SOURCE_DIR}/${_input}") - set(_pch_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${_target}_pch") - set(_pchfile "${_pch_binary_dir}/${_name}") - set(_outdir "${_pch_binary_dir}/${_name}.gch") - make_directory(${_outdir}) - set(_output_cxx "${_outdir}/.c++") - set(_output_c "${_outdir}/.c") - - get_property(_sources TARGET ${_target} PROPERTY SOURCES) - foreach(_source ${_sources}) - if(_source MATCHES \\.\(c\)$ AND NOT _source_for_c_flags) - set(_source_for_c_flags "${_source}") - elseif(_source MATCHES \\.\(cc|cxx|cpp\)$ AND NOT _source_for_cpp_flags) - set(_source_for_cpp_flags "${_source}") - endif() - endforeach() - - add_custom_command( - OUTPUT "${_pchfile}" - COMMAND "${CMAKE_COMMAND}" -E copy "${_pch_header}" "${_pchfile}" - DEPENDS "${_pch_header}" - IMPLICIT_DEPENDS CXX "${_pch_header}" - IMPLICIT_DEPENDS C "${_pch_header}" - COMMENT "Updating ${_name}") - - if(_source_for_c_flags) - set(_pch_c_flags_file "${_pch_binary_dir}/compile_flags_c.rsp") - export_all_flags("${_pch_c_flags_file}" "${_source_for_c_flags}") - set(_compiler_FLAGS "@${_pch_c_flags_file}") - add_custom_command( - OUTPUT "${_output_c}" - COMMAND "${CMAKE_C_COMPILER}" ${_compiler_FLAGS} -x c-header -o "${_output_c}" -c "${_pchfile}" - DEPENDS "${_pchfile}" "${_pch_c_flags_file}" - IMPLICIT_DEPENDS C "${_pch_header}" - COMMENT "Precompiling ${_name} for ${_target} (C)") - endif() - if(_source_for_cpp_flags) - set(_pch_cpp_flags_file "${_pch_binary_dir}/compile_flags_cpp.rsp") - export_all_flags("${_pch_cpp_flags_file}" "${_source_for_cpp_flags}") - set(_compiler_FLAGS "@${_pch_cpp_flags_file}") - add_custom_command( - OUTPUT "${_output_cxx}" - COMMAND "${CMAKE_CXX_COMPILER}" ${_compiler_FLAGS} -x c++-header -o "${_output_cxx}" -c "${_pchfile}" - DEPENDS "${_pchfile}" "${_pch_cpp_flags_file}" - IMPLICIT_DEPENDS CXX "${_pch_header}" - COMMENT "Precompiling header ${_name} for ${_target} (C++)") - endif() - - foreach(_source ${_sources}) - set(_pch_compile_flags "") - - if(_source MATCHES \\.\(cc|cxx|cpp|c\)$) - get_source_file_property(_pch_compile_flags "${_source}" COMPILE_FLAGS) - if(NOT _pch_compile_flags) - set(_pch_compile_flags) - endif() - separate_arguments(_pch_compile_flags) - if(_source MATCHES \\.\(cc|cxx|cpp\)$) - list(APPEND _pch_compile_flags -include "${_pchfile}") - else() - list(APPEND _pch_compile_flags "-I${_pch_binary_dir}") - endif() - - get_source_file_property(_object_depends "${_source}" OBJECT_DEPENDS) - if(NOT _object_depends) - set(_object_depends) - endif() - list(APPEND _object_depends "${_pchfile}") - if(_source MATCHES \\.\(cc|cxx|cpp\)$) - list(APPEND _object_depends "${_output_cxx}") - else() - list(APPEND _object_depends "${_output_c}") - endif() - - combine_arguments(_pch_compile_flags) - set_source_files_properties(${_source} PROPERTIES - COMPILE_FLAGS "${_pch_compile_flags}" - OBJECT_DEPENDS "${_object_depends}") - endif() - endforeach() - - if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - if(NOT _PCH_FORCEINCLUDE) - set(_PCH_FORCEINCLUDE ON) - endif() - endif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - endif(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU|Clang") -endfunction() diff --git a/Telegram/gyp/helpers b/Telegram/gyp/helpers index 14890fb48..0d9d39ab7 160000 --- a/Telegram/gyp/helpers +++ b/Telegram/gyp/helpers @@ -1 +1 @@ -Subproject commit 14890fb48acb3ee8151f98589847ffcfd3aa7826 +Subproject commit 0d9d39ab7dd1ecd80235c647fc4bf62d580cbf6c diff --git a/Telegram/gyp/linux_glibc_wraps.gyp b/Telegram/gyp/linux_glibc_wraps.gyp deleted file mode 100644 index d40a91515..000000000 --- a/Telegram/gyp/linux_glibc_wraps.gyp +++ /dev/null @@ -1,27 +0,0 @@ -# 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 - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'linux_glibc_wraps', - 'type': 'static_library', - 'sources': [ - '<(src_loc)/platform/linux/linux_glibc_wraps.c', - ], - 'conditions': [[ '" Date: Thu, 26 Sep 2019 10:15:41 +0300 Subject: [PATCH 17/59] Remove some unused code. --- Telegram/SourceFiles/platform/linux/main_window_linux.cpp | 8 -------- Telegram/SourceFiles/platform/linux/main_window_linux.h | 5 ----- Telegram/SourceFiles/platform/mac/main_window_mac.h | 5 ----- Telegram/SourceFiles/platform/mac/main_window_mac.mm | 8 -------- 4 files changed, 26 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 0a2cad531..e3a60a92f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -585,8 +585,6 @@ void MainWindow::psFirstShow() { LOG(("Not using Unity Launcher count.")); } - psUpdateMargins(); - bool showShadows = true; show(); @@ -611,12 +609,6 @@ void MainWindow::psFirstShow() { setPositionInited(); } -void MainWindow::psInitSysMenu() { -} - -void MainWindow::psUpdateMargins() { -} - MainWindow::~MainWindow() { #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION if (_trayIcon) { diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index cea307c5e..bc7eb98c1 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -20,11 +20,6 @@ public: explicit MainWindow(not_null controller); void psFirstShow(); - void psInitSysMenu(); - void psUpdateMargins(); - - void psRefreshTaskbarIcon() { - } virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index 967ea8168..933c0e84c 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -24,11 +24,6 @@ public: explicit MainWindow(not_null controller); void psFirstShow(); - void psInitSysMenu(); - void psUpdateMargins(); - - void psRefreshTaskbarIcon() { - } bool psFilterNativeEvent(void *event); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 487970e34..a67c278e4 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -635,8 +635,6 @@ void MainWindow::updateIconCounters() { } void MainWindow::psFirstShow() { - psUpdateMargins(); - bool showShadows = true; show(); @@ -775,12 +773,6 @@ void MainWindow::psMacClearFormat() { SendKeySequence(Qt::Key_N, Qt::ControlModifier | Qt::ShiftModifier); } -void MainWindow::psInitSysMenu() { -} - -void MainWindow::psUpdateMargins() { -} - void MainWindow::updateGlobalMenuHook() { if (!App::wnd() || !positionInited()) return; From 25b6dea5e3ab3983f1a3810cf709023a26df00d9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 12:23:57 +0300 Subject: [PATCH 18/59] Build cmake 3.15.3 from source. --- docs/building-cmake.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/building-cmake.md b/docs/building-cmake.md index 0790eda0e..6bcd90d66 100644 --- a/docs/building-cmake.md +++ b/docs/building-cmake.md @@ -10,20 +10,17 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. ### Install software and required packages -You will need GCC 8.1 and CMake 3.2 installed. To install them and all the required dependencies run +You will need GCC 8.1 installed. To install them and all the required dependencies run sudo apt-get install software-properties-common -y && \ - sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev libappindicator-dev libicu-dev libdee-dev libdrm-dev dh-autoreconf autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libxcb-render-util0-dev libxcb-util0-dev libxrender-dev libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libx11-xcb-dev libffi-dev libncurses5-dev pkg-config texi2html zlib1g-dev yasm cmake xutils-dev bison python-xcbgen chrpath -y && \ - + sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev libappindicator-dev libicu-dev libdee-dev libdrm-dev dh-autoreconf autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libxcb-render-util0-dev libxcb-util0-dev libxrender-dev libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libx11-xcb-dev libffi-dev libncurses5-dev pkg-config texi2html zlib1g-dev yasm xutils-dev bison python-xcbgen chrpath gperf -y && \ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \ - sudo add-apt-repository ppa:george-edison55/cmake-3.x -y && \ sudo apt-get update && \ - sudo apt-get install gcc-8 g++-8 cmake -y && \ + sudo apt-get install gcc-8 g++-8 -y && \ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 && \ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 60 && \ sudo update-alternatives --config gcc && \ sudo add-apt-repository --remove ppa:ubuntu-toolchain-r/test -y && \ - sudo add-apt-repository --remove ppa:george-edison55/cmake-3.x -y You can set the multithreaded make parameter by running @@ -38,6 +35,13 @@ Go to ***BuildPath*** and run mkdir Libraries cd Libraries + git clone https://github.com/Kitware/CMake cmake + cd cmake + git checkout v3.15.3 + ./bootstrap + make $MAKE_THREADS_CNT + sudo make install + git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 git clone https://github.com/telegramdesktop/zlib.git From e757e6f67ba1be38efa842537ee6fa29ade50c2f Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 12:52:36 +0300 Subject: [PATCH 19/59] Move private folder. --- Telegram/Resources/langs/download.sh | 2 +- Telegram/Resources/langs/refresh.sh | 2 +- Telegram/Resources/langs/upload.sh | 2 +- Telegram/SourceFiles/_other/packer.cpp | 4 ++-- Telegram/SourceFiles/config.h | 2 +- Telegram/build/build.bat | 4 ++-- Telegram/build/build.sh | 2 +- Telegram/build/deploy.sh | 6 +++--- Telegram/build/release.py | 2 +- Telegram/build/release.sh | 2 +- Telegram/build/test_package.bat | 4 ++-- Telegram/build/updates.py | 2 +- Telegram/build/updates.sh | 2 +- Telegram/gyp/generate.py | 4 ++-- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Telegram/Resources/langs/download.sh b/Telegram/Resources/langs/download.sh index 92f1ef1db..a4da69404 100755 --- a/Telegram/Resources/langs/download.sh +++ b/Telegram/Resources/langs/download.sh @@ -5,7 +5,7 @@ pushd `dirname $0` > /dev/null FullScriptPath=`pwd` popd > /dev/null -if [ ! -d "$FullScriptPath/../../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/Resources/langs/refresh.sh b/Telegram/Resources/langs/refresh.sh index 033b37382..cd705f35f 100755 --- a/Telegram/Resources/langs/refresh.sh +++ b/Telegram/Resources/langs/refresh.sh @@ -6,7 +6,7 @@ popd > /dev/null FileName="$1" -if [ ! -d "$FullScriptPath/../../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/Resources/langs/upload.sh b/Telegram/Resources/langs/upload.sh index e20706990..fcbf4f2a0 100755 --- a/Telegram/Resources/langs/upload.sh +++ b/Telegram/Resources/langs/upload.sh @@ -5,7 +5,7 @@ pushd `dirname $0` > /dev/null FullScriptPath=`pwd` popd > /dev/null -if [ ! -d "$FullScriptPath/../../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp index 4b0df9668..d6332970d 100644 --- a/Telegram/SourceFiles/_other/packer.cpp +++ b/Telegram/SourceFiles/_other/packer.cpp @@ -35,8 +35,8 @@ w/CVnbwQOw0g5GBwwFV3r0uTTvy44xx8XXxk+Qknu4eBCsmrAFNnAgMBAAE=\n\ extern const char *PrivateKey; extern const char *PrivateBetaKey; -#include "../../../../TelegramPrivate/packer_private.h" // RSA PRIVATE KEYS for update signing -#include "../../../../TelegramPrivate/alpha_private.h" // private key for alpha version file generation +#include "../../../../DesktopPrivate/packer_private.h" // RSA PRIVATE KEYS for update signing +#include "../../../../DesktopPrivate/alpha_private.h" // private key for alpha version file generation QString countAlphaVersionSignature(quint64 version); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index d3460cc08..fb7b54e4d 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -183,7 +183,7 @@ constexpr auto ApiHash = "344583e45741c457fe1862106095a5eb"; #if (TDESKTOP_ALPHA_VERSION != 0) // Private key for downloading closed alphas. -#include "../../../TelegramPrivate/alpha_private.h" +#include "../../../DesktopPrivate/alpha_private.h" #else static const char *AlphaPrivateKey = ""; diff --git a/Telegram/build/build.bat b/Telegram/build/build.bat index f66abd7fb..d23c583ff 100644 --- a/Telegram/build/build.bat +++ b/Telegram/build/build.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion set "FullScriptPath=%~dp0" set "FullExecPath=%cd%" -if not exist "%FullScriptPath%..\..\..\TelegramPrivate" ( +if not exist "%FullScriptPath%..\..\..\DesktopPrivate" ( echo. echo This script is for building the production version of Telegram Desktop. echo. @@ -54,7 +54,7 @@ set "SetupFile=tsetup.%AppVersionStrFull%.exe" set "PortableFile=tportable.%AppVersionStrFull%.zip" set "ReleasePath=%HomePath%\..\out\Release" set "DeployPath=%ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStrFull%" -set "SignPath=%HomePath%\..\..\TelegramPrivate\Sign.bat" +set "SignPath=%HomePath%\..\..\DesktopPrivate\Sign.bat" set "BinaryName=Telegram" set "DropboxSymbolsPath=Y:\Telegram\symbols" set "FinalReleasePath=Z:\Telegram\backup" diff --git a/Telegram/build/build.sh b/Telegram/build/build.sh index 02cc9e639..02a4aa188 100755 --- a/Telegram/build/build.sh +++ b/Telegram/build/build.sh @@ -4,7 +4,7 @@ pushd `dirname $0` > /dev/null FullScriptPath=`pwd` popd > /dev/null -if [ ! -d "$FullScriptPath/../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/build/deploy.sh b/Telegram/build/deploy.sh index a1b99d31d..036b63c0f 100755 --- a/Telegram/build/deploy.sh +++ b/Telegram/build/deploy.sh @@ -4,7 +4,7 @@ pushd `dirname $0` > /dev/null FullScriptPath=`pwd` popd > /dev/null -if [ ! -d "$FullScriptPath/../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" @@ -186,7 +186,7 @@ if [ "$DeployLinux32" == "1" ]; then fi fi -$FullScriptPath/../../../TelegramPrivate/mount.sh +$FullScriptPath/../../../DesktopPrivate/mount.sh declare -a Files if [ "$DeployMac" == "1" ]; then @@ -208,7 +208,7 @@ if [ "$DeployLinux32" == "1" ]; then Files+=("tlinux32/$Linux32UpdateFile" "tlinux32/$Linux32SetupFile") fi cd $DeployPath -rsync -avR --progress ${Files[@]} "$FullScriptPath/../../../TelegramPrivate/remote/files" +rsync -avR --progress ${Files[@]} "$FullScriptPath/../../../DesktopPrivate/remote/files" echo "Version $AppVersionStrFull was deployed!" cd $FullExecPath diff --git a/Telegram/build/release.py b/Telegram/build/release.py index e1ac78055..7678adfc5 100644 --- a/Telegram/build/release.py +++ b/Telegram/build/release.py @@ -4,7 +4,7 @@ from subprocess import call from os.path import expanduser changelog_file = '../../changelog.txt' -token_file = '../../../TelegramPrivate/github-releases-token.txt' +token_file = '../../../DesktopPrivate/github-releases-token.txt' version = '' commit = '' diff --git a/Telegram/build/release.sh b/Telegram/build/release.sh index abbb04996..57be119f1 100755 --- a/Telegram/build/release.sh +++ b/Telegram/build/release.sh @@ -9,7 +9,7 @@ Param2="$2" Param3="$3" Param4="$4" -if [ ! -d "$FullScriptPath/../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/build/test_package.bat b/Telegram/build/test_package.bat index b7875c9dd..bec56f831 100644 --- a/Telegram/build/test_package.bat +++ b/Telegram/build/test_package.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion set "FullScriptPath=%~dp0" set "FullExecPath=%cd%" -if not exist "%FullScriptPath%..\..\..\TelegramPrivate" ( +if not exist "%FullScriptPath%..\..\..\DesktopPrivate" ( echo. echo This script is for building the production version of Telegram Desktop. echo. @@ -13,7 +13,7 @@ if not exist "%FullScriptPath%..\..\..\TelegramPrivate" ( ) set "HomePath=%FullScriptPath%.." -set "SignAppxPath=%HomePath%\..\..\TelegramPrivate\AppxSign.bat" +set "SignAppxPath=%HomePath%\..\..\DesktopPrivate\AppxSign.bat" set "ResourcesPath=%HomePath%\Resources" set "SolutionPath=%HomePath%\.." set "ReleasePath=%HomePath%\..\out\Debug" diff --git a/Telegram/build/updates.py b/Telegram/build/updates.py index 69a3f15f5..8ffd5e96b 100644 --- a/Telegram/build/updates.py +++ b/Telegram/build/updates.py @@ -183,7 +183,7 @@ if building: commandPath = scriptPath + '/../../out/Debug/' + outputFolder + '/command.txt' if composing: - templatePath = scriptPath + '/../../../TelegramPrivate/updates_template.txt' + templatePath = scriptPath + '/../../../DesktopPrivate/updates_template.txt' if not os.path.exists(templatePath): finish(1, 'Template file "' + templatePath + '" not found.') diff --git a/Telegram/build/updates.sh b/Telegram/build/updates.sh index 1dad4e4b9..ffb76f1aa 100755 --- a/Telegram/build/updates.sh +++ b/Telegram/build/updates.sh @@ -4,7 +4,7 @@ pushd `dirname $0` > /dev/null FullScriptPath=`pwd` popd > /dev/null -if [ ! -d "$FullScriptPath/../../../TelegramPrivate" ]; then +if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then echo "" echo "This script is for building the production version of Telegram Desktop." echo "" diff --git a/Telegram/gyp/generate.py b/Telegram/gyp/generate.py index b6c6df7b4..4ffcdd87a 100644 --- a/Telegram/gyp/generate.py +++ b/Telegram/gyp/generate.py @@ -42,9 +42,9 @@ if os.path.isfile(officialTargetFile): officialTarget = line.strip() if officialTarget != '': - officialApiIdFile = scriptPath + '/../../../TelegramPrivate/custom_api_id.h' + officialApiIdFile = scriptPath + '/../../../DesktopPrivate/custom_api_id.h' if not os.path.isfile(officialApiIdFile): - print("[ERROR] TelegramPrivate/custom_api_id.h not found.") + print("[ERROR] DesktopPrivate/custom_api_id.h not found.") finish(1) with open(officialApiIdFile, 'r') as f: for line in f: From 09a7daf164c5447e5a5da4e4e766acbb5e7506b6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 13:20:34 +0300 Subject: [PATCH 20/59] Update submodules. --- Telegram/SourceFiles/platform/mac/file_utilities_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/launcher_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/mac_touchbar.mm | 2 +- Telegram/SourceFiles/platform/mac/main_window_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/specific_mac.mm | 2 +- Telegram/SourceFiles/platform/mac/specific_mac_p.mm | 2 +- Telegram/SourceFiles/platform/win/launcher_win.cpp | 2 +- Telegram/SourceFiles/platform/win/main_window_win.h | 2 +- Telegram/SourceFiles/platform/win/specific_win.h | 2 +- Telegram/SourceFiles/platform/win/windows_app_user_model_id.h | 2 +- Telegram/SourceFiles/platform/win/windows_dlls.h | 2 +- Telegram/SourceFiles/platform/win/windows_event_filter.h | 2 +- Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp | 2 +- Telegram/SourceFiles/storage/storage_file_lock_win.cpp | 2 +- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm index 000f3db4f..42b30fdef 100644 --- a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm +++ b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/file_utilities_mac.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "lang/lang_keys.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/platform/mac/launcher_mac.mm b/Telegram/SourceFiles/platform/mac/launcher_mac.mm index 2182e97ca..28da1bf59 100644 --- a/Telegram/SourceFiles/platform/mac/launcher_mac.mm +++ b/Telegram/SourceFiles/platform/mac/launcher_mac.mm @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" #include "base/platform/base_platform_info.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "platform/platform_specific.h" #include diff --git a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm index f8869f488..0c305126a 100644 --- a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm +++ b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm @@ -28,7 +28,7 @@ #include "mainwidget.h" #include "mainwindow.h" #include "observer_peer.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "stickers.h" #include "styles/style_dialogs.h" #include "styles/style_media_player.h" diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index a67c278e4..482d12d76 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/about_box.h" #include "lang/lang_keys.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "ui/widgets/input_fields.h" #include "facades.h" #include "app.h" diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 99c1c7669..3459086fe 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "history/history.h" #include "ui/empty_userpic.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index bcc0132dc..ec7631c78 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "mainwindow.h" #include "history/history_location_manager.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "facades.h" #include diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 3b4c3ee24..82805bf8e 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" #include "platform/mac/mac_touchbar.h" -#include "base/platform/mac/base_platform_utilities_mac.h" +#include "base/platform/mac/base_utilities_mac.h" #include "base/platform/base_platform_info.h" #include "lang/lang_keys.h" #include "base/timer.h" diff --git a/Telegram/SourceFiles/platform/win/launcher_win.cpp b/Telegram/SourceFiles/platform/win/launcher_win.cpp index 8dde12456..176dd041a 100644 --- a/Telegram/SourceFiles/platform/win/launcher_win.cpp +++ b/Telegram/SourceFiles/platform/win/launcher_win.cpp @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" #include "base/platform/base_platform_info.h" -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" #include #include diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index ef5efd377..6ec9648d1 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_main_window.h" -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" #include "base/flags.h" #include diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 6be74a4e2..49a5836d6 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_specific.h" -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" namespace Data { class LocationPoint; diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h index 160693a8c..464e44041 100644 --- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h +++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" namespace Platform { namespace AppUserModelId { diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.h b/Telegram/SourceFiles/platform/win/windows_dlls.h index ee9b266a3..341da5073 100644 --- a/Telegram/SourceFiles/platform/win/windows_dlls.h +++ b/Telegram/SourceFiles/platform/win/windows_dlls.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" #include #include diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.h b/Telegram/SourceFiles/platform/win/windows_event_filter.h index 920d31caf..84ac63093 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.h +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" #include diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp index 00a73f0a0..c93a93992 100644 --- a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp +++ b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "storage/storage_clear_legacy.h" -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" namespace Storage { namespace details { diff --git a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp index e06667c38..84ada9397 100644 --- a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp +++ b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_file_lock.h" #include "platform/win/windows_dlls.h" -#include "base/platform/win/base_platform_windows_h.h" +#include "base/platform/win/base_windows_h.h" #include #include diff --git a/Telegram/lib_base b/Telegram/lib_base index d19c67201..cfc1a874e 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit d19c6720195269e252038563e370253fea968f1c +Subproject commit cfc1a874eb603c345f60be2ce98a885d9f336d14 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index c9990aee0..84d68e48c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit c9990aee058d0b321a01a3f564b6eaff886628d0 +Subproject commit 84d68e48c0808f6641b4ea7badc5a977052010a1 From 30a1bd7ba2370f68e42d2dfc17b96433fa35d243 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 13:55:35 +0300 Subject: [PATCH 21/59] Use base::call_delayed. --- Telegram/SourceFiles/apiwrap.cpp | 5 ++-- Telegram/SourceFiles/boxes/boxes.style | 4 ++-- Telegram/SourceFiles/boxes/connection_box.cpp | 3 ++- .../SourceFiles/boxes/create_poll_box.cpp | 4 ++-- .../chat_helpers/stickers_emoji_pack.cpp | 4 ++-- Telegram/SourceFiles/core/application.h | 5 ---- Telegram/SourceFiles/data/data_session.cpp | 5 ++-- Telegram/SourceFiles/facades.cpp | 7 ------ Telegram/SourceFiles/facades.h | 24 ++----------------- .../SourceFiles/history/history_widget.cpp | 3 ++- .../view/history_view_compose_controls.cpp | 3 ++- .../view/history_view_scheduled_section.cpp | 3 ++- Telegram/SourceFiles/info/info.style | 4 ++-- Telegram/SourceFiles/intro/intro.style | 4 ++-- Telegram/SourceFiles/main/main_app_config.cpp | 4 ++-- Telegram/SourceFiles/mainwindow.cpp | 3 ++- .../mtproto/dedicated_file_loader.cpp | 6 ++--- Telegram/SourceFiles/mtproto/mtp_instance.cpp | 5 ++-- .../mtproto/special_config_request.cpp | 5 ++-- .../passport/passport_form_controller.cpp | 6 ++--- .../platform/linux/file_utilities_linux.cpp | 5 ++-- .../SourceFiles/settings/settings_chat.cpp | 3 ++- .../settings/settings_information.cpp | 4 ++-- .../SourceFiles/settings/settings_main.cpp | 3 ++- .../support/support_autocomplete.cpp | 4 ++-- Telegram/SourceFiles/window/main_window.cpp | 5 ++-- .../window/notifications_manager_default.cpp | 5 ++-- .../window/themes/window_theme_editor.cpp | 11 +++++---- .../themes/window_theme_editor_block.cpp | 4 ++-- 29 files changed, 68 insertions(+), 83 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index f2f8bd648..b33afa76a 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "base/openssl_help.h" #include "base/unixtime.h" +#include "base/call_delayed.h" #include "observer_peer.h" #include "lang/lang_keys.h" #include "mainwindow.h" @@ -361,7 +362,7 @@ void ApiWrap::requestTermsUpdate() { } const auto now = crl::now(); if (_termsUpdateSendAt && now < _termsUpdateSendAt) { - App::CallDelayed(_termsUpdateSendAt - now, _session, [=] { + base::call_delayed(_termsUpdateSendAt - now, _session, [=] { requestTermsUpdate(); }); return; @@ -1283,7 +1284,7 @@ void ApiWrap::gotUserFull( const auto &d = result.c_userFull(); if (user == _session->user() && !_session->validateSelf(d.vuser())) { constexpr auto kRequestUserAgainTimeout = crl::time(10000); - App::CallDelayed(kRequestUserAgainTimeout, _session, [=] { + base::call_delayed(kRequestUserAgainTimeout, _session, [=] { requestFullPeer(user); }); return; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 69b46a1d7..14d65894a 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -780,8 +780,8 @@ themesMenuToggle: IconButton(defaultIconButton) { width: 44px; height: 44px; - icon: icon {{ "title_menu_dots", menuIconFg }}; - iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; + icon: menuToggleIcon; + iconOver: menuToggleIconOver; iconPosition: point(-1px, -1px); rippleAreaPosition: point(4px, 4px); diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 316caefd3..a4873f019 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "storage/localstorage.h" #include "base/qthelp_url.h" +#include "base/call_delayed.h" #include "core/application.h" #include "main/main_account.h" #include "ui/widgets/checkbox.h" @@ -1437,7 +1438,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) { ProxiesBoxController::~ProxiesBoxController() { if (_saveTimer.isActive()) { - App::CallDelayed( + base::call_delayed( kSaveSettingsDelayedTimeout, QCoreApplication::instance(), [] { Local::writeSettings(); }); diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index e93824ba2..ad9a1db47 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "base/unique_qptr.h" #include "base/event_filter.h" -#include "facades.h" +#include "base/call_delayed.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_settings.h" @@ -400,7 +400,7 @@ void Options::Option::destroy(FnMut done) { return; } _field->hide(anim::type::normal); - App::CallDelayed( + base::call_delayed( st::slideWrapDuration * 2, _field.get(), std::move(done)); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 8a7128c99..b583c61bf 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -16,9 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/data_document.h" +#include "base/call_delayed.h" #include "apiwrap.h" #include "app.h" -#include "facades.h" #include "styles/style_history.h" #include @@ -601,7 +601,7 @@ base::flat_map> EmojiPack::collectStickers( } void EmojiPack::refreshDelayed() { - App::CallDelayed(details::kRefreshTimeout, _session, [=] { + base::call_delayed(details::kRefreshTimeout, _session, [=] { refresh(); }); } diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index e9c284414..6129e71bd 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -220,10 +220,6 @@ public: void call_handleDelayedPeerUpdates(); void call_handleObservables(); - void callDelayed(int duration, FnMut &&lambda) { - _callDelayedTimer.call(duration, std::move(lambda)); - } - protected: bool eventFilter(QObject *object, QEvent *event) override; @@ -283,7 +279,6 @@ private: rpl::event_stream _termsLockChanges; std::unique_ptr _termsLock; - base::DelayedCallTimer _callDelayedTimer; base::Timer _saveSettingsTimer; struct LeaveSubscription { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 1d1c27f06..540979331 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" +#include "base/call_delayed.h" #include "facades.h" #include "app.h" #include "styles/style_boxes.h" // st::backgroundSize @@ -933,7 +934,7 @@ void Session::suggestStartExport() { ? 0 : (_exportAvailableAt - now); if (left) { - App::CallDelayed( + base::call_delayed( std::min(left + 5, 3600) * crl::time(1000), _session, [=] { suggestStartExport(); }); @@ -991,7 +992,7 @@ void Session::rememberPassportCredentials( _passportCredentials = std::make_unique( std::move(data), ++generation); - App::CallDelayed(rememberFor, _session, [=, check = generation] { + base::call_delayed(rememberFor, _session, [=, check = generation] { if (_passportCredentials && _passportCredentials->second == check) { forgetPassportCredentials(); } diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index cb8cd9262..19419045a 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -33,13 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" namespace App { -namespace internal { - -void CallDelayed(int duration, FnMut &&lambda) { - Core::App().callDelayed(duration, std::move(lambda)); -} - -} // namespace internal void sendBotCommand(PeerData *peer, UserData *bot, const QString &cmd, MsgId replyTo) { if (auto m = App::main()) { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 42c8f1c28..951764a2a 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/type_traits.h" #include "base/observer.h" +#include "base/call_delayed.h" #include "ui/effects/animation_value.h" class History; @@ -28,27 +29,6 @@ class ItemBase; } // namespace InlineBots namespace App { -namespace internal { - -void CallDelayed(int duration, FnMut &&lambda); - -} // namespace internal - -template -inline void CallDelayed( - int duration, - crl::guarded_wrap &&guarded) { - return internal::CallDelayed( - duration, - std::move(guarded)); -} - -template -inline void CallDelayed(int duration, Guard &&object, Lambda &&lambda) { - return internal::CallDelayed(duration, crl::guard( - std::forward(object), - std::forward(lambda))); -} template [[nodiscard]] inline auto LambdaDelayed(int duration, Guard &&object, Lambda &&lambda) { @@ -57,7 +37,7 @@ template std::forward(lambda)); return [saved = std::move(guarded), duration] { auto copy = saved; - internal::CallDelayed(duration, std::move(copy)); + base::call_delayed(duration, std::move(copy)); }; } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 20fbd79a0..af5bf73b7 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_result.h" #include "base/event_filter.h" #include "base/unixtime.h" +#include "base/call_delayed.h" #include "data/data_drafts.h" #include "data/data_session.h" #include "data/data_web_page.h" @@ -3755,7 +3756,7 @@ void HistoryWidget::updateSendButtonType() { && (type == Type::Send || type == Type::Record)); if (delay != 0) { - App::CallDelayed( + base::call_delayed( kRefreshSlowmodeLabelTimeout, this, [=] { updateSendButtonType(); }); diff --git a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp index 7bb12d966..b6c27a34e 100644 --- a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "lang/lang_keys.h" #include "base/event_filter.h" +#include "base/call_delayed.h" #include "base/qt_signal_producer.h" #include "history/history.h" #include "chat_helpers/tabbed_panel.h" @@ -281,7 +282,7 @@ void ComposeControls::updateSendButtonType() { // && (type == Type::Send || type == Type::Record)); //if (delay != 0) { - // App::CallDelayed( + // base::call_delayed( // kRefreshSlowmodeLabelTimeout, // this, // [=] { updateSendButtonType(); }); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 6b9d7fbd3..93e61fa71 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "base/event_filter.h" +#include "base/call_delayed.h" #include "core/file_utilities.h" #include "main/main_session.h" #include "data/data_session.h" @@ -143,7 +144,7 @@ void ScheduledWidget::setupComposeControls() { return !_choosingAttach; }) | rpl::start_with_next([=] { _choosingAttach = true; - App::CallDelayed( + base::call_delayed( st::historyAttach.ripple.hideDuration, this, [=] { _choosingAttach = false; chooseAttach(); }); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index fdee4ebae..9867eaa2b 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -753,8 +753,8 @@ topBarCallSkip: -1px; topBarMenuToggle: IconButton(topBarSearch) { width: 44px; - icon: icon {{ "title_menu_dots", menuIconFg }}; - iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; + icon: menuToggleIcon; + iconOver: menuToggleIconOver; iconPosition: point(16px, 17px); rippleAreaPosition: point(0px, 7px); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index be68680a9..fdae237d7 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -150,8 +150,8 @@ introBackButton: IconButton(defaultIconButton) { width: 56px; height: 56px; - icon: icon {{ "box_button_back", menuIconFg }}; - iconOver: icon {{ "box_button_back", menuIconFgOver }}; + icon: backButtonIcon; + iconOver: backButtonIconOver; rippleAreaPosition: point(8px, 8px); rippleAreaSize: 40px; diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp index d6684a6a7..2f6d76bb1 100644 --- a/Telegram/SourceFiles/main/main_app_config.cpp +++ b/Telegram/SourceFiles/main/main_app_config.cpp @@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_app_config.h" #include "main/main_session.h" +#include "base/call_delayed.h" #include "apiwrap.h" -#include "facades.h" namespace Main { namespace { @@ -44,7 +44,7 @@ void AppConfig::refresh() { } void AppConfig::refreshDelayed() { - App::CallDelayed(kRefreshTimeout, _session, [=] { + base::call_delayed(kRefreshTimeout, _session, [=] { refresh(); }); } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index ae1af98e0..b6d9734de 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_intro.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" +#include "base/call_delayed.h" #include "window/notifications_manager.h" #include "window/themes/window_theme.h" #include "window/themes/window_theme_warning.h" @@ -689,7 +690,7 @@ void MainWindow::fixOrder() { void MainWindow::showFromTray(QSystemTrayIcon::ActivationReason reason) { if (reason != QSystemTrayIcon::Context) { - App::CallDelayed(1, this, [this] { + base::call_delayed(1, this, [this] { updateTrayMenu(); updateGlobalMenu(); }); diff --git a/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp b/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp index b66eb6f44..543418a20 100644 --- a/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp +++ b/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp @@ -8,9 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/dedicated_file_loader.h" #include "main/main_session.h" -#include "core/application.h" #include "main/main_account.h" // Account::sessionChanges. -#include "facades.h" +#include "core/application.h" +#include "base/call_delayed.h" namespace MTP { namespace { @@ -321,7 +321,7 @@ void DedicatedLoader::sendRequest() { _offset += kChunkSize; if (_requests.size() < kRequestsCount) { - App::CallDelayed(kNextRequestDelay, this, [=] { sendRequest(); }); + base::call_delayed(kNextRequestDelay, this, [=] { sendRequest(); }); } } diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index c51ea664b..87f5b4f88 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -21,9 +21,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "apiwrap.h" #include "core/application.h" -#include "base/unixtime.h" #include "lang/lang_instance.h" #include "lang/lang_cloud_manager.h" +#include "base/unixtime.h" +#include "base/call_delayed.h" #include "base/timer.h" #include "facades.h" @@ -450,7 +451,7 @@ void Instance::Private::requestConfigIfOld() { void Instance::Private::requestConfigIfExpired() { const auto requestIn = (_configExpiresAt - crl::now()); if (requestIn > 0) { - App::CallDelayed( + base::call_delayed( std::min(requestIn, 3600 * crl::time(1000)), _instance, [=] { requestConfigIfExpired(); }); diff --git a/Telegram/SourceFiles/mtproto/special_config_request.cpp b/Telegram/SourceFiles/mtproto/special_config_request.cpp index 264fa8fa7..362a21d7a 100644 --- a/Telegram/SourceFiles/mtproto/special_config_request.cpp +++ b/Telegram/SourceFiles/mtproto/special_config_request.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/auth_key.h" #include "base/unixtime.h" #include "base/openssl_help.h" +#include "base/call_delayed.h" #include "facades.h" #include @@ -415,7 +416,7 @@ void SpecialConfigRequest::sendNextRequest() { const auto attempt = _attempts.back(); _attempts.pop_back(); if (!_attempts.empty()) { - App::CallDelayed(kSendNextTimeout, this, [=] { + base::call_delayed(kSendNextTimeout, this, [=] { sendNextRequest(); }); } @@ -778,7 +779,7 @@ void DomainResolver::sendNextRequest(const AttemptKey &key) { list.pop_back(); if (!list.empty()) { - App::CallDelayed(kSendNextTimeout, &attempts.guard, [=] { + base::call_delayed(kSendNextTimeout, &attempts.guard, [=] { sendNextRequest(key); }); } diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 1cb9206e4..d43349366 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/openssl_help.h" #include "base/qthelp_url.h" #include "base/unixtime.h" +#include "base/call_delayed.h" #include "data/data_session.h" #include "data/data_user.h" #include "mainwindow.h" @@ -28,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "storage/file_upload.h" #include "storage/file_download.h" -#include "facades.h" #include "app.h" #include @@ -756,7 +756,7 @@ std::vector> FormController::submitGetErrors() { _view->showToast(tr::lng_passport_success(tr::now)); - App::CallDelayed( + base::call_delayed( (st::toastFadeInDuration + Ui::Toast::kDefaultDuration + st::toastFadeOutDuration), @@ -2669,7 +2669,7 @@ void FormController::cancelSure() { UrlClickHandler::Open(url); } const auto timeout = _view->closeGetDuration(); - App::CallDelayed(timeout, this, [=] { + base::call_delayed(timeout, this, [=] { _controller->clearPassportForm(); }); } diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 9a2fbb2e0..a09aff7ce 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -7,16 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/file_utilities_linux.h" -#include #include "platform/linux/linux_libs.h" #include "platform/linux/linux_gdk_helper.h" #include "core/application.h" #include "mainwindow.h" #include "boxes/abstract_box.h" #include "storage/localstorage.h" +#include "base/call_delayed.h" #include "facades.h" #include +#include QStringList qt_make_filter_list(const QString &filter); @@ -419,7 +420,7 @@ int GtkFileDialog::exec() { show(); if (const auto parent = parentWidget()) { - App::CallDelayed(200, parent, [=] { + base::call_delayed(200, parent, [=] { parent->activateWindow(); }); } diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 29c5fbbed..9b9518d70 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "chat_helpers/emoji_sets_manager.h" #include "base/platform/base_platform_info.h" +#include "base/call_delayed.h" #include "support/support_common.h" #include "support/support_templates.h" #include "main/main_session.h" @@ -809,7 +810,7 @@ void SetupExport( )->addClickHandler([=] { const auto session = &controller->session(); Ui::hideSettingsAndLayer(); - App::CallDelayed( + base::call_delayed( st::boxDuration, session, [=] { session->data().startExport(); }); diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp index dc8342008..bffb3c547 100644 --- a/Telegram/SourceFiles/settings/settings_information.cpp +++ b/Telegram/SourceFiles/settings/settings_information.cpp @@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "apiwrap.h" #include "core/file_utilities.h" -#include "facades.h" +#include "base/call_delayed.h" #include "app.h" #include "styles/style_layers.h" #include "styles/style_settings.h" @@ -367,7 +367,7 @@ BioManager SetupBio( ) | rpl::start_with_next([=](bool changed) { if (changed) { const auto saved = *generation = std::abs(*generation) + 1; - App::CallDelayed(kSaveBioTimeout, bio, [=] { + base::call_delayed(kSaveBioTimeout, bio, [=] { if (*generation == saved) { save(nullptr); *generation = 0; diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 331a1ac16..685ee62ed 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "window/window_session_controller.h" #include "core/file_utilities.h" +#include "base/call_delayed.h" #include "facades.h" #include "app.h" #include "styles/style_settings.h" @@ -176,7 +177,7 @@ void SetupInterfaceScale( App::restart(); }); const auto cancelled = crl::guard(button, [=] { - App::CallDelayed( + base::call_delayed( st::defaultSettingsSlider.duration, button, [=] { (*setScale)(cConfigScale()); }); diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp index 844b02593..ddc65146d 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.cpp +++ b/Telegram/SourceFiles/support/support_autocomplete.cpp @@ -19,9 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "data/data_session.h" #include "base/unixtime.h" +#include "base/call_delayed.h" #include "main/main_session.h" #include "apiwrap.h" -#include "facades.h" #include "styles/style_chat_helpers.h" #include "styles/style_window.h" #include "styles/style_layers.h" @@ -426,7 +426,7 @@ void Autocomplete::setupContent() { inner->activated() | rpl::start_with_next(submit, lifetime()); connect(input, &Ui::InputField::blurred, [=] { - App::CallDelayed(10, this, [=] { + base::call_delayed(10, this, [=] { if (!input->hasFocus()) { deactivate(); } diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index a984b487a..c4d4802f8 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "base/crc32hash.h" +#include "base/call_delayed.h" #include "ui/toast/toast.h" #include "ui/ui_utility.h" #include "apiwrap.h" @@ -348,7 +349,7 @@ void MainWindow::handleActiveChanged() { if (isActiveWindow()) { Core::App().checkMediaViewActivation(); } - App::CallDelayed(1, this, [this] { + base::call_delayed(1, this, [this] { updateTrayMenu(); handleActiveChangedHook(); }); @@ -590,7 +591,7 @@ void MainWindow::reActivateWindow() { } }; crl::on_main(this, reActivate); - App::CallDelayed(200, this, reActivate); + base::call_delayed(200, this, reActivate); #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index e0ba399a1..5d4239515 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "platform/platform_specific.h" +#include "base/call_delayed.h" #include "facades.h" #include "app.h" #include "styles/style_dialogs.h" @@ -402,7 +403,7 @@ void Widget::destroyDelayed() { _deleted = true; // Ubuntu has a lag if a fully transparent widget is destroyed immediately. - App::CallDelayed(1000, this, [this] { + base::call_delayed(1000, this, [this] { manager()->removeWidget(this); }); } @@ -433,7 +434,7 @@ bool Widget::shiftAnimationCallback(crl::time now) { void Widget::hideSlow() { if (anim::Disabled()) { _hiding = true; - App::CallDelayed( + base::call_delayed( st::notifySlowHide, this, [=, guard = _hidingDelayed.make_guard()] { diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 769698566..c9f2f332b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "base/parse_helper.h" #include "base/zlib_help.h" +#include "base/call_delayed.h" #include "core/file_utilities.h" #include "core/application.h" #include "boxes/edit_color_box.h" @@ -419,7 +420,7 @@ Editor::Inner::Inner(QWidget *parent, const QString &path) : TWidget(parent) if (update.type == BackgroundUpdate::Type::TestingTheme) { Revert(); - App::CallDelayed(st::slideDuration, this, [] { + base::call_delayed(st::slideDuration, this, [] { Ui::show(Box( tr::lng_theme_editor_cant_change_theme(tr::now))); }); @@ -674,7 +675,9 @@ Editor::Editor( }); }); _inner->setFocusCallback([this] { - App::CallDelayed(2 * st::boxDuration, this, [this] { _select->setInnerFocus(); }); + base::call_delayed(2 * st::boxDuration, this, [this] { + _select->setInnerFocus(); + }); }); _inner->setScrollCallback([this](int top, int bottom) { _scroll->scrollToY(top, bottom); @@ -720,12 +723,12 @@ void Editor::showMenu() { _menuToggle->installEventFilter(_menu); _menu->addAction(tr::lng_theme_editor_menu_export(tr::now), [=] { - App::CallDelayed(st::defaultRippleAnimation.hideDuration, this, [=] { + base::call_delayed(st::defaultRippleAnimation.hideDuration, this, [=] { exportTheme(); }); }); _menu->addAction(tr::lng_theme_editor_menu_import(tr::now), [=] { - App::CallDelayed(st::defaultRippleAnimation.hideDuration, this, [=] { + base::call_delayed(st::defaultRippleAnimation.hideDuration, this, [=] { importTheme(); }); }); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index 5922ddb7e..9995ae4f9 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "boxes/edit_color_box.h" #include "lang/lang_keys.h" -#include "facades.h" +#include "base/call_delayed.h" namespace Window { namespace Theme { @@ -522,7 +522,7 @@ void EditorBlock::mouseReleaseEvent(QMouseEvent *e) { if (_context->box) { chooseRow(); } else if (_selected >= 0) { - App::CallDelayed(st::defaultRippleAnimation.hideDuration, this, [this, index = findRowIndex(&rowAtIndex(_selected))] { + base::call_delayed(st::defaultRippleAnimation.hideDuration, this, [this, index = findRowIndex(&rowAtIndex(_selected))] { if (index >= 0 && index < _data.size()) { activateRow(_data[index]); } From 7bb23519f99093355c06e6eba503f1296cc6f0c3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 14:16:14 +0300 Subject: [PATCH 22/59] Use base::Platform::ShowInFolder. --- .../platform/linux/file_utilities_linux.cpp | 28 ++----------------- .../platform/mac/file_utilities_mac.mm | 9 ++---- .../platform/win/file_utilities_win.cpp | 12 ++------ 3 files changed, 6 insertions(+), 43 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index a09aff7ce..823cccaaf 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "boxes/abstract_box.h" #include "storage/localstorage.h" +#include "base/platform/base_platform_file_utilities.h" #include "base/call_delayed.h" #include "facades.h" @@ -56,32 +57,7 @@ QByteArray EscapeShell(const QByteArray &content) { void UnsafeShowInFolder(const QString &filepath) { // Hide mediaview to make other apps visible. Ui::hideLayer(anim::type::instant); - - auto absolutePath = QFileInfo(filepath).absoluteFilePath(); - QProcess process; - process.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory"); - process.waitForFinished(); - auto output = QString::fromLatin1(process.readLine().simplified()); - auto command = qsl("xdg-open"); - auto arguments = QStringList(); - if (output == qstr("dolphin.desktop") || output == qstr("org.kde.dolphin.desktop")) { - command = qsl("dolphin"); - arguments << "--select" << absolutePath; - } else if (output == qstr("nautilus.desktop") || output == qstr("org.gnome.Nautilus.desktop") || output == qstr("nautilus-folder-handler.desktop")) { - command = qsl("nautilus"); - arguments << absolutePath; - } else if (output == qstr("nemo.desktop")) { - command = qsl("nemo"); - arguments << "--no-desktop" << absolutePath; - } else if (output == qstr("konqueror.desktop") || output == qstr("kfmclient_dir.desktop")) { - command = qsl("konqueror"); - arguments << "--select" << absolutePath; - } else { - arguments << QFileInfo(filepath).absoluteDir().absolutePath(); - } - if (!process.startDetached(command, arguments)) { - LOG(("Failed to launch '%1 %2'").arg(command).arg(arguments.join(' '))); - } + base::Platform::ShowInFolder(filepath); } } // namespace File diff --git a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm index 42b30fdef..81f924734 100644 --- a/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm +++ b/Telegram/SourceFiles/platform/mac/file_utilities_mac.mm @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/mac/file_utilities_mac.h" #include "base/platform/mac/base_utilities_mac.h" +#include "base/platform/base_platform_file_utilities.h" #include "lang/lang_keys.h" #include "styles/style_window.h" @@ -573,13 +574,7 @@ void UnsafeLaunch(const QString &filepath) { } void UnsafeShowInFolder(const QString &filepath) { - auto folder = QFileInfo(filepath).absolutePath(); - - @autoreleasepool { - - [[NSWorkspace sharedWorkspace] selectFile:Q2NSString(filepath) inFileViewerRootedAtPath:Q2NSString(folder)]; - - } + base::Platform::ShowInFolder(filepath); } } // namespace File diff --git a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp index a2bf9710c..5c3b13ba7 100644 --- a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp +++ b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "storage/localstorage.h" #include "platform/win/windows_dlls.h" +#include "base/platform/base_platform_file_utilities.h" #include "lang/lang_keys.h" #include "core/application.h" #include "core/crash_reports.h" @@ -260,16 +261,7 @@ void UnsafeLaunch(const QString &filepath) { } void UnsafeShowInFolder(const QString &filepath) { - auto nativePath = QDir::toNativeSeparators(filepath); - auto wstringPath = nativePath.toStdWString(); - if (auto pidl = ILCreateFromPathW(wstringPath.c_str())) { - SHOpenFolderAndSelectItems(pidl, 0, nullptr, 0); - ILFree(pidl); - } else { - auto pathEscaped = nativePath.replace('"', qsl("\"\"")); - auto wstringParam = (qstr("/select,") + pathEscaped).toStdWString(); - ShellExecute(0, 0, L"explorer", wstringParam.c_str(), 0, SW_SHOWNORMAL); - } + base::Platform::ShowInFolder(filepath); } void PostprocessDownloaded(const QString &filepath) { From bc2a0fb505447c0eefe3d74efd9ccc21876781f0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Sep 2019 17:26:36 +0300 Subject: [PATCH 23/59] Use TL code generator from lib_tl. --- .gitmodules | 3 + Telegram/Resources/tl/mtproto.tl | 10 +- .../codegen/scheme/codegen_scheme.py | 1237 ++--------------- Telegram/SourceFiles/mtproto/core_types.h | 630 +-------- .../SourceFiles/settings/settings_codes.cpp | 1 + Telegram/gyp/lib_scheme.gyp | 5 +- Telegram/lib_tl | 1 + 7 files changed, 154 insertions(+), 1733 deletions(-) create mode 160000 Telegram/lib_tl diff --git a/.gitmodules b/.gitmodules index 2e49012be..2876230de 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "Telegram/lib_lottie"] path = Telegram/lib_lottie url = https://github.com/desktop-app/lib_lottie.git +[submodule "Telegram/lib_tl"] + path = Telegram/lib_tl + url = https://github.com/desktop-app/lib_tl.git diff --git a/Telegram/Resources/tl/mtproto.tl b/Telegram/Resources/tl/mtproto.tl index 1b26ca9a2..83a770292 100644 --- a/Telegram/Resources/tl/mtproto.tl +++ b/Telegram/Resources/tl/mtproto.tl @@ -1,6 +1,14 @@ // Core types (no need to gen) -//vector#1cb5c415 {t:Type} # [ t ] = Vector t; +int ? = Int; +long ? = Long; +double ? = Double; +string ? = String; + +vector {t:Type} # [ t ] = Vector t; + +int128 4*[ int ] = Int128; +int256 8*[ int ] = Int256; /////////////////////////////// /// Authorization key creation diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py index 4159cdbaf..08e546979 100644 --- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py +++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py @@ -7,1155 +7,88 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL ''' import glob, re, binascii, os, sys -input_files = [] -output_path = '' -next_output_path = False -for arg in sys.argv[1:]: - if next_output_path: - next_output_path = False - output_path = arg - elif arg == '-o': - next_output_path = True - elif re.match(r'^-o(.+)', arg): - output_path = arg[2:] - else: - input_files.append(arg) - -if len(input_files) == 0: - print('Input file required.') - sys.exit(1) -if output_path == '': - print('Output path required.') - sys.exit(1) - -output_header = output_path + '/scheme.h' -output_source = output_path + '/scheme.cpp' - -# define some checked flag conversions -# the key flag type should be a subset of the value flag type -# with exact the same names, then the key flag can be implicitly -# casted to the value flag type -parentFlags = {}; -parentFlagsList = []; -def addChildParentFlags(child, parent): - parentFlagsList.append(child); - parentFlags[child] = parent; -addChildParentFlags('MTPDmessageService', 'MTPDmessage'); -addChildParentFlags('MTPDupdateShortMessage', 'MTPDmessage'); -addChildParentFlags('MTPDupdateShortChatMessage', 'MTPDmessage'); -addChildParentFlags('MTPDupdateShortSentMessage', 'MTPDmessage'); -addChildParentFlags('MTPDreplyKeyboardHide', 'MTPDreplyKeyboardMarkup'); -addChildParentFlags('MTPDreplyKeyboardForceReply', 'MTPDreplyKeyboardMarkup'); -addChildParentFlags('MTPDinputPeerNotifySettings', 'MTPDpeerNotifySettings'); -addChildParentFlags('MTPDpeerNotifySettings', 'MTPDinputPeerNotifySettings'); -addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel'); -addChildParentFlags('MTPDdialogFolder', 'MTPDdialog'); - -# this is a map (key flags -> map (flag name -> flag bit)) -# each key flag of parentFlags should be a subset of the value flag here -parentFlagsCheck = {}; - -countedTypeIdExceptions = {}; -countedTypeIdExceptions['channel#c88974ac'] = True -countedTypeIdExceptions['ipPortSecret#37982646'] = True -countedTypeIdExceptions['accessPointRule#4679b65f'] = True -countedTypeIdExceptions['help.configSimple#5a592a6c'] = True - -renamedTypes = {}; -renamedTypes['passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'] = 'passwordKdfAlgoModPow'; - -lines = []; -layer = ''; -layerIndex = 0; -funcs = 0 -types = 0; -consts = 0 -funcsNow = 0 -enums = []; -funcsDict = {}; -funcsList = []; -typesDict = {}; -TypesDict = {}; -typesList = []; -boxed = {}; -funcsText = ''; -typesText = ''; -dataTexts = ''; -creatorProxyText = ''; -factories = ''; -flagOperators = ''; -methods = ''; -inlineMethods = ''; -visitorMethods = ''; -textSerializeInit = ''; -textSerializeMethods = ''; -forwards = ''; -forwTypedefs = ''; - -for input_file in input_files: - lines.append('---types---') - with open(input_file) as f: - for line in f: - layerline = re.match(r'// LAYER (\d+)', line) - if (layerline): - layerIndex = int(layerline.group(1)); - layer = 'inline constexpr mtpPrime CurrentLayer = mtpPrime(' + str(layerIndex) + ');'; - else: - lines.append(line); - -for line in lines: - nocomment = re.match(r'^(.*?)//', line) - if (nocomment): - line = nocomment.group(1); - if (re.match(r'\-\-\-functions\-\-\-', line)): - funcsNow = 1; - continue; - if (re.match(r'\-\-\-types\-\-\-', line)): - funcsNow = 0; - continue; - if (re.match(r'^\s*$', line)): - continue; - - nametype = re.match(r'([a-zA-Z\.0-9_]+)(#[0-9a-f]+)?([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line); - if (not nametype): - if (not re.match(r'vector#1cb5c415 \{t:Type\} # \[ t \] = Vector t;', line)): - print('Bad line found: ' + line); - sys.exit(1); - continue; - - originalname = nametype.group(1); - name = originalname; - if (name in renamedTypes): - name = renamedTypes[name]; - nameInd = name.find('.'); - if (nameInd >= 0): - Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:]; - name = name.replace('.', '_'); - else: - Name = name[0:1].upper() + name[1:]; - typeid = nametype.group(2); - if (typeid and len(typeid) > 0): - typeid = typeid[1:]; # Skip '#' - while (typeid and len(typeid) > 0 and typeid[0] == '0'): - typeid = typeid[1:]; - - cleanline = nametype.group(1) + nametype.group(3) + '= ' + nametype.group(4); - cleanline = re.sub(r' [a-zA-Z0-9_]+\:flags\.[0-9]+\?true', '', cleanline); - cleanline = cleanline.replace('<', ' ').replace('>', ' ').replace(' ', ' '); - cleanline = re.sub(r'^ ', '', cleanline); - cleanline = re.sub(r' $', '', cleanline); - cleanline = cleanline.replace(':bytes ', ':string '); - cleanline = cleanline.replace('?bytes ', '?string '); - cleanline = cleanline.replace('{', ''); - cleanline = cleanline.replace('}', ''); - countTypeId = binascii.crc32(binascii.a2b_qp(cleanline)); - if (countTypeId < 0): - countTypeId += 2 ** 32; - countTypeId = re.sub(r'^0x|L$', '', hex(countTypeId)); - if (typeid and len(typeid) > 0): - typeid = typeid; - if (typeid != countTypeId): - key = originalname + '#' + typeid; - if (not key in countedTypeIdExceptions): - print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')'); - continue; - else: - typeid = countTypeId; - - typeid = '0x' + typeid; - - params = nametype.group(3); - restype = nametype.group(4); - if (restype.find('<') >= 0): - templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', restype); - if (templ): - vectemplate = templ.group(2); - if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+\.[A-Z]', vectemplate)): - restype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string' or vectemplate == 'bytes'): - restype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - else: - foundmeta = ''; - for metatype in typesDict: - for typedata in typesDict[metatype]: - if (typedata[0] == vectemplate): - foundmeta = metatype; - break; - if (len(foundmeta) > 0): - break; - if (len(foundmeta) > 0): - ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>'; - else: - print('Bad vector param: ' + vectemplate); - sys.exit(1); - else: - print('Bad template type: ' + restype); - sys.exit(1); - resType = restype.replace('.', '_'); - if (restype.find('.') >= 0): - parts = re.match(r'([a-z]+)\.([A-Z][A-Za-z0-9<>\._]+)', restype) - if (parts): - restype = parts.group(1) + '_' + parts.group(2)[0:1].lower() + parts.group(2)[1:]; - else: - print('Bad result type name with dot: ' + restype); - sys.exit(1); - else: - if (re.match(r'^[A-Z]', restype)): - restype = restype[:1].lower() + restype[1:]; - else: - print('Bad result type name: ' + restype); - sys.exit(1); - - boxed[resType] = restype; - boxed[Name] = name; - - enums.append('\tmtpc_' + name + ' = ' + typeid); - - paramsList = params.strip().split(' '); - prms = {}; - conditions = {}; - trivialConditions = {}; # true type - prmsList = []; - conditionsList = []; - isTemplate = hasFlags = hasTemplate = ''; - for param in paramsList: - if (re.match(r'^\s*$', param)): - continue; - templ = re.match(r'^{([A-Za-z]+):Type}$', param); - if (templ): - hasTemplate = templ.group(1); - continue; - pnametype = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param); - if (not pnametype): - print('Bad param found: "' + param + '" in line: ' + line); - sys.exit(1); - pname = pnametype.group(1); - ptypewide = pnametype.group(2); - if (re.match(r'^!([a-zA-Z]+)$', ptypewide)): - if ('!' + hasTemplate == ptypewide): - isTemplate = pname; - ptype = 'TQueryType'; - else: - print('Bad template param name: "' + param + '" in line: ' + line); - sys.exit(1); - elif (ptypewide == '#'): - hasFlags = pname; - if funcsNow: - ptype = 'flags'; - else: - ptype = 'flags'; - else: - ptype = ptypewide; - if (ptype.find('?') >= 0): - pmasktype = re.match(r'([a-z_][a-z0-9_]*)\.([0-9]+)\?([A-Za-z0-9<>\._]+)', ptype); - if (not pmasktype or pmasktype.group(1) != hasFlags): - print('Bad param found: "' + param + '" in line: ' + line); - sys.exit(1); - ptype = pmasktype.group(3); - if (ptype.find('<') >= 0): - templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype); - if (templ): - vectemplate = templ.group(2); - if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+\.[A-Z]', vectemplate)): - ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string' or vectemplate == 'bytes'): - ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - else: - foundmeta = ''; - for metatype in typesDict: - for typedata in typesDict[metatype]: - if (typedata[0] == vectemplate): - foundmeta = metatype; - break; - if (len(foundmeta) > 0): - break; - if (len(foundmeta) > 0): - ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>'; - else: - print('Bad vector param: ' + vectemplate); - sys.exit(1); - else: - print('Bad template type: ' + ptype); - sys.exit(1); - if (not pname in conditions): - conditionsList.append(pname); - conditions[pname] = pmasktype.group(2); - if (ptype == 'true'): - trivialConditions[pname] = 1; - elif (ptype.find('<') >= 0): - templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype); - if (templ): - vectemplate = templ.group(2); - if (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+\.[A-Z]', vectemplate)): - ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - elif (vectemplate == 'int' or vectemplate == 'long' or vectemplate == 'string' or vectemplate == 'bytes'): - ptype = templ.group(1) + 'MTP' + vectemplate.replace('.', '_') + '>'; - else: - foundmeta = ''; - for metatype in typesDict: - for typedata in typesDict[metatype]: - if (typedata[0] == vectemplate): - foundmeta = metatype; - break; - if (len(foundmeta) > 0): - break; - if (len(foundmeta) > 0): - ptype = templ.group(1) + 'MTP' + foundmeta.replace('.', '_') + '>'; - else: - print('Bad vector param: ' + vectemplate); - sys.exit(1); - else: - print('Bad template type: ' + ptype); - sys.exit(1); - prmsList.append(pname); - prms[pname] = ptype.replace('.', '_'); - - if (isTemplate == '' and resType == 'X'): - print('Bad response type "X" in "' + name +'" in line: ' + line); - sys.exit(1); - - if funcsNow: - methodBodies = '' - if (isTemplate != ''): - funcsText += '\ntemplate '; - funcsText += '\nclass MTP' + name + ' { // RPC method \'' + nametype.group(1) + '\'\n'; # class - - funcsText += 'public:\n'; - - prmsStr = []; - prmsInit = []; - prmsNames = []; - if (hasFlags != ''): - funcsText += '\tenum class Flag : uint32 {\n'; - maxbit = 0; - parentFlagsCheck['MTP' + name] = {}; - for paramName in conditionsList: - funcsText += '\t\tf_' + paramName + ' = (1U << ' + conditions[paramName] + '),\n'; - parentFlagsCheck['MTP' + name][paramName] = conditions[paramName]; - maxbit = max(maxbit, int(conditions[paramName])); - if (maxbit > 0): - funcsText += '\n'; - funcsText += '\t\tMAX_FIELD = (1U << ' + str(maxbit) + '),\n'; - funcsText += '\t};\n'; - funcsText += '\tusing Flags = base::flags;\n'; - funcsText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n'; - funcsText += '\n'; - - if (len(prms) > len(trivialConditions)): - for paramName in prmsList: - if (paramName in trivialConditions): - continue; - paramType = prms[paramName]; - prmsInit.append('_' + paramName + '(' + paramName + '_)'); - prmsNames.append(paramName + '_'); - if (paramName == isTemplate): - ptypeFull = paramType; - else: - ptypeFull = 'MTP' + paramType; - if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags']): - prmsStr.append(ptypeFull + ' ' + paramName + '_'); - else: - prmsStr.append('const ' + ptypeFull + ' &' + paramName + '_'); - - funcsText += '\tMTP' + name + '();\n';# = default; # constructor - if (isTemplate != ''): - methodBodies += 'template \n' - methodBodies += 'MTP' + name + '::MTP' + name + '() = default;\n'; - else: - methodBodies += 'MTP' + name + '::MTP' + name + '() = default;\n'; - if (len(prms) > len(trivialConditions)): - funcsText += '\tMTP' + name + '(' + ', '.join(prmsStr) + ');\n'; - if (isTemplate != ''): - methodBodies += 'template \n' - methodBodies += 'MTP' + name + '::MTP' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'; - else: - methodBodies += 'MTP' + name + '::MTP' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'; - - funcsText += '\n'; - funcsText += '\tuint32 innerLength() const;\n'; # count size - if (isTemplate != ''): - methodBodies += 'template \n' - methodBodies += 'uint32 MTP' + name + '::innerLength() const {\n'; - else: - methodBodies += 'uint32 MTP' + name + '::innerLength() const {\n'; - size = []; - for k in prmsList: - v = prms[k]; - if (k in conditionsList): - if (not k in trivialConditions): - size.append('((_' + hasFlags + '.v & Flag::f_' + k + ') ? _' + k + '.innerLength() : 0)'); - else: - size.append('_' + k + '.innerLength()'); - if (not len(size)): - size.append('0'); - methodBodies += '\treturn ' + ' + '.join(size) + ';\n'; - methodBodies += '}\n'; - - funcsText += '\tmtpTypeId type() const {\n\t\treturn mtpc_' + name + ';\n\t}\n'; # type id - - funcsText += '\t[[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ');\n'; # read method - if (isTemplate != ''): - methodBodies += 'template \n' - methodBodies += 'bool MTP' + name + '::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {\n'; - else: - methodBodies += 'bool MTP' + name + '::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {\n'; - readFunc = '' - for k in prmsList: - v = prms[k]; - if (k in conditionsList): - if (not k in trivialConditions): - readFunc += '\t\t&& ((_' + hasFlags + '.v & Flag::f_' + k + ') ? _' + k + '.read(from, end) : ((_' + k + ' = MTP' + v + '()), true))\n'; - else: - readFunc += '\t\t&& _' + k + '.read(from, end)\n'; - if readFunc != '': - methodBodies += '\treturn' + readFunc[4:len(readFunc)-1] + ';\n'; - else: - methodBodies += '\treturn true;\n'; - methodBodies += '}\n'; - - funcsText += '\tvoid write(mtpBuffer &to) const;\n'; # write method - if (isTemplate != ''): - methodBodies += 'template \n' - methodBodies += 'void MTP' + name + '::write(mtpBuffer &to) const {\n'; - else: - methodBodies += 'void MTP' + name + '::write(mtpBuffer &to) const {\n'; - for k in prmsList: - v = prms[k]; - if (k in conditionsList): - if (not k in trivialConditions): - methodBodies += '\tif (_' + hasFlags + '.v & Flag::f_' + k + ') _' + k + '.write(to);\n'; - else: - methodBodies += '\t_' + k + '.write(to);\n'; - methodBodies += '}\n'; - - if (isTemplate != ''): - funcsText += '\n\tusing ResponseType = typename TQueryType::ResponseType;\n\n'; - inlineMethods += methodBodies; - else: - funcsText += '\n\tusing ResponseType = MTP' + resType + ';\n\n'; # method return type - methods += methodBodies; - - if (len(prms) > len(trivialConditions)): - funcsText += 'private:\n'; - for paramName in prmsList: - if (paramName in trivialConditions): - continue; - paramType = prms[paramName]; - if (paramName == isTemplate): - ptypeFull = paramType; - else: - ptypeFull = 'MTP' + paramType; - funcsText += '\t' + ptypeFull + ' _' + paramName + ';\n'; - funcsText += '\n'; - - funcsText += '};\n'; # class ending - if (isTemplate != ''): - funcsText += 'template \n'; - funcsText += 'using MTP' + Name + ' = MTPBoxed>;\n'; - else: - funcsText += 'using MTP' + Name + ' = MTPBoxed;\n'; - funcs = funcs + 1; - - if (not restype in funcsDict): - funcsList.append(restype); - funcsDict[restype] = []; -# TypesDict[restype] = resType; - funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions, trivialConditions, isTemplate]); - else: - if (isTemplate != ''): - print('Template types not allowed: "' + resType + '" in line: ' + line); - continue; - if (not restype in typesDict): - typesList.append(restype); - typesDict[restype] = []; - TypesDict[restype] = resType; - typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions, trivialConditions, isTemplate]); - - consts = consts + 1; - -# text serialization: types and funcs -def addTextSerialize(lst, dct, dataLetter): - result = ''; - for restype in lst: - v = dct[restype]; - for data in v: - name = data[0]; - prmsList = data[2]; - prms = data[3]; - hasFlags = data[4]; - conditionsList = data[5]; - conditions = data[6]; - trivialConditions = data[7]; - isTemplate = data[8]; - - templateArgument = '' - if (isTemplate != ''): - templateArgument = '' - - result += 'bool Serialize_' + name + '(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag) {\n'; - if (len(conditions)): - result += '\tauto flag = MTP' + dataLetter + name + templateArgument + '::Flags::from_raw(iflag);\n\n'; - if (len(prms)): - result += '\tif (stage) {\n'; - result += '\t\tto.add(",\\n").addSpaces(lev);\n'; - result += '\t} else {\n'; - result += '\t\tto.add("{ ' + name + '");\n'; - result += '\t\tto.add("\\n").addSpaces(lev);\n'; - result += '\t}\n'; - result += '\tswitch (stage) {\n'; - stage = 0; - for k in prmsList: - v = prms[k]; - result += '\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); '; - if (k == hasFlags): - result += 'if (start >= end) return false; else flags.back() = *start; '; - if (k in trivialConditions): - result += 'if (flag & MTP' + dataLetter + name + templateArgument + '::Flag::f_' + k + ') { '; - result += 'to.add("YES [ BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); '; - result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } '; - else: - if (k in conditions): - result += 'if (flag & MTP' + dataLetter + name + templateArgument + '::Flag::f_' + k + ') { '; - result += 'types.push_back('; - vtypeget = re.match(r'^[Vv]ector', v); - if (vtypeget): - if (not re.match(r'^[A-Z]', v)): - result += 'mtpc_vector'; - else: - result += '0'; - restype = vtypeget.group(1); - try: - if boxed[restype]: - restype = 0; - except KeyError: - if re.match(r'^[A-Z]', restype): - restype = 0; - else: - restype = v; - try: - if boxed[restype]: - restype = 0; - except KeyError: - if re.match(r'^[A-Z]', restype): - restype = 0; - if (restype): - try: - conses = typesDict[restype]; - if (len(conses) > 1): - print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"'); - continue; - if (vtypeget): - result += '); vtypes.push_back('; - result += 'mtpc_' + conses[0][0]; - if (not vtypeget): - result += '); vtypes.push_back(0'; - except KeyError: - if (vtypeget): - result += '); vtypes.push_back('; - if (re.match(r'^flags<', restype)): - result += 'mtpc_flags'; - else: - result += 'mtpc_' + restype + '+0'; - if (not vtypeget): - result += '); vtypes.push_back(0'; - else: - if (not vtypeget): - result += '0'; - result += '); vtypes.push_back(0'; - result += '); stages.push_back(0); flags.push_back(0); '; - if (k in conditions): - result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } '; - result += 'break;\n'; - stage = stage + 1; - result += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n'; - result += '\t}\n'; - else: - result += '\tto.add("{ ' + name + ' }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n'; - result += '\treturn true;\n'; - result += '}\n\n'; - return result; - -# text serialization: types and funcs -def addTextSerializeInit(lst, dct): - result = ''; - for restype in lst: - v = dct[restype]; - for data in v: - name = data[0]; - result += '\tresult.insert(mtpc_' + name + ', Serialize_' + name + ');\n'; - return result; - -textSerializeMethods += addTextSerialize(typesList, typesDict, 'D'); -textSerializeInit += addTextSerializeInit(typesList, typesDict) + '\n'; -textSerializeMethods += addTextSerialize(funcsList, funcsDict, ''); -textSerializeInit += addTextSerializeInit(funcsList, funcsDict) + '\n'; - -for restype in typesList: - v = typesDict[restype]; - resType = TypesDict[restype]; - withData = 0; - creatorsDeclarations = ''; - creatorsBodies = ''; - flagDeclarations = ''; - constructsText = ''; - constructsBodies = ''; - - forwards += 'class MTP' + restype + ';\n'; - forwTypedefs += 'using MTP' + resType + ' = MTPBoxed;\n'; - - withType = (len(v) > 1); - switchLines = ''; - friendDecl = ''; - getters = ''; - visitor = ''; - reader = ''; - writer = ''; - sizeList = []; - sizeFast = ''; - newFast = ''; - sizeCases = ''; - for data in v: - name = data[0]; - typeid = data[1]; - prmsList = data[2]; - prms = data[3]; - hasFlags = data[4]; - conditionsList = data[5]; - conditions = data[6]; - trivialConditions = data[7]; - - dataText = ''; - if (len(prms) > len(trivialConditions)): - withData = 1; - dataText += '\nclass MTPD' + name + ' : public MTP::internal::TypeData {\n'; # data class - else: - dataText += '\nclass MTPD' + name + ' {\n'; # empty data class for visitors - dataText += 'public:\n'; - dataText += '\ttemplate \n'; - dataText += '\tstatic constexpr bool Is() { return std::is_same_v, MTPD' + name + '>; };\n\n'; - sizeList = []; - creatorParams = []; - creatorParamsList = []; - readText = ''; - writeText = ''; - - if (hasFlags != ''): - dataText += '\tenum class Flag : uint32 {\n'; - maxbit = 0; - parentFlagsCheck['MTPD' + name] = {}; - for paramName in conditionsList: - dataText += '\t\tf_' + paramName + ' = (1U << ' + conditions[paramName] + '),\n'; - parentFlagsCheck['MTPD' + name][paramName] = conditions[paramName]; - maxbit = max(maxbit, int(conditions[paramName])); - if (maxbit > 0): - dataText += '\n'; - dataText += '\t\tMAX_FIELD = (1U << ' + str(maxbit) + '),\n'; - dataText += '\t};\n'; - dataText += '\tusing Flags = base::flags;\n'; - dataText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n'; - dataText += '\n'; - if (len(conditions)): - for paramName in conditionsList: - if (paramName in trivialConditions): - dataText += '\t[[nodiscard]] bool is_' + paramName + '() const;\n'; - constructsBodies += 'bool MTPD' + name + '::is_' + paramName + '() const {\n'; - constructsBodies += '\treturn _' + hasFlags + '.v & Flag::f_' + paramName + ';\n'; - constructsBodies += '}\n'; - dataText += '\n'; - - switchLines += '\tcase mtpc_' + name + ': '; # for by-type-id type constructor - getters += '\t[[nodiscard]] const MTPD' + name + ' &c_' + name + '() const;\n'; # const getter - visitor += '\tcase mtpc_' + name + ': return base::match_method(c_' + name + '(), std::forward(method), std::forward(methods)...);\n'; - - forwards += 'class MTPD' + name + ';\n'; # data class forward declaration - if (len(prms) > len(trivialConditions)): - dataText += '\tMTPD' + name + '();\n'; # default constructor - switchLines += 'setData(new MTPD' + name + '()); '; - - constructsBodies += 'MTPD' + name + '::MTPD' + name + '() = default;\n'; - constructsBodies += 'const MTPD' + name + ' &MTP' + restype + '::c_' + name + '() const {\n'; - if (withType): - constructsBodies += '\tExpects(_type == mtpc_' + name + ');\n\n'; - constructsBodies += '\treturn queryData();\n'; - constructsBodies += '}\n'; - - constructsText += '\texplicit MTP' + restype + '(const MTPD' + name + ' *data);\n'; # by-data type constructor - constructsBodies += 'MTP' + restype + '::MTP' + restype + '(const MTPD' + name + ' *data) : TypeDataOwner(data)'; - if (withType): - constructsBodies += ', _type(mtpc_' + name + ')'; - constructsBodies += ' {\n}\n'; - - dataText += '\tMTPD' + name + '('; # params constructor - prmsStr = []; - prmsInit = []; - for paramName in prmsList: - if (paramName in trivialConditions): - continue; - paramType = prms[paramName]; - - if (paramType in ['int', 'Int', 'bool', 'Bool']): - prmsStr.append('MTP' + paramType + ' ' + paramName + '_'); - creatorParams.append('MTP' + paramType + ' ' + paramName + '_'); - else: - prmsStr.append('const MTP' + paramType + ' &' + paramName + '_'); - creatorParams.append('const MTP' + paramType + ' &' + paramName + '_'); - creatorParamsList.append(paramName + '_'); - prmsInit.append('_' + paramName + '(' + paramName + '_)'); - if (paramName in conditions): - readText += '\t\t&& (v' + paramName + '() ? _' + paramName + '.read(from, end) : ((_' + paramName + ' = MTP' + paramType + '()), true))\n'; - writeText += '\t\tif (const auto v' + paramName + ' = v.v' + paramName + '()) v' + paramName + '->write(to);\n'; - sizeList.append('(v.v' + paramName + '() ? v.v' + paramName + '()->innerLength() : 0)'); - else: - readText += '\t\t&& _' + paramName + '.read(from, end)\n'; - writeText += '\t\tv.v' + paramName + '().write(to);\n'; - sizeList.append('v.v' + paramName + '().innerLength()'); - - dataText += ', '.join(prmsStr) + ');\n'; - - constructsBodies += 'MTPD' + name + '::MTPD' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'; - - dataText += '\n'; - dataText += '\t[[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end);\n'; - dataText += '\n'; - - constructsBodies += 'bool MTPD' + name + '::read(const mtpPrime *&from, const mtpPrime *end) {\n'; - if readText != '': - constructsBodies += '\treturn' + readText[4:len(readText)-1] + ';\n'; - else: - constructsBodies += '\treturn true;\n'; - constructsBodies += '}\n'; - - if len(prmsList) > 0: - for paramName in prmsList: # getters - if (paramName in trivialConditions): - continue; - paramType = prms[paramName]; - if (paramName in conditions): - dataText += '\t[[nodiscard]] MTP::conditional v' + paramName + '() const;\n'; - constructsBodies += 'MTP::conditional MTPD' + name + '::v' + paramName + '() const {\n'; - constructsBodies += '\treturn (_' + hasFlags + '.v & Flag::f_' + paramName + ') ? &_' + paramName + ' : nullptr;\n'; - constructsBodies += '}\n'; - else: - dataText += '\t[[nodiscard]] const MTP' + paramType + ' &v' + paramName + '() const;\n'; - constructsBodies += 'const MTP' + paramType + ' &MTPD' + name + '::v' + paramName + '() const {\n'; - constructsBodies += '\treturn _' + paramName + ';\n'; - constructsBodies += '}\n'; - dataText += '\n'; - dataText += 'private:\n'; - for paramName in prmsList: # fields declaration - if (paramName in trivialConditions): - continue; - paramType = prms[paramName]; - dataText += '\tMTP' + paramType + ' _' + paramName + ';\n'; - dataText += '\n'; - sizeCases += '\tcase mtpc_' + name + ': {\n'; - sizeCases += '\t\tconst MTPD' + name + ' &v(c_' + name + '());\n'; - sizeCases += '\t\treturn ' + ' + '.join(sizeList) + ';\n'; - sizeCases += '\t}\n'; - sizeFast = '\tconst MTPD' + name + ' &v(c_' + name + '());\n\treturn ' + ' + '.join(sizeList) + ';\n'; - newFast = 'new MTPD' + name + '()'; - else: - constructsBodies += 'const MTPD' + name + ' &MTP' + restype + '::c_' + name + '() const {\n'; - if (withType): - constructsBodies += '\tExpects(_type == mtpc_' + name + ');\n\n'; - constructsBodies += '\tstatic const MTPD' + name + ' result;\n'; - constructsBodies += '\treturn result;\n'; - constructsBodies += '}\n'; - - sizeFast = '\treturn 0;\n'; - - switchLines += 'break;\n'; - dataText += '};\n'; # class ending - - dataTexts += dataText; # add data class - - if (not friendDecl): - friendDecl += '\tfriend class MTP::internal::TypeCreator;\n'; - creatorProxyText += '\tinline static MTP' + restype + ' new_' + name + '(' + ', '.join(creatorParams) + ') {\n'; - if (len(prms) > len(trivialConditions)): # creator with params - creatorProxyText += '\t\treturn MTP' + restype + '(new MTPD' + name + '(' + ', '.join(creatorParamsList) + '));\n'; - else: - if (withType): # creator by type - creatorProxyText += '\t\treturn MTP' + restype + '(mtpc_' + name + ');\n'; - else: # single creator - creatorProxyText += '\t\treturn MTP' + restype + '();\n'; - creatorProxyText += '\t}\n'; - creatorsDeclarations += 'MTP' + restype + ' MTP_' + name + '(' + ', '.join(creatorParams) + ');\n'; - creatorsBodies += 'MTP' + restype + ' MTP_' + name + '(' + ', '.join(creatorParams) + ') {\n'; - creatorsBodies += '\treturn MTP::internal::TypeCreator::new_' + name + '(' + ', '.join(creatorParamsList) + ');\n'; - creatorsBodies += '}\n'; - - if (withType): - reader += '\tcase mtpc_' + name + ': _type = cons; '; # read switch line - if (len(prms) > len(trivialConditions)): - reader += '{\n'; - reader += '\t\tif (const auto data = new MTPD' + name + '(); data->read(from, end)) {\n'; - reader += '\t\t\tsetData(data);\n'; - reader += '\t\t} else {\n'; - reader += '\t\t\tdelete data;\n'; - reader += '\t\t\treturn false;\n'; - reader += '\t\t}\n'; - reader += '\t} break;\n'; - - writer += '\tcase mtpc_' + name + ': {\n'; # write switch line - writer += '\t\tconst MTPD' + name + ' &v = c_' + name + '();\n'; - writer += writeText; - writer += '\t} break;\n'; - else: - reader += 'break;\n'; - else: - if (len(prms) > len(trivialConditions)): - reader += '\tif (const auto data = new MTPD' + name + '(); data->read(from, end)) {\n'; - reader += '\t\tsetData(data);\n'; - reader += '\t} else {\n'; - reader += '\t\tdelete data;\n'; - reader += '\t\treturn false;\n'; - reader += '\t}\n'; - - writer += '\tconst MTPD' + name + ' &v = c_' + name + '();\n'; - writer += writeText; - - forwards += '\n'; - - typesText += '\nclass MTP' + restype; # type class declaration - if (withData): - typesText += ' : private MTP::internal::TypeDataOwner'; # if has data fields - typesText += ' {\n'; - typesText += 'public:\n'; - typesText += '\tMTP' + restype + '();\n'; # default constructor - if (withData and not withType): - methods += '\nMTP' + restype + '::MTP' + restype + '() : TypeDataOwner(' + newFast + ') {\n}\n'; - else: - methods += '\nMTP' + restype + '::MTP' + restype + '() = default;\n'; - - typesText += getters; - typesText += '\n'; - typesText += '\ttemplate \n'; - typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n'; - visitorMethods += 'template \n'; - visitorMethods += 'decltype(auto) MTP' + restype + '::match(Method &&method, Methods &&...methods) const {\n'; - if (withType): - visitorMethods += '\tswitch (_type) {\n'; - visitorMethods += visitor; - visitorMethods += '\t}\n'; - visitorMethods += '\tUnexpected("Type in MTP' + restype + '::match.");\n'; - else: - visitorMethods += '\treturn base::match_method(c_' + v[0][0] + '(), std::forward(method), std::forward(methods)...);\n'; - visitorMethods += '}\n\n'; - - typesText += '\n\tuint32 innerLength() const;\n'; # size method - methods += '\nuint32 MTP' + restype + '::innerLength() const {\n'; - if (withType and sizeCases): - methods += '\tswitch (_type) {\n'; - methods += sizeCases; - methods += '\t}\n'; - methods += '\treturn 0;\n'; - else: - methods += sizeFast; - methods += '}\n'; - - typesText += '\tmtpTypeId type() const;\n'; # type id method - methods += 'mtpTypeId MTP' + restype + '::type() const {\n'; - if (withType): - methods += '\tExpects(_type != 0);\n\n'; - methods += '\treturn _type;\n'; - else: - methods += '\treturn mtpc_' + v[0][0] + ';\n'; - methods += '}\n'; - - typesText += '\t[[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons'; # read method - if (not withType): - typesText += ' = mtpc_' + name; - typesText += ');\n'; - methods += 'bool MTP' + restype + '::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {\n'; - if (withData): - if not (withType): - methods += '\tif (cons != mtpc_' + v[0][0] + ') return false;\n'; - if (withType): - methods += '\tswitch (cons) {\n' - methods += reader; - methods += '\tdefault: return false;\n'; - methods += '\t}\n'; - else: - methods += reader; - methods += '\treturn true;\n'; - methods += '}\n'; - - typesText += '\tvoid write(mtpBuffer &to) const;\n'; # write method - methods += 'void MTP' + restype + '::write(mtpBuffer &to) const {\n'; - if (withType and writer != ''): - methods += '\tswitch (_type) {\n'; - methods += writer; - methods += '\t}\n'; - else: - methods += writer; - methods += '}\n'; - - typesText += '\n\tusing ResponseType = void;\n'; # no response types declared - - typesText += '\nprivate:\n'; # private constructors - if (withType): # by-type-id constructor - typesText += '\texplicit MTP' + restype + '(mtpTypeId type);\n'; - methods += 'MTP' + restype + '::MTP' + restype + '(mtpTypeId type) : '; - methods += '_type(type)'; - methods += ' {\n'; - methods += '\tswitch (type) {\n'; # type id check - methods += switchLines; - methods += '\tdefault: Unexpected("Type in MTP' + restype + '::MTP' + restype + '.");\n'; - methods += '\t}\n'; - methods += '}\n'; # by-type-id constructor end - - if (withData): - typesText += constructsText; - methods += constructsBodies; - - if (friendDecl): - typesText += '\n' + friendDecl; - - if (withType): - typesText += '\n\tmtpTypeId _type = 0;\n'; # type field var - - typesText += '};\n'; # type class ended - - flagOperators += flagDeclarations; - factories += creatorsDeclarations; - methods += creatorsBodies; - typesText += 'using MTP' + resType + ' = MTPBoxed;\n'; # boxed type definition - -flagOperators += '\n' - -for childName in parentFlagsList: - parentName = parentFlags[childName]; - for flag in parentFlagsCheck[childName]: -# -# 'channelForbidden' has 'until_date' flag and 'channel' doesn't have it. -# But as long as flags don't collide this is not a problem. -# -# if (not flag in parentFlagsCheck[parentName]): -# print('Flag ' + flag + ' not found in ' + parentName + ' which should be a flags-parent of ' + childName); -# sys.exit(1); -# - if (flag in parentFlagsCheck[parentName]): - if (parentFlagsCheck[childName][flag] != parentFlagsCheck[parentName][flag]): - print('Flag ' + flag + ' has different value in ' + parentName + ' which should be a flags-parent of ' + childName); - sys.exit(1); - else: - parentFlagsCheck[parentName][flag] = parentFlagsCheck[childName][flag]; - flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(' + childName + '::Flags flags) { return static_cast<' + parentName + '::Flag>(flags.value()); }\n'; - flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(MTPflags<' + childName + '::Flags> flags) { return mtpCastFlags(flags.v); }\n'; - -# manual types added here -textSerializeMethods += '\ -bool _serialize_rpc_result(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag) {\n\ - if (stage) {\n\ - to.add(",\\n").addSpaces(lev);\n\ - } else {\n\ - to.add("{ rpc_result");\n\ - to.add("\\n").addSpaces(lev);\n\ - }\n\ - switch (stage) {\n\ - case 0: to.add(" req_msg_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - case 1: to.add(" result: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ - }\n\ - return true;\n\ -}\n\ -\n\ -bool _serialize_msg_container(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag) {\n\ - if (stage) {\n\ - to.add(",\\n").addSpaces(lev);\n\ - } else {\n\ - to.add("{ msg_container");\n\ - to.add("\\n").addSpaces(lev);\n\ - }\n\ - switch (stage) {\n\ - case 0: to.add(" messages: "); ++stages.back(); types.push_back(mtpc_vector); vtypes.push_back(mtpc_core_message); stages.push_back(0); flags.push_back(0); break;\n\ - default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ - }\n\ - return true;\n\ -}\n\ -\n\ -bool _serialize_core_message(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag) {\n\ - if (stage) {\n\ - to.add(",\\n").addSpaces(lev);\n\ - } else {\n\ - to.add("{ core_message");\n\ - to.add("\\n").addSpaces(lev);\n\ - }\n\ - switch (stage) {\n\ - case 0: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - case 1: to.add(" seq_no: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - case 2: to.add(" bytes: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - case 3: to.add(" body: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ - default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ - }\n\ - return true;\n\ -}\n\ -\n'; - -textSerializeInit += '\ - result.insert(mtpc_rpc_result, _serialize_rpc_result);\n\ - result.insert(mtpc_msg_container, _serialize_msg_container);\n\ - result.insert(mtpc_core_message, _serialize_core_message);\n'; - -# module itself - -header = '\ -/*\n\ -WARNING! All changes made in this file will be lost!\n\ -Created from \'' + os.path.basename(input_file) + '\' by \'codegen_scheme\'\n\ -\n\ -This file is part of Telegram Desktop,\n\ -the official desktop application for the Telegram messaging service.\n\ -\n\ -For license and copyright information please follow this link:\n\ -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL\n\ -*/\n\ -#pragma once\n\ -\n\ -#include "mtproto/core_types.h"\n\ -#include "base/flags.h"\n\ -\n\ -// Creator current layer and proxy class declaration\n\ -namespace MTP {\n\ -namespace internal {\n\ -\n\ -' + layer + '\n\ -\n\ -class TypeCreator;\n\ -\n\ -} // namespace internal\n\ -} // namespace MTP\n\ -\n\ -// Type id constants\n\ -enum {\n\ -' + ',\n'.join(enums) + '\n\ -};\n\ -\n\ -// Type forward declarations\n\ -' + forwards + '\n\ -// Boxed types definitions\n\ -' + forwTypedefs + '\n\ -// Type classes definitions\n\ -' + typesText + '\n\ -// Type constructors with data\n\ -' + dataTexts + '\n\ -// RPC methods\n\ -' + funcsText + '\n\ -// Template methods definition\n\ -' + inlineMethods + '\n\ -// Visitor definition\n\ -' + visitorMethods + '\n\ -// Flag operators definition\n\ -' + flagOperators + '\n\ -// Factory methods declaration\n\ -' + factories + '\n\ -// Human-readable text serialization\n\ -[[nodiscard]] bool mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons);\n' - -source = '\ -/*\n\ -WARNING! All changes made in this file will be lost!\n\ -Created from \'' + os.path.basename(input_file) + '\' by \'codegen_scheme\'\n\ -\n\ -This file is part of Telegram Desktop,\n\ -the official desktop application for the Telegram messaging service.\n\ -\n\ -For license and copyright information please follow this link:\n\ -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL\n\ -*/\n\ -#include "scheme.h"\n\ -\n\ -// Creator proxy class definition\n\ -namespace MTP {\n\ -namespace internal {\n\ -\n\ -class TypeCreator {\n\ -public:\n\ -' + creatorProxyText + '\n\ -};\n\ -\n\ -} // namespace internal\n\ -} // namespace MTP\n\ -\n\ -// Methods definition\n\ -' + methods + '\n\ -\n\ -using Types = QVector;\n\ -using StagesFlags = QVector;\n\ -\n\ -' + textSerializeMethods + '\n\ -namespace {\n\ -\n\ -using TextSerializer = bool (*)(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag);\n\ -using TextSerializers = QMap;\n\ -\n\ -QMap createTextSerializers() {\n\ - auto result = QMap();\n\ -\n\ -' + textSerializeInit + '\n\ - return result;\n\ -}\n\ -\n\ -} // namespace\n\ -\n\ -bool mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons) {\n\ - static auto serializers = createTextSerializers();\n\ -\n\ - QVector types, vtypes;\n\ - QVector stages, flags;\n\ - types.reserve(20); vtypes.reserve(20); stages.reserve(20); flags.reserve(20);\n\ - types.push_back(mtpTypeId(cons)); vtypes.push_back(mtpTypeId(vcons)); stages.push_back(0); flags.push_back(0);\n\ -\n\ - mtpTypeId type = cons, vtype = vcons;\n\ - int32 stage = 0, flag = 0;\n\ -\n\ - while (!types.isEmpty()) {\n\ - type = types.back();\n\ - vtype = vtypes.back();\n\ - stage = stages.back();\n\ - flag = flags.back();\n\ - if (!type) {\n\ - if (from >= end) {\n\ - to.error("insufficient data");\n\ - return false;\n\ - } else if (stage) {\n\ - to.error("unknown type on stage > 0");\n\ - return false;\n\ - }\n\ - types.back() = type = *from;\n\ - ++from;\n\ - }\n\ -\n\ - int32 lev = level + types.size() - 1;\n\ - auto it = serializers.constFind(type);\n\ - if (it != serializers.cend()) {\n\ - if (!(*it.value())(to, stage, lev, types, vtypes, stages, flags, from, end, flag)) {\n\ - to.error();\n\ - return false;\n\ - }\n\ - } else if (mtpTextSerializeCore(to, from, end, type, lev, vtype)) {\n\ - types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n\ - } else {\n\ - to.error();\n\ - return false;\n\ - }\n\ - }\n\ - return true;\n\ -}\n'; - -already_header = '' -if os.path.isfile(output_header): - with open(output_header, 'r') as already: - already_header = already.read() -if already_header != header: - with open(output_header, 'w') as out: - out.write(header) - -already_source = '' -if os.path.isfile(output_source): - with open(output_source, 'r') as already: - already_source = already.read() -if already_source != source: - with open(output_source, 'w') as out: - out.write(source) +sys.dont_write_bytecode = True +scriptPath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(scriptPath + '/../../../lib_tl/tl') +from generate_tl import generate + +generate({ + 'namespaces': { + 'creator': 'MTP::internal', + }, + 'prefixes': { + 'type': 'MTP', + 'data': 'MTPD', + 'id': 'mtpc', + 'construct': 'MTP_', + }, + 'types': { + 'prime': 'mtpPrime', + 'typeId': 'mtpTypeId', + 'buffer': 'mtpBuffer', + }, + 'sections': [ + 'serialization', + 'read-write', + ], + + # define some checked flag conversions + # the key flag type should be a subset of the value flag type + # with exact the same names, then the key flag can be implicitly + # casted to the value flag type + 'flagInheritance': { + 'messageService': 'message', + 'updateShortMessage': 'message', + 'updateShortChatMessage': 'message', + 'updateShortSentMessage': 'message', + 'replyKeyboardHide': 'replyKeyboardMarkup', + 'replyKeyboardForceReply': 'replyKeyboardMarkup', + 'inputPeerNotifySettings': 'peerNotifySettings', + 'peerNotifySettings': 'inputPeerNotifySettings', + 'channelForbidden': 'channel', + 'dialogFolder': 'dialog', + }, + + 'typeIdExceptions': [ + 'channel#c88974ac', + 'ipPortSecret#37982646', + 'accessPointRule#4679b65f', + 'help.configSimple#5a592a6c', + ], + + 'renamedTypes': { + 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow': 'passwordKdfAlgoModPow', + }, + + 'skip': [ + 'int ? = Int;', + 'long ? = Long;', + 'double ? = Double;', + 'string ? = String;', + + 'vector {t:Type} # [ t ] = Vector t;', + + 'int128 4*[ int ] = Int128;', + 'int256 8*[ int ] = Int256;', + + 'vector#1cb5c415 {t:Type} # [ t ] = Vector t;', + ], + 'builtin': [ + 'int', + 'long', + 'double', + 'string', + 'bytes', + 'int128', + 'int256', + ], + 'builtinTemplates': [ + 'vector', + 'flags', + ], + 'synonyms': { + 'bytes': 'string', + }, + 'builtinInclude': 'mtproto/core_types.h', + +}) diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 9bf5bbad0..9b8d8e8ec 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -7,16 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include -#include -#include -#include #include "base/basic_types.h" #include "base/match_method.h" #include "base/flags.h" #include "base/bytes.h" #include "base/algorithm.h" #include "base/assertion.h" +#include "tl/tl_basic_types.h" + +#include +#include +#include +#include using mtpPrime = int32; using mtpRequestId = int32; @@ -58,108 +60,17 @@ constexpr int GetDcIdShift(ShiftedDcId shiftedDcId) { } // namespace MTP -namespace MTP { -namespace internal { - -class TypeData { -public: - TypeData() = default; - TypeData(const TypeData &other) = delete; - TypeData(TypeData &&other) = delete; - TypeData &operator=(const TypeData &other) = delete; - TypeData &operator=(TypeData &&other) = delete; - - virtual ~TypeData() { - } - -private: - void incrementCounter() const { - _counter.ref(); - } - bool decrementCounter() const { - return _counter.deref(); - } - friend class TypeDataOwner; - - mutable QAtomicInt _counter = { 1 }; - -}; - -class TypeDataOwner { -public: - TypeDataOwner(TypeDataOwner &&other) : _data(base::take(other._data)) { - } - TypeDataOwner(const TypeDataOwner &other) : _data(other._data) { - incrementCounter(); - } - TypeDataOwner &operator=(TypeDataOwner &&other) { - if (other._data != _data) { - decrementCounter(); - _data = base::take(other._data); - } - return *this; - } - TypeDataOwner &operator=(const TypeDataOwner &other) { - if (other._data != _data) { - setData(other._data); - incrementCounter(); - } - return *this; - } - ~TypeDataOwner() { - decrementCounter(); - } - -protected: - TypeDataOwner() = default; - TypeDataOwner(const TypeData *data) : _data(data) { - } - - void setData(const TypeData *data) { - decrementCounter(); - _data = data; - } - - // Unsafe cast, type should be checked by the caller. - template - const DataType &queryData() const { - Expects(_data != nullptr); - - return static_cast(*_data); - } - -private: - void incrementCounter() { - if (_data) { - _data->incrementCounter(); - } - } - void decrementCounter() { - if (_data && !_data->decrementCounter()) { - delete base::take(_data); - } - } - - const TypeData * _data = nullptr; - -}; - -struct ZeroFlagsHelper { -}; - -} // namespace internal -} // namespace MTP - enum { // core types - mtpc_int = 0xa8509bda, - mtpc_long = 0x22076cba, - mtpc_int128 = 0x4bb5362b, - mtpc_int256 = 0x929c32f, - mtpc_double = 0x2210c154, - mtpc_string = 0xb5286e24, - - mtpc_vector = 0x1cb5c415, + mtpc_int = tl::id_int, + mtpc_long = tl::id_long, + mtpc_int128 = tl::id_int128, + mtpc_int256 = tl::id_int256, + mtpc_double = tl::id_double, + mtpc_string = tl::id_string, + mtpc_vector = tl::id_vector, + mtpc_bytes = tl::id_bytes, + mtpc_flags = tl::id_flags, // layers mtpc_invokeWithLayer1 = 0x53835315, @@ -187,8 +98,6 @@ enum { // mtpc_msg_copy = 0xe06046b2, mtpc_gzip_packed = 0x3072cfa1 }; -static const mtpTypeId mtpc_bytes = mtpc_string; -static const mtpTypeId mtpc_flags = mtpc_int; static const mtpTypeId mtpc_core_message = -1; // undefined type, but is used static const mtpTypeId mtpLayers[] = { mtpTypeId(mtpc_invokeWithLayer1), @@ -212,43 +121,6 @@ static const mtpTypeId mtpLayers[] = { }; static const uint32 mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]); -template -class MTPBoxed : public bareT { -public: - using bareT::bareT; - MTPBoxed() = default; - MTPBoxed(const MTPBoxed &v) = default; - MTPBoxed &operator=(const MTPBoxed &v) = default; - MTPBoxed(const bareT &v) : bareT(v) { - } - MTPBoxed &operator=(const bareT &v) { - *((bareT*)this) = v; - return *this; - } - - uint32 innerLength() const { - return sizeof(mtpTypeId) + bareT::innerLength(); - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) { - if (from + 1 > end) { - return false; - } - cons = (mtpTypeId)*(from++); - return bareT::read(from, end, cons); - } - void write(mtpBuffer &to) const { - to.push_back(bareT::type()); - bareT::write(to); - } - - using Unboxed = bareT; - -}; -template -class MTPBoxed > { - typename T::CantMakeBoxedBoxedType v; -}; - namespace MTP { namespace details { @@ -257,17 +129,6 @@ struct SecureRequestCreateTag { } // namespace details -template -struct is_boxed : std::false_type { -}; - -template -struct is_boxed> : std::true_type { -}; - -template -constexpr bool is_boxed_v = is_boxed::value; - class SecureRequestData; class SecureRequest { public: @@ -290,7 +151,7 @@ public: template < typename Request, - typename = std::enable_if_t>> + typename = std::enable_if_t>> static SecureRequest Serialize(const Request &request); // For template MTP requests and MTPBoxed instanciation. @@ -341,511 +202,122 @@ SecureRequest SecureRequest::Serialize(const Request &request) { return serialized; } -template -struct RepeatHelper { - using type = Type; -}; -template -using Repeat = typename RepeatHelper::type; - -struct InnerHelper { - static void Check(...); - template ().v)> - static Result Check(const Type&); - - template - using type = std::decay_t()))>; -}; - -template -class conditional { -public: - conditional() = default; - conditional(const Type *value) : _value(value) { - } - - operator const Type*() const { - return _value; - } - const Type *operator->() const { - Expects(_value != nullptr); - - return _value; - } - const Type &operator*() const { - Expects(_value != nullptr); - - return *_value; - } - - template < - typename Inner = InnerHelper::type, - typename = std::enable_if_t>> - Inner value_or(Repeat fallback) const { - return _value ? _value->v : fallback; - } - - template < - typename Inner = InnerHelper::type, - typename = std::enable_if_t>> - Inner value_or_empty() const { - return _value ? _value->v : Inner(); - } - -private: - const Type *_value = nullptr; - -}; - } // namespace MTP -class MTPint { -public: - int32 v = 0; +using MTPint = tl::int_type; - MTPint() = default; - - uint32 innerLength() const { - return sizeof(int32); - } - mtpTypeId type() const { - return mtpc_int; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int) { - if (from + 1 > end || cons != mtpc_int) { - return false; - } - v = (int32)*(from++); - return true; - } - void write(mtpBuffer &to) const { - to.push_back((mtpPrime)v); - } - -private: - explicit MTPint(int32 val) : v(val) { - } - - friend MTPint MTP_int(int32 v); -}; inline MTPint MTP_int(int32 v) { - return MTPint(v); + return tl::make_int(v); } -using MTPInt = MTPBoxed; template -class MTPflags { -public: - Flags v = 0; - static_assert( - sizeof(Flags) == sizeof(int32), - "MTPflags are allowed only wrapping int32 flag types!"); - - MTPflags() = default; - MTPflags(MTP::internal::ZeroFlagsHelper helper) { - } - - uint32 innerLength() const { - return sizeof(Flags); - } - mtpTypeId type() const { - return mtpc_flags; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_flags) { - if (from + 1 > end || cons != mtpc_flags) { - return false; - } - v = Flags::from_raw(static_cast(*(from++))); - return true; - } - void write(mtpBuffer &to) const { - to.push_back(static_cast(v.value())); - } - -private: - explicit MTPflags(Flags val) : v(val) { - } - - template - friend MTPflags> MTP_flags(base::flags v); - - template - friend MTPflags> MTP_flags(T v); - -}; +using MTPflags = tl::flags_type; template inline MTPflags> MTP_flags(base::flags v) { - return MTPflags>(v); + return tl::make_flags(v); } template ::value>> inline MTPflags> MTP_flags(T v) { - return MTPflags>(v); + return tl::make_flags(v); } -inline MTP::internal::ZeroFlagsHelper MTP_flags(void(MTP::internal::ZeroFlagsHelper::*)()) { - return MTP::internal::ZeroFlagsHelper(); +inline tl::details::zero_flags_helper MTP_flags(void(tl::details::zero_flags_helper::*)()) { + return tl::details::zero_flags_helper(); } -template -using MTPFlags = MTPBoxed>; +using MTPlong = tl::long_type; -inline bool operator==(const MTPint &a, const MTPint &b) { - return a.v == b.v; -} -inline bool operator!=(const MTPint &a, const MTPint &b) { - return a.v != b.v; -} - -class MTPlong { -public: - uint64 v = 0; - - MTPlong() = default; - - uint32 innerLength() const { - return sizeof(uint64); - } - mtpTypeId type() const { - return mtpc_long; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_long) { - if (from + 2 > end || cons != mtpc_long) { - return false; - } - v = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); - from += 2; - return true; - } - void write(mtpBuffer &to) const { - to.push_back((mtpPrime)(v & 0xFFFFFFFFL)); - to.push_back((mtpPrime)(v >> 32)); - } - -private: - explicit MTPlong(uint64 val) : v(val) { - } - - friend MTPlong MTP_long(uint64 v); -}; inline MTPlong MTP_long(uint64 v) { - return MTPlong(v); -} -using MTPLong = MTPBoxed; - -inline bool operator==(const MTPlong &a, const MTPlong &b) { - return a.v == b.v; -} -inline bool operator!=(const MTPlong &a, const MTPlong &b) { - return a.v != b.v; + return tl::make_long(v); } -class MTPint128 { -public: - uint64 l = 0; - uint64 h = 0; +using MTPint128 = tl::int128_type; - MTPint128() = default; - - uint32 innerLength() const { - return sizeof(uint64) + sizeof(uint64); - } - mtpTypeId type() const { - return mtpc_int128; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int128) { - if (from + 4 > end || cons != mtpc_int128) { - return false; - } - l = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); - h = (uint64)(((uint32*)from)[2]) | ((uint64)(((uint32*)from)[3]) << 32); - from += 4; - return true; - } - void write(mtpBuffer &to) const { - to.push_back((mtpPrime)(l & 0xFFFFFFFFL)); - to.push_back((mtpPrime)(l >> 32)); - to.push_back((mtpPrime)(h & 0xFFFFFFFFL)); - to.push_back((mtpPrime)(h >> 32)); - } - -private: - explicit MTPint128(uint64 low, uint64 high) : l(low), h(high) { - } - - friend MTPint128 MTP_int128(uint64 l, uint64 h); -}; inline MTPint128 MTP_int128(uint64 l, uint64 h) { - return MTPint128(l, h); -} -using MTPInt128 = MTPBoxed; - -inline bool operator==(const MTPint128 &a, const MTPint128 &b) { - return a.l == b.l && a.h == b.h; -} -inline bool operator!=(const MTPint128 &a, const MTPint128 &b) { - return a.l != b.l || a.h != b.h; + return tl::make_int128(l, h); } -class MTPint256 { -public: - MTPint128 l; - MTPint128 h; +using MTPint256 = tl::int256_type; - MTPint256() = default; - - uint32 innerLength() const { - return l.innerLength() + h.innerLength(); - } - mtpTypeId type() const { - return mtpc_int256; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int256) { - if (cons != mtpc_int256) { - return false; - } - return l.read(from, end) - && h.read(from, end); - } - void write(mtpBuffer &to) const { - l.write(to); - h.write(to); - } - -private: - explicit MTPint256(MTPint128 low, MTPint128 high) : l(low), h(high) { - } - - friend MTPint256 MTP_int256(const MTPint128 &l, const MTPint128 &h); -}; inline MTPint256 MTP_int256(const MTPint128 &l, const MTPint128 &h) { - return MTPint256(l, h); -} -using MTPInt256 = MTPBoxed; - -inline bool operator==(const MTPint256 &a, const MTPint256 &b) { - return a.l == b.l && a.h == b.h; -} -inline bool operator!=(const MTPint256 &a, const MTPint256 &b) { - return a.l != b.l || a.h != b.h; + return tl::make_int256(l, h); } -class MTPdouble { -public: - float64 v = 0.; +using MTPdouble = tl::double_type; - MTPdouble() = default; - - uint32 innerLength() const { - return sizeof(float64); - } - mtpTypeId type() const { - return mtpc_double; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_double) { - if (from + 2 > end || cons != mtpc_double) { - return false; - } - auto nv = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); - std::memcpy(&v, &nv, sizeof(v)); - from += 2; - return true; - } - void write(mtpBuffer &to) const { - uint64 iv; - std::memcpy(&iv, &v, sizeof(v)); - to.push_back((mtpPrime)(iv & 0xFFFFFFFFL)); - to.push_back((mtpPrime)(iv >> 32)); - } - -private: - explicit MTPdouble(float64 val) : v(val) { - } - - friend MTPdouble MTP_double(float64 v); -}; inline MTPdouble MTP_double(float64 v) { - return MTPdouble(v); -} -using MTPDouble = MTPBoxed; - -inline bool operator==(const MTPdouble &a, const MTPdouble &b) { - return a.v == b.v; -} -inline bool operator!=(const MTPdouble &a, const MTPdouble &b) { - return a.v != b.v; + return tl::make_double(v); } -class MTPstring; -using MTPbytes = MTPstring; - -class MTPstring { -public: - MTPstring() = default; - - uint32 innerLength() const; - mtpTypeId type() const { - return mtpc_string; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_string); - void write(mtpBuffer &to) const; - - QByteArray v; - -private: - explicit MTPstring(QByteArray &&data) : v(std::move(data)) { - } - - friend MTPstring MTP_string(const std::string &v); - friend MTPstring MTP_string(const QString &v); - friend MTPstring MTP_string(const char *v); - friend MTPstring MTP_string(); - - friend MTPbytes MTP_bytes(const QByteArray &v); - friend MTPbytes MTP_bytes(QByteArray &&v); - friend MTPbytes MTP_bytes(); - -}; -using MTPString = MTPBoxed; -using MTPBytes = MTPBoxed; +using MTPstring = tl::string_type; +using MTPbytes = tl::bytes_type; inline MTPstring MTP_string(const std::string &v) { - return MTPstring(QByteArray(v.data(), v.size())); + return tl::make_string(v); } inline MTPstring MTP_string(const QString &v) { - return MTPstring(v.toUtf8()); + return tl::make_string(v); } inline MTPstring MTP_string(const char *v) { - return MTPstring(QByteArray(v, strlen(v))); + return tl::make_string(v); } inline MTPstring MTP_string() { - return MTPstring(QByteArray()); + return tl::make_string(); } MTPstring MTP_string(const QByteArray &v) = delete; inline MTPbytes MTP_bytes(const QByteArray &v) { - return MTPbytes(QByteArray(v)); + return tl::make_bytes(v); } inline MTPbytes MTP_bytes(QByteArray &&v) { - return MTPbytes(std::move(v)); + return tl::make_bytes(std::move(v)); } inline MTPbytes MTP_bytes() { - return MTPbytes(QByteArray()); + return tl::make_bytes(); } inline MTPbytes MTP_bytes(bytes::const_span buffer) { - return MTP_bytes(QByteArray( - reinterpret_cast(buffer.data()), - buffer.size())); + return tl::make_bytes(buffer); } inline MTPbytes MTP_bytes(const bytes::vector &buffer) { - return MTP_bytes(bytes::make_span(buffer)); -} - -inline bool operator==(const MTPstring &a, const MTPstring &b) { - return a.v == b.v; -} -inline bool operator!=(const MTPstring &a, const MTPstring &b) { - return a.v != b.v; + return tl::make_bytes(buffer); } inline QString qs(const MTPstring &v) { - return QString::fromUtf8(v.v); + return tl::utf16(v); } inline QString qs(const QByteArray &v) { - return QString::fromUtf8(v); + return tl::utf16(v); } inline QByteArray qba(const MTPstring &v) { - return v.v; + return tl::utf8(v); } template -class MTPvector { -public: - MTPvector() = default; +using MTPvector = tl::vector_type; - uint32 innerLength() const { - auto result = uint32(sizeof(uint32)); - for (const auto &item : v) { - result += item.innerLength(); - } - return result; - } - mtpTypeId type() const { - return mtpc_vector; - } - [[nodiscard]] bool read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_vector) { - if (from + 1 > end || cons != mtpc_vector) { - return false; - } - auto count = static_cast(*(from++)); - - auto vector = QVector(count, T()); - for (auto &item : vector) { - if (!item.read(from, end)) { - return false; - } - } - v = std::move(vector); - return true; - } - void write(mtpBuffer &to) const { - to.push_back(v.size()); - for (const auto &item : v) { - item.write(to); - } - } - - QVector v; - -private: - explicit MTPvector(QVector &&data) : v(std::move(data)) { - } - - template - friend MTPvector MTP_vector(uint32 count); - template - friend MTPvector MTP_vector(uint32 count, const U &value); - template - friend MTPvector MTP_vector(const QVector &v); - template - friend MTPvector MTP_vector(QVector &&v); - template - friend MTPvector MTP_vector(); - -}; template inline MTPvector MTP_vector(uint32 count) { - return MTPvector(QVector(count)); + return tl::make_vector(count); } template inline MTPvector MTP_vector(uint32 count, const T &value) { - return MTPvector(QVector(count, value)); + return tl::make_vector(count, value); } template inline MTPvector MTP_vector(const QVector &v) { - return MTPvector(QVector(v)); + return tl::make_vector(v); } template inline MTPvector MTP_vector(QVector &&v) { - return MTPvector(std::move(v)); + return tl::make_vector(std::move(v)); } template inline MTPvector MTP_vector() { - return MTPvector(); -} -template -using MTPVector = MTPBoxed>; - -template -inline bool operator==(const MTPvector &a, const MTPvector &b) { - return a.c_vector().v == b.c_vector().v; -} -template -inline bool operator!=(const MTPvector &a, const MTPvector &b) { - return a.c_vector().v != b.c_vector().v; + return tl::make_vector(); } // Human-readable text serialization diff --git a/Telegram/SourceFiles/settings/settings_codes.cpp b/Telegram/SourceFiles/settings/settings_codes.cpp index a97f13942..505146b57 100644 --- a/Telegram/SourceFiles/settings/settings_codes.cpp +++ b/Telegram/SourceFiles/settings/settings_codes.cpp @@ -156,6 +156,7 @@ auto GenerateCodes() { Ui::show(Box("All sound overrides were reset.")); } }); + return codes; } diff --git a/Telegram/gyp/lib_scheme.gyp b/Telegram/gyp/lib_scheme.gyp index d9ec6806c..9b58783d5 100644 --- a/Telegram/gyp/lib_scheme.gyp +++ b/Telegram/gyp/lib_scheme.gyp @@ -31,9 +31,11 @@ }]], 'dependencies': [ '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', + '<(submodules_loc)/lib_tl/lib_tl.gyp:lib_tl', ], 'export_dependent_settings': [ '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', + '<(submodules_loc)/lib_tl/lib_tl.gyp:lib_tl', ], 'include_dirs': [ '<(src_loc)', @@ -49,6 +51,7 @@ 'action_name': 'codegen_scheme', 'inputs': [ '<(src_loc)/codegen/scheme/codegen_scheme.py', + '<(submodules_loc)/lib_tl/tl/generate_tl.py', '<(res_loc)/tl/mtproto.tl', '<(res_loc)/tl/api.tl', ], @@ -58,7 +61,7 @@ ], 'action': [ 'python', '<(src_loc)/codegen/scheme/codegen_scheme.py', - '-o', '<(SHARED_INTERMEDIATE_DIR)', + '-o', '<(SHARED_INTERMEDIATE_DIR)/scheme', '<(res_loc)/tl/mtproto.tl', '<(res_loc)/tl/api.tl', ], diff --git a/Telegram/lib_tl b/Telegram/lib_tl new file mode 160000 index 000000000..ab4e30d38 --- /dev/null +++ b/Telegram/lib_tl @@ -0,0 +1 @@ +Subproject commit ab4e30d38914142d6abbb64389a00cd6f8ef55d2 From c88ee34b1d81270833ca9660b587f89b782d1bec Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 2 Oct 2019 14:43:54 +0300 Subject: [PATCH 24/59] Fix build with range-v3 0.9.1. --- Telegram/gyp/helpers | 2 +- Telegram/lib_ui | 2 +- docs/building-cmake.md | 7 ++++--- docs/building-msvc.md | 9 ++++++--- docs/building-xcode.md | 7 ++++--- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Telegram/gyp/helpers b/Telegram/gyp/helpers index 0d9d39ab7..ac60d8fa2 160000 --- a/Telegram/gyp/helpers +++ b/Telegram/gyp/helpers @@ -1 +1 @@ -Subproject commit 0d9d39ab7dd1ecd80235c647fc4bf62d580cbf6c +Subproject commit ac60d8fa23ffca6bfc23efa0ee10696a47579ea6 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 84d68e48c..199e51706 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 84d68e48c0808f6641b4ea7badc5a977052010a1 +Subproject commit 199e51706f651922cc9ab30f08306d63d462941d diff --git a/docs/building-cmake.md b/docs/building-cmake.md index 6bcd90d66..5f5a0df91 100644 --- a/docs/building-cmake.md +++ b/docs/building-cmake.md @@ -42,6 +42,7 @@ Go to ***BuildPath*** and run make $MAKE_THREADS_CNT sudo make install + git clone https://github.com/desktop-app/patches.git git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 git clone https://github.com/telegramdesktop/zlib.git @@ -225,7 +226,7 @@ Go to ***BuildPath*** and run git checkout v5.6.2 cd qtimageformats && git checkout v5.6.2 && cd .. cd qtbase && git checkout v5.6.2 && cd .. - cd qtbase && git apply ../../../tdesktop/Telegram/Patches/qtbase_5_6_2.diff && cd .. + cd qtbase && git apply ../../patches/qtbase_5_6_2.diff && cd .. cd qtbase/src/plugins/platforminputcontexts git clone https://github.com/telegramdesktop/fcitx.git git clone https://github.com/telegramdesktop/hime.git @@ -240,8 +241,8 @@ Go to ***BuildPath*** and run git clone https://chromium.googlesource.com/external/gyp cd gyp - git checkout 702ac58e47 - git apply ../../tdesktop/Telegram/Patches/gyp.diff + git checkout 9f2a7bb1 + git apply ../patches/gyp.diff cd .. git clone https://chromium.googlesource.com/breakpad/breakpad diff --git a/docs/building-msvc.md b/docs/building-msvc.md index 74e513eba..8324432a0 100644 --- a/docs/building-msvc.md +++ b/docs/building-msvc.md @@ -30,9 +30,11 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** and run cd ThirdParty - git clone https://github.com/telegramdesktop/gyp.git + git clone https://github.com/desktop-app/patches.git + git clone https://chromium.googlesource.com/external/gyp cd gyp - git checkout tdesktop + git checkout 9f2a7bb1 + git apply ../patches/gyp.diff cd ..\.. Add **GYP** and **Ninja** to your PATH: @@ -55,6 +57,7 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** mkdir Libraries cd Libraries + git clone https://github.com/desktop-app/patches.git git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 range-v3 git clone https://github.com/telegramdesktop/lzma.git @@ -138,7 +141,7 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** git checkout v5.6.2 cd ..\qtbase git checkout v5.6.2 - git apply ../../../tdesktop/Telegram/Patches/qtbase_5_6_2.diff + git apply ../../patches/qtbase_5_6_2.diff cd .. configure -debug-and-release -force-debug-info -opensource -confirm-license -static -I "%cd%\..\openssl\inc32" -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="%cd%\..\openssl\out32.dbg\ssleay32.lib %cd%\..\openssl\out32.dbg\libeay32.lib" OPENSSL_LIBS_RELEASE="%cd%\..\openssl\out32\ssleay32.lib %cd%\..\openssl\out32\libeay32.lib" -mp -nomake examples -nomake tests -platform win32-msvc2015 diff --git a/docs/building-xcode.md b/docs/building-xcode.md index c96c0bb87..a0b09c1d8 100644 --- a/docs/building-xcode.md +++ b/docs/building-xcode.md @@ -30,6 +30,7 @@ Go to ***BuildPath*** and run cd Libraries + git clone https://github.com/desktop-app/patches.git git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 cd xz-5.0.5 @@ -192,8 +193,8 @@ Go to ***BuildPath*** and run git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH="$PWD/depot_tools:$PATH" cd gyp - git checkout 702ac58e47 - git apply ../../tdesktop/Telegram/Patches/gyp.diff + git checkout 9f2a7bb1 + git apply ../patches/gyp.diff ./setup.py build sudo ./setup.py install cd .. @@ -223,7 +224,7 @@ Go to ***BuildPath*** and run perl init-repository --module-subset=qtbase,qtimageformats git checkout v5.6.2 cd qtimageformats && git checkout v5.6.2 && cd .. - cd qtbase && git checkout v5.6.2 && git apply ../../../tdesktop/Telegram/Patches/qtbase_5_6_2.diff && cd .. + cd qtbase && git checkout v5.6.2 && git apply ../../patches/qtbase_5_6_2.diff && cd .. ./configure -prefix "/usr/local/desktop-app/Qt-5.6.2" -debug-and-release -force-debug-info -opensource -confirm-license -static -opengl desktop -no-openssl -securetransport -nomake examples -nomake tests -platform macx-clang make $MAKE_THREADS_CNT From 9a498616c67a7b396754b7f7158c1d27f1288072 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 2 Oct 2019 18:54:36 +0300 Subject: [PATCH 25/59] Fix build for Xcode 11. --- Telegram/gyp/telegram/mac.gypi | 3 +++ docs/building-xcode.md | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Telegram/gyp/telegram/mac.gypi b/Telegram/gyp/telegram/mac.gypi index 4a9f29c7a..d43b9c16c 100644 --- a/Telegram/gyp/telegram/mac.gypi +++ b/Telegram/gyp/telegram/mac.gypi @@ -17,6 +17,9 @@ 'CURRENT_PROJECT_VERSION': ' Date: Thu, 3 Oct 2019 12:11:10 +0300 Subject: [PATCH 26/59] Update lib_ui. Fixes #6632, fixes #6635. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 199e51706..f762cc8b8 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 199e51706f651922cc9ab30f08306d63d462941d +Subproject commit f762cc8b87ba16706e402b6d66b1451402357555 From 01936b5f1b4d7b4b6945eb85a71ebfaa783124a1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Oct 2019 10:32:26 +0400 Subject: [PATCH 27/59] Build with Qt 5.12.5 and OpenSSL 1.1.1. --- Telegram/SourceFiles/core/utils.cpp | 3 - .../SourceFiles/ffmpeg/ffmpeg_utility.cpp | 4 +- Telegram/SourceFiles/mainwindow.cpp | 2 +- Telegram/SourceFiles/mtproto/core_types.h | 1 + .../platform/mac/specific_mac_p.mm | 10 +++ .../platform/win/file_utilities_win.cpp | 6 +- Telegram/SourceFiles/qt_static_plugins.cpp | 6 +- Telegram/codegen | 2 +- Telegram/gyp/helpers | 2 +- Telegram/gyp/telegram/linux.gypi | 5 ++ Telegram/gyp/tests/tests.gyp | 2 +- Telegram/gyp/utils.gyp | 18 +--- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- docs/building-cmake.md | 25 +++--- docs/building-msvc.md | 42 +++++----- docs/building-xcode.md | 84 ++++++++++--------- 17 files changed, 116 insertions(+), 100 deletions(-) diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp index 0daea75a1..54ded4de8 100644 --- a/Telegram/SourceFiles/core/utils.cpp +++ b/Telegram/SourceFiles/core/utils.cpp @@ -392,7 +392,6 @@ namespace ThirdParty { } else { LOG(("MTP Error: Could not init OpenSSL threads, CRYPTO_num_locks() returned zero!")); } - CRYPTO_THREADID_set_callback(_sslThreadId); } if (!CRYPTO_get_dynlock_create_callback()) { CRYPTO_set_dynlock_create_callback(_sslCreateFunction); @@ -419,9 +418,7 @@ namespace ThirdParty { #endif ENGINE_cleanup(); CONF_modules_unload(1); - ERR_remove_state(0); ERR_free_strings(); - ERR_remove_thread_state(nullptr); EVP_cleanup(); delete[] base::take(_sslLocks); diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp index 3775f7503..85347c75b 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp @@ -48,7 +48,7 @@ void AlignedImageBufferCleanupHandler(void* data) { } void UnPremultiplyLine(uchar *dst, const uchar *src, int intsCount) { -#ifdef TDESKTOP_OFFICIAL_TARGET +#ifndef TDESKTOP_OFFICIAL_TARGET // #TODO 5.12.5 const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; const auto convert = layout->convertFromARGB32PM; #else // TDESKTOP_OFFICIAL_TARGET @@ -74,7 +74,7 @@ void UnPremultiplyLine(uchar *dst, const uchar *src, int intsCount) { } void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) { -#ifdef TDESKTOP_OFFICIAL_TARGET +#ifndef TDESKTOP_OFFICIAL_TARGET // #TODO 5.12.5 const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; const auto convert = layout->convertToARGB32PM; #else // TDESKTOP_OFFICIAL_TARGET diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index b6d9734de..ef8fcf6db 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -578,7 +578,7 @@ void MainWindow::updateTrayMenu(bool force) { // On macOS just remove trayIcon menu if the window is not active. // So we will activate the window on click instead of showing the menu. - if (!active && Platform::IsMac()) { + if (!active && Platform::IsMac() && false) { // #TODO 5.12.5 iconMenu = nullptr; } } diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 9b8d8e8ec..7eebb9172 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/algorithm.h" #include "base/assertion.h" #include "tl/tl_basic_types.h" +#include "tl/tl_boxed.h" #include #include diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 82805bf8e..824bec2f6 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +#include #include #include @@ -284,6 +285,15 @@ void objc_outputDebugString(const QString &str) { } void objc_start() { + // Patch: Fix macOS regression. On 10.14.4, it crashes on GPU switches. + // See https://bugreports.qt.io/browse/QTCREATORBUG-22215 + const auto version = QOperatingSystemVersion::current(); + if (version.majorVersion() == 10 + && version.minorVersion() == 14 + && version.microVersion() == 4) { + qputenv("QT_MAC_PRO_WEBENGINE_WORKAROUND", "1"); + } + _sharedDelegate = [[ApplicationDelegate alloc] init]; [[NSApplication sharedApplication] setDelegate:_sharedDelegate]; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: _sharedDelegate diff --git a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp index 5c3b13ba7..d8ebc80ce 100644 --- a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp +++ b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp @@ -421,9 +421,9 @@ bool Get( } else { files = dialog.selectedFiles().mid(0, 1); } - if (type == Type::ReadFile || type == Type::ReadFiles) { - remoteContent = dialog.selectedRemoteContent(); - } + //if (type == Type::ReadFile || type == Type::ReadFiles) { + // remoteContent = dialog.selectedRemoteContent(); + //} return true; } diff --git a/Telegram/SourceFiles/qt_static_plugins.cpp b/Telegram/SourceFiles/qt_static_plugins.cpp index a757d085f..a33f12701 100644 --- a/Telegram/SourceFiles/qt_static_plugins.cpp +++ b/Telegram/SourceFiles/qt_static_plugins.cpp @@ -7,15 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include -#ifdef Q_OS_WIN Q_IMPORT_PLUGIN(QWebpPlugin) +Q_IMPORT_PLUGIN(QJpegPlugin) +Q_IMPORT_PLUGIN(QGifPlugin) +#ifdef Q_OS_WIN Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) #elif defined Q_OS_MAC // Q_OS_WIN -Q_IMPORT_PLUGIN(QWebpPlugin) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) Q_IMPORT_PLUGIN(QGenericEnginePlugin) #elif defined Q_OS_LINUX // Q_OS_WIN | Q_OS_MAC -Q_IMPORT_PLUGIN(QWebpPlugin) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) Q_IMPORT_PLUGIN(QConnmanEnginePlugin) Q_IMPORT_PLUGIN(QGenericEnginePlugin) diff --git a/Telegram/codegen b/Telegram/codegen index 017228939..3a61c3a32 160000 --- a/Telegram/codegen +++ b/Telegram/codegen @@ -1 +1 @@ -Subproject commit 01722893988a6573e1ba910028fbc0c596c2ae5b +Subproject commit 3a61c3a32c51e652f639ae506201a31db48359f1 diff --git a/Telegram/gyp/helpers b/Telegram/gyp/helpers index ac60d8fa2..f70c0fda2 160000 --- a/Telegram/gyp/helpers +++ b/Telegram/gyp/helpers @@ -1 +1 @@ -Subproject commit ac60d8fa23ffca6bfc23efa0ee10696a47579ea6 +Subproject commit f70c0fda21bfe9d011bb1ff780f476826bb5f8a2 diff --git a/Telegram/gyp/telegram/linux.gypi b/Telegram/gyp/telegram/linux.gypi index 635036031..494d1381e 100644 --- a/Telegram/gyp/telegram/linux.gypi +++ b/Telegram/gyp/telegram/linux.gypi @@ -20,6 +20,7 @@ ], }, 'libraries': [ + '-Wl,-Bstatic', '-lbreakpad_client', '-llzma', '-lopenal', @@ -35,6 +36,10 @@ '-lvdpau', '-ldrm', '-lz', + '-lXi', + '-lXext', + '-lXfixes', + '-lXrender', # ' /dev/null --libs <@(pkgconfig_libs))', ], 'cflags_cc': [ diff --git a/Telegram/gyp/tests/tests.gyp b/Telegram/gyp/tests/tests.gyp index 0b43a0635..43ae1d696 100644 --- a/Telegram/gyp/tests/tests.gyp +++ b/Telegram/gyp/tests/tests.gyp @@ -12,7 +12,7 @@ 'src_loc': '../../SourceFiles', 'base_loc': '<(submodules_loc)/lib_base', 'rpl_loc': '<(submodules_loc)/lib_rpl', - 'mac_target': '10.10', + 'mac_target': '10.12', 'list_tests_command': 'python <(DEPTH)/tests/list_tests.py --input <(DEPTH)/tests/tests_list.txt', }, 'targets': [{ diff --git a/Telegram/gyp/utils.gyp b/Telegram/gyp/utils.gyp index 15c5f65ae..7d5dafaba 100644 --- a/Telegram/gyp/utils.gyp +++ b/Telegram/gyp/utils.gyp @@ -57,40 +57,30 @@ 'target_name': 'Packer', 'variables': { 'src_loc': '../SourceFiles', - 'mac_target': '10.10', + 'mac_target': '10.12', }, 'includes': [ 'helpers/common/executable.gypi', 'helpers/modules/qt.gypi', + 'helpers/modules/openssl.gypi', ], 'conditions': [ [ 'build_win', { 'libraries': [ - 'libeay32', - 'ssleay32', - 'Crypt32', 'zlibstat', 'LzmaLib', ], }], [ 'build_linux', { 'libraries': [ - 'ssl', - 'crypto', + '<(linux_lib_ssl)', + '<(linux_lib_crypto)', 'lzma', ], }], [ 'build_mac', { - 'include_dirs': [ - '<(libs_loc)/openssl/include' - ], - 'library_dirs': [ - '<(libs_loc)/openssl', - ], 'xcode_settings': { 'OTHER_LDFLAGS': [ - '-lssl', - '-lcrypto', '-llzma', ], }, diff --git a/Telegram/lib_base b/Telegram/lib_base index cfc1a874e..dd5ca832f 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit cfc1a874eb603c345f60be2ce98a885d9f336d14 +Subproject commit dd5ca832f9b212e553ea5afee9a2dab2c3acd982 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index f762cc8b8..aa6bf8b0e 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit f762cc8b87ba16706e402b6d66b1451402357555 +Subproject commit aa6bf8b0e7c18426140a15e79a43f7d24b62c80a diff --git a/docs/building-cmake.md b/docs/building-cmake.md index 5f5a0df91..bda4edefc 100644 --- a/docs/building-cmake.md +++ b/docs/building-cmake.md @@ -204,10 +204,10 @@ Go to ***BuildPath*** and run sudo make install cd ../.. - git clone https://github.com/openssl/openssl - cd openssl - git checkout OpenSSL_1_0_1-stable - ./config + git clone https://github.com/openssl/openssl openssl_1_1_1 + cd openssl_1_1_1 + git checkout OpenSSL_1_1_1-stable + ./config --prefix=/usr/local/desktop-app/openssl-1.1.1 make $MAKE_THREADS_CNT sudo make install cd .. @@ -220,20 +220,21 @@ Go to ***BuildPath*** and run sudo make install cd .. - git clone git://code.qt.io/qt/qt5.git qt5_6_2 - cd qt5_6_2 + git clone git://code.qt.io/qt/qt5.git qt_5_12_5 + cd qt_5_12_5 perl init-repository --module-subset=qtbase,qtimageformats - git checkout v5.6.2 - cd qtimageformats && git checkout v5.6.2 && cd .. - cd qtbase && git checkout v5.6.2 && cd .. - cd qtbase && git apply ../../patches/qtbase_5_6_2.diff && cd .. - cd qtbase/src/plugins/platforminputcontexts + git checkout v5.12.5 + git submodule update qtbase + git submodule update qtimageformats + cd qtbase + git apply ../../patches/qtbase_5_12_5.diff + cd src/plugins/platforminputcontexts git clone https://github.com/telegramdesktop/fcitx.git git clone https://github.com/telegramdesktop/hime.git git clone https://github.com/telegramdesktop/nimf.git cd ../../../.. - ./configure -prefix "/usr/local/desktop-app/Qt-5.6.2" -release -force-debug-info -opensource -confirm-license -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb -qt-xkbcommon-x11 -no-opengl -no-gtkstyle -static -openssl-linked -nomake examples -nomake tests + ./configure -prefix "/usr/local/desktop-app/Qt-5.12.5" -release -force-debug-info -opensource -confirm-license -qt-zlib -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-pcre -qt-xcb -system-freetype -fontconfig -no-opengl -no-gtk -static -openssl-linked -I "/usr/local/desktop-app/openssl-1.1.1/include" OPENSSL_LIBS="/usr/local/desktop-app/openssl-1.1.1/lib/libssl.a /usr/local/desktop-app/openssl-1.1.1/lib/libcrypto.a -ldl -lpthread" -nomake examples -nomake tests make $MAKE_THREADS_CNT sudo make install diff --git a/docs/building-msvc.md b/docs/building-msvc.md index 8324432a0..57bcbdc5d 100644 --- a/docs/building-msvc.md +++ b/docs/building-msvc.md @@ -57,6 +57,8 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** mkdir Libraries cd Libraries + SET LibrariesPath=%cd% + git clone https://github.com/desktop-app/patches.git git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 range-v3 @@ -66,15 +68,21 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** msbuild LzmaLib.sln /property:Configuration=Release cd ..\..\..\.. - git clone https://github.com/openssl/openssl.git - cd openssl - git checkout OpenSSL_1_0_1-stable - perl Configure no-shared --prefix="C:\Program Files (x86)\OpenSSL" --openssldir="C:\Program Files (x86)\Common Files\SSL" VC-WIN32 - ms\do_ms - nmake -f ms\nt.mak - perl Configure no-shared --prefix="C:\Program Files (x86)\OpenSSL" --openssldir="C:\Program Files (x86)\Common Files\SSL" debug-VC-WIN32 - ms\do_ms - nmake -f ms\nt.mak + git clone https://github.com/openssl/openssl.git openssl_1_1_1 + cd openssl_1_1_1 + git checkout OpenSSL_1_1_1-stable + perl Configure no-share VC-WIN32 + nmake + mkdir out32 + move libcrypto.lib out32 + move libssl.lib out32 + move ossl_static.pdb out32 + perl Configure no-shared debug-VC-WIN32 + nmake + mkdir out32.dbg + move libcrypto.lib out32.dbg + move libssl.lib out32.dbg + move ossl_static.pdb out32.dbg cd .. git clone https://github.com/telegramdesktop/zlib.git @@ -133,18 +141,14 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** SET PATH=%PATH_BACKUP_% cd .. - git clone git://code.qt.io/qt/qt5.git qt5_6_2 - cd qt5_6_2 + git clone git://code.qt.io/qt/qt5.git qt_5_12_5 + cd qt_5_12_5 perl init-repository --module-subset=qtbase,qtimageformats - git checkout v5.6.2 - cd qtimageformats - git checkout v5.6.2 - cd ..\qtbase - git checkout v5.6.2 - git apply ../../patches/qtbase_5_6_2.diff - cd .. + git checkout v5.12.5 + git submodule update qtbase + git submodule update qtimageformats - configure -debug-and-release -force-debug-info -opensource -confirm-license -static -I "%cd%\..\openssl\inc32" -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="%cd%\..\openssl\out32.dbg\ssleay32.lib %cd%\..\openssl\out32.dbg\libeay32.lib" OPENSSL_LIBS_RELEASE="%cd%\..\openssl\out32\ssleay32.lib %cd%\..\openssl\out32\libeay32.lib" -mp -nomake examples -nomake tests -platform win32-msvc2015 + configure -prefix "%LibrariesPath%\Qt-5.12.5" -debug-and-release -force-debug-info -opensource -confirm-license -static -static-runtime -I "%cd%\..\openssl_1_1_1\include" -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="%cd%\..\openssl_1_1_1\out32.dbg\libssl.lib %cd%\..\openssl_1_1_1\out32.dbg\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" OPENSSL_LIBS_RELEASE="%cd%\..\openssl_1_1_1\out32\libssl.lib %cd%\..\openssl_1_1_1\out32\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" -mp -nomake examples -nomake tests -platform win32-msvc jom -j4 jom -j4 install diff --git a/docs/building-xcode.md b/docs/building-xcode.md index 2fa5a2ac6..736ecdc23 100644 --- a/docs/building-xcode.md +++ b/docs/building-xcode.md @@ -10,16 +10,16 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. ### Download libraries -Download [**xz-5.0.5**](http://tukaani.org/xz/xz-5.0.5.tar.gz) and unpack to ***BuildPath*/Libraries/xz-5.0.5** +Download [**xz-5.0.5**](http://tukaani.org/xz/xz-5.0.5.tar.gz) and unpack to ***BuildPath*/Libraries/macos/xz-5.0.5** -Download [**libiconv-1.15**](http://www.gnu.org/software/libiconv/#downloading) and unpack to ***BuildPath*/Libraries/libiconv-1.15** +Download [**libiconv-1.15**](http://www.gnu.org/software/libiconv/#downloading) and unpack to ***BuildPath*/Libraries/macos/libiconv-1.15** ### Clone source code and prepare libraries Go to ***BuildPath*** and run - MAKE_THREADS_CNT=-j4 - MACOSX_DEPLOYMENT_TARGET=10.8 + MAKE_THREADS_CNT=-j8 + MACOSX_DEPLOYMENT_TARGET=10.12 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew install automake cmake fdk-aac git lame libass libtool libvorbis libvpx ninja opus sdl shtool texi2html theora wget x264 xvid yasm pkg-config @@ -28,28 +28,43 @@ Go to ***BuildPath*** and run git clone --recursive https://github.com/telegramdesktop/tdesktop.git - cd Libraries + mkdir ThirdParty + cd ThirdParty + + git clone https://github.com/desktop-app/patches.git + git clone https://chromium.googlesource.com/external/gyp + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + export PATH="$PWD/depot_tools:$PATH" + cd gyp + git checkout 9f2a7bb1 + git apply ../patches/gyp.diff + ./setup.py build + sudo ./setup.py install + cd ../.. + + cd Libraries/macos + LibrariesPath=`pwd` git clone https://github.com/desktop-app/patches.git git clone --branch 0.9.1 https://github.com/ericniebler/range-v3 cd xz-5.0.5 - CFLAGS="-mmacosx-version-min=10.8" LDFLAGS="-mmacosx-version-min=10.8" ./configure - make + CFLAGS="-mmacosx-version-min=10.12" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos + make $MAKE_THREADS_CNT sudo make install cd .. git clone https://github.com/telegramdesktop/zlib.git cd zlib - CFLAGS="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.8" ./configure + CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos make $MAKE_THREADS_CNT sudo make install cd .. - git clone https://github.com/openssl/openssl - cd openssl - git checkout OpenSSL_1_0_1-stable - ./Configure darwin64-x86_64-cc -static -mmacosx-version-min=10.8 + git clone https://github.com/openssl/openssl openssl_1_1_1 + cd openssl_1_1_1 + git checkout OpenSSL_1_1_1-stable + ./Configure --prefix=/usr/local/macos darwin64-x86_64-cc -static -mmacosx-version-min=10.12 make build_libs $MAKE_THREADS_CNT cd .. @@ -57,13 +72,13 @@ Go to ***BuildPath*** and run cd opus git checkout v1.3 ./autogen.sh - CFLAGS="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.8" ./configure + CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos make $MAKE_THREADS_CNT sudo make install cd .. cd libiconv-1.15 - CFLAGS="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.8" ./configure --enable-static + CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --enable-static --prefix=/usr/local/macos make $MAKE_THREADS_CNT sudo make install cd .. @@ -75,10 +90,10 @@ Go to ***BuildPath*** and run LDFLAGS=`freetype-config --libs` PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig - ./configure --prefix=/usr/local \ - --extra-cflags="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" \ - --extra-cxxflags="-mmacosx-version-min=10.8 -Werror=unguarded-availability-new" \ - --extra-ldflags="-mmacosx-version-min=10.8" \ + ./configure --prefix=/usr/local/macos \ + --extra-cflags="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" \ + --extra-cxxflags="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" \ + --extra-ldflags="-mmacosx-version-min=10.12" \ --enable-protocol=file --enable-libopus \ --disable-programs \ --disable-doc \ @@ -184,49 +199,42 @@ Go to ***BuildPath*** and run cd openal-soft git checkout v1.19 cd build - CFLAGS='-Werror=unguarded-availability-new' CPPFLAGS='-Werror=unguarded-availability-new' LDFLAGS='-stdlib=libc++' cmake -D ALSOFT_EXAMPLES=OFF -D LIBTYPE:STRING=STATIC -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.8 .. + CFLAGS='-Werror=unguarded-availability-new' CPPFLAGS='-Werror=unguarded-availability-new' cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr/local/macos -D ALSOFT_EXAMPLES=OFF -D LIBTYPE:STRING=STATIC -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.12 .. make $MAKE_THREADS_CNT sudo make install cd ../.. - git clone https://chromium.googlesource.com/external/gyp - git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git - export PATH="$PWD/depot_tools:$PATH" - cd gyp - git checkout 9f2a7bb1 - git apply ../patches/gyp.diff - ./setup.py build - sudo ./setup.py install - cd .. - git clone https://chromium.googlesource.com/crashpad/crashpad.git cd crashpad git checkout feb3aa3923 - git apply ../../tdesktop/Telegram/Patches/crashpad.diff + git apply ../../../tdesktop/Telegram/Patches/crashpad.diff cd third_party/mini_chromium git clone https://chromium.googlesource.com/chromium/mini_chromium cd mini_chromium git checkout 7c5b0c1ab4 - git apply ../../../../../tdesktop/Telegram/Patches/mini_chromium.diff + git apply ../../../../../../tdesktop/Telegram/Patches/mini_chromium.diff cd ../../gtest git clone https://chromium.googlesource.com/external/github.com/google/googletest gtest cd gtest git checkout d62d6c6556 cd ../../.. - build/gyp_crashpad.py -Dmac_deployment_target=10.8 + build/gyp_crashpad.py -Dmac_deployment_target=10.10 ninja -C out/Debug ninja -C out/Release cd .. - git clone git://code.qt.io/qt/qt5.git qt5_6_2 - cd qt5_6_2 + git clone git://code.qt.io/qt/qt5.git qt5_12_5 + cd qt5_12_5 perl init-repository --module-subset=qtbase,qtimageformats - git checkout v5.6.2 - cd qtimageformats && git checkout v5.6.2 && cd .. - cd qtbase && git checkout v5.6.2 && git apply ../../patches/qtbase_5_6_2.diff && cd .. + git checkout v5.12.5 + git submodule update qtbase + git submodule update qtimageformats + cd qtbase + git apply ../../patches/qtbase_5_12_5.diff + cd .. - ./configure -prefix "/usr/local/desktop-app/Qt-5.6.2" -debug-and-release -force-debug-info -opensource -confirm-license -static -opengl desktop -no-openssl -securetransport -nomake examples -nomake tests -platform macx-clang + ./configure -prefix "/usr/local/desktop-app/Qt-5.12.5" -debug-and-release -force-debug-info -opensource -confirm-license -static -opengl desktop -no-openssl -securetransport -nomake examples -nomake tests -platform macx-clang make $MAKE_THREADS_CNT sudo make install cd .. From b880d4aa30bd0d75483d3c947d10429a1aededbc Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Oct 2019 11:14:55 +0400 Subject: [PATCH 28/59] Use custom named fonts. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index aa6bf8b0e..e824bb2f6 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit aa6bf8b0e7c18426140a15e79a43f7d24b62c80a +Subproject commit e824bb2f6bbfbc061506c07b879005a36a7025c1 From 85acdbc7ed34167844c18006c7796889cb641735 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Oct 2019 12:54:38 +0400 Subject: [PATCH 29/59] Disable custom scaling by envvar. --- Telegram/SourceFiles/core/launcher.cpp | 10 +++++++--- Telegram/lib_ui | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 883c985a7..4b04f8ed4 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "base/platform/base_platform_info.h" #include "ui/main_queue_processor.h" +#include "ui/ui_utility.h" #include "core/crash_reports.h" #include "core/update_checker.h" #include "core/sandbox.h" @@ -268,9 +269,12 @@ int Launcher::exec() { return psCleanup(); } - // both are finished in Sandbox::closeApplication - Logs::start(this); // must be started before Platform is started - Platform::start(); // must be started before Sandbox is created + // Must be started before Platform is started. + Logs::start(this); + + // Must be started before Sandbox is created. + Platform::start(); + Ui::DisableCustomScaling(); auto result = executeApplication(); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index e824bb2f6..bc62f87f0 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit e824bb2f6bbfbc061506c07b879005a36a7025c1 +Subproject commit bc62f87f0ec7a0ef21ad4f676ef65cbb3d3631a4 From 28719939a0b37111107e0a64edd615ac69cf1108 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Oct 2019 15:30:52 +0400 Subject: [PATCH 30/59] Fix tray icon on macOS in Qt 5.12.5. --- Telegram/SourceFiles/mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index ef8fcf6db..24e641e7f 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -578,7 +578,7 @@ void MainWindow::updateTrayMenu(bool force) { // On macOS just remove trayIcon menu if the window is not active. // So we will activate the window on click instead of showing the menu. - if (!active && Platform::IsMac() && false) { // #TODO 5.12.5 + if (!active && Platform::IsMac()) { iconMenu = nullptr; } } @@ -703,6 +703,9 @@ void MainWindow::handleTrayIconActication( QSystemTrayIcon::ActivationReason reason) { updateIsActive(0); if (Platform::IsMac() && isActive()) { + if (trayIcon && !trayIcon->contextMenu()) { + showFromTray(reason); + } return; } if (reason == QSystemTrayIcon::Context) { From 3ce72d069689a7a0a95eea19b2e69bb9dd0b20f4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Oct 2019 15:38:43 +0400 Subject: [PATCH 31/59] Closed alpha version 1.8.15.1. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++--- Telegram/Resources/winrc/Updater.rc | 8 ++--- Telegram/SourceFiles/core/version.h | 2 +- Telegram/build/build.sh | 32 ++++++++++---------- Telegram/build/version | 2 +- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 7904b4d0e..aff2328db 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="1.8.15.1" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 90f7cd204..7b3302095 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -33,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,15,0 - PRODUCTVERSION 1,8,15,0 + FILEVERSION 1,8,15,1 + PRODUCTVERSION 1,8,15,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "1.8.15.0" + VALUE "FileVersion", "1.8.15.1" VALUE "LegalCopyright", "Copyright (C) 2014-2019" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.8.15.0" + VALUE "ProductVersion", "1.8.15.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index b2f182d8e..cf8e1762c 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -24,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,15,0 - PRODUCTVERSION 1,8,15,0 + FILEVERSION 1,8,15,1 + PRODUCTVERSION 1,8,15,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,10 +42,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "1.8.15.0" + VALUE "FileVersion", "1.8.15.1" VALUE "LegalCopyright", "Copyright (C) 2014-2019" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.8.15.0" + VALUE "ProductVersion", "1.8.15.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 51ac20105..0d27e859e 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#define TDESKTOP_REQUESTED_ALPHA_VERSION (0ULL) +#define TDESKTOP_REQUESTED_ALPHA_VERSION (1008015001ULL) #ifdef TDESKTOP_OFFICIAL_TARGET #define TDESKTOP_ALPHA_VERSION TDESKTOP_REQUESTED_ALPHA_VERSION diff --git a/Telegram/build/build.sh b/Telegram/build/build.sh index 02a4aa188..91d3a6d69 100755 --- a/Telegram/build/build.sh +++ b/Telegram/build/build.sh @@ -135,25 +135,25 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then Error "$BinaryName not found!" fi - BadCount=`objdump -T $ReleasePath/$BinaryName | grep GLIBC_2\.1[6-9] | wc -l` - if [ "$BadCount" != "0" ]; then - Error "Bad GLIBC usages found: $BadCount" - fi + # BadCount=`objdump -T $ReleasePath/$BinaryName | grep GLIBC_2\.1[6-9] | wc -l` + # if [ "$BadCount" != "0" ]; then + # Error "Bad GLIBC usages found: $BadCount" + # fi - BadCount=`objdump -T $ReleasePath/$BinaryName | grep GLIBC_2\.2[0-9] | wc -l` - if [ "$BadCount" != "0" ]; then - Error "Bad GLIBC usages found: $BadCount" - fi + # BadCount=`objdump -T $ReleasePath/$BinaryName | grep GLIBC_2\.2[0-9] | wc -l` + # if [ "$BadCount" != "0" ]; then + # Error "Bad GLIBC usages found: $BadCount" + # fi - BadCount=`objdump -T $ReleasePath/$BinaryName | grep GCC_4\.[3-9] | wc -l` - if [ "$BadCount" != "0" ]; then - Error "Bad GCC usages found: $BadCount" - fi + # BadCount=`objdump -T $ReleasePath/$BinaryName | grep GCC_4\.[3-9] | wc -l` + # if [ "$BadCount" != "0" ]; then + # Error "Bad GCC usages found: $BadCount" + # fi - BadCount=`objdump -T $ReleasePath/$BinaryName | grep GCC_[5-9]\. | wc -l` - if [ "$BadCount" != "0" ]; then - Error "Bad GCC usages found: $BadCount" - fi + # BadCount=`objdump -T $ReleasePath/$BinaryName | grep GCC_[5-9]\. | wc -l` + # if [ "$BadCount" != "0" ]; then + # Error "Bad GCC usages found: $BadCount" + # fi if [ ! -f "$ReleasePath/Updater" ]; then Error "Updater not found!" diff --git a/Telegram/build/version b/Telegram/build/version index 8afc73acc..3c197d77a 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 1.8 AppVersionStrSmall 1.8.15 AppVersionStr 1.8.15 BetaChannel 0 -AlphaVersion 0 +AlphaVersion 1008015001 From e745bb7718316d38d68a6ce47ebf3b53d86425bb Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 17 Oct 2019 19:07:54 +0400 Subject: [PATCH 32/59] Fix instructions for MSVC. --- docs/building-msvc.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/building-msvc.md b/docs/building-msvc.md index 57bcbdc5d..86dbc7beb 100644 --- a/docs/building-msvc.md +++ b/docs/building-msvc.md @@ -71,18 +71,20 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** git clone https://github.com/openssl/openssl.git openssl_1_1_1 cd openssl_1_1_1 git checkout OpenSSL_1_1_1-stable - perl Configure no-share VC-WIN32 - nmake - mkdir out32 - move libcrypto.lib out32 - move libssl.lib out32 - move ossl_static.pdb out32 perl Configure no-shared debug-VC-WIN32 nmake mkdir out32.dbg move libcrypto.lib out32.dbg move libssl.lib out32.dbg - move ossl_static.pdb out32.dbg + move ossl_static.pdb out32.dbg\ossl_static + nmake clean + move out32.dbg\ossl_static out32.dbg\ossl_static.pdb + perl Configure no-shared VC-WIN32 + nmake + mkdir out32 + move libcrypto.lib out32 + move libssl.lib out32 + move ossl_static.pdb out32 cd .. git clone https://github.com/telegramdesktop/zlib.git @@ -147,8 +149,11 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** git checkout v5.12.5 git submodule update qtbase git submodule update qtimageformats + cd qtbase + git apply ../../patches/qtbase_5_12_5.diff + cd .. - configure -prefix "%LibrariesPath%\Qt-5.12.5" -debug-and-release -force-debug-info -opensource -confirm-license -static -static-runtime -I "%cd%\..\openssl_1_1_1\include" -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="%cd%\..\openssl_1_1_1\out32.dbg\libssl.lib %cd%\..\openssl_1_1_1\out32.dbg\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" OPENSSL_LIBS_RELEASE="%cd%\..\openssl_1_1_1\out32\libssl.lib %cd%\..\openssl_1_1_1\out32\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" -mp -nomake examples -nomake tests -platform win32-msvc + configure -prefix "%LibrariesPath%\Qt-5.12.5" -debug-and-release -force-debug-info -opensource -confirm-license -static -static-runtime -I "%LibrariesPath%\openssl_1_1_1\include" -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="%LibrariesPath%\openssl_1_1_1\out32.dbg\libssl.lib %LibrariesPath%\openssl_1_1_1\out32.dbg\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" OPENSSL_LIBS_RELEASE="%LibrariesPath%\openssl_1_1_1\out32\libssl.lib %LibrariesPath%\openssl_1_1_1\out32\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" -mp -nomake examples -nomake tests -platform win32-msvc jom -j4 jom -j4 install From e952c513d5076e3304e64bca216b00d8da3d6f6d Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 12:13:20 +0400 Subject: [PATCH 33/59] Use old non-crashing rlottie. --- Telegram/ThirdParty/rlottie | 2 +- Telegram/lib_rlottie | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/rlottie b/Telegram/ThirdParty/rlottie index fceedfb5e..c490c7a09 160000 --- a/Telegram/ThirdParty/rlottie +++ b/Telegram/ThirdParty/rlottie @@ -1 +1 @@ -Subproject commit fceedfb5e1a226934771d146839518c6dc4780c0 +Subproject commit c490c7a098b9b3cbc3195b00e90d6fc3989e2ba2 diff --git a/Telegram/lib_rlottie b/Telegram/lib_rlottie index b52e0c2cc..0671bf705 160000 --- a/Telegram/lib_rlottie +++ b/Telegram/lib_rlottie @@ -1 +1 @@ -Subproject commit b52e0c2cc1daa8cba6a0cec278ee9914020aabe1 +Subproject commit 0671bf70547381effcf442ec9618e04502a8adbc From 3ae7f9f93db0dfaa3f531a963671c740443eda80 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 13:00:36 +0400 Subject: [PATCH 34/59] Fix color conversion for Qt 5.12.5. --- .../SourceFiles/ffmpeg/ffmpeg_utility.cpp | 70 +++++++------------ 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp index 85347c75b..77378351e 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp @@ -48,55 +48,37 @@ void AlignedImageBufferCleanupHandler(void* data) { } void UnPremultiplyLine(uchar *dst, const uchar *src, int intsCount) { -#ifndef TDESKTOP_OFFICIAL_TARGET // #TODO 5.12.5 - const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; - const auto convert = layout->convertFromARGB32PM; -#else // TDESKTOP_OFFICIAL_TARGET - const auto layout = nullptr; - const auto convert = []( - uint *dst, - const uint *src, - int count, - std::nullptr_t, - std::nullptr_t) { - for (auto i = 0; i != count; ++i) { - dst[i] = qUnpremultiply(src[i]); - } - }; -#endif // TDESKTOP_OFFICIAL_TARGET + [[maybe_unused]] const auto udst = reinterpret_cast(dst); + const auto usrc = reinterpret_cast(src); - convert( - reinterpret_cast(dst), - reinterpret_cast(src), - intsCount, - layout, - nullptr); +#ifndef TDESKTOP_OFFICIAL_TARGET + for (auto i = 0; i != intsCount; ++i) { + udst[i] = qUnpremultiply(usrc[i]); + } +#elif QT_VERSION < QT_VERSION_CHECK(5, 12, 0) + static const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; + layout->convertFromARGB32PM(udst, usrc, intsCount, layout, nullptr); +#else // Qt >= 5.12 + static const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; + layout->storeFromARGB32PM(dst, usrc, 0, intsCount, nullptr, nullptr); +#endif // Qt >= 5.12 } void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) { -#ifndef TDESKTOP_OFFICIAL_TARGET // #TODO 5.12.5 - const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; - const auto convert = layout->convertToARGB32PM; -#else // TDESKTOP_OFFICIAL_TARGET - const auto layout = nullptr; - const auto convert = []( - uint *dst, - const uint *src, - int count, - std::nullptr_t, - std::nullptr_t) { - for (auto i = 0; i != count; ++i) { - dst[i] = qPremultiply(src[i]); - } - }; -#endif // TDESKTOP_OFFICIAL_TARGET + const auto udst = reinterpret_cast(dst); + [[maybe_unused]] const auto usrc = reinterpret_cast(src); - convert( - reinterpret_cast(dst), - reinterpret_cast(src), - intsCount, - layout, - nullptr); +#ifndef TDESKTOP_OFFICIAL_TARGET + for (auto i = 0; i != count; ++i) { + dst[i] = qPremultiply(src[i]); + } +#elif QT_VERSION < QT_VERSION_CHECK(5, 12, 0) + static const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; + layout->convertToARGB32PM(udst, usrc, intsCount, layout, nullptr); +#else // Qt >= 5.12 + static const auto layout = &qPixelLayouts[QImage::Format_ARGB32]; + layout->fetchToARGB32PM(udst, src, 0, intsCount, nullptr, nullptr); +#endif // Qt >= 5.12 } } // namespace From 27a83a7a09ca2d0e00c1a36dbaa59d9eae972148 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 14:22:46 +0400 Subject: [PATCH 35/59] Use base::FileNameFromUserString. --- Telegram/SourceFiles/core/file_utilities.cpp | 47 ----------------- Telegram/SourceFiles/core/file_utilities.h | 2 - Telegram/SourceFiles/data/data_document.cpp | 9 +--- .../export/data/export_data_types.cpp | 51 ++----------------- .../window/themes/window_theme_editor_box.cpp | 3 +- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 7 files changed, 9 insertions(+), 107 deletions(-) diff --git a/Telegram/SourceFiles/core/file_utilities.cpp b/Telegram/SourceFiles/core/file_utilities.cpp index 3b0591df6..a90ee52b9 100644 --- a/Telegram/SourceFiles/core/file_utilities.cpp +++ b/Telegram/SourceFiles/core/file_utilities.cpp @@ -160,53 +160,6 @@ QString DefaultDownloadPath() { + '/'; } -QString NameFromUserString(QString name) { - static const auto Bad = { '/', '\\', '<', '>', ':', '"', '|', '?', '*' }; - for (auto &ch : name) { - if (ch < 32 || ranges::find(Bad, ch.unicode()) != end(Bad)) { - ch = '_'; - } - } - if (name.isEmpty() || name.endsWith(' ') || name.endsWith('.')) { - name.append('_'); - } -#ifdef Q_OS_WIN - static const auto BadNames = { - qstr("CON"), - qstr("PRN"), - qstr("AUX"), - qstr("NUL"), - qstr("COM1"), - qstr("COM2"), - qstr("COM3"), - qstr("COM4"), - qstr("COM5"), - qstr("COM6"), - qstr("COM7"), - qstr("COM8"), - qstr("COM9"), - qstr("LPT1"), - qstr("LPT2"), - qstr("LPT3"), - qstr("LPT4"), - qstr("LPT5"), - qstr("LPT6"), - qstr("LPT7"), - qstr("LPT8"), - qstr("LPT9") - }; - for (const auto bad : BadNames) { - if (name.startsWith(bad, Qt::CaseInsensitive)) { - if (name.size() == bad.size() || name[bad.size()] == '.') { - name = '_' + name; - break; - } - } - } -#endif // Q_OS_WIN - return name; -} - namespace internal { void UnsafeOpenEmailLinkDefault(const QString &email) { diff --git a/Telegram/SourceFiles/core/file_utilities.h b/Telegram/SourceFiles/core/file_utilities.h index 8d913f2a9..8e25f691e 100644 --- a/Telegram/SourceFiles/core/file_utilities.h +++ b/Telegram/SourceFiles/core/file_utilities.h @@ -37,8 +37,6 @@ void ShowInFolder(const QString &filepath); [[nodiscard]] QString DefaultDownloadPath(); -[[nodiscard]] QString NameFromUserString(QString name); - namespace internal { inline QString UrlToLocalDefault(const QUrl &url) { diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index a036b84b2..f23a484b0 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/image/image_source.h" #include "ui/text/text_utilities.h" +#include "base/base_file_utilities.h" #include "mainwindow.h" #include "core/application.h" #include "lottie/lottie_animation.h" @@ -136,13 +137,7 @@ QString FileNameUnsafe( QString name, bool savingAs, const QDir &dir) { -#ifdef Q_OS_WIN - name = name.replace(QRegularExpression(qsl("[\\\\\\/\\:\\*\\?\\\"\\<\\>\\|]")), qsl("_")); -#elif defined Q_OS_MAC - name = name.replace(QRegularExpression(qsl("[\\:]")), qsl("_")); -#elif defined Q_OS_LINUX - name = name.replace(QRegularExpression(qsl("[\\/]")), qsl("_")); -#endif + name = base::FileNameFromUserString(name); if (Global::AskDownloadPath() || savingAs) { if (!name.isEmpty() && name.at(0) == QChar::fromLatin1('.')) { name = filedialogDefaultName(prefix, name); diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index aab49845e..1b006d2df 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "export/export_settings.h" #include "export/output/export_output_file.h" +#include "base/base_file_utilities.h" #include "core/mime_type.h" #include "core/utils.h" #include @@ -337,53 +338,6 @@ QString ComputeDocumentName( } } -QString CleanDocumentName(QString name) { - // We don't want LTR/RTL mark/embedding/override/isolate chars - // in filenames, because they introduce a security issue, when - // an executable "Fil[x]gepj.exe" may look like "Filexe.jpeg". - QChar controls[] = { - 0x200E, // LTR Mark - 0x200F, // RTL Mark - 0x202A, // LTR Embedding - 0x202B, // RTL Embedding - 0x202D, // LTR Override - 0x202E, // RTL Override - 0x2066, // LTR Isolate - 0x2067, // RTL Isolate -#ifdef Q_OS_WIN - '\\', - '/', - ':', - '*', - '?', - '"', - '<', - '>', - '|', -#elif defined Q_OS_MAC // Q_OS_WIN - ':', -#elif defined Q_OS_LINUX // Q_OS_WIN || Q_OS_MAC - '/', -#endif // Q_OS_WIN || Q_OS_MAC || Q_OS_LINUX - }; - for (const auto ch : controls) { - name = std::move(name).replace(ch, '_'); - } - -#ifdef Q_OS_WIN - const auto lower = name.trimmed().toLower(); - const auto kBadExtensions = { qstr(".lnk"), qstr(".scf") }; - const auto kMaskExtension = qsl(".download"); - for (const auto extension : kBadExtensions) { - if (lower.endsWith(extension)) { - return name + kMaskExtension; - } - } -#endif // Q_OS_WIN - - return name; -} - QString DocumentFolder(const Document &data) { if (data.isVideoFile) { return "video_files"; @@ -469,7 +423,8 @@ Document ParseDocument( MTP_string()); result.file.suggestedPath = suggestedFolder + DocumentFolder(result) + '/' - + CleanDocumentName(ComputeDocumentName(context, result, date)); + + base::FileNameFromUserString( + ComputeDocumentName(context, result, date)); result.thumb = ParseDocumentThumb( data, diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 31d5af17f..06ecd901e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "lang/lang_keys.h" #include "base/event_filter.h" +#include "base/base_file_utilities.h" #include "base/zlib_help.h" #include "base/unixtime.h" #include "data/data_session.h" @@ -435,7 +436,7 @@ SendMediaReady PrepareThemeMedia( }; push("s", std::move(thumbnail)); - const auto filename = File::NameFromUserString(name) + const auto filename = base::FileNameFromUserString(name) + qsl(".tdesktop-theme"); auto attributes = QVector( 1, diff --git a/Telegram/lib_base b/Telegram/lib_base index dd5ca832f..2169055d7 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit dd5ca832f9b212e553ea5afee9a2dab2c3acd982 +Subproject commit 2169055d740dc069924e609fcc473006e5511134 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index bc62f87f0..37f777e23 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit bc62f87f0ec7a0ef21ad4f676ef65cbb3d3631a4 +Subproject commit 37f777e230215aeba0ee6eba149cff7b64d7ed0d From 768a3d5a1213c6bf1e2af6008b756e3640d5e4d9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 14:23:02 +0400 Subject: [PATCH 36/59] Backport a fix for custom window title. --- .../platform/mac/main_window_mac.mm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 482d12d76..6bd5211d9 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - (void) screenIsUnlocked:(NSNotification *)aNotification; - (void) windowWillEnterFullScreen:(NSNotification *)aNotification; - (void) windowWillExitFullScreen:(NSNotification *)aNotification; +- (void) windowDidExitFullScreen:(NSNotification *)aNotification; @end // @interface MainWindowObserver @@ -138,6 +139,7 @@ public: void willEnterFullScreen(); void willExitFullScreen(); + void didExitFullScreen(); bool clipboardHasText(); @@ -148,6 +150,7 @@ public: private: void initCustomTitle(); void refreshWeakTitleReferences(); + void enforceCorrectStyleMask(); not_null _public; friend class MainWindow; @@ -211,6 +214,10 @@ private: _private->willExitFullScreen(); } +- (void) windowDidExitFullScreen:(NSNotification *)aNotification { + _private->didExitFullScreen(); +} + @end // @implementation MainWindowObserver namespace Platform { @@ -284,6 +291,7 @@ void MainWindow::Private::initCustomTitle() { [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:_nativeWindow]; [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:_nativeWindow]; + [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:_nativeWindow]; // Qt has bug with layer-backed widgets containing QOpenGLWidgets. // See https://bugreports.qt.io/browse/QTBUG-64494 @@ -403,6 +411,17 @@ void MainWindow::Private::willEnterFullScreen() { void MainWindow::Private::willExitFullScreen() { _inFullScreen = false; _public->setTitleVisible(true); + enforceCorrectStyleMask(); +} + +void MainWindow::Private::didExitFullScreen() { + enforceCorrectStyleMask(); +} + +void MainWindow::Private::enforceCorrectStyleMask() { + if (_nativeWindow && _public->_customTitleHeight > 0) { + [_nativeWindow setStyleMask:[_nativeWindow styleMask] | NSFullSizeContentViewWindowMask]; + } } void MainWindow::Private::enableShadow(WId winId) { From 467be135d64cd1dca5040f00d4d5322870cf854d Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 15:24:09 +0400 Subject: [PATCH 37/59] Fix build in macOS. --- Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp | 2 +- .../window/themes/window_theme_preview.cpp | 18 +++++++++--------- Telegram/lib_base | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp index 77378351e..4693f2e11 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp @@ -69,7 +69,7 @@ void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) { [[maybe_unused]] const auto usrc = reinterpret_cast(src); #ifndef TDESKTOP_OFFICIAL_TARGET - for (auto i = 0; i != count; ++i) { + for (auto i = 0; i != intsCount; ++i) { dst[i] = qPremultiply(src[i]); } #elif QT_VERSION < QT_VERSION_CHECK(5, 12, 0) diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index 27b62369f..8df16f30b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -992,19 +992,19 @@ QImage GeneratePreview( } int DefaultPreviewTitleHeight() { - return st::titleHeight; + return st::defaultWindowTitle.height; } void DefaultPreviewWindowTitle(Painter &p, const style::palette &palette, QRect body, int outerWidth) { - auto titleRect = QRect(body.x(), body.y() - st::titleHeight, body.width(), st::titleHeight); + auto titleRect = QRect(body.x(), body.y() - st::defaultWindowTitle.height, body.width(), st::defaultWindowTitle.height); p.fillRect(titleRect, QColor(0, 0, 0)); p.fillRect(titleRect, st::titleBgActive[palette]); - auto right = st::titleButtonClose.width; - st::titleButtonClose.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonClose.iconPosition.x(), titleRect.y() + st::titleButtonClose.iconPosition.y(), outerWidth); - right += st::titleButtonMaximize.width; - st::titleButtonMaximize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonMaximize.iconPosition.x(), titleRect.y() + st::titleButtonMaximize.iconPosition.y(), outerWidth); - right += st::titleButtonMinimize.width; - st::titleButtonMinimize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonMinimize.iconPosition.x(), titleRect.y() + st::titleButtonMinimize.iconPosition.y(), outerWidth); + auto right = st::windowTitleButtonClose.width; + st::windowTitleButtonClose.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::windowTitleButtonClose.iconPosition.x(), titleRect.y() + st::windowTitleButtonClose.iconPosition.y(), outerWidth); + right += st::defaultWindowTitle.maximize.width; + st::defaultWindowTitle.maximize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::defaultWindowTitle.maximize.iconPosition.x(), titleRect.y() + st::defaultWindowTitle.maximize.iconPosition.y(), outerWidth); + right += st::defaultWindowTitle.minimize.width; + st::defaultWindowTitle.minimize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::defaultWindowTitle.minimize.iconPosition.x(), titleRect.y() + st::defaultWindowTitle.minimize.iconPosition.y(), outerWidth); p.fillRect(titleRect.x(), titleRect.y() + titleRect.height() - st::lineWidth, titleRect.width(), st::lineWidth, st::titleShadow[palette]); } @@ -1052,7 +1052,7 @@ void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palet Painter p(&preview); DefaultPreviewWindowTitle(p, palette, body, outerWidth); - auto inner = QRect(body.x(), body.y() - st::titleHeight, body.width(), body.height() + st::titleHeight); + auto inner = QRect(body.x(), body.y() - st::defaultWindowTitle.height, body.width(), body.height() + st::defaultWindowTitle.height); p.setClipRegion(QRegion(inner.marginsAdded(QMargins(size, size, size, size))) - inner); p.drawImage(inner.x() - left, inner.y() - top, topLeft); p.drawImage(inner.x() + inner.width() + right - width, inner.y() - top, topRight); diff --git a/Telegram/lib_base b/Telegram/lib_base index 2169055d7..7f9a2d5e9 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 2169055d740dc069924e609fcc473006e5511134 +Subproject commit 7f9a2d5e9a3145106c3181ad38356f6335101f84 From 92d48a7846d306d8f8e5dff3bad306eafeb5baa6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 15:32:44 +0400 Subject: [PATCH 38/59] Fix build on Windows. --- .../platform/win/main_window_win.cpp | 2 +- .../platform/win/window_title_win.cpp | 42 ++++++++++++++----- .../platform/win/window_title_win.h | 5 +++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 721eb3dd3..1b6c8c370 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -192,7 +192,7 @@ public: max_w = avail.width(); accumulate_max(max_w, st::windowMinWidth); max_h = avail.height(); - accumulate_max(max_h, st::titleHeight + st::windowMinHeight); + accumulate_max(max_h, st::defaultWindowTitle.height + st::windowMinHeight); HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); HWND hwnd = _window ? _window->psHwnd() : nullptr; diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index 194eda739..9e2687f4f 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -15,10 +15,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { -TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) -, _minimize(this, st::titleButtonMinimize) -, _maximizeRestore(this, st::titleButtonMaximize) -, _close(this, st::titleButtonClose) +TitleWidget::TitleWidget(QWidget *parent) +: Window::TitleWidget(parent) +, _st(st::defaultWindowTitle) +, _minimize(this, _st.minimize) +, _maximizeRestore(this, _st.maximize) +, _close(this, _st.close) , _shadow(this, st::titleShadow) , _maximizedState(parent->window()->windowState() & Qt::WindowMaximized) { _minimize->setClickedCallback([this]() { @@ -38,7 +40,7 @@ TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) _close->setPointerCursor(false); setAttribute(Qt::WA_OpaquePaintEvent); - resize(width(), st::titleHeight); + resize(width(), _st.height); } void TitleWidget::init() { @@ -54,7 +56,7 @@ void TitleWidget::paintEvent(QPaintEvent *e) { _activeState = active; updateButtonsState(); } - Painter(this).fillRect(rect(), active ? st::titleBgActive : st::titleBg); + Painter(this).fillRect(rect(), active ? _st.bgActive : _st.bg); } void TitleWidget::updateControlsPosition() { @@ -85,13 +87,33 @@ void TitleWidget::onWindowStateChanged(Qt::WindowState state) { } void TitleWidget::updateButtonsState() { - _minimize->setIconOverride(_activeState ? &st::titleButtonMinimizeIconActive : nullptr, _activeState ? &st::titleButtonMinimizeIconActiveOver : nullptr); + _minimize->setIconOverride(_activeState + ? &_st.minimizeIconActive + : nullptr, + _activeState + ? &_st.minimizeIconActiveOver + : nullptr); if (_maximizedState) { - _maximizeRestore->setIconOverride(_activeState ? &st::titleButtonRestoreIconActive : &st::titleButtonRestoreIcon, _activeState ? &st::titleButtonRestoreIconActiveOver : &st::titleButtonRestoreIconOver); + _maximizeRestore->setIconOverride( + _activeState + ? &_st.restoreIconActive : &_st.restoreIcon, + _activeState + ? &_st.restoreIconActiveOver + : &_st.restoreIconOver); } else { - _maximizeRestore->setIconOverride(_activeState ? &st::titleButtonMaximizeIconActive : nullptr, _activeState ? &st::titleButtonMaximizeIconActiveOver : nullptr); + _maximizeRestore->setIconOverride(_activeState + ? &_st.maximizeIconActive + : nullptr, + _activeState + ? &_st.maximizeIconActiveOver + : nullptr); } - _close->setIconOverride(_activeState ? &st::titleButtonCloseIconActive : nullptr, _activeState ? &st::titleButtonCloseIconActiveOver : nullptr); + _close->setIconOverride(_activeState + ? &_st.closeIconActive + : nullptr, + _activeState + ? &_st.closeIconActiveOver + : nullptr); } Window::HitTestResult TitleWidget::hitTest(const QPoint &p) const { diff --git a/Telegram/SourceFiles/platform/win/window_title_win.h b/Telegram/SourceFiles/platform/win/window_title_win.h index 19e0a1246..ce7af371e 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.h +++ b/Telegram/SourceFiles/platform/win/window_title_win.h @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_window_title.h" #include "base/object_ptr.h" +namespace style { +struct WindowTitle; +} // namespace style + namespace Ui { class IconButton; class PlainShadow; @@ -48,6 +52,7 @@ private: void updateButtonsState(); void updateControlsPosition(); + const style::WindowTitle &_st; object_ptr _minimize; object_ptr _maximizeRestore; object_ptr _close; From d535f5b3bcd1df413ab09efefd9e38b5e1f228c0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 21:09:07 +0400 Subject: [PATCH 39/59] Fix build in Linux. --- Telegram/gyp/telegram/linux.gypi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/gyp/telegram/linux.gypi b/Telegram/gyp/telegram/linux.gypi index 494d1381e..ba6bc45e8 100644 --- a/Telegram/gyp/telegram/linux.gypi +++ b/Telegram/gyp/telegram/linux.gypi @@ -40,6 +40,8 @@ '-lXext', '-lXfixes', '-lXrender', + '<(linux_lib_ssl)', + '<(linux_lib_crypto)', # ' /dev/null --libs <@(pkgconfig_libs))', ], 'cflags_cc': [ From 1056021059dc17e8b86be216e8e33c0947454165 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 5 Oct 2019 19:38:25 +0300 Subject: [PATCH 40/59] Added new setting to disable spellchecker. --- Telegram/Resources/langs/lang.strings | 3 ++ Telegram/SourceFiles/main/main_settings.cpp | 6 ++++ Telegram/SourceFiles/main/main_settings.h | 14 ++++++++ .../settings/settings_advanced.cpp | 32 +++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 5b6d8f672..9a338b7e7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -410,6 +410,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_enable_animations" = "Enable animations"; "lng_settings_autoplay_gifs" = "Autoplay GIFs"; +"lng_settings_spellchecker" = "Spell checker"; +"lng_settings_system_spellchecker" = "Use system spell checker"; + "lng_backgrounds_header" = "Choose your new chat background"; "lng_theme_sure_keep" = "Keep this theme?"; "lng_theme_reverting#one" = "Reverting to the old theme in {count} second."; diff --git a/Telegram/SourceFiles/main/main_settings.cpp b/Telegram/SourceFiles/main/main_settings.cpp index 52b719711..6bb4173fb 100644 --- a/Telegram/SourceFiles/main/main_settings.cpp +++ b/Telegram/SourceFiles/main/main_settings.cpp @@ -89,6 +89,7 @@ QByteArray Settings::serialize() const { stream << qint32(_variables.replaceEmoji.current() ? 1 : 0); stream << qint32(_variables.suggestEmoji ? 1 : 0); stream << qint32(_variables.suggestStickersByEmoji ? 1 : 0); + stream << qint32(_variables.spellcheckerEnabled.current() ? 1 : 0); } return result; } @@ -135,6 +136,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { qint32 replaceEmoji = _variables.replaceEmoji.current() ? 1 : 0; qint32 suggestEmoji = _variables.suggestEmoji ? 1 : 0; qint32 suggestStickersByEmoji = _variables.suggestStickersByEmoji ? 1 : 0; + qint32 spellcheckerEnabled = _variables.spellcheckerEnabled.current() ? 1 : 0; stream >> selectorTab; stream >> lastSeenWarningSeen; @@ -234,6 +236,9 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { stream >> suggestEmoji; stream >> suggestStickersByEmoji; } + if (!stream.atEnd()) { + stream >> spellcheckerEnabled; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Main::Settings::constructFromSerialized()")); @@ -313,6 +318,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { _variables.replaceEmoji = (replaceEmoji == 1); _variables.suggestEmoji = (suggestEmoji == 1); _variables.suggestStickersByEmoji = (suggestStickersByEmoji == 1); + _variables.spellcheckerEnabled = (spellcheckerEnabled == 1); } void Settings::setSupportChatsTimeSlice(int slice) { diff --git a/Telegram/SourceFiles/main/main_settings.h b/Telegram/SourceFiles/main/main_settings.h index c92242c82..a02e6abe5 100644 --- a/Telegram/SourceFiles/main/main_settings.h +++ b/Telegram/SourceFiles/main/main_settings.h @@ -231,6 +231,19 @@ public: _variables.suggestStickersByEmoji = value; } + void setSpellcheckerEnabled(bool value) { + _variables.spellcheckerEnabled = value; + } + bool spellcheckerEnabled() const { + return _variables.spellcheckerEnabled.current(); + } + rpl::producer spellcheckerEnabledValue() const { + return _variables.spellcheckerEnabled.value(); + } + rpl::producer spellcheckerEnabledChanges() const { + return _variables.spellcheckerEnabled.changes(); + } + private: struct Variables { Variables(); @@ -270,6 +283,7 @@ private: rpl::variable replaceEmoji = true; bool suggestEmoji = true; bool suggestStickersByEmoji = true; + rpl::variable spellcheckerEnabled = true; static constexpr auto kDefaultSupportChatsLimitSlice = 7 * 24 * 60 * 60; diff --git a/Telegram/SourceFiles/settings/settings_advanced.cpp b/Telegram/SourceFiles/settings/settings_advanced.cpp index 36720cc6b..d7db99416 100644 --- a/Telegram/SourceFiles/settings/settings_advanced.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced.cpp @@ -243,6 +243,30 @@ void SetupUpdate(not_null container) { }); } +bool HasSystemSpellchecker() { + return (Platform::IsWindows() && Platform::IsWindows8OrGreater()) + || Platform::IsMac(); +} + +void SetupSpellchecker( + not_null controller, + not_null container) { + const auto session = &controller->session(); + AddButton( + container, + tr::lng_settings_system_spellchecker(), + st::settingsButton + )->toggleOn( + rpl::single(session->settings().spellcheckerEnabled()) + )->toggledValue( + ) | rpl::filter([=](bool enabled) { + return (enabled != session->settings().spellcheckerEnabled()); + }) | rpl::start_with_next([=](bool enabled) { + session->settings().setSpellcheckerEnabled(enabled); + session->saveSettingsDelayed(); + }, container->lifetime()); +} + bool HasTray() { return cSupportTray() || Platform::IsWindows(); } @@ -515,6 +539,14 @@ void Advanced::setupContent(not_null controller) { SetupPerformance(controller, content); AddSkip(content); + if (HasSystemSpellchecker()) { + AddSkip(content); + AddDivider(content); + AddSubsectionTitle(content, tr::lng_settings_spellchecker()); + SetupSpellchecker(controller, content); + AddSkip(content); + } + if (cAutoUpdate()) { addUpdate(); } From 6e95cfc24dacd2a29841648e8a50c410cc112fd6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 18 Oct 2019 16:51:00 +0300 Subject: [PATCH 41/59] Added lib_spellcheck. --- .gitmodules | 3 +++ Telegram/gyp/Telegram.gyp | 1 + Telegram/lib_spellcheck | 1 + 3 files changed, 5 insertions(+) create mode 160000 Telegram/lib_spellcheck diff --git a/.gitmodules b/.gitmodules index 2876230de..f51e8e319 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,6 @@ [submodule "Telegram/lib_tl"] path = Telegram/lib_tl url = https://github.com/desktop-app/lib_tl.git +[submodule "Telegram/lib_spellcheck"] + path = Telegram/lib_spellcheck + url = https://github.com/desktop-app/lib_spellcheck diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 3cfba863d..cf3e1619c 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -76,6 +76,7 @@ '<(submodules_loc)/codegen/codegen.gyp:codegen_style', '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', '<(submodules_loc)/lib_ui/lib_ui.gyp:lib_ui', + '<(submodules_loc)/lib_spellcheck/lib_spellcheck.gyp:lib_spellcheck', '<(third_party_loc)/libtgvoip/libtgvoip.gyp:libtgvoip', '<(submodules_loc)/lib_lottie/lib_lottie.gyp:lib_lottie', 'tests/tests.gyp:tests', diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck new file mode 160000 index 000000000..157037fab --- /dev/null +++ b/Telegram/lib_spellcheck @@ -0,0 +1 @@ +Subproject commit 157037fab0aa4c60507a14b622ce582f5832b370 From 4be178c75fd7402c0258ddacb1c18258a274aa42 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 18 Oct 2019 16:54:26 +0300 Subject: [PATCH 42/59] Added SpellingHighlighter to InputField in HistoryWidget and some boxes. --- Telegram/SourceFiles/boxes/edit_caption_box.cpp | 2 ++ Telegram/SourceFiles/boxes/edit_caption_box.h | 5 +++++ Telegram/SourceFiles/boxes/send_files_box.cpp | 2 ++ Telegram/SourceFiles/boxes/send_files_box.h | 5 +++++ Telegram/SourceFiles/boxes/share_box.cpp | 1 + Telegram/SourceFiles/boxes/share_box.h | 5 +++++ Telegram/SourceFiles/chat_helpers/message_field.cpp | 13 +++++++++++++ Telegram/SourceFiles/chat_helpers/message_field.h | 4 ++++ Telegram/SourceFiles/history/history_widget.cpp | 1 + Telegram/SourceFiles/history/history_widget.h | 5 +++++ 10 files changed, 43 insertions(+) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 8870d947b..06d17191b 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -268,6 +268,8 @@ EditCaptionBox::EditCaptionBox( _field->setEditLinkCallback( DefaultEditLinkCallback(&_controller->session(), _field)); + _spelling = InitSpellchecker(&_controller->session(), _field); + auto r = object_ptr>( this, object_ptr( diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 14e7cd5c3..825bc8aeb 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -24,6 +24,10 @@ namespace Data { class Media; } // namespace Data +namespace Spellchecker { +class SpellingHighlighter; +} // namespace Spellchecker + namespace Ui { class InputField; class EmojiButton; @@ -99,6 +103,7 @@ private: object_ptr _emojiToggle = { nullptr }; base::unique_qptr _emojiPanel; base::unique_qptr _emojiFilter; + base::unique_qptr _spelling; int _thumbx = 0; int _thumbw = 0; diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 2a458900e..cb0a589fd 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -1679,6 +1679,8 @@ void SendFilesBox::setupCaption() { _caption, &_controller->session()); + _spelling = InitSpellchecker(&_controller->session(), _caption); + updateCaptionPlaceholder(); setupEmojiPanel(); } diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 23f741295..716f5e739 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -40,6 +40,10 @@ namespace Window { class SessionController; } // namespace Window +namespace Spellchecker { +class SpellingHighlighter; +} // namespace Spellchecker + enum class SendMenuType; enum class SendFilesWay { @@ -150,6 +154,7 @@ private: object_ptr _emojiToggle = { nullptr }; base::unique_qptr _emojiPanel; base::unique_qptr _emojiFilter; + base::unique_qptr _spelling; object_ptr> _sendAlbum = { nullptr }; object_ptr> _sendPhotos = { nullptr }; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 82fea368f..9d234ff89 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -207,6 +207,7 @@ void ShareBox::prepareCommentField() { field->setEditLinkCallback( DefaultEditLinkCallback(&_navigation->session(), field)); + _spelling = InitSpellchecker(&_navigation->session(), field); Ui::SendPendingMoveResizeEvents(_comment); } diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 55d223c04..00e9e3d7a 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -36,6 +36,10 @@ namespace Notify { struct PeerUpdate; } // namespace Notify +namespace Spellchecker { +class SpellingHighlighter; +} // namespace Spellchecker + namespace Ui { class MultiSelect; class InputField; @@ -113,6 +117,7 @@ private: object_ptr _select; object_ptr> _comment; + base::unique_qptr _spelling; class Inner; QPointer _inner; diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 43fb29500..0987915cf 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -78,6 +78,7 @@ private: QString _startLink; Fn _callback; Fn _setInnerFocus; + base::unique_qptr _spelling; }; @@ -127,6 +128,7 @@ void EditLinkBox::prepare() { getDelegate()->outerContainer(), text, _session); + _spelling = InitSpellchecker(_session, text); const auto url = content->add( object_ptr( @@ -273,6 +275,17 @@ void InitMessageField( DefaultEditLinkCallback(&controller->session(), field)); } +base::unique_qptr InitSpellchecker( + not_null session, + not_null field) { + auto s = base::make_unique_q( + field->rawTextEdit(), + session->settings().spellcheckerEnabledValue(), + field->documentContentsChanges()); + field->setExtendedContextMenu(s->contextMenuCreated()); + return s; +} + bool HasSendText(not_null field) { const auto &text = field->getTextWithTags().text; for (const auto ch : text) { diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index 8077c032b..e327327d1 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "spellcheck/spelling_highlighter.h" #include "ui/widgets/input_fields.h" #include "base/timer.h" #include "base/qt_connection.h" @@ -34,6 +35,9 @@ Fn controller, not_null field); +base::unique_qptr InitSpellchecker( + not_null session, + not_null field); bool HasSendText(not_null field); struct InlineBotQuery { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index af5bf73b7..d11525600 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -411,6 +411,7 @@ HistoryWidget::HistoryWidget( } Unexpected("action in MimeData hook."); }); + _spelling = InitSpellchecker(&controller->session(), _field); const auto suggestions = Ui::Emoji::SuggestionsController::Init( this, diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index df9eb8c98..48706e9ff 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -86,6 +86,10 @@ class ContactStatus; class Element; } // namespace HistoryView +namespace Spellchecker { +class SpellingHighlighter; +} // namespace Spellchecker + class DragArea; class SendFilesBox; class BotKeyboard; @@ -741,6 +745,7 @@ private: object_ptr _scheduled = { nullptr }; bool _cmdStartShown = false; object_ptr _field; + base::unique_qptr _spelling; bool _recording = false; bool _inField = false; bool _inReplyEditForward = false; From aa9dc2bee20f8ae11d3baba4ea9c7060292a2b56 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 18 Oct 2019 18:13:51 +0300 Subject: [PATCH 43/59] Added phrases for spellchecker. --- Telegram/Resources/langs/lang.strings | 4 ++++ Telegram/SourceFiles/chat_helpers/message_field.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9a338b7e7..f72fad17b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1594,6 +1594,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_formatting_link_url" = "URL"; "lng_formatting_link_create" = "Create"; +"lng_spellchecker_add" = "Add to Dictionary"; +"lng_spellchecker_remove" = "Remove from Dictionary"; +"lng_spellchecker_ignore" = "Ignore word"; + "lng_full_name" = "{first_name} {last_name}"; "lng_confirm_phone_link_invalid" = "This link is broken or has expired."; diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 0987915cf..253ee4e7a 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -282,6 +282,11 @@ base::unique_qptr InitSpellchecker( field->rawTextEdit(), session->settings().spellcheckerEnabledValue(), field->documentContentsChanges()); + Spellchecker::SetPhrases({ { + { &ph::lng_spellchecker_add, tr::lng_spellchecker_add() }, + { &ph::lng_spellchecker_remove, tr::lng_spellchecker_remove() }, + { &ph::lng_spellchecker_ignore, tr::lng_spellchecker_ignore() }, + } }); field->setExtendedContextMenu(s->contextMenuCreated()); return s; } From 155d28d6d0c66ca2926c58a90f83d1c6a2963527 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 20 Oct 2019 16:05:29 +0300 Subject: [PATCH 44/59] Updated lib_ui. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 37f777e23..fe845712a 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 37f777e230215aeba0ee6eba149cff7b64d7ed0d +Subproject commit fe845712aaab48fa7b2afd5dc45ecb69b3fdf1fa From 6529edff9227c7e2b80e0b826e72b05b099e709d Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 21:43:31 +0400 Subject: [PATCH 45/59] Closed alpha version 1.8.15.2. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 2 +- Telegram/build/version | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index aff2328db..c51c16bef 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="1.8.15.2" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 7b3302095..0eb931289 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -33,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,15,1 - PRODUCTVERSION 1,8,15,1 + FILEVERSION 1,8,15,2 + PRODUCTVERSION 1,8,15,2 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "1.8.15.1" + VALUE "FileVersion", "1.8.15.2" VALUE "LegalCopyright", "Copyright (C) 2014-2019" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.8.15.1" + VALUE "ProductVersion", "1.8.15.2" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index cf8e1762c..5d251e924 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -24,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,15,1 - PRODUCTVERSION 1,8,15,1 + FILEVERSION 1,8,15,2 + PRODUCTVERSION 1,8,15,2 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,10 +42,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "1.8.15.1" + VALUE "FileVersion", "1.8.15.2" VALUE "LegalCopyright", "Copyright (C) 2014-2019" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.8.15.1" + VALUE "ProductVersion", "1.8.15.2" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 0d27e859e..2f0f29290 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#define TDESKTOP_REQUESTED_ALPHA_VERSION (1008015001ULL) +#define TDESKTOP_REQUESTED_ALPHA_VERSION (1008015002ULL) #ifdef TDESKTOP_OFFICIAL_TARGET #define TDESKTOP_ALPHA_VERSION TDESKTOP_REQUESTED_ALPHA_VERSION diff --git a/Telegram/build/version b/Telegram/build/version index 3c197d77a..f833b5b80 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 1.8 AppVersionStrSmall 1.8.15 AppVersionStr 1.8.15 BetaChannel 0 -AlphaVersion 1008015001 +AlphaVersion 1008015002 From 272f2d937b43bbf16da4200e28926bd2df17b298 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Oct 2019 22:05:35 +0400 Subject: [PATCH 46/59] Fix build in Linux. --- Telegram/lib_spellcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 157037fab..0dc3f7048 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 157037fab0aa4c60507a14b622ce582f5832b370 +Subproject commit 0dc3f7048a829c9209b31607d28b748aa3890cf1 From d63e50944a7949ffffa0ecd5113d5ef82166eafc Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 2 Nov 2019 20:06:47 +0300 Subject: [PATCH 47/59] Update submodules to the latest spellcheck. --- .../SourceFiles/boxes/add_contact_box.cpp | 4 +- .../SourceFiles/boxes/auto_download_box.cpp | 2 +- Telegram/SourceFiles/boxes/boxes.style | 8 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 22 +-- .../SourceFiles/boxes/edit_caption_box.cpp | 2 +- .../SourceFiles/boxes/edit_privacy_box.cpp | 1 - Telegram/SourceFiles/boxes/language_box.cpp | 6 +- .../SourceFiles/boxes/local_storage_box.cpp | 2 +- Telegram/SourceFiles/boxes/peer_list_box.cpp | 2 +- .../boxes/peers/edit_linked_chat_box.cpp | 6 +- .../boxes/peers/edit_participant_box.cpp | 1 - .../boxes/peers/edit_peer_info_box.cpp | 12 +- .../boxes/peers/edit_peer_info_box.h | 13 +- .../boxes/peers/edit_peer_permissions_box.cpp | 2 +- .../boxes/peers/edit_peer_type_box.h | 9 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 2 +- Telegram/SourceFiles/boxes/sessions_box.cpp | 5 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 8 +- .../chat_helpers/chat_helpers.style | 2 +- .../SourceFiles/core/base_integration.cpp | 32 ++++ Telegram/SourceFiles/core/base_integration.h | 23 +++ Telegram/SourceFiles/core/crash_reports.h | 21 --- Telegram/SourceFiles/core/launcher.cpp | 2 + Telegram/SourceFiles/core/launcher.h | 3 + .../export/view/export_view_settings.cpp | 14 +- Telegram/SourceFiles/info/info.style | 38 ++--- .../info/media/info_media_buttons.h | 4 +- .../info/media/info_media_inner_widget.cpp | 2 +- .../info/profile/info_profile_actions.cpp | 12 +- .../info/profile/info_profile_button.cpp | 156 ------------------ .../info/profile/info_profile_button.h | 60 ------- .../profile/info_profile_inner_widget.cpp | 1 - .../info/profile/info_profile_members.cpp | 3 +- .../info/profile/info_profile_members.h | 4 +- Telegram/SourceFiles/passport/passport.style | 4 +- .../passport/passport_panel_edit_contact.cpp | 5 +- .../passport/passport_panel_edit_document.cpp | 3 +- .../passport/passport_panel_edit_document.h | 9 +- .../passport/passport_panel_edit_scans.cpp | 7 +- .../passport/passport_panel_edit_scans.h | 9 +- Telegram/SourceFiles/settings/settings.style | 16 +- .../settings/settings_advanced.cpp | 2 +- .../SourceFiles/settings/settings_calls.cpp | 2 +- .../SourceFiles/settings/settings_chat.cpp | 2 +- .../SourceFiles/settings/settings_common.cpp | 8 +- .../SourceFiles/settings/settings_common.h | 17 +- .../settings/settings_information.cpp | 1 - .../SourceFiles/settings/settings_main.cpp | 2 +- .../settings/settings_notifications.cpp | 1 - .../settings/settings_privacy_security.cpp | 2 +- Telegram/SourceFiles/ui/countryinput.cpp | 2 +- .../window/themes/window_theme_editor.cpp | 4 +- .../window/themes/window_theme_editor_box.cpp | 4 +- .../window/themes/window_theme_warning.cpp | 6 +- Telegram/SourceFiles/window/window.style | 2 +- Telegram/build/build.bat | 4 +- Telegram/gyp/telegram/sources.txt | 4 +- Telegram/gyp/telegram/win.gypi | 1 + Telegram/lib_base | 2 +- Telegram/lib_spellcheck | 2 +- Telegram/lib_ui | 2 +- 61 files changed, 198 insertions(+), 409 deletions(-) create mode 100644 Telegram/SourceFiles/core/base_integration.cpp create mode 100644 Telegram/SourceFiles/core/base_integration.h delete mode 100644 Telegram/SourceFiles/info/profile/info_profile_button.cpp delete mode 100644 Telegram/SourceFiles/info/profile/info_profile_button.h diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 9d5356464..ed831de5f 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -766,7 +766,7 @@ SetupChannelBox::SetupChannelBox( st::defaultBoxCheckbox) , _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - - st::boxButtonPadding.right() + - st::defaultBox.buttonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x()) @@ -1345,7 +1345,7 @@ void RevokePublicLinkBox::prepare() { if (callback) { callback(); } - }), st::boxLayerScroll, _innerTop); + }), st::boxScroll, _innerTop); addButton(tr::lng_cancel(), [=] { closeBox(); }); diff --git a/Telegram/SourceFiles/boxes/auto_download_box.cpp b/Telegram/SourceFiles/boxes/auto_download_box.cpp index bc4351e33..21d44e0c2 100644 --- a/Telegram/SourceFiles/boxes/auto_download_box.cpp +++ b/Telegram/SourceFiles/boxes/auto_download_box.cpp @@ -10,8 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_session.h" #include "data/data_session.h" -#include "info/profile/info_profile_button.h" #include "ui/widgets/continuous_sliders.h" +#include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/wrap.h" #include "storage/localstorage.h" diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 14d65894a..04e21e28c 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -42,7 +42,7 @@ countryRowBgOver: windowBgOver; countryRowCodeFg: windowSubTextFg; countryRowCodeFgOver: windowSubTextFgOver; countriesSkip: 12px; -countriesScroll: ScrollArea(boxLayerScroll) { +countriesScroll: ScrollArea(boxScroll) { deltat: 9px; deltab: 3px; } @@ -352,7 +352,7 @@ membersAbout: FlatLabel(defaultFlatLabel) { style: boxLabelStyle; } -sessionsScroll: boxLayerScroll; +sessionsScroll: boxScroll; sessionsHeight: 350px; sessionHeight: 70px; sessionCurrentPadding: margins(0px, 7px, 0px, 4px); @@ -490,7 +490,7 @@ langsRadio: Radio(defaultRadio) { backgroundPadding: 10px; backgroundSize: size(108px, 193px); -backgroundScroll: ScrollArea(boxLayerScroll) { +backgroundScroll: ScrollArea(boxScroll) { deltax: 3px; width: 10px; deltat: 10px; @@ -852,7 +852,7 @@ createPollWarningPosition: point(16px, 6px); callSettingsButton: IconButton { width: 50px; - height: boxLayerTitleHeight; + height: boxTitleHeight; icon: icon {{ "menu_settings", boxTitleCloseFg }}; iconOver: icon {{ "menu_settings", boxTitleCloseFgOver }}; iconPosition: point(8px, -1px); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index c7db08c40..402facdc0 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -56,7 +56,7 @@ ConfirmBox::ConfirmBox( : _confirmText(tr::lng_box_ok(tr::now)) , _cancelText(tr::lng_cancel(tr::now)) , _confirmStyle(st::defaultBoxButton) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -71,7 +71,7 @@ ConfirmBox::ConfirmBox( : _confirmText(confirmText) , _cancelText(tr::lng_cancel(tr::now)) , _confirmStyle(st::defaultBoxButton) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -86,7 +86,7 @@ ConfirmBox::ConfirmBox( : _confirmText(confirmText) , _cancelText(tr::lng_cancel(tr::now)) , _confirmStyle(st::defaultBoxButton) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -102,7 +102,7 @@ ConfirmBox::ConfirmBox( : _confirmText(confirmText) , _cancelText(tr::lng_cancel(tr::now)) , _confirmStyle(confirmStyle) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -118,7 +118,7 @@ ConfirmBox::ConfirmBox( : _confirmText(confirmText) , _cancelText(cancelText) , _confirmStyle(st::defaultBoxButton) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -135,7 +135,7 @@ ConfirmBox::ConfirmBox( : _confirmText(confirmText) , _cancelText(cancelText) , _confirmStyle(st::defaultBoxButton) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(std::move(confirmedCallback)) , _cancelledCallback(std::move(cancelledCallback)) { init(text); @@ -149,7 +149,7 @@ ConfirmBox::ConfirmBox( : _confirmText(doneText) , _confirmStyle(st::defaultBoxButton) , _informative(true) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(generateInformCallback(closedCallback)) , _cancelledCallback(generateInformCallback(closedCallback)) { init(text); @@ -163,7 +163,7 @@ ConfirmBox::ConfirmBox( : _confirmText(doneText) , _confirmStyle(st::defaultBoxButton) , _informative(true) -, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +, _text(st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) , _confirmedCallback(generateInformCallback(closedCallback)) , _cancelledCallback(generateInformCallback(closedCallback)) { init(text); @@ -220,7 +220,7 @@ void ConfirmBox::setMaxLineCount(int count) { } void ConfirmBox::textUpdated() { - _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); + _textWidth = st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right(); _textHeight = _text.countHeight(_textWidth); if (_maxLineCount > 0) { accumulate_min(_textHeight, _maxLineCount * st::boxLabelStyle.lineHeight); @@ -324,7 +324,7 @@ InformBox::InformBox(QWidget*, const TextWithEntities &text, const QString &done MaxInviteBox::MaxInviteBox(QWidget*, not_null channel) : BoxContent() , _channel(channel) -, _text(st::boxLabelStyle, tr::lng_participant_invite_sorry(tr::now, lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) { +, _text(st::boxLabelStyle, tr::lng_participant_invite_sorry(tr::now, lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) { } void MaxInviteBox::prepare() { @@ -332,7 +332,7 @@ void MaxInviteBox::prepare() { addButton(tr::lng_box_ok(), [=] { closeBox(); }); - _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); + _textWidth = st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right(); _textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight); setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom()); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 06d17191b..8cc81d2c4 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -756,7 +756,7 @@ void EditCaptionBox::updateBoxSize() { } int EditCaptionBox::errorTopSkip() const { - return (st::boxButtonPadding.top() / 2); + return (st::defaultBox.buttonPadding.top() / 2); } void EditCaptionBox::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index d693492a1..03667da4e 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "history/history.h" #include "boxes/peer_list_controllers.h" -#include "info/profile/info_profile_button.h" #include "settings/settings_common.h" #include "settings/settings_privacy_security.h" #include "calls/calls_instance.h" diff --git a/Telegram/SourceFiles/boxes/language_box.cpp b/Telegram/SourceFiles/boxes/language_box.cpp index a169f7a1f..135186d91 100644 --- a/Telegram/SourceFiles/boxes/language_box.cpp +++ b/Telegram/SourceFiles/boxes/language_box.cpp @@ -336,8 +336,8 @@ void Rows::mousePressEvent(QMouseEvent *e) { QRect Rows::menuToggleArea() const { const auto size = st::topBarSearch.width; const auto top = (DefaultRowHeight() - size) / 2; - const auto skip = st::boxLayerScroll.width - - st::boxLayerScroll.deltax + const auto skip = st::boxScroll.width + - st::boxScroll.deltax + top; const auto left = width() - skip - size; return QRect(left, top, size, size); @@ -1072,7 +1072,7 @@ void LanguageBox::prepare() { const auto [recent, official] = PrepareLists(); const auto inner = setInnerWidget( object_ptr(this, recent, official), - st::boxLayerScroll, + st::boxScroll, select->height()); inner->resizeToWidth(st::boxWidth); diff --git a/Telegram/SourceFiles/boxes/local_storage_box.cpp b/Telegram/SourceFiles/boxes/local_storage_box.cpp index 618c1035a..b850b4005 100644 --- a/Telegram/SourceFiles/boxes/local_storage_box.cpp +++ b/Telegram/SourceFiles/boxes/local_storage_box.cpp @@ -231,7 +231,7 @@ int LocalStorageBox::Row::resizeGetHeight(int newWidth) { newWidth); } _clear->moveToRight( - st::boxLayerButtonPadding.right(), + st::layerBox.buttonPadding.right(), (height - _clear->height()) / 2, newWidth); return height; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 0542e0e34..3259bc737 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -110,7 +110,7 @@ void PeerListBox::prepare() { this, _controller.get(), st::peerListBox), - st::boxLayerScroll)); + st::boxScroll)); content()->resizeToWidth(_controller->contentWidth()); _controller->setDelegate(this); diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp index e813bb3f1..d490a38f4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -11,9 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "ui/widgets/labels.h" +#include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper -#include "info/profile/info_profile_button.h" #include "boxes/peer_list_box.h" #include "boxes/confirm_box.h" #include "boxes/add_contact_box.h" @@ -244,7 +244,7 @@ object_ptr SetupCreateGroup( Fn callback) { Expects(channel->isBroadcast()); - auto result = object_ptr( + auto result = object_ptr( parent, tr::lng_manage_discussion_group_create( ) | Ui::Text::ToUpper(), @@ -266,7 +266,7 @@ object_ptr SetupUnlink( not_null parent, not_null channel, Fn callback) { - auto result = object_ptr( + auto result = object_ptr( parent, (channel->isBroadcast() ? tr::lng_manage_discussion_group_unlink diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 289708e36..c082c447f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -22,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text_options.h" #include "ui/special_buttons.h" #include "chat_helpers/emoji_suggestions_widget.h" -#include "info/profile/info_profile_button.h" #include "settings/settings_privacy_security.h" #include "boxes/calendar_box.h" #include "boxes/confirm_box.h" diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 56bf1dfac..918901b88 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_session.h" #include "history/admin_log/history_admin_log_section.h" -#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "mainwidget.h" @@ -35,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/special_buttons.h" #include "ui/toast/toast.h" #include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/widgets/box_content_divider.h" @@ -93,7 +93,7 @@ void AddButtonWithCount( &icon)); } -object_ptr CreateButtonWithText( +object_ptr CreateButtonWithText( not_null parent, rpl::producer &&text, rpl::producer &&label, @@ -107,7 +107,7 @@ object_ptr CreateButtonWithText( nullptr); } -Info::Profile::Button *AddButtonWithText( +Ui::SettingsButton *AddButtonWithText( not_null parent, rpl::producer &&text, rpl::producer &&label, @@ -1478,14 +1478,14 @@ void EditPeerInfoBox::prepare() { std::move(content))); } -object_ptr EditPeerInfoBox::CreateButton( +object_ptr EditPeerInfoBox::CreateButton( not_null parent, rpl::producer &&text, rpl::producer &&count, Fn callback, - const style::InfoProfileCountButton &st, + const style::SettingsCountButton &st, const style::icon *icon) { - auto result = object_ptr( + auto result = object_ptr( parent, rpl::duplicate(text), st.button); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 59c85a48a..11ffd36ec 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" namespace style { -struct InfoProfileCountButton; +struct SettingsCountButton; } // namespace style namespace Window { @@ -20,14 +20,9 @@ class SessionNavigation; namespace Ui { class VerticalLayout; +class SettingsButton; } // namespace Ui -namespace Info { -namespace Profile { -class Button; -} // namespace Profile -} // namespace Info - class EditPeerInfoBox : public Ui::BoxContent { public: EditPeerInfoBox( @@ -41,12 +36,12 @@ public: static bool Available(not_null peer); - [[nodiscard]] static object_ptr CreateButton( + [[nodiscard]] static object_ptr CreateButton( not_null parent, rpl::producer &&text, rpl::producer &&count, Fn callback, - const style::InfoProfileCountButton &st, + const style::SettingsCountButton &st, const style::icon *icon = nullptr); protected: diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 0bbe227c7..c518e56f3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -13,10 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/box_content_divider.h" #include "ui/toast/toast.h" -#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_values.h" #include "boxes/peers/edit_participants_box.h" diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 5e5ba890a..7e0e6c620 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -11,19 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" namespace style { -struct InfoProfileCountButton; +struct SettingsCountButton; } // namespace style namespace Ui { class VerticalLayout; +class SettingsButton; } // namespace Ui -namespace Info { -namespace Profile { -class Button; -} // namespace Profile -} // namespace Info - enum class Privacy { HasUsername, NoUsername, diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index cb0a589fd..a20a084a2 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -1436,7 +1436,7 @@ void SendFilesBox::prepareAlbumPreview() { const auto wrap = Ui::CreateChild( this, - st::boxLayerScroll); + st::boxScroll); _albumPreview = wrap->setOwnedWidget(object_ptr( this, _list, diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index a2888695f..91247790d 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "base/unixtime.h" #include "boxes/confirm_box.h" -#include "info/profile/info_profile_button.h" #include "settings/settings_common.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" @@ -70,7 +69,7 @@ private: void setupContent(); QPointer _current; - QPointer _terminateAll; + QPointer _terminateAll; QPointer _incomplete; QPointer _list; @@ -369,7 +368,7 @@ void SessionsBox::Inner::setupContent() { object_ptr(content)))->setDuration(0); const auto terminateInner = terminateWrap->entity(); _terminateAll = terminateInner->add( - object_ptr( + object_ptr( terminateInner, tr::lng_sessions_terminate_all(), st::terminateSessionsButton)); diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 0d3048172..c426a2316 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -769,7 +769,7 @@ void StickersBox::Inner::resizeEvent(QResizeEvent *e) { void StickersBox::Inner::updateControlsGeometry() { if (_megagroupSet) { auto top = st::groupStickersFieldPadding.top(); - auto fieldLeft = st::boxLayerTitlePosition.x(); + auto fieldLeft = st::boxTitlePosition.x(); _megagroupSetField->setGeometryToLeft(fieldLeft, top, width() - fieldLeft - st::groupStickersFieldPadding.right(), _megagroupSetField->height()); top += _megagroupSetField->height() + st::groupStickersFieldPadding.bottom(); if (_megagroupSelectedRemove) { @@ -780,8 +780,8 @@ void StickersBox::Inner::updateControlsGeometry() { } _megagroupDivider->setGeometryToLeft(0, top, width(), _megagroupDivider->height()); top += _megagroupDivider->height(); - _megagroupSubTitle->resizeToNaturalWidth(width() - 2 * st::boxLayerTitlePosition.x()); - _megagroupSubTitle->moveToLeft(st::boxLayerTitlePosition.x(), top + st::boxLayerTitlePosition.y()); + _megagroupSubTitle->resizeToNaturalWidth(width() - 2 * st::boxTitlePosition.x()); + _megagroupSubTitle->moveToLeft(st::boxTitlePosition.x(), top + st::boxTitlePosition.y()); } } @@ -1913,7 +1913,7 @@ void StickersBox::Inner::readVisibleSets() { } void StickersBox::Inner::updateScrollbarWidth() { - auto width = (_visibleBottom - _visibleTop < height()) ? (st::boxLayerScroll.width - st::boxLayerScroll.deltax) : 0; + auto width = (_visibleBottom - _visibleTop < height()) ? (st::boxScroll.width - st::boxScroll.deltax) : 0; if (_scrollbar != width) { _scrollbar = width; update(); diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 06bc2196e..7aa5580d9 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -79,7 +79,7 @@ stickersFeaturedInstalled: icon {{ "send_control_save", lightButtonFg }}; stickersMaxHeight: 320px; stickersPadding: margins(19px, 13px, 19px, 13px); stickersSize: size(64px, 64px); -stickersScroll: ScrollArea(boxLayerScroll) { +stickersScroll: ScrollArea(boxScroll) { deltat: 19px; deltab: 9px; } diff --git a/Telegram/SourceFiles/core/base_integration.cpp b/Telegram/SourceFiles/core/base_integration.cpp new file mode 100644 index 000000000..d6cf7cb8d --- /dev/null +++ b/Telegram/SourceFiles/core/base_integration.cpp @@ -0,0 +1,32 @@ +/* +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 "core/base_integration.h" + +#include "core/sandbox.h" +#include "core/crash_reports.h" + +namespace Core { + +BaseIntegration::BaseIntegration(int argc, char *argv[]) +: Integration(argc, argv) { +} + +void BaseIntegration::enterFromEventLoop(FnMut &&method) { + Core::Sandbox::Instance().customEnterFromEventLoop( + std::move(method)); +} + +void BaseIntegration::logAssertionViolation(const QString &info) { +#ifdef LOG + LOG(("Assertion Failed! ") + info); +#endif // LOG + + CrashReports::SetAnnotation("Assertion", info); +} + +} // namespace Core diff --git a/Telegram/SourceFiles/core/base_integration.h b/Telegram/SourceFiles/core/base_integration.h new file mode 100644 index 000000000..5d7221b8d --- /dev/null +++ b/Telegram/SourceFiles/core/base_integration.h @@ -0,0 +1,23 @@ +/* +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 +*/ +#pragma once + +#include "base/integration.h" + +namespace Core { + +class BaseIntegration : public base::Integration { +public: + BaseIntegration(int argc, char *argv[]); + + void enterFromEventLoop(FnMut &&method) override; + void logAssertionViolation(const QString &info) override; + +}; + +} // namespace Core diff --git a/Telegram/SourceFiles/core/crash_reports.h b/Telegram/SourceFiles/core/crash_reports.h index 5e60afd0e..665c431f9 100644 --- a/Telegram/SourceFiles/core/crash_reports.h +++ b/Telegram/SourceFiles/core/crash_reports.h @@ -57,24 +57,3 @@ void StartCatching(not_null launcher); void FinishCatching(); } // namespace CrashReports - -namespace base { -namespace assertion { - -inline void log(const char *message, const char *file, int line) { - const auto info = QStringLiteral("%1 %2:%3" - ).arg(message - ).arg(file - ).arg(line - ); - const auto entry = QStringLiteral("Assertion Failed! ") + info; - -#ifdef LOG - LOG((entry)); -#endif // LOG - - CrashReports::SetAnnotation("Assertion", info); -} - -} // namespace assertion -} // namespace base diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 4b04f8ed4..25473426f 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -235,8 +235,10 @@ Launcher::Launcher( const QString &systemVersion) : _argc(argc) , _argv(argv) +, _baseIntegration(_argc, _argv) , _deviceModel(deviceModel) , _systemVersion(systemVersion) { + base::Integration::Set(&_baseIntegration); } void Launcher::init() { diff --git a/Telegram/SourceFiles/core/launcher.h b/Telegram/SourceFiles/core/launcher.h index 6060e96ff..05648bdb1 100644 --- a/Telegram/SourceFiles/core/launcher.h +++ b/Telegram/SourceFiles/core/launcher.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "core/base_integration.h" + namespace Core { class Launcher { @@ -63,6 +65,7 @@ private: int _argc; char **_argv; QStringList _arguments; + BaseIntegration _baseIntegration; const QString _deviceModel; const QString _systemVersion; diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index 772211254..579efb161 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -103,7 +103,7 @@ void SettingsWidget::changeData(Callback &&callback) { void SettingsWidget::setupContent() { const auto scroll = Ui::CreateChild( this, - st::boxLayerScroll); + st::boxScroll); const auto wrap = scroll->setOwnedWidget( object_ptr( scroll, @@ -406,7 +406,7 @@ not_null SettingsWidget::setupButtons( not_null wrap) { using namespace rpl::mappers; - const auto buttonsPadding = st::boxButtonPadding; + const auto buttonsPadding = st::defaultBox.buttonPadding; const auto buttonsHeight = buttonsPadding.top() + st::defaultBoxButton.height + buttonsPadding.bottom(); @@ -654,8 +654,8 @@ void SettingsWidget::refreshButtons( container->sizeValue( ) | rpl::start_with_next([=](QSize size) { - const auto right = st::boxButtonPadding.right(); - const auto top = st::boxButtonPadding.top(); + const auto right = st::defaultBox.buttonPadding.right(); + const auto top = st::defaultBox.buttonPadding.top(); start->moveToRight(right, top); }, start->lifetime()); } @@ -674,9 +674,9 @@ void SettingsWidget::refreshButtons( container->sizeValue(), start ? start->widthValue() : rpl::single(0) ) | rpl::start_with_next([=](QSize size, int width) { - const auto right = st::boxButtonPadding.right() - + (width ? width + st::boxButtonPadding.left() : 0); - const auto top = st::boxButtonPadding.top(); + const auto right = st::defaultBox.buttonPadding.right() + + (width ? width + st::defaultBox.buttonPadding.left() : 0); + const auto top = st::defaultBox.buttonPadding.top(); cancel->moveToRight(right, top); }, cancel->lifetime()); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 9867eaa2b..673fd5862 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -196,7 +196,7 @@ infoTopBarDuration: 150; infoLayerTopMinimal: 20px; infoLayerTopMaximal: 40px; -infoLayerTopBarHeight: boxLayerTitleHeight; +infoLayerTopBarHeight: boxTitleHeight; infoLayerTopBarBackIcon: icon {{ "info_back", boxTitleCloseFg }}; infoLayerTopBarBackIconOver: icon {{ "info_back", boxTitleCloseFgOver }}; infoLayerTopBarBack: IconButton(infoTopBarBack) { @@ -256,7 +256,7 @@ infoLayerTopBar: InfoTopBar(infoTopBar) { height: infoLayerTopBarHeight; back: infoLayerTopBarBack; title: boxTitle; - titlePosition: boxLayerTitlePosition; + titlePosition: boxTitlePosition; bg: boxBg; mediaCancel: infoLayerTopBarMediaCancel; mediaActionsSkip: 6px; @@ -402,7 +402,7 @@ infoProfileToggle: Toggle(defaultToggle) { infoProfileToggleOver: Toggle(infoProfileToggle) { untoggledFg: menuIconFgOver; } -infoProfileButton: InfoProfileButton { +infoProfileButton: SettingsButton { textFg: windowBoldFg; textFgOver: windowBoldFgOver; textBg: windowBg; @@ -419,10 +419,10 @@ infoProfileButton: InfoProfileButton { ripple: defaultRippleAnimation; } -infoNotificationsButton: InfoProfileButton(infoProfileButton) { +infoNotificationsButton: SettingsButton(infoProfileButton) { padding: margins(79px, 13px, 8px, 9px); } -infoMainButton: InfoProfileButton(infoProfileButton) { +infoMainButton: SettingsButton(infoProfileButton) { textFg: lightButtonFg; textFgOver: lightButtonFgOver; font: semiboldFont; @@ -431,14 +431,14 @@ infoSharedMediaCoverHeight: 62px; infoSharedMediaButton: infoProfileButton; infoSharedMediaBottomSkip: 12px; -infoBlockButton: InfoProfileButton(infoProfileButton) { +infoBlockButton: SettingsButton(infoProfileButton) { textFg: attentionButtonFg; textFgOver: attentionButtonFgOver; } -infoCreateLinkedChatButton: InfoProfileButton(infoMainButton) { +infoCreateLinkedChatButton: SettingsButton(infoMainButton) { padding: margins(74px, 10px, 8px, 8px); } -infoUnlinkChatButton: InfoProfileButton(infoCreateLinkedChatButton) { +infoUnlinkChatButton: SettingsButton(infoCreateLinkedChatButton) { textFg: attentionButtonFg; textFgOver: attentionButtonFgOver; } @@ -587,8 +587,8 @@ infoChannelsList: PeerList(infoCommonGroupsList) { } } -managePeerButton: InfoProfileCountButton { - button: InfoProfileButton(infoProfileButton) { +managePeerButton: SettingsCountButton { + button: SettingsButton(infoProfileButton) { padding: margins(76px, 12px, 76px, 10px); } iconPosition: point(20px, 5px); @@ -598,31 +598,31 @@ managePeerButton: InfoProfileCountButton { labelPosition: point(25px, 12px); } -peerPermissionsButton: InfoProfileCountButton(managePeerButton) { - button: InfoProfileButton(infoProfileButton) { +peerPermissionsButton: SettingsCountButton(managePeerButton) { + button: SettingsButton(infoProfileButton) { padding: margins(24px, 12px, 24px, 10px); } iconPosition: point(24px, 5px); } -manageGroupButton: InfoProfileCountButton(managePeerButton) { - button: InfoProfileButton(infoProfileButton) { +manageGroupButton: SettingsCountButton(managePeerButton) { + button: SettingsButton(infoProfileButton) { padding: margins(72px, 10px, 24px, 8px); } labelPosition: point(22px, 12px); iconPosition: point(20px, 4px); } -manageGroupTopButtonWithText: InfoProfileCountButton(manageGroupButton) { - button: InfoProfileButton(infoProfileButton) { +manageGroupTopButtonWithText: SettingsCountButton(manageGroupButton) { + button: SettingsButton(infoProfileButton) { padding: margins(22px, 10px, 24px, 8px); } labelPosition: point(22px, 10px); iconPosition: point(0px, 0px); } -manageDeleteGroupButton: InfoProfileCountButton(manageGroupTopButtonWithText) { - button: InfoProfileButton(infoProfileButton) { +manageDeleteGroupButton: SettingsCountButton(manageGroupTopButtonWithText) { + button: SettingsButton(infoProfileButton) { padding: margins(25px, 11px, 24px, 8px); textFg: attentionButtonFg; textFgOver: attentionButtonFg; @@ -634,7 +634,7 @@ manageDeleteGroupButton: InfoProfileCountButton(manageGroupTopButtonWithText) { editPeerSkip: 7px; editPeerHistoryVisibilityMargins: margins(15px, 0px, 20px, 16px); -terminateSessionsButton: InfoProfileButton(infoBlockButton) { +terminateSessionsButton: SettingsButton(infoBlockButton) { padding: margins(23px, 12px, 23px, 10px); } diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.h b/Telegram/SourceFiles/info/media/info_media_buttons.h index d70a594c5..e79cac60d 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.h +++ b/Telegram/SourceFiles/info/media/info_media_buttons.h @@ -13,10 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_shared_media.h" #include "info/info_memento.h" #include "info/info_controller.h" -#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_values.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "ui/widgets/buttons.h" #include "window/window_session_controller.h" #include "data/data_channel.h" #include "data/data_user.h" @@ -53,7 +53,7 @@ inline auto AddCountedButton( Ui::MultiSlideTracker &tracker) { using namespace rpl::mappers; - using Button = Profile::Button; + using Button = Ui::SettingsButton; auto forked = std::move(count) | start_spawning(parent->lifetime()); auto text = rpl::duplicate( diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index d94f87651..1f72ed406 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -12,11 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/media/info_media_list_widget.h" #include "info/media/info_media_buttons.h" #include "info/media/info_media_empty_widget.h" -#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_icon.h" #include "info/info_controller.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/box_content_divider.h" #include "ui/wrap/vertical_layout.h" #include "ui/search_field_controller.h" diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index 859e3d68a..0b08107ba 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/widgets/shadow.h" #include "ui/widgets/labels.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/box_content_divider.h" #include "ui/layers/generic_box.h" #include "ui/toast/toast.h" @@ -34,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_memento.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_values.h" -#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_text.h" #include "support/support_helper.h" #include "window/window_session_controller.h" @@ -76,11 +76,11 @@ auto AddActionButton( Text &&text, ToggleOn &&toggleOn, Callback &&callback, - const style::InfoProfileButton &st + const style::SettingsButton &st = st::infoSharedMediaButton) { - auto result = parent->add(object_ptr>( + auto result = parent->add(object_ptr>( parent, - object_ptr