From 246c45ce0eebe14c78566b222e9b3fec109efc31 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 17 Sep 2019 19:13:12 +0300 Subject: [PATCH] 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);