Updated lib_ui to latest patches-track-wip

This commit is contained in:
Eric Kotato 2024-07-01 17:53:11 +03:00
commit 91038c8f17
280 changed files with 24069 additions and 7713 deletions

View file

@ -25,19 +25,23 @@ generate_styles(lib_ui ${src_loc} "${style_files}" ui/colors.palette)
generate_emoji(lib_ui emoji.txt emoji_suggestions/emoji_autocomplete.json) generate_emoji(lib_ui emoji.txt emoji_suggestions/emoji_autocomplete.json)
set_target_properties(lib_ui PROPERTIES AUTOMOC ON) set_target_properties(lib_ui PROPERTIES AUTOMOC ON)
target_prepare_qrc(lib_ui)
target_precompile_headers(lib_ui PRIVATE ${src_loc}/ui/ui_pch.h) target_precompile_headers(lib_ui PRIVATE $<$<COMPILE_LANGUAGE:CXX,OBJCXX>:${src_loc}/ui/ui_pch.h>)
nice_target_sources(lib_ui ${src_loc} nice_target_sources(lib_ui ${src_loc}
PRIVATE PRIVATE
${style_files} ${style_files}
ui/colors.palette ui/colors.palette
emoji_suggestions/emoji_autocomplete.json emoji_suggestions/emoji_autocomplete.json
fonts/fonts.qrc
qt_conf/win.qrc qt_conf/win.qrc
ui/dpr/dpr_icon.cpp
ui/dpr/dpr_icon.h
ui/dpr/dpr_image.h
ui/effects/animation_value.cpp ui/effects/animation_value.cpp
ui/effects/animation_value.h ui/effects/animation_value.h
ui/effects/animation_value_f.h
ui/effects/animations.cpp ui/effects/animations.cpp
ui/effects/animations.h ui/effects/animations.h
ui/effects/cross_animation.cpp ui/effects/cross_animation.cpp
@ -46,6 +50,9 @@ PRIVATE
ui/effects/cross_line.h ui/effects/cross_line.h
ui/effects/fade_animation.cpp ui/effects/fade_animation.cpp
ui/effects/fade_animation.h ui/effects/fade_animation.h
ui/effects/frame_generator.cpp
ui/effects/frame_generator.h
ui/effects/gradient.cpp
ui/effects/gradient.h ui/effects/gradient.h
ui/effects/numbers_animation.cpp ui/effects/numbers_animation.cpp
ui/effects/numbers_animation.h ui/effects/numbers_animation.h
@ -57,8 +64,14 @@ PRIVATE
ui/effects/radial_animation.h ui/effects/radial_animation.h
ui/effects/ripple_animation.cpp ui/effects/ripple_animation.cpp
ui/effects/ripple_animation.h ui/effects/ripple_animation.h
ui/effects/round_area_with_shadow.cpp
ui/effects/round_area_with_shadow.h
ui/effects/show_animation.cpp
ui/effects/show_animation.h
ui/effects/slide_animation.cpp ui/effects/slide_animation.cpp
ui/effects/slide_animation.h ui/effects/slide_animation.h
ui/effects/spoiler_mess.cpp
ui/effects/spoiler_mess.h
ui/gl/gl_detection.cpp ui/gl/gl_detection.cpp
ui/gl/gl_detection.h ui/gl/gl_detection.h
ui/gl/gl_image.cpp ui/gl/gl_image.cpp
@ -85,6 +98,8 @@ PRIVATE
ui/layers/layer_manager.h ui/layers/layer_manager.h
ui/layers/layer_widget.cpp ui/layers/layer_widget.cpp
ui/layers/layer_widget.h ui/layers/layer_widget.h
ui/layers/show.cpp
ui/layers/show.h
ui/paint/arcs.cpp ui/paint/arcs.cpp
ui/paint/arcs.h ui/paint/arcs.h
ui/paint/blob.cpp ui/paint/blob.cpp
@ -93,10 +108,10 @@ PRIVATE
ui/paint/blobs.h ui/paint/blobs.h
ui/paint/blobs_linear.cpp ui/paint/blobs_linear.cpp
ui/paint/blobs_linear.h ui/paint/blobs_linear.h
ui/platform/linux/ui_linux_wayland_integration.cpp
ui/platform/linux/ui_linux_wayland_integration.h
ui/platform/linux/ui_window_linux.cpp ui/platform/linux/ui_window_linux.cpp
ui/platform/linux/ui_window_linux.h ui/platform/linux/ui_window_linux.h
ui/platform/linux/ui_window_title_linux.cpp
ui/platform/linux/ui_window_title_linux.h
ui/platform/linux/ui_utility_linux.cpp ui/platform/linux/ui_utility_linux.cpp
ui/platform/linux/ui_utility_linux.h ui/platform/linux/ui_utility_linux.h
ui/platform/mac/ui_window_mac.h ui/platform/mac/ui_window_mac.h
@ -111,13 +126,14 @@ PRIVATE
ui/platform/win/ui_window_title_win.h ui/platform/win/ui_window_title_win.h
ui/platform/win/ui_window_win.cpp ui/platform/win/ui_window_win.cpp
ui/platform/win/ui_window_win.h ui/platform/win/ui_window_win.h
ui/platform/win/ui_windows_direct_manipulation.cpp
ui/platform/win/ui_windows_direct_manipulation.h
ui/platform/win/ui_utility_win.cpp ui/platform/win/ui_utility_win.cpp
ui/platform/win/ui_utility_win.h ui/platform/win/ui_utility_win.h
ui/platform/ui_platform_window_title.cpp ui/platform/ui_platform_window_title.cpp
ui/platform/ui_platform_window_title.h ui/platform/ui_platform_window_title.h
ui/platform/ui_platform_window.cpp ui/platform/ui_platform_window.cpp
ui/platform/ui_platform_window.h ui/platform/ui_platform_window.h
ui/platform/ui_platform_utility.cpp
ui/platform/ui_platform_utility.h ui/platform/ui_platform_utility.h
ui/style/style_core.cpp ui/style/style_core.cpp
ui/style/style_core.h ui/style/style_core.h
@ -139,15 +155,27 @@ PRIVATE
ui/style/style_core_types.h ui/style/style_core_types.h
ui/style/style_palette_colorizer.cpp ui/style/style_palette_colorizer.cpp
ui/style/style_palette_colorizer.h ui/style/style_palette_colorizer.h
ui/text/custom_emoji_instance.cpp
ui/text/custom_emoji_instance.h
ui/text/text.cpp ui/text/text.cpp
ui/text/text.h ui/text/text.h
ui/text/text_block.cpp ui/text/text_block.cpp
ui/text/text_block.h ui/text/text_block.h
ui/text/text_custom_emoji.cpp
ui/text/text_custom_emoji.h
ui/text/text_entity.cpp ui/text/text_entity.cpp
ui/text/text_entity.h ui/text/text_entity.h
ui/text/text_extended_data.cpp
ui/text/text_extended_data.h
ui/text/text_isolated_emoji.h ui/text/text_isolated_emoji.h
ui/text/text_parser.cpp
ui/text/text_parser.h
ui/text/text_renderer.cpp
ui/text/text_renderer.h
ui/text/text_utilities.cpp ui/text/text_utilities.cpp
ui/text/text_utilities.h ui/text/text_utilities.h
ui/text/text_variant.cpp
ui/text/text_variant.h
ui/toast/toast.cpp ui/toast/toast.cpp
ui/toast/toast.h ui/toast/toast.h
ui/toast/toast_manager.cpp ui/toast/toast_manager.cpp
@ -164,28 +192,48 @@ PRIVATE
ui/widgets/checkbox.h ui/widgets/checkbox.h
ui/widgets/dropdown_menu.cpp ui/widgets/dropdown_menu.cpp
ui/widgets/dropdown_menu.h ui/widgets/dropdown_menu.h
ui/widgets/icon_button_with_text.cpp
ui/widgets/icon_button_with_text.h
ui/widgets/inner_dropdown.cpp ui/widgets/inner_dropdown.cpp
ui/widgets/inner_dropdown.h ui/widgets/inner_dropdown.h
ui/widgets/input_fields.cpp ui/widgets/fields/input_field.cpp
ui/widgets/input_fields.h ui/widgets/fields/input_field.h
ui/widgets/fields/masked_input_field.cpp
ui/widgets/fields/masked_input_field.h
ui/widgets/fields/number_input.cpp
ui/widgets/fields/number_input.h
ui/widgets/fields/password_input.cpp
ui/widgets/fields/password_input.h
ui/widgets/fields/time_part_input.cpp
ui/widgets/fields/time_part_input.h
ui/widgets/labels.cpp ui/widgets/labels.cpp
ui/widgets/labels.h ui/widgets/labels.h
ui/widgets/menu/menu.cpp ui/widgets/menu/menu.cpp
ui/widgets/menu/menu.h ui/widgets/menu/menu.h
ui/widgets/menu/menu_action.cpp ui/widgets/menu/menu_action.cpp
ui/widgets/menu/menu_action.h ui/widgets/menu/menu_action.h
ui/widgets/menu/menu_add_action_callback.cpp
ui/widgets/menu/menu_add_action_callback.h
ui/widgets/menu/menu_add_action_callback_factory.cpp
ui/widgets/menu/menu_add_action_callback_factory.h
ui/widgets/menu/menu_common.cpp ui/widgets/menu/menu_common.cpp
ui/widgets/menu/menu_common.h ui/widgets/menu/menu_common.h
ui/widgets/menu/menu_item_base.cpp ui/widgets/menu/menu_item_base.cpp
ui/widgets/menu/menu_item_base.h ui/widgets/menu/menu_item_base.h
ui/widgets/menu/menu_multiline_action.cpp
ui/widgets/menu/menu_multiline_action.h
ui/widgets/menu/menu_separator.cpp ui/widgets/menu/menu_separator.cpp
ui/widgets/menu/menu_separator.h ui/widgets/menu/menu_separator.h
ui/widgets/menu/menu_toggle.cpp ui/widgets/menu/menu_toggle.cpp
ui/widgets/menu/menu_toggle.h ui/widgets/menu/menu_toggle.h
ui/widgets/elastic_scroll.cpp
ui/widgets/elastic_scroll.h
ui/widgets/popup_menu.cpp ui/widgets/popup_menu.cpp
ui/widgets/popup_menu.h ui/widgets/popup_menu.h
ui/widgets/rp_window.cpp ui/widgets/rp_window.cpp
ui/widgets/rp_window.h ui/widgets/rp_window.h
ui/widgets/separate_panel.cpp
ui/widgets/separate_panel.h
ui/widgets/scroll_area.cpp ui/widgets/scroll_area.cpp
ui/widgets/scroll_area.h ui/widgets/scroll_area.h
ui/widgets/side_bar_button.cpp ui/widgets/side_bar_button.cpp
@ -198,10 +246,14 @@ PRIVATE
ui/widgets/tooltip.h ui/widgets/tooltip.h
ui/wrap/fade_wrap.cpp ui/wrap/fade_wrap.cpp
ui/wrap/fade_wrap.h ui/wrap/fade_wrap.h
ui/wrap/follow_slide_wrap.cpp
ui/wrap/follow_slide_wrap.h
ui/wrap/padding_wrap.cpp ui/wrap/padding_wrap.cpp
ui/wrap/padding_wrap.h ui/wrap/padding_wrap.h
ui/wrap/slide_wrap.cpp ui/wrap/slide_wrap.cpp
ui/wrap/slide_wrap.h ui/wrap/slide_wrap.h
ui/wrap/table_layout.cpp
ui/wrap/table_layout.h
ui/wrap/vertical_layout.cpp ui/wrap/vertical_layout.cpp
ui/wrap/vertical_layout.h ui/wrap/vertical_layout.h
ui/wrap/vertical_layout_reorder.cpp ui/wrap/vertical_layout_reorder.cpp
@ -209,12 +261,19 @@ PRIVATE
ui/wrap/wrap.h ui/wrap/wrap.h
ui/abstract_button.cpp ui/abstract_button.cpp
ui/abstract_button.h ui/abstract_button.h
ui/animated_icon.cpp
ui/animated_icon.h
ui/basic_click_handlers.cpp ui/basic_click_handlers.cpp
ui/basic_click_handlers.h ui/basic_click_handlers.h
ui/cached_special_layer_shadow_corners.cpp
ui/cached_special_layer_shadow_corners.h
ui/click_handler.cpp ui/click_handler.cpp
ui/click_handler.h ui/click_handler.h
ui/delayed_activation.cpp ui/delayed_activation.cpp
ui/delayed_activation.h ui/delayed_activation.h
ui/dragging_scroll_manager.cpp
ui/dragging_scroll_manager.h
ui/dynamic_image.h
ui/emoji_config.cpp ui/emoji_config.cpp
ui/emoji_config.h ui/emoji_config.h
ui/focus_persister.h ui/focus_persister.h
@ -227,11 +286,10 @@ PRIVATE
ui/painter.h ui/painter.h
ui/ph.cpp ui/ph.cpp
ui/ph.h ui/ph.h
ui/rect.h
ui/rect_part.h ui/rect_part.h
ui/round_rect.cpp ui/round_rect.cpp
ui/round_rect.h ui/round_rect.h
ui/spoiler_click_handler.cpp
ui/spoiler_click_handler.h
ui/rp_widget.cpp ui/rp_widget.cpp
ui/rp_widget.h ui/rp_widget.h
ui/ui_utility.cpp ui/ui_utility.cpp
@ -244,23 +302,13 @@ PRIVATE
emoji_suggestions/emoji_suggestions_helper.h emoji_suggestions/emoji_suggestions_helper.h
) )
if (DESKTOP_APP_USE_PACKAGED) if (DESKTOP_APP_USE_PACKAGED_FONTS)
nice_target_sources(lib_ui ${src_loc} PRIVATE ui/text/qtextitemint.cpp) target_compile_definitions(lib_ui PRIVATE LIB_UI_USE_PACKAGED_FONTS)
remove_target_sources(lib_ui ${src_loc} fonts/fonts.qrc)
endif() endif()
if (NOT DESKTOP_APP_USE_PACKAGED_FONTS) if (WIN32)
nice_target_sources(lib_ui ${src_loc} PRIVATE fonts/fonts.qrc) nuget_add_winrt(lib_ui)
endif()
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
remove_target_sources(Telegram ${src_loc} ui/platform/linux/ui_linux_wayland_integration.cpp)
nice_target_sources(Telegram ${src_loc} PRIVATE ui/platform/linux/ui_linux_wayland_integration_dummy.cpp)
elseif(LINUX)
target_link_libraries(lib_ui
PUBLIC
desktop-app::lib_waylandshells
desktop-app::external_kwayland
)
endif() endif()
target_include_directories(lib_ui target_include_directories(lib_ui
@ -274,4 +322,9 @@ PUBLIC
desktop-app::lib_base desktop-app::lib_base
PRIVATE PRIVATE
desktop-app::external_zlib desktop-app::external_zlib
desktop-app::external_jpeg
desktop-app::external_lz4
desktop-app::external_xxhash
) )
target_prepare_qrc(lib_ui)

View file

@ -1,22 +1,28 @@
"😀","😃","😄","😁","😆","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🤭","🤫","🤥","😶","😐","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾", "😀","😃","😄","😁","😆","🥹","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🫣","🤭","🫢","🫡","🤫","🫠","🤥","😶","🫥","😐","🫤","😑","🫨","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🫶","🫶🏻","🫶🏼","🫶🏽","🫶🏾","🫶🏿",
"🤲","🤲🏻","🤲🏼","🤲🏽","🤲🏾","🤲🏿", "🤲","🤲🏻","🤲🏼","🤲🏽","🤲🏾","🤲🏿",
"👐","👐🏻","👐🏼","👐🏽","👐🏾","👐🏿", "👐","👐🏻","👐🏼","👐🏽","👐🏾","👐🏿",
"🙌","🙌🏻","🙌🏼","🙌🏽","🙌🏾","🙌🏿", "🙌","🙌🏻","🙌🏼","🙌🏽","🙌🏾","🙌🏿",
"👏","👏🏻","👏🏼","👏🏽","👏🏾","👏🏿", "👏","👏🏻","👏🏼","👏🏽","👏🏾","👏🏿",
"🤝", "🤝","🤝🏻","🫱🏻‍🫲🏼","🫱🏻‍🫲🏽","🫱🏻‍🫲🏾","🫱🏻‍🫲🏿","🫱🏼‍🫲🏻","🤝🏼","🫱🏼‍🫲🏽","🫱🏼‍🫲🏾","🫱🏼‍🫲🏿","🫱🏽‍🫲🏻","🫱🏽‍🫲🏼","🤝🏽","🫱🏽‍🫲🏾","🫱🏽‍🫲🏿","🫱🏾‍🫲🏻","🫱🏾‍🫲🏼","🫱🏾‍🫲🏽","🤝🏾","🫱🏾‍🫲🏿","🫱🏿‍🫲🏻","🫱🏿‍🫲🏼","🫱🏿‍🫲🏽","🫱🏿‍🫲🏾","🤝🏿",
"👍","👍🏻","👍🏼","👍🏽","👍🏾","👍🏿", "👍","👍🏻","👍🏼","👍🏽","👍🏾","👍🏿",
"👎","👎🏻","👎🏼","👎🏽","👎🏾","👎🏿", "👎","👎🏻","👎🏼","👎🏽","👎🏾","👎🏿",
"👊","👊🏻","👊🏼","👊🏽","👊🏾","👊🏿", "👊","👊🏻","👊🏼","👊🏽","👊🏾","👊🏿",
"✊","✊🏻","✊🏼","✊🏽","✊🏾","✊🏿", "✊","✊🏻","✊🏼","✊🏽","✊🏾","✊🏿",
"🤛","🤛🏻","🤛🏼","🤛🏽","🤛🏾","🤛🏿", "🤛","🤛🏻","🤛🏼","🤛🏽","🤛🏾","🤛🏿",
"🤜","🤜🏻","🤜🏼","🤜🏽","🤜🏾","🤜🏿", "🤜","🤜🏻","🤜🏼","🤜🏽","🤜🏾","🤜🏿",
"🫷","🫷🏻","🫷🏼","🫷🏽","🫷🏾","🫷🏿",
"🫸","🫸🏻","🫸🏼","🫸🏽","🫸🏾","🫸🏿",
"🤞","🤞🏻","🤞🏼","🤞🏽","🤞🏾","🤞🏿", "🤞","🤞🏻","🤞🏼","🤞🏽","🤞🏾","🤞🏿",
"✌️","✌🏻","✌🏼","✌🏽","✌🏾","✌🏿", "✌️","✌🏻","✌🏼","✌🏽","✌🏾","✌🏿",
"🫰","🫰🏻","🫰🏼","🫰🏽","🫰🏾","🫰🏿",
"🤟","🤟🏻","🤟🏼","🤟🏽","🤟🏾","🤟🏿", "🤟","🤟🏻","🤟🏼","🤟🏽","🤟🏾","🤟🏿",
"🤘","🤘🏻","🤘🏼","🤘🏽","🤘🏾","🤘🏿", "🤘","🤘🏻","🤘🏼","🤘🏽","🤘🏾","🤘🏿",
"👌","👌🏻","👌🏼","👌🏽","👌🏾","👌🏿", "👌","👌🏻","👌🏼","👌🏽","👌🏾","👌🏿",
"🤌","🤌🏻","🤌🏼","🤌🏽","🤌🏾","🤌🏿", "🤌","🤌🏻","🤌🏼","🤌🏽","🤌🏾","🤌🏿",
"🤏","🤏🏻","🤏🏼","🤏🏽","🤏🏾","🤏🏿", "🤏","🤏🏻","🤏🏼","🤏🏽","🤏🏾","🤏🏿",
"🫳","🫳🏻","🫳🏼","🫳🏽","🫳🏾","🫳🏿",
"🫴","🫴🏻","🫴🏼","🫴🏽","🫴🏾","🫴🏿",
"👈","👈🏻","👈🏼","👈🏽","👈🏾","👈🏿", "👈","👈🏻","👈🏼","👈🏽","👈🏾","👈🏿",
"👉","👉🏻","👉🏼","👉🏽","👉🏾","👉🏿", "👉","👉🏻","👉🏼","👉🏽","👉🏾","👉🏿",
"👆","👆🏻","👆🏼","👆🏽","👆🏾","👆🏿", "👆","👆🏻","👆🏼","👆🏽","👆🏾","👆🏿",
@ -28,14 +34,17 @@
"🖖","🖖🏻","🖖🏼","🖖🏽","🖖🏾","🖖🏿", "🖖","🖖🏻","🖖🏼","🖖🏽","🖖🏾","🖖🏿",
"👋","👋🏻","👋🏼","👋🏽","👋🏾","👋🏿", "👋","👋🏻","👋🏼","👋🏽","👋🏾","👋🏿",
"🤙","🤙🏻","🤙🏼","🤙🏽","🤙🏾","🤙🏿", "🤙","🤙🏻","🤙🏼","🤙🏽","🤙🏾","🤙🏿",
"🫲","🫲🏻","🫲🏼","🫲🏽","🫲🏾","🫲🏿",
"🫱","🫱🏻","🫱🏼","🫱🏽","🫱🏾","🫱🏿",
"💪","💪🏻","💪🏼","💪🏽","💪🏾","💪🏿", "💪","💪🏻","💪🏼","💪🏽","💪🏾","💪🏿",
"🦾", "🦾",
"🖕","🖕🏻","🖕🏼","🖕🏽","🖕🏾","🖕🏿", "🖕","🖕🏻","🖕🏼","🖕🏽","🖕🏾","🖕🏿",
"✍️","✍🏻","✍🏼","✍🏽","✍🏾","✍🏿", "✍️","✍🏻","✍🏼","✍🏽","✍🏾","✍🏿",
"🙏","🙏🏻","🙏🏼","🙏🏽","🙏🏾","🙏🏿", "🙏","🙏🏻","🙏🏼","🙏🏽","🙏🏾","🙏🏿",
"🫵","🫵🏻","🫵🏼","🫵🏽","🫵🏾","🫵🏿",
"🦶","🦶🏻","🦶🏼","🦶🏽","🦶🏾","🦶🏿", "🦶","🦶🏻","🦶🏼","🦶🏽","🦶🏾","🦶🏿",
"🦵","🦵🏻","🦵🏼","🦵🏽","🦵🏾","🦵🏿", "🦵","🦵🏻","🦵🏼","🦵🏽","🦵🏾","🦵🏿",
"🦿","💄","💋","👄","🦷","👅", "🦿","💄","💋","👄","🫦","🦷","👅",
"👂","👂🏻","👂🏼","👂🏽","👂🏾","👂🏿", "👂","👂🏻","👂🏼","👂🏽","👂🏾","👂🏿",
"🦻","🦻🏻","🦻🏼","🦻🏽","🦻🏾","🦻🏿", "🦻","🦻🏻","🦻🏼","🦻🏽","🦻🏾","🦻🏿",
"👃","👃🏻","👃🏼","👃🏽","👃🏾","👃🏿", "👃","👃🏻","👃🏼","👃🏽","👃🏾","👃🏿",
@ -140,6 +149,7 @@
"🤵","🤵🏻","🤵🏼","🤵🏽","🤵🏾","🤵🏿", "🤵","🤵🏻","🤵🏼","🤵🏽","🤵🏾","🤵🏿",
"🤵‍♂️","🤵🏻‍♂️","🤵🏼‍♂️","🤵🏽‍♂️","🤵🏾‍♂️","🤵🏿‍♂️", "🤵‍♂️","🤵🏻‍♂️","🤵🏼‍♂️","🤵🏽‍♂️","🤵🏾‍♂️","🤵🏿‍♂️",
"👸","👸🏻","👸🏼","👸🏽","👸🏾","👸🏿", "👸","👸🏻","👸🏼","👸🏽","👸🏾","👸🏿",
"🫅","🫅🏻","🫅🏼","🫅🏽","🫅🏾","🫅🏿",
"🤴","🤴🏻","🤴🏼","🤴🏽","🤴🏾","🤴🏿", "🤴","🤴🏻","🤴🏼","🤴🏽","🤴🏾","🤴🏿",
"🥷","🥷🏻","🥷🏼","🥷🏽","🥷🏾","🥷🏿", "🥷","🥷🏻","🥷🏼","🥷🏽","🥷🏾","🥷🏿",
"🦸‍♀️","🦸🏻‍♀️","🦸🏼‍♀️","🦸🏽‍♀️","🦸🏾‍♀️","🦸🏿‍♀️", "🦸‍♀️","🦸🏻‍♀️","🦸🏼‍♀️","🦸🏽‍♀️","🦸🏾‍♀️","🦸🏿‍♀️",
@ -157,6 +167,7 @@
"🧝‍♀️","🧝🏻‍♀️","🧝🏼‍♀️","🧝🏽‍♀️","🧝🏾‍♀️","🧝🏿‍♀️", "🧝‍♀️","🧝🏻‍♀️","🧝🏼‍♀️","🧝🏽‍♀️","🧝🏾‍♀️","🧝🏿‍♀️",
"🧝","🧝🏻","🧝🏼","🧝🏽","🧝🏾","🧝🏿", "🧝","🧝🏻","🧝🏼","🧝🏽","🧝🏾","🧝🏿",
"🧝‍♂️","🧝🏻‍♂️","🧝🏼‍♂️","🧝🏽‍♂️","🧝🏾‍♂️","🧝🏿‍♂️", "🧝‍♂️","🧝🏻‍♂️","🧝🏼‍♂️","🧝🏽‍♂️","🧝🏾‍♂️","🧝🏿‍♂️",
"🧌",
"🧛‍♀️","🧛🏻‍♀️","🧛🏼‍♀️","🧛🏽‍♀️","🧛🏾‍♀️","🧛🏿‍♀️", "🧛‍♀️","🧛🏻‍♀️","🧛🏼‍♀️","🧛🏽‍♀️","🧛🏾‍♀️","🧛🏿‍♀️",
"🧛","🧛🏻","🧛🏼","🧛🏽","🧛🏾","🧛🏿", "🧛","🧛🏻","🧛🏼","🧛🏽","🧛🏾","🧛🏿",
"🧛‍♂️","🧛🏻‍♂️","🧛🏼‍♂️","🧛🏽‍♂️","🧛🏾‍♂️","🧛🏿‍♂️", "🧛‍♂️","🧛🏻‍♂️","🧛🏼‍♂️","🧛🏽‍♂️","🧛🏾‍♂️","🧛🏿‍♂️",
@ -169,6 +180,8 @@
"🧚‍♂️","🧚🏻‍♂️","🧚🏼‍♂️","🧚🏽‍♂️","🧚🏾‍♂️","🧚🏿‍♂️", "🧚‍♂️","🧚🏻‍♂️","🧚🏼‍♂️","🧚🏽‍♂️","🧚🏾‍♂️","🧚🏿‍♂️",
"👼","👼🏻","👼🏼","👼🏽","👼🏾","👼🏿", "👼","👼🏻","👼🏼","👼🏽","👼🏾","👼🏿",
"🤰","🤰🏻","🤰🏼","🤰🏽","🤰🏾","🤰🏿", "🤰","🤰🏻","🤰🏼","🤰🏽","🤰🏾","🤰🏿",
"🫄","🫄🏻","🫄🏼","🫄🏽","🫄🏾","🫄🏿",
"🫃","🫃🏻","🫃🏼","🫃🏽","🫃🏾","🫃🏿",
"🤱","🤱🏻","🤱🏼","🤱🏽","🤱🏾","🤱🏿", "🤱","🤱🏻","🤱🏼","🤱🏽","🤱🏾","🤱🏿",
"👩‍🍼","👩🏻‍🍼","👩🏼‍🍼","👩🏽‍🍼","👩🏾‍🍼","👩🏿‍🍼", "👩‍🍼","👩🏻‍🍼","👩🏼‍🍼","👩🏽‍🍼","👩🏾‍🍼","👩🏿‍🍼",
"🧑‍🍼","🧑🏻‍🍼","🧑🏼‍🍼","🧑🏽‍🍼","🧑🏾‍🍼","🧑🏿‍🍼", "🧑‍🍼","🧑🏻‍🍼","🧑🏼‍🍼","🧑🏽‍🍼","🧑🏾‍🍼","🧑🏿‍🍼",
@ -253,13 +266,13 @@
"👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂" "👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂"
"🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼","🐻‍❄️","🐨","🐯","🦁","🐮","🐷","🐽","🐸","🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦","🐤","🐣","🐥","🦆","🦅","🦉","🦇","🐺","🐗","🐴","🦄","🐝","🪱","🐛","🦋","🐌","🐞","🐜","🪰","🪲","🪳","🦟","🦗","🕷","🕸","🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑","🦐","🦞","🦀","🐡","🐠","🐟","🐬","🐳","🐋","🦈","🦭","🐊","🐅","🐆","🦓","🦍","🦧","🦣","🐘","🦛","🦏","🐪","🐫","🦒","🦘","🦬","🐃","🐂","🐄","🐎","🐖","🐏","🐑","🦙","🐐","🦌","🐕","🐩","🦮","🐕‍🦺","🐈","🐈‍⬛","🪶","🐓","🦃","🦤","🦚","🦜","🦢","🦩","🕊","🐇","🦝","🦨","🦡","🦫","🦦","🦥","🐁","🐀","🐿","🦔","🐾","🐉","🐲","🌵","🎄","🌲","🌳","🌴","🪵","🌱","🌿","☘️","🍀","🎍","🪴","🎋","🍃","🍂","🍁","🍄","🐚","🪨","🌾","💐","🌷","🌹","🥀","🌺","🌸","🌼","🌻","🌞","🌝","🌛","🌜","🌚","🌕","🌖","🌗","🌘","🌑","🌒","🌓","🌔","🌙","🌎","🌍","🌏","🪐","💫","⭐️","🌟","✨","⚡️","☄️","💥","🔥","🌪","🌈","☀️","🌤","⛅️","🌥","☁️","🌦","🌧","⛈","🌩","🌨","❄️","☃️","⛄️","🌬","💨","💧","💦","☔️","☂️","🌊","🌫" "🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼","🐻‍❄️","🐨","🐯","🦁","🐮","🐷","🐽","🐸","🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦","🐤","🐣","🐥","🪿","🦆","🐦‍⬛️","🦅","🦉","🦇","🐺","🐗","🐴","🦄","🫎","🐝","🪱","🐛","🦋","🐌","🐞","🐜","🪰","🪲","🪳","🦟","🦗","🕷","🕸","🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑","🪼","🦐","🦞","🦀","🐡","🐠","🐟","🐬","🐳","🐋","🦈","🦭","🐊","🐅","🐆","🦓","🦍","🦧","🦣","🐘","🦛","🦏","🐪","🐫","🦒","🦘","🦬","🐃","🐂","🐄","🫏","🐎","🐖","🐏","🐑","🦙","🐐","🦌","🐕","🐩","🦮","🐕‍🦺","🐈","🐈‍⬛","🪶","🪽","🐓","🦃","🦤","🦚","🦜","🦢","🦩","🕊","🐇","🦝","🦨","🦡","🦫","🦦","🦥","🐁","🐀","🐿","🦔","🐾","🐉","🐲","🌵","🎄","🌲","🌳","🌴","🪵","🌱","🌿","☘️","🍀","🎍","🪴","🎋","🍃","🍂","🍁","🪺","🪹","🍄","🐚","🪸","🪨","🌾","💐","🌷","🌹","🥀","🪻","🪷","🌺","🌸","🌼","🌻","🌞","🌝","🌛","🌜","🌚","🌕","🌖","🌗","🌘","🌑","🌒","🌓","🌔","🌙","🌎","🌍","🌏","🪐","💫","⭐️","🌟","✨","⚡️","☄️","💥","🔥","🌪","🌈","☀️","🌤","⛅️","🌥","☁️","🌦","🌧","⛈","🌩","🌨","❄️","☃️","⛄️","🌬","💨","💧","💦","🫧","☔️","☂️","🌊","🌫"
"🍏","🍎","🍐","🍊","🍋","🍌","🍉","🍇","🍓","🫐","🍈","🍒","🍑","🥭","🍍","🥥","🥝","🍅","🍆","🥑","🥦","🥬","🥒","🌶","🫑","🌽","🥕","🫒","🧄","🧅","🥔","🍠","🥐","🥯","🍞","🥖","🥨","🧀","🥚","🍳","🧈","🥞","🧇","🥓","🥩","🍗","🍖","🦴","🌭","🍔","🍟","🍕","🫓","🥪","🥙","🧆","🌮","🌯","🫔","🥗","🥘","🫕","🥫","🍝","🍜","🍲","🍛","🍣","🍱","🥟","🦪","🍤","🍙","🍚","🍘","🍥","🥠","🥮","🍢","🍡","🍧","🍨","🍦","🥧","🧁","🍰","🎂","🍮","🍭","🍬","🍫","🍿","🍩","🍪","🌰","🥜","🍯","🥛","🍼","🫖","☕️","🍵","🧃","🥤","🧋","🍶","🍺","🍻","🥂","🍷","🥃","🍸","🍹","🧉","🍾","🧊","🥄","🍴","🍽","🥣","🥡","🥢","🧂" "🍏","🍎","🍐","🍊","🍋","🍌","🍉","🍇","🍓","🫐","🍈","🍒","🍑","🥭","🍍","🥥","🥝","🍅","🍆","🥑","🫛","🥦","🥬","🥒","🌶","🫑","🌽","🥕","🫒","🧄","🧅","🥔","🍠","🫚","🥐","🥯","🍞","🥖","🥨","🧀","🥚","🍳","🧈","🥞","🧇","🥓","🥩","🍗","🍖","🦴","🌭","🍔","🍟","🍕","🫓","🥪","🥙","🧆","🌮","🌯","🫔","🥗","🥘","🫕","🥫","🍝","🍜","🍲","🫙","🍛","🍣","🍱","🥟","🦪","🍤","🍙","🍚","🍘","🍥","🥠","🥮","🍢","🍡","🍧","🍨","🍦","🥧","🧁","🍰","🎂","🍮","🍭","🍬","🍫","🍿","🍩","🍪","🌰","🥜","🫘","🍯","🥛","🫗","🍼","🫖","☕️","🍵","🧃","🥤","🧋","🍶","🍺","🍻","🥂","🍷","🥃","🍸","🍹","🧉","🍾","🧊","🥄","🍴","🍽","🥣","🥡","🥢","🧂"
"⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂", "⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🛝","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂",
"🏋️‍♀️","🏋🏻‍♀️","🏋🏼‍♀️","🏋🏽‍♀️","🏋🏾‍♀️","🏋🏿‍♀️", "🏋️‍♀️","🏋🏻‍♀️","🏋🏼‍♀️","🏋🏽‍♀️","🏋🏾‍♀️","🏋🏿‍♀️",
"🏋️","🏋🏻","🏋🏼","🏋🏽","🏋🏾","🏋🏿", "🏋️","🏋🏻","🏋🏼","🏋🏽","🏋🏾","🏋🏿",
"🏋️‍♂️","🏋🏻‍♂️","🏋🏼‍♂️","🏋🏽‍♂️","🏋🏾‍♂️","🏋🏿‍♂️", "🏋️‍♂️","🏋🏻‍♂️","🏋🏼‍♂️","🏋🏽‍♂️","🏋🏾‍♂️","🏋🏿‍♂️",
@ -306,20 +319,20 @@
"🤹‍♀️","🤹🏻‍♀️","🤹🏼‍♀️","🤹🏽‍♀️","🤹🏾‍♀️","🤹🏿‍♀️", "🤹‍♀️","🤹🏻‍♀️","🤹🏼‍♀️","🤹🏽‍♀️","🤹🏾‍♀️","🤹🏿‍♀️",
"🤹","🤹🏻","🤹🏼","🤹🏽","🤹🏾","🤹🏿", "🤹","🤹🏻","🤹🏼","🤹🏽","🤹🏾","🤹🏿",
"🤹‍♂️","🤹🏻‍♂️","🤹🏼‍♂️","🤹🏽‍♂️","🤹🏾‍♂️","🤹🏿‍♂️", "🤹‍♂️","🤹🏻‍♂️","🤹🏼‍♂️","🤹🏽‍♂️","🤹🏾‍♂️","🤹🏿‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩" "🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🪇","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🪈","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"🚗","🚕","🚙","🚌","🚎","🏎","🚓","🚑","🚒","🚐","🛻","🚚","🚛","🚜","🦯","🦽","🦼","🛴","🚲","🛵","🏍","🛺","🚨","🚔","🚍","🚘","🚖","🚡","🚠","🚟","🚃","🚋","🚞","🚝","🚄","🚅","🚈","🚂","🚆","🚇","🚊","🚉","✈️","🛫","🛬","🛩","💺","🛰","🚀","🛸","🚁","🛶","⛵️","🚤","🛥","🛳","⛴","🚢","⚓️","🪝","⛽️","🚧","🚦","🚥","🚏","🗺","🗿","🗽","🗼","🏰","🏯","🏟","🎡","🎢","🎠","⛲️","⛱","🏖","🏝","🏜","🌋","⛰","🏔","🗻","🏕","⛺️","🛖","🏠","🏡","🏘","🏚","🏗","🏭","🏢","🏬","🏣","🏤","🏥","🏦","🏨","🏪","🏫","🏩","💒","🏛","⛪️","🕌","🕍","🛕","🕋","⛩","🛤","🛣","🗾","🎑","🏞","🌅","🌄","🌠","🎇","🎆","🌇","🌆","🏙","🌃","🌌","🌉","🌁" "🚗","🚕","🚙","🚌","🚎","🏎","🚓","🚑","🚒","🚐","🛻","🚚","🚛","🚜","🦯","🦽","🦼","🩼","🛴","🚲","🛵","🏍","🛺","🛞","🚨","🚔","🚍","🚘","🚖","🚡","🚠","🚟","🚃","🚋","🚞","🚝","🚄","🚅","🚈","🚂","🚆","🚇","🚊","🚉","✈️","🛫","🛬","🛩","💺","🛰","🚀","🛸","🚁","🛶","⛵️","🚤","🛥","🛳","⛴","🚢","🛟","⚓️","🪝","⛽️","🚧","🚦","🚥","🚏","🗺","🗿","🗽","🗼","🏰","🏯","🏟","🎡","🎢","🎠","⛲️","⛱","🏖","🏝","🏜","🌋","⛰","🏔","🗻","🏕","⛺️","🛖","🏠","🏡","🏘","🏚","🏗","🏭","🏢","🏬","🏣","🏤","🏥","🏦","🏨","🏪","🏫","🏩","💒","🏛","⛪️","🕌","🕍","🛕","🕋","⛩","🛤","🛣","🗾","🎑","🏞","🌅","🌄","🌠","🎇","🎆","🌇","🌆","🏙","🌃","🌌","🌉","🌁"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","💈","⚗️","🔭","🔬","🕳","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁", "⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🪫","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","🪪","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","🪬","💈","⚗️","🔭","🔬","🕳","🩻","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀","🛀🏻","🛀🏼","🛀🏽","🛀🏾","🛀🏿", "🛀","🛀🏻","🛀🏼","🛀🏽","🛀🏾","🛀🏿",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓" "🧼","🪥","🪒","🪮","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🪭","🏮","🎐","🪩","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
"❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎","💔","❤️‍🔥","❤️‍🩹","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✝️","☪️","🕉","☸️","✡️","🔯","🕎","☯️","☦️","🛐","⛎","♈️","♉️","♊️","♋️","♌️","♍️","♎️","♏️","♐️","♑️","♒️","♓️","🆔","⚛️","🉑","☢️","☣️","📴","📳","🈶","🈚️","🈸","🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️","🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑","🅾️","🆘","❌","⭕️","🛑","⛔️","📛","🚫","💯","💢","♨️","🚷","🚯","🚳","🚱","🔞","📵","🚭","❗️","❕","❓","❔","‼️","⁉️","🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰","♻️","✅","🈯️","💹","❇️","✳️","❎","🌐","💠","Ⓜ️","🌀","💤","🏧","🚾","♿️","🅿️","🛗","🈳","🈂️","🛂","🛃","🛄","🛅","🚹","🚺","🚼","⚧","🚻","🚮","🎦","📶","🈁","🔣","","🔤","🔡","🔠","🆖","🆗","🆙","🆒","🆕","🆓","0⃣","1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣","🔟","🔢","#️⃣","*️⃣","⏏️","▶️","⏸","⏯","⏹","⏺","⏭","⏮","⏩","⏪","⏫","⏬","◀️","🔼","🔽","➡️","⬅️","⬆️","⬇️","↗️","↘️","↙️","↖️","↕️","↔️","↪️","↩️","⤴️","⤵️","🔀","🔁","🔂","🔄","🔃","🎵","🎶","","","➗","✖️","♾","💲","💱","™️","©️","®️","👁‍🗨","🔚","🔙","🔛","🔝","🔜","〰️","➰","➿","✔️","☑️","🔘","🔴","🟠","🟡","🟢","🔵","🟣","⚫️","⚪️","🟤","🔺","🔻","🔸","🔹","🔶","🔷","🔳","🔲","▪️","▫️","◾️","◽️","◼️","◻️","🟥","🟧","🟨","🟩","🟦","🟪","⬛️","⬜️","🟫","🔈","🔇","🔉","🔊","🔔","🔕","📣","📢","💬","💭","🗯","♠️","♣️","♥️","♦️","🃏","🎴","🀄️","🕐","🕑","🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚","🕛","🕜","🕝","🕞","🕟","🕠","🕡","🕢","🕣","🕤","🕥","🕦","🕧" "🩷","❤️","🧡","💛","💚","🩵","💙","💜","🖤","🩶","🤍","🤎","💔","❤️‍🔥","❤️‍🩹","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✝️","☪️","🕉","☸️","🪯","✡️","🔯","🕎","☯️","☦️","🛐","⛎","♈️","♉️","♊️","♋️","♌️","♍️","♎️","♏️","♐️","♑️","♒️","♓️","🆔","⚛️","🉑","☢️","☣️","📴","📳","🈶","🈚️","🈸","🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️","🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑","🅾️","🆘","❌","⭕️","🛑","⛔️","📛","🚫","💯","💢","♨️","🚷","🚯","🚳","🚱","🔞","📵","🚭","❗️","❕","❓","❔","‼️","⁉️","🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰","♻️","✅","🈯️","💹","❇️","✳️","❎","🌐","💠","Ⓜ️","🌀","💤","🏧","🚾","♿️","🅿️","🛗","🈳","🈂️","🛂","🛃","🛄","🛅","🛜","🚹","🚺","🚼","⚧","🚻","🚮","🎦","📶","🈁","🔣","","🔤","🔡","🔠","🆖","🆗","🆙","🆒","🆕","🆓","0⃣","1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣","🔟","🔢","#️⃣","*️⃣","⏏️","▶️","⏸","⏯","⏹","⏺","⏭","⏮","⏩","⏪","⏫","⏬","◀️","🔼","🔽","➡️","⬅️","⬆️","⬇️","↗️","↘️","↙️","↖️","↕️","↔️","↪️","↩️","⤴️","⤵️","🔀","🔁","🔂","🔄","🔃","🎵","🎶","","","➗","✖️","🟰","♾","💲","💱","™️","©️","®️","👁‍🗨","🔚","🔙","🔛","🔝","🔜","〰️","➰","➿","✔️","☑️","🔘","🔴","🟠","🟡","🟢","🔵","🟣","⚫️","⚪️","🟤","🔺","🔻","🔸","🔹","🔶","🔷","🔳","🔲","▪️","▫️","◾️","◽️","◼️","◻️","🟥","🟧","🟨","🟩","🟦","🟪","⬛️","⬜️","🟫","🔈","🔇","🔉","🔊","🔔","🔕","📣","📢","💬","💭","🗯","♠️","♣️","♥️","♦️","🃏","🎴","🀄️","🕐","🕑","🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚","🕛","🕜","🕝","🕞","🕟","🕠","🕡","🕢","🕣","🕤","🕥","🕦","🕧"
"🏳️","🏴","🏴‍☠️","🏁","🚩","🏳️‍🌈","🏳️‍⚧️","🇺🇳","🇦🇫","🇦🇽","🇦🇱","🇩🇿","🇦🇸","🇦🇩","🇦🇴","🇦🇮","🇦🇶","🇦🇬","🇦🇷","🇦🇲","🇦🇼","🇦🇺","🇦🇹","🇦🇿","🇧🇸","🇧🇭","🇧🇩","🇧🇧","🇧🇾","🇧🇪","🇧🇿","🇧🇯","🇧🇲","🇧🇹","🇧🇴","🇧🇦","🇧🇼","🇧🇷","🇻🇬","🇧🇳","🇧🇬","🇧🇫","🇧🇮","🇰🇭","🇨🇲","🇨🇦","🇮🇨","🇨🇻","🇧🇶","🇰🇾","🇨🇫","🇹🇩","🇮🇴","🇨🇱","🇨🇳","🇨🇽","🇨🇨","🇨🇴","🇰🇲","🇨🇬","🇨🇩","🇨🇰","🇨🇷","🇨🇮","🇭🇷","🇨🇺","🇨🇼","🇨🇾","🇨🇿","🇩🇰","🇩🇯","🇩🇲","🇩🇴","🇪🇨","🇪🇬","🇸🇻","🇬🇶","🇪🇷","🇪🇪","🇸🇿","🇪🇹","🇪🇺","🇫🇰","🇫🇴","🇫🇯","🇫🇮","🇫🇷","🇬🇫","🇵🇫","🇹🇫","🇬🇦","🇬🇲","🇬🇪","🇩🇪","🇬🇭","🇬🇮","🇬🇷","🇬🇱","🇬🇩","🇬🇵","🇬🇺","🇬🇹","🇬🇬","🇬🇳","🇬🇼","🇬🇾","🇭🇹","🇭🇳","🇭🇰","🇭🇺","🇮🇸","🇮🇳","🇮🇩","🇮🇷","🇮🇶","🇮🇪","🇮🇲","🇮🇱","🇮🇹","🇯🇲","🇯🇵","🎌","🇯🇪","🇯🇴","🇰🇿","🇰🇪","🇰🇮","🇽🇰","🇰🇼","🇰🇬","🇱🇦","🇱🇻","🇱🇧","🇱🇸","🇱🇷","🇱🇾","🇱🇮","🇱🇹","🇱🇺","🇲🇴","🇲🇬","🇲🇼","🇲🇾","🇲🇻","🇲🇱","🇲🇹","🇲🇭","🇲🇶","🇲🇷","🇲🇺","🇾🇹","🇲🇽","🇫🇲","🇲🇩","🇲🇨","🇲🇳","🇲🇪","🇲🇸","🇲🇦","🇲🇿","🇲🇲","🇳🇦","🇳🇷","🇳🇵","🇳🇱","🇳🇨","🇳🇿","🇳🇮","🇳🇪","🇳🇬","🇳🇺","🇳🇫","🇰🇵","🇲🇰","🇲🇵","🇳🇴","🇴🇲","🇵🇰","🇵🇼","🇵🇸","🇵🇦","🇵🇬","🇵🇾","🇵🇪","🇵🇭","🇵🇳","🇵🇱","🇵🇹","🇵🇷","🇶🇦","🇷🇪","🇷🇴","🇷🇺","🇷🇼","🇼🇸","🇸🇲","🇸🇹","🇸🇦","🇸🇳","🇷🇸","🇸🇨","🇸🇱","🇸🇬","🇸🇽","🇸🇰","🇸🇮","🇬🇸","🇸🇧","🇸🇴","🇿🇦","🇰🇷","🇸🇸","🇪🇸","🇱🇰","🇧🇱","🇸🇭","🇰🇳","🇱🇨","🇵🇲","🇻🇨","🇸🇩","🇸🇷","🇸🇪","🇨🇭","🇸🇾","🇹🇼","🇹🇯","🇹🇿","🇹🇭","🇹🇱","🇹🇬","🇹🇰","🇹🇴","🇹🇹","🇹🇳","🇹🇷","🇹🇲","🇹🇨","🇹🇻","🇺🇬","🇺🇦","🇦🇪","🇬🇧","🏴󠁧󠁢󠁥󠁮󠁧󠁿","🏴󠁧󠁢󠁳󠁣󠁴󠁿","🏴󠁧󠁢󠁷󠁬󠁳󠁿","🇺🇸","🇺🇾","🇻🇮","🇺🇿","🇻🇺","🇻🇦","🇻🇪","🇻🇳","🇼🇫","🇪🇭","🇾🇪","🇿🇲","🇿🇼" "🏳️","🏴","🏴‍☠️","🏁","🚩","🏳️‍🌈","🏳️‍⚧️","🇺🇳","🇦🇫","🇦🇽","🇦🇱","🇩🇿","🇦🇸","🇦🇩","🇦🇴","🇦🇮","🇦🇶","🇦🇬","🇦🇷","🇦🇲","🇦🇼","🇦🇺","🇦🇹","🇦🇿","🇧🇸","🇧🇭","🇧🇩","🇧🇧","🇧🇾","🇧🇪","🇧🇿","🇧🇯","🇧🇲","🇧🇹","🇧🇴","🇧🇦","🇧🇼","🇧🇷","🇻🇬","🇧🇳","🇧🇬","🇧🇫","🇧🇮","🇰🇭","🇨🇲","🇨🇦","🇮🇨","🇨🇻","🇧🇶","🇰🇾","🇨🇫","🇹🇩","🇮🇴","🇨🇱","🇨🇳","🇨🇽","🇨🇨","🇨🇴","🇰🇲","🇨🇬","🇨🇩","🇨🇰","🇨🇷","🇨🇮","🇭🇷","🇨🇺","🇨🇼","🇨🇾","🇨🇿","🇩🇰","🇩🇯","🇩🇲","🇩🇴","🇪🇨","🇪🇬","🇸🇻","🇬🇶","🇪🇷","🇪🇪","🇸🇿","🇪🇹","🇪🇺","🇫🇰","🇫🇴","🇫🇯","🇫🇮","🇫🇷","🇬🇫","🇵🇫","🇹🇫","🇬🇦","🇬🇲","🇬🇪","🇩🇪","🇬🇭","🇬🇮","🇬🇷","🇬🇱","🇬🇩","🇬🇵","🇬🇺","🇬🇹","🇬🇬","🇬🇳","🇬🇼","🇬🇾","🇭🇹","🇭🇳","🇭🇰","🇭🇺","🇮🇸","🇮🇳","🇮🇩","🇮🇷","🇮🇶","🇮🇪","🇮🇲","🇮🇱","🇮🇹","🇯🇲","🇯🇵","🎌","🇯🇪","🇯🇴","🇰🇿","🇰🇪","🇰🇮","🇽🇰","🇰🇼","🇰🇬","🇱🇦","🇱🇻","🇱🇧","🇱🇸","🇱🇷","🇱🇾","🇱🇮","🇱🇹","🇱🇺","🇲🇴","🇲🇬","🇲🇼","🇲🇾","🇲🇻","🇲🇱","🇲🇹","🇲🇭","🇲🇶","🇲🇷","🇲🇺","🇾🇹","🇲🇽","🇫🇲","🇲🇩","🇲🇨","🇲🇳","🇲🇪","🇲🇸","🇲🇦","🇲🇿","🇲🇲","🇳🇦","🇳🇷","🇳🇵","🇳🇱","🇳🇨","🇳🇿","🇳🇮","🇳🇪","🇳🇬","🇳🇺","🇳🇫","🇰🇵","🇲🇰","🇲🇵","🇳🇴","🇴🇲","🇵🇰","🇵🇼","🇵🇸","🇵🇦","🇵🇬","🇵🇾","🇵🇪","🇵🇭","🇵🇳","🇵🇱","🇵🇹","🇵🇷","🇶🇦","🇷🇪","🇷🇴","🇷🇺","🇷🇼","🇼🇸","🇸🇲","🇸🇹","🇸🇦","🇸🇳","🇷🇸","🇸🇨","🇸🇱","🇸🇬","🇸🇽","🇸🇰","🇸🇮","🇬🇸","🇸🇧","🇸🇴","🇿🇦","🇰🇷","🇸🇸","🇪🇸","🇱🇰","🇧🇱","🇸🇭","🇰🇳","🇱🇨","🇵🇲","🇻🇨","🇸🇩","🇸🇷","🇸🇪","🇨🇭","🇸🇾","🇹🇼","🇹🇯","🇹🇿","🇹🇭","🇹🇱","🇹🇬","🇹🇰","🇹🇴","🇹🇹","🇹🇳","🇹🇷","🇹🇲","🇹🇨","🇹🇻","🇺🇬","🇺🇦","🇦🇪","🇬🇧","🏴󠁧󠁢󠁥󠁮󠁧󠁿","🏴󠁧󠁢󠁳󠁣󠁴󠁿","🏴󠁧󠁢󠁷󠁬󠁳󠁿","🇺🇸","🇺🇾","🇻🇮","🇺🇿","🇻🇺","🇻🇦","🇻🇪","🇻🇳","🇼🇫","🇪🇭","🇾🇪","🇿🇲","🇿🇼"
@ -329,7 +342,8 @@
========================================= =========================================
"😀","😃","😄","😁","😆","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🤭","🤫","🤥","😶","😐","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾", "😀","😃","😄","😁","😆","🥹","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🫣","🤭","🫢","🫡","🤫","🫠","🤥","😶","🫥","😐","🫤","😑","🫨","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🫶",
"🤲", "🤲",
"👐", "👐",
"🙌", "🙌",
@ -341,13 +355,18 @@
"✊", "✊",
"🤛", "🤛",
"🤜", "🤜",
"🫷",
"🫸",
"🤞", "🤞",
"✌️", "✌️",
"🫰",
"🤟", "🤟",
"🤘", "🤘",
"👌", "👌",
"🤌", "🤌",
"🤏", "🤏",
"🫳",
"🫴",
"👈", "👈",
"👉", "👉",
"👆", "👆",
@ -359,14 +378,17 @@
"🖖", "🖖",
"👋", "👋",
"🤙", "🤙",
"🫲",
"🫱",
"💪", "💪",
"🦾", "🦾",
"🖕", "🖕",
"✍️", "✍️",
"🙏", "🙏",
"🫵",
"🦶", "🦶",
"🦵", "🦵",
"🦿","💄","💋","👄","🦷","👅", "🦿","💄","💋","👄","🫦","🦷","👅",
"👂", "👂",
"🦻", "🦻",
"👃", "👃",
@ -471,6 +493,7 @@
"🤵", "🤵",
"🤵‍♂️", "🤵‍♂️",
"👸", "👸",
"🫅",
"🤴", "🤴",
"🥷", "🥷",
"🦸‍♀️", "🦸‍♀️",
@ -488,6 +511,7 @@
"🧝‍♀️", "🧝‍♀️",
"🧝", "🧝",
"🧝‍♂️", "🧝‍♂️",
"🧌",
"🧛‍♀️", "🧛‍♀️",
"🧛", "🧛",
"🧛‍♂️", "🧛‍♂️",
@ -500,6 +524,8 @@
"🧚‍♂️", "🧚‍♂️",
"👼", "👼",
"🤰", "🤰",
"🫄",
"🫃",
"🤱", "🤱",
"👩‍🍼", "👩‍🍼",
"🧑‍🍼", "🧑‍🍼",
@ -631,24 +657,24 @@
"🤹‍♀️", "🤹‍♀️",
"🤹", "🤹",
"🤹‍♂️", "🤹‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩" "🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🪇","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🪈","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","💈","⚗️","🔭","🔬","🕳","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁", "⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🪫","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","🪪","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","🪬","💈","⚗️","🔭","🔬","🕳","🩻","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀", "🛀",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓" "🧼","🪥","🪒","🪮","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🪭","🏮","🎐","🪩","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
========================================= =========================================
"🤲","👐","🙌","👏","👍","👎","👊","✊","🤛","🤜","🤞","✌️","🤟","🤘","👌","🤌","🤏","👈","👉","👆","👇","☝️","✋","🤚","🖐","🖖","👋","🤙","💪","🖕","✍️","🙏","🦶","🦵","👂","🦻","👃","👶","👧","🧒","👦","👩","🧑","👨","👩‍🦱","🧑‍🦱","👨‍🦱","👩‍🦰","🧑‍🦰","👨‍🦰","👱‍♀️","👱","👱‍♂️","👩‍🦳","🧑‍🦳","👨‍🦳","👩‍🦲","🧑‍🦲","👨‍🦲","🧔‍♀️","🧔","🧔‍♂️","👵","🧓","👴","👲","👳‍♀️","👳","👳‍♂️","🧕","👮‍♀️","👮","👮‍♂️","👷‍♀️","👷","👷‍♂️","💂‍♀️","💂","💂‍♂️","🕵️‍♀️","🕵️","🕵️‍♂️","👩‍⚕️","🧑‍⚕️","👨‍⚕️","👩‍🌾","🧑‍🌾","👨‍🌾","👩‍🍳","🧑‍🍳","👨‍🍳","👩‍🎓","🧑‍🎓","👨‍🎓","👩‍🎤","🧑‍🎤","👨‍🎤","👩‍🏫","🧑‍🏫","👨‍🏫","👩‍🏭","🧑‍🏭","👨‍🏭","👩‍💻","🧑‍💻","👨‍💻","👩‍💼","🧑‍💼","👨‍💼","👩‍🔧","🧑‍🔧","👨‍🔧","👩‍🔬","🧑‍🔬","👨‍🔬","👩‍🎨","🧑‍🎨","👨‍🎨","👩‍🚒","🧑‍🚒","👨‍🚒","👩‍✈️","🧑‍✈️","👨‍✈️","👩‍🚀","🧑‍🚀","👨‍🚀","👩‍⚖️","🧑‍⚖️","👨‍⚖️","👰‍♀️","👰","👰‍♂️","🤵‍♀️","🤵","🤵‍♂️","👸","🤴","🥷","🦸‍♀️","🦸","🦸‍♂️","🦹‍♀️","🦹","🦹‍♂️","🤶","🧑‍🎄","🎅","🧙‍♀️","🧙","🧙‍♂️","🧝‍♀️","🧝","🧝‍♂️","🧛‍♀️","🧛","🧛‍♂️","🧜‍♀️","🧜","🧜‍♂️","🧚‍♀️","🧚","🧚‍♂️","👼","🤰","🤱","👩‍🍼","🧑‍🍼","👨‍🍼","🙇‍♀️","🙇","🙇‍♂️","💁‍♀️","💁","💁‍♂️","🙅‍♀️","🙅","🙅‍♂️","🙆‍♀️","🙆","🙆‍♂️","🙋‍♀️","🙋","🙋‍♂️","🧏‍♀️","🧏","🧏‍♂️","🤦‍♀️","🤦","🤦‍♂️","🤷‍♀️","🤷","🤷‍♂️","🙎‍♀️","🙎","🙎‍♂️","🙍‍♀️","🙍","🙍‍♂️","💇‍♀️","💇","💇‍♂️","💆‍♀️","💆","💆‍♂️","🧖‍♀️","🧖","🧖‍♂️","💅","🤳","💃","🕺","🕴","👩‍🦽","🧑‍🦽","👨‍🦽","👩‍🦼","🧑‍🦼","👨‍🦼","🚶‍♀️","🚶","🚶‍♂️","👩‍🦯","🧑‍🦯","👨‍🦯","🧎‍♀️","🧎","🧎‍♂️","🏃‍♀️","🏃","🏃‍♂️","🧍‍♀️","🧍","🧍‍♂️","🏋️‍♀️","🏋️","🏋️‍♂️","🤸‍♀️","🤸","🤸‍♂️","⛹️‍♀️","⛹️","⛹️‍♂️","🤾‍♀️","🤾","🤾‍♂️","🏌️‍♀️","🏌️","🏌️‍♂️","🏇","🧘‍♀️","🧘","🧘‍♂️","🏄‍♀️","🏄","🏄‍♂️","🏊‍♀️","🏊","🏊‍♂️","🤽‍♀️","🤽","🤽‍♂️","🚣‍♀️","🚣","🚣‍♂️","🧗‍♀️","🧗","🧗‍♂️","🚵‍♀️","🚵","🚵‍♂️","🚴‍♀️","🚴","🚴‍♂️","🤹‍♀️","🤹","🤹‍♂️","🛀" "🫶","🤲","👐","🙌","👏","👍","👎","👊","✊","🤛","🤜","🫷","🫸","🤞","✌️","🫰","🤟","🤘","👌","🤌","🤏","🫳","🫴","👈","👉","👆","👇","☝️","✋","🤚","🖐","🖖","👋","🤙","🫲","🫱","💪","🖕","✍️","🙏","🫵","🦶","🦵","👂","🦻","👃","👶","👧","🧒","👦","👩","🧑","👨","👩‍🦱","🧑‍🦱","👨‍🦱","👩‍🦰","🧑‍🦰","👨‍🦰","👱‍♀️","👱","👱‍♂️","👩‍🦳","🧑‍🦳","👨‍🦳","👩‍🦲","🧑‍🦲","👨‍🦲","🧔‍♀️","🧔","🧔‍♂️","👵","🧓","👴","👲","👳‍♀️","👳","👳‍♂️","🧕","👮‍♀️","👮","👮‍♂️","👷‍♀️","👷","👷‍♂️","💂‍♀️","💂","💂‍♂️","🕵️‍♀️","🕵️","🕵️‍♂️","👩‍⚕️","🧑‍⚕️","👨‍⚕️","👩‍🌾","🧑‍🌾","👨‍🌾","👩‍🍳","🧑‍🍳","👨‍🍳","👩‍🎓","🧑‍🎓","👨‍🎓","👩‍🎤","🧑‍🎤","👨‍🎤","👩‍🏫","🧑‍🏫","👨‍🏫","👩‍🏭","🧑‍🏭","👨‍🏭","👩‍💻","🧑‍💻","👨‍💻","👩‍💼","🧑‍💼","👨‍💼","👩‍🔧","🧑‍🔧","👨‍🔧","👩‍🔬","🧑‍🔬","👨‍🔬","👩‍🎨","🧑‍🎨","👨‍🎨","👩‍🚒","🧑‍🚒","👨‍🚒","👩‍✈️","🧑‍✈️","👨‍✈️","👩‍🚀","🧑‍🚀","👨‍🚀","👩‍⚖️","🧑‍⚖️","👨‍⚖️","👰‍♀️","👰","👰‍♂️","🤵‍♀️","🤵","🤵‍♂️","👸","🫅","🤴","🥷","🦸‍♀️","🦸","🦸‍♂️","🦹‍♀️","🦹","🦹‍♂️","🤶","🧑‍🎄","🎅","🧙‍♀️","🧙","🧙‍♂️","🧝‍♀️","🧝","🧝‍♂️","🧛‍♀️","🧛","🧛‍♂️","🧜‍♀️","🧜","🧜‍♂️","🧚‍♀️","🧚","🧚‍♂️","👼","🤰","🫄","🫃","🤱","👩‍🍼","🧑‍🍼","👨‍🍼","🙇‍♀️","🙇","🙇‍♂️","💁‍♀️","💁","💁‍♂️","🙅‍♀️","🙅","🙅‍♂️","🙆‍♀️","🙆","🙆‍♂️","🙋‍♀️","🙋","🙋‍♂️","🧏‍♀️","🧏","🧏‍♂️","🤦‍♀️","🤦","🤦‍♂️","🤷‍♀️","🤷","🤷‍♂️","🙎‍♀️","🙎","🙎‍♂️","🙍‍♀️","🙍","🙍‍♂️","💇‍♀️","💇","💇‍♂️","💆‍♀️","💆","💆‍♂️","🧖‍♀️","🧖","🧖‍♂️","💅","🤳","💃","🕺","🕴","👩‍🦽","🧑‍🦽","👨‍🦽","👩‍🦼","🧑‍🦼","👨‍🦼","🚶‍♀️","🚶","🚶‍♂️","👩‍🦯","🧑‍🦯","👨‍🦯","🧎‍♀️","🧎","🧎‍♂️","🏃‍♀️","🏃","🏃‍♂️","🧍‍♀️","🧍","🧍‍♂️","🏋️‍♀️","🏋️","🏋️‍♂️","🤸‍♀️","🤸","🤸‍♂️","⛹️‍♀️","⛹️","⛹️‍♂️","🤾‍♀️","🤾","🤾‍♂️","🏌️‍♀️","🏌️","🏌️‍♂️","🏇","🧘‍♀️","🧘","🧘‍♂️","🏄‍♀️","🏄","🏄‍♂️","🏊‍♀️","🏊","🏊‍♂️","🤽‍♀️","🤽","🤽‍♂️","🚣‍♀️","🚣","🚣‍♂️","🧗‍♀️","🧗","🧗‍♂️","🚵‍♀️","🚵","🚵‍♂️","🚴‍♀️","🚴","🚴‍♂️","🤹‍♀️","🤹","🤹‍♂️","🛀"
"👫","👭","👬","👩‍❤️‍👨","👩‍❤️‍👩","💑","👨‍❤️‍👨","👩‍❤️‍💋‍👨","👩‍❤️‍💋‍👩","💏","👨‍❤️‍💋‍👨" "🤝","👫","👭","👬","👩‍❤️‍👨","👩‍❤️‍👩","💑","👨‍❤️‍👨","👩‍❤️‍💋‍👨","👩‍❤️‍💋‍👩","💏","👨‍❤️‍💋‍👨"
========================================= =========================================
"🗨", "👪" "🗨", "👪", "🇦🇨", "🇧🇻", "🇩🇬", "🇪🇦", "🇭🇲", "🇲🇫", "🇸🇯", "🇹🇦", "🇺🇲"

654
emoji_old/data4.txt Normal file
View file

@ -0,0 +1,654 @@
"😀","😃","😄","😁","😆","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🤭","🤫","🤥","😶","😐","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🤲","🤲🏻","🤲🏼","🤲🏽","🤲🏾","🤲🏿",
"👐","👐🏻","👐🏼","👐🏽","👐🏾","👐🏿",
"🙌","🙌🏻","🙌🏼","🙌🏽","🙌🏾","🙌🏿",
"👏","👏🏻","👏🏼","👏🏽","👏🏾","👏🏿",
"🤝",
"👍","👍🏻","👍🏼","👍🏽","👍🏾","👍🏿",
"👎","👎🏻","👎🏼","👎🏽","👎🏾","👎🏿",
"👊","👊🏻","👊🏼","👊🏽","👊🏾","👊🏿",
"✊","✊🏻","✊🏼","✊🏽","✊🏾","✊🏿",
"🤛","🤛🏻","🤛🏼","🤛🏽","🤛🏾","🤛🏿",
"🤜","🤜🏻","🤜🏼","🤜🏽","🤜🏾","🤜🏿",
"🤞","🤞🏻","🤞🏼","🤞🏽","🤞🏾","🤞🏿",
"✌️","✌🏻","✌🏼","✌🏽","✌🏾","✌🏿",
"🤟","🤟🏻","🤟🏼","🤟🏽","🤟🏾","🤟🏿",
"🤘","🤘🏻","🤘🏼","🤘🏽","🤘🏾","🤘🏿",
"👌","👌🏻","👌🏼","👌🏽","👌🏾","👌🏿",
"🤌","🤌🏻","🤌🏼","🤌🏽","🤌🏾","🤌🏿",
"🤏","🤏🏻","🤏🏼","🤏🏽","🤏🏾","🤏🏿",
"👈","👈🏻","👈🏼","👈🏽","👈🏾","👈🏿",
"👉","👉🏻","👉🏼","👉🏽","👉🏾","👉🏿",
"👆","👆🏻","👆🏼","👆🏽","👆🏾","👆🏿",
"👇","👇🏻","👇🏼","👇🏽","👇🏾","👇🏿",
"☝️","☝🏻","☝🏼","☝🏽","☝🏾","☝🏿",
"✋","✋🏻","✋🏼","✋🏽","✋🏾","✋🏿",
"🤚","🤚🏻","🤚🏼","🤚🏽","🤚🏾","🤚🏿",
"🖐","🖐🏻","🖐🏼","🖐🏽","🖐🏾","🖐🏿",
"🖖","🖖🏻","🖖🏼","🖖🏽","🖖🏾","🖖🏿",
"👋","👋🏻","👋🏼","👋🏽","👋🏾","👋🏿",
"🤙","🤙🏻","🤙🏼","🤙🏽","🤙🏾","🤙🏿",
"💪","💪🏻","💪🏼","💪🏽","💪🏾","💪🏿",
"🦾",
"🖕","🖕🏻","🖕🏼","🖕🏽","🖕🏾","🖕🏿",
"✍️","✍🏻","✍🏼","✍🏽","✍🏾","✍🏿",
"🙏","🙏🏻","🙏🏼","🙏🏽","🙏🏾","🙏🏿",
"🦶","🦶🏻","🦶🏼","🦶🏽","🦶🏾","🦶🏿",
"🦵","🦵🏻","🦵🏼","🦵🏽","🦵🏾","🦵🏿",
"🦿","💄","💋","👄","🦷","👅",
"👂","👂🏻","👂🏼","👂🏽","👂🏾","👂🏿",
"🦻","🦻🏻","🦻🏼","🦻🏽","🦻🏾","🦻🏿",
"👃","👃🏻","👃🏼","👃🏽","👃🏾","👃🏿",
"👣","👁","👀","🫀","🫁","🧠","🗣","👤","👥","🫂",
"👶","👶🏻","👶🏼","👶🏽","👶🏾","👶🏿",
"👧","👧🏻","👧🏼","👧🏽","👧🏾","👧🏿",
"🧒","🧒🏻","🧒🏼","🧒🏽","🧒🏾","🧒🏿",
"👦","👦🏻","👦🏼","👦🏽","👦🏾","👦🏿",
"👩","👩🏻","👩🏼","👩🏽","👩🏾","👩🏿",
"🧑","🧑🏻","🧑🏼","🧑🏽","🧑🏾","🧑🏿",
"👨","👨🏻","👨🏼","👨🏽","👨🏾","👨🏿",
"👩‍🦱","👩🏻‍🦱","👩🏼‍🦱","👩🏽‍🦱","👩🏾‍🦱","👩🏿‍🦱",
"🧑‍🦱","🧑🏻‍🦱","🧑🏼‍🦱","🧑🏽‍🦱","🧑🏾‍🦱","🧑🏿‍🦱",
"👨‍🦱","👨🏻‍🦱","👨🏼‍🦱","👨🏽‍🦱","👨🏾‍🦱","👨🏿‍🦱",
"👩‍🦰","👩🏻‍🦰","👩🏼‍🦰","👩🏽‍🦰","👩🏾‍🦰","👩🏿‍🦰",
"🧑‍🦰","🧑🏻‍🦰","🧑🏼‍🦰","🧑🏽‍🦰","🧑🏾‍🦰","🧑🏿‍🦰",
"👨‍🦰","👨🏻‍🦰","👨🏼‍🦰","👨🏽‍🦰","👨🏾‍🦰","👨🏿‍🦰",
"👱‍♀️","👱🏻‍♀️","👱🏼‍♀️","👱🏽‍♀️","👱🏾‍♀️","👱🏿‍♀️",
"👱","👱🏻","👱🏼","👱🏽","👱🏾","👱🏿",
"👱‍♂️","👱🏻‍♂️","👱🏼‍♂️","👱🏽‍♂️","👱🏾‍♂️","👱🏿‍♂️",
"👩‍🦳","👩🏻‍🦳","👩🏼‍🦳","👩🏽‍🦳","👩🏾‍🦳","👩🏿‍🦳",
"🧑‍🦳","🧑🏻‍🦳","🧑🏼‍🦳","🧑🏽‍🦳","🧑🏾‍🦳","🧑🏿‍🦳",
"👨‍🦳","👨🏻‍🦳","👨🏼‍🦳","👨🏽‍🦳","👨🏾‍🦳","👨🏿‍🦳",
"👩‍🦲","👩🏻‍🦲","👩🏼‍🦲","👩🏽‍🦲","👩🏾‍🦲","👩🏿‍🦲",
"🧑‍🦲","🧑🏻‍🦲","🧑🏼‍🦲","🧑🏽‍🦲","🧑🏾‍🦲","🧑🏿‍🦲",
"👨‍🦲","👨🏻‍🦲","👨🏼‍🦲","👨🏽‍🦲","👨🏾‍🦲","👨🏿‍🦲",
"🧔‍♀️","🧔🏻‍♀️","🧔🏼‍♀️","🧔🏽‍♀️","🧔🏾‍♀️","🧔🏿‍♀️",
"🧔","🧔🏻","🧔🏼","🧔🏽","🧔🏾","🧔🏿",
"🧔‍♂️","🧔🏻‍♂️","🧔🏼‍♂️","🧔🏽‍♂️","🧔🏾‍♂️","🧔🏿‍♂️",
"👵","👵🏻","👵🏼","👵🏽","👵🏾","👵🏿",
"🧓","🧓🏻","🧓🏼","🧓🏽","🧓🏾","🧓🏿",
"👴","👴🏻","👴🏼","👴🏽","👴🏾","👴🏿",
"👲","👲🏻","👲🏼","👲🏽","👲🏾","👲🏿",
"👳‍♀️","👳🏻‍♀️","👳🏼‍♀️","👳🏽‍♀️","👳🏾‍♀️","👳🏿‍♀️",
"👳","👳🏻","👳🏼","👳🏽","👳🏾","👳🏿",
"👳‍♂️","👳🏻‍♂️","👳🏼‍♂️","👳🏽‍♂️","👳🏾‍♂️","👳🏿‍♂️",
"🧕","🧕🏻","🧕🏼","🧕🏽","🧕🏾","🧕🏿",
"👮‍♀️","👮🏻‍♀️","👮🏼‍♀️","👮🏽‍♀️","👮🏾‍♀️","👮🏿‍♀️",
"👮","👮🏻","👮🏼","👮🏽","👮🏾","👮🏿",
"👮‍♂️","👮🏻‍♂️","👮🏼‍♂️","👮🏽‍♂️","👮🏾‍♂️","👮🏿‍♂️",
"👷‍♀️","👷🏻‍♀️","👷🏼‍♀️","👷🏽‍♀️","👷🏾‍♀️","👷🏿‍♀️",
"👷","👷🏻","👷🏼","👷🏽","👷🏾","👷🏿",
"👷‍♂️","👷🏻‍♂️","👷🏼‍♂️","👷🏽‍♂️","👷🏾‍♂️","👷🏿‍♂️",
"💂‍♀️","💂🏻‍♀️","💂🏼‍♀️","💂🏽‍♀️","💂🏾‍♀️","💂🏿‍♀️",
"💂","💂🏻","💂🏼","💂🏽","💂🏾","💂🏿",
"💂‍♂️","💂🏻‍♂️","💂🏼‍♂️","💂🏽‍♂️","💂🏾‍♂️","💂🏿‍♂️",
"🕵️‍♀️","🕵🏻‍♀️","🕵🏼‍♀️","🕵🏽‍♀️","🕵🏾‍♀️","🕵🏿‍♀️",
"🕵️","🕵🏻","🕵🏼","🕵🏽","🕵🏾","🕵🏿",
"🕵️‍♂️","🕵🏻‍♂️","🕵🏼‍♂️","🕵🏽‍♂️","🕵🏾‍♂️","🕵🏿‍♂️",
"👩‍⚕️","👩🏻‍⚕️","👩🏼‍⚕️","👩🏽‍⚕️","👩🏾‍⚕️","👩🏿‍⚕️",
"🧑‍⚕️","🧑🏻‍⚕️","🧑🏼‍⚕️","🧑🏽‍⚕️","🧑🏾‍⚕️","🧑🏿‍⚕️",
"👨‍⚕️","👨🏻‍⚕️","👨🏼‍⚕️","👨🏽‍⚕️","👨🏾‍⚕️","👨🏿‍⚕️",
"👩‍🌾","👩🏻‍🌾","👩🏼‍🌾","👩🏽‍🌾","👩🏾‍🌾","👩🏿‍🌾",
"🧑‍🌾","🧑🏻‍🌾","🧑🏼‍🌾","🧑🏽‍🌾","🧑🏾‍🌾","🧑🏿‍🌾",
"👨‍🌾","👨🏻‍🌾","👨🏼‍🌾","👨🏽‍🌾","👨🏾‍🌾","👨🏿‍🌾",
"👩‍🍳","👩🏻‍🍳","👩🏼‍🍳","👩🏽‍🍳","👩🏾‍🍳","👩🏿‍🍳",
"🧑‍🍳","🧑🏻‍🍳","🧑🏼‍🍳","🧑🏽‍🍳","🧑🏾‍🍳","🧑🏿‍🍳",
"👨‍🍳","👨🏻‍🍳","👨🏼‍🍳","👨🏽‍🍳","👨🏾‍🍳","👨🏿‍🍳",
"👩‍🎓","👩🏻‍🎓","👩🏼‍🎓","👩🏽‍🎓","👩🏾‍🎓","👩🏿‍🎓",
"🧑‍🎓","🧑🏻‍🎓","🧑🏼‍🎓","🧑🏽‍🎓","🧑🏾‍🎓","🧑🏿‍🎓",
"👨‍🎓","👨🏻‍🎓","👨🏼‍🎓","👨🏽‍🎓","👨🏾‍🎓","👨🏿‍🎓",
"👩‍🎤","👩🏻‍🎤","👩🏼‍🎤","👩🏽‍🎤","👩🏾‍🎤","👩🏿‍🎤",
"🧑‍🎤","🧑🏻‍🎤","🧑🏼‍🎤","🧑🏽‍🎤","🧑🏾‍🎤","🧑🏿‍🎤",
"👨‍🎤","👨🏻‍🎤","👨🏼‍🎤","👨🏽‍🎤","👨🏾‍🎤","👨🏿‍🎤",
"👩‍🏫","👩🏻‍🏫","👩🏼‍🏫","👩🏽‍🏫","👩🏾‍🏫","👩🏿‍🏫",
"🧑‍🏫","🧑🏻‍🏫","🧑🏼‍🏫","🧑🏽‍🏫","🧑🏾‍🏫","🧑🏿‍🏫",
"👨‍🏫","👨🏻‍🏫","👨🏼‍🏫","👨🏽‍🏫","👨🏾‍🏫","👨🏿‍🏫",
"👩‍🏭","👩🏻‍🏭","👩🏼‍🏭","👩🏽‍🏭","👩🏾‍🏭","👩🏿‍🏭",
"🧑‍🏭","🧑🏻‍🏭","🧑🏼‍🏭","🧑🏽‍🏭","🧑🏾‍🏭","🧑🏿‍🏭",
"👨‍🏭","👨🏻‍🏭","👨🏼‍🏭","👨🏽‍🏭","👨🏾‍🏭","👨🏿‍🏭",
"👩‍💻","👩🏻‍💻","👩🏼‍💻","👩🏽‍💻","👩🏾‍💻","👩🏿‍💻",
"🧑‍💻","🧑🏻‍💻","🧑🏼‍💻","🧑🏽‍💻","🧑🏾‍💻","🧑🏿‍💻",
"👨‍💻","👨🏻‍💻","👨🏼‍💻","👨🏽‍💻","👨🏾‍💻","👨🏿‍💻",
"👩‍💼","👩🏻‍💼","👩🏼‍💼","👩🏽‍💼","👩🏾‍💼","👩🏿‍💼",
"🧑‍💼","🧑🏻‍💼","🧑🏼‍💼","🧑🏽‍💼","🧑🏾‍💼","🧑🏿‍💼",
"👨‍💼","👨🏻‍💼","👨🏼‍💼","👨🏽‍💼","👨🏾‍💼","👨🏿‍💼",
"👩‍🔧","👩🏻‍🔧","👩🏼‍🔧","👩🏽‍🔧","👩🏾‍🔧","👩🏿‍🔧",
"🧑‍🔧","🧑🏻‍🔧","🧑🏼‍🔧","🧑🏽‍🔧","🧑🏾‍🔧","🧑🏿‍🔧",
"👨‍🔧","👨🏻‍🔧","👨🏼‍🔧","👨🏽‍🔧","👨🏾‍🔧","👨🏿‍🔧",
"👩‍🔬","👩🏻‍🔬","👩🏼‍🔬","👩🏽‍🔬","👩🏾‍🔬","👩🏿‍🔬",
"🧑‍🔬","🧑🏻‍🔬","🧑🏼‍🔬","🧑🏽‍🔬","🧑🏾‍🔬","🧑🏿‍🔬",
"👨‍🔬","👨🏻‍🔬","👨🏼‍🔬","👨🏽‍🔬","👨🏾‍🔬","👨🏿‍🔬",
"👩‍🎨","👩🏻‍🎨","👩🏼‍🎨","👩🏽‍🎨","👩🏾‍🎨","👩🏿‍🎨",
"🧑‍🎨","🧑🏻‍🎨","🧑🏼‍🎨","🧑🏽‍🎨","🧑🏾‍🎨","🧑🏿‍🎨",
"👨‍🎨","👨🏻‍🎨","👨🏼‍🎨","👨🏽‍🎨","👨🏾‍🎨","👨🏿‍🎨",
"👩‍🚒","👩🏻‍🚒","👩🏼‍🚒","👩🏽‍🚒","👩🏾‍🚒","👩🏿‍🚒",
"🧑‍🚒","🧑🏻‍🚒","🧑🏼‍🚒","🧑🏽‍🚒","🧑🏾‍🚒","🧑🏿‍🚒",
"👨‍🚒","👨🏻‍🚒","👨🏼‍🚒","👨🏽‍🚒","👨🏾‍🚒","👨🏿‍🚒",
"👩‍✈️","👩🏻‍✈️","👩🏼‍✈️","👩🏽‍✈️","👩🏾‍✈️","👩🏿‍✈️",
"🧑‍✈️","🧑🏻‍✈️","🧑🏼‍✈️","🧑🏽‍✈️","🧑🏾‍✈️","🧑🏿‍✈️",
"👨‍✈️","👨🏻‍✈️","👨🏼‍✈️","👨🏽‍✈️","👨🏾‍✈️","👨🏿‍✈️",
"👩‍🚀","👩🏻‍🚀","👩🏼‍🚀","👩🏽‍🚀","👩🏾‍🚀","👩🏿‍🚀",
"🧑‍🚀","🧑🏻‍🚀","🧑🏼‍🚀","🧑🏽‍🚀","🧑🏾‍🚀","🧑🏿‍🚀",
"👨‍🚀","👨🏻‍🚀","👨🏼‍🚀","👨🏽‍🚀","👨🏾‍🚀","👨🏿‍🚀",
"👩‍⚖️","👩🏻‍⚖️","👩🏼‍⚖️","👩🏽‍⚖️","👩🏾‍⚖️","👩🏿‍⚖️",
"🧑‍⚖️","🧑🏻‍⚖️","🧑🏼‍⚖️","🧑🏽‍⚖️","🧑🏾‍⚖️","🧑🏿‍⚖️",
"👨‍⚖️","👨🏻‍⚖️","👨🏼‍⚖️","👨🏽‍⚖️","👨🏾‍⚖️","👨🏿‍⚖️",
"👰‍♀️","👰🏻‍♀️","👰🏼‍♀️","👰🏽‍♀️","👰🏾‍♀️","👰🏿‍♀️",
"👰","👰🏻","👰🏼","👰🏽","👰🏾","👰🏿",
"👰‍♂️","👰🏻‍♂️","👰🏼‍♂️","👰🏽‍♂️","👰🏾‍♂️","👰🏿‍♂️",
"🤵‍♀️","🤵🏻‍♀️","🤵🏼‍♀️","🤵🏽‍♀️","🤵🏾‍♀️","🤵🏿‍♀️",
"🤵","🤵🏻","🤵🏼","🤵🏽","🤵🏾","🤵🏿",
"🤵‍♂️","🤵🏻‍♂️","🤵🏼‍♂️","🤵🏽‍♂️","🤵🏾‍♂️","🤵🏿‍♂️",
"👸","👸🏻","👸🏼","👸🏽","👸🏾","👸🏿",
"🤴","🤴🏻","🤴🏼","🤴🏽","🤴🏾","🤴🏿",
"🥷","🥷🏻","🥷🏼","🥷🏽","🥷🏾","🥷🏿",
"🦸‍♀️","🦸🏻‍♀️","🦸🏼‍♀️","🦸🏽‍♀️","🦸🏾‍♀️","🦸🏿‍♀️",
"🦸","🦸🏻","🦸🏼","🦸🏽","🦸🏾","🦸🏿",
"🦸‍♂️","🦸🏻‍♂️","🦸🏼‍♂️","🦸🏽‍♂️","🦸🏾‍♂️","🦸🏿‍♂️",
"🦹‍♀️","🦹🏻‍♀️","🦹🏼‍♀️","🦹🏽‍♀️","🦹🏾‍♀️","🦹🏿‍♀️",
"🦹","🦹🏻","🦹🏼","🦹🏽","🦹🏾","🦹🏿",
"🦹‍♂️","🦹🏻‍♂️","🦹🏼‍♂️","🦹🏽‍♂️","🦹🏾‍♂️","🦹🏿‍♂️",
"🤶","🤶🏻","🤶🏼","🤶🏽","🤶🏾","🤶🏿",
"🧑‍🎄","🧑🏻‍🎄","🧑🏼‍🎄","🧑🏽‍🎄","🧑🏾‍🎄","🧑🏿‍🎄",
"🎅","🎅🏻","🎅🏼","🎅🏽","🎅🏾","🎅🏿",
"🧙‍♀️","🧙🏻‍♀️","🧙🏼‍♀️","🧙🏽‍♀️","🧙🏾‍♀️","🧙🏿‍♀️",
"🧙","🧙🏻","🧙🏼","🧙🏽","🧙🏾","🧙🏿",
"🧙‍♂️","🧙🏻‍♂️","🧙🏼‍♂️","🧙🏽‍♂️","🧙🏾‍♂️","🧙🏿‍♂️",
"🧝‍♀️","🧝🏻‍♀️","🧝🏼‍♀️","🧝🏽‍♀️","🧝🏾‍♀️","🧝🏿‍♀️",
"🧝","🧝🏻","🧝🏼","🧝🏽","🧝🏾","🧝🏿",
"🧝‍♂️","🧝🏻‍♂️","🧝🏼‍♂️","🧝🏽‍♂️","🧝🏾‍♂️","🧝🏿‍♂️",
"🧛‍♀️","🧛🏻‍♀️","🧛🏼‍♀️","🧛🏽‍♀️","🧛🏾‍♀️","🧛🏿‍♀️",
"🧛","🧛🏻","🧛🏼","🧛🏽","🧛🏾","🧛🏿",
"🧛‍♂️","🧛🏻‍♂️","🧛🏼‍♂️","🧛🏽‍♂️","🧛🏾‍♂️","🧛🏿‍♂️",
"🧟‍♀️","🧟","🧟‍♂️","🧞‍♀️","🧞","🧞‍♂️",
"🧜‍♀️","🧜🏻‍♀️","🧜🏼‍♀️","🧜🏽‍♀️","🧜🏾‍♀️","🧜🏿‍♀️",
"🧜","🧜🏻","🧜🏼","🧜🏽","🧜🏾","🧜🏿",
"🧜‍♂️","🧜🏻‍♂️","🧜🏼‍♂️","🧜🏽‍♂️","🧜🏾‍♂️","🧜🏿‍♂️",
"🧚‍♀️","🧚🏻‍♀️","🧚🏼‍♀️","🧚🏽‍♀️","🧚🏾‍♀️","🧚🏿‍♀️",
"🧚","🧚🏻","🧚🏼","🧚🏽","🧚🏾","🧚🏿",
"🧚‍♂️","🧚🏻‍♂️","🧚🏼‍♂️","🧚🏽‍♂️","🧚🏾‍♂️","🧚🏿‍♂️",
"👼","👼🏻","👼🏼","👼🏽","👼🏾","👼🏿",
"🤰","🤰🏻","🤰🏼","🤰🏽","🤰🏾","🤰🏿",
"🤱","🤱🏻","🤱🏼","🤱🏽","🤱🏾","🤱🏿",
"👩‍🍼","👩🏻‍🍼","👩🏼‍🍼","👩🏽‍🍼","👩🏾‍🍼","👩🏿‍🍼",
"🧑‍🍼","🧑🏻‍🍼","🧑🏼‍🍼","🧑🏽‍🍼","🧑🏾‍🍼","🧑🏿‍🍼",
"👨‍🍼","👨🏻‍🍼","👨🏼‍🍼","👨🏽‍🍼","👨🏾‍🍼","👨🏿‍🍼",
"🙇‍♀️","🙇🏻‍♀️","🙇🏼‍♀️","🙇🏽‍♀️","🙇🏾‍♀️","🙇🏿‍♀️",
"🙇","🙇🏻","🙇🏼","🙇🏽","🙇🏾","🙇🏿",
"🙇‍♂️","🙇🏻‍♂️","🙇🏼‍♂️","🙇🏽‍♂️","🙇🏾‍♂️","🙇🏿‍♂️",
"💁‍♀️","💁🏻‍♀️","💁🏼‍♀️","💁🏽‍♀️","💁🏾‍♀️","💁🏿‍♀️",
"💁","💁🏻","💁🏼","💁🏽","💁🏾","💁🏿",
"💁‍♂️","💁🏻‍♂️","💁🏼‍♂️","💁🏽‍♂️","💁🏾‍♂️","💁🏿‍♂️",
"🙅‍♀️","🙅🏻‍♀️","🙅🏼‍♀️","🙅🏽‍♀️","🙅🏾‍♀️","🙅🏿‍♀️",
"🙅","🙅🏻","🙅🏼","🙅🏽","🙅🏾","🙅🏿",
"🙅‍♂️","🙅🏻‍♂️","🙅🏼‍♂️","🙅🏽‍♂️","🙅🏾‍♂️","🙅🏿‍♂️",
"🙆‍♀️","🙆🏻‍♀️","🙆🏼‍♀️","🙆🏽‍♀️","🙆🏾‍♀️","🙆🏿‍♀️",
"🙆","🙆🏻","🙆🏼","🙆🏽","🙆🏾","🙆🏿",
"🙆‍♂️","🙆🏻‍♂️","🙆🏼‍♂️","🙆🏽‍♂️","🙆🏾‍♂️","🙆🏿‍♂️",
"🙋‍♀️","🙋🏻‍♀️","🙋🏼‍♀️","🙋🏽‍♀️","🙋🏾‍♀️","🙋🏿‍♀️",
"🙋","🙋🏻","🙋🏼","🙋🏽","🙋🏾","🙋🏿",
"🙋‍♂️","🙋🏻‍♂️","🙋🏼‍♂️","🙋🏽‍♂️","🙋🏾‍♂️","🙋🏿‍♂️",
"🧏‍♀️","🧏🏻‍♀️","🧏🏼‍♀️","🧏🏽‍♀️","🧏🏾‍♀️","🧏🏿‍♀️",
"🧏","🧏🏻","🧏🏼","🧏🏽","🧏🏾","🧏🏿",
"🧏‍♂️","🧏🏻‍♂️","🧏🏼‍♂️","🧏🏽‍♂️","🧏🏾‍♂️","🧏🏿‍♂️",
"🤦‍♀️","🤦🏻‍♀️","🤦🏼‍♀️","🤦🏽‍♀️","🤦🏾‍♀️","🤦🏿‍♀️",
"🤦","🤦🏻","🤦🏼","🤦🏽","🤦🏾","🤦🏿",
"🤦‍♂️","🤦🏻‍♂️","🤦🏼‍♂️","🤦🏽‍♂️","🤦🏾‍♂️","🤦🏿‍♂️",
"🤷‍♀️","🤷🏻‍♀️","🤷🏼‍♀️","🤷🏽‍♀️","🤷🏾‍♀️","🤷🏿‍♀️",
"🤷","🤷🏻","🤷🏼","🤷🏽","🤷🏾","🤷🏿",
"🤷‍♂️","🤷🏻‍♂️","🤷🏼‍♂️","🤷🏽‍♂️","🤷🏾‍♂️","🤷🏿‍♂️",
"🙎‍♀️","🙎🏻‍♀️","🙎🏼‍♀️","🙎🏽‍♀️","🙎🏾‍♀️","🙎🏿‍♀️",
"🙎","🙎🏻","🙎🏼","🙎🏽","🙎🏾","🙎🏿",
"🙎‍♂️","🙎🏻‍♂️","🙎🏼‍♂️","🙎🏽‍♂️","🙎🏾‍♂️","🙎🏿‍♂️",
"🙍‍♀️","🙍🏻‍♀️","🙍🏼‍♀️","🙍🏽‍♀️","🙍🏾‍♀️","🙍🏿‍♀️",
"🙍","🙍🏻","🙍🏼","🙍🏽","🙍🏾","🙍🏿",
"🙍‍♂️","🙍🏻‍♂️","🙍🏼‍♂️","🙍🏽‍♂️","🙍🏾‍♂️","🙍🏿‍♂️",
"💇‍♀️","💇🏻‍♀️","💇🏼‍♀️","💇🏽‍♀️","💇🏾‍♀️","💇🏿‍♀️",
"💇","💇🏻","💇🏼","💇🏽","💇🏾","💇🏿",
"💇‍♂️","💇🏻‍♂️","💇🏼‍♂️","💇🏽‍♂️","💇🏾‍♂️","💇🏿‍♂️",
"💆‍♀️","💆🏻‍♀️","💆🏼‍♀️","💆🏽‍♀️","💆🏾‍♀️","💆🏿‍♀️",
"💆","💆🏻","💆🏼","💆🏽","💆🏾","💆🏿",
"💆‍♂️","💆🏻‍♂️","💆🏼‍♂️","💆🏽‍♂️","💆🏾‍♂️","💆🏿‍♂️",
"🧖‍♀️","🧖🏻‍♀️","🧖🏼‍♀️","🧖🏽‍♀️","🧖🏾‍♀️","🧖🏿‍♀️",
"🧖","🧖🏻","🧖🏼","🧖🏽","🧖🏾","🧖🏿",
"🧖‍♂️","🧖🏻‍♂️","🧖🏼‍♂️","🧖🏽‍♂️","🧖🏾‍♂️","🧖🏿‍♂️",
"💅","💅🏻","💅🏼","💅🏽","💅🏾","💅🏿",
"🤳","🤳🏻","🤳🏼","🤳🏽","🤳🏾","🤳🏿",
"💃","💃🏻","💃🏼","💃🏽","💃🏾","💃🏿",
"🕺","🕺🏻","🕺🏼","🕺🏽","🕺🏾","🕺🏿",
"👯‍♀️","👯","👯‍♂️",
"🕴","🕴🏻","🕴🏼","🕴🏽","🕴🏾","🕴🏿",
"👩‍🦽","👩🏻‍🦽","👩🏼‍🦽","👩🏽‍🦽","👩🏾‍🦽","👩🏿‍🦽",
"🧑‍🦽","🧑🏻‍🦽","🧑🏼‍🦽","🧑🏽‍🦽","🧑🏾‍🦽","🧑🏿‍🦽",
"👨‍🦽","👨🏻‍🦽","👨🏼‍🦽","👨🏽‍🦽","👨🏾‍🦽","👨🏿‍🦽",
"👩‍🦼","👩🏻‍🦼","👩🏼‍🦼","👩🏽‍🦼","👩🏾‍🦼","👩🏿‍🦼",
"🧑‍🦼","🧑🏻‍🦼","🧑🏼‍🦼","🧑🏽‍🦼","🧑🏾‍🦼","🧑🏿‍🦼",
"👨‍🦼","👨🏻‍🦼","👨🏼‍🦼","👨🏽‍🦼","👨🏾‍🦼","👨🏿‍🦼",
"🚶‍♀️","🚶🏻‍♀️","🚶🏼‍♀️","🚶🏽‍♀️","🚶🏾‍♀️","🚶🏿‍♀️",
"🚶","🚶🏻","🚶🏼","🚶🏽","🚶🏾","🚶🏿",
"🚶‍♂️","🚶🏻‍♂️","🚶🏼‍♂️","🚶🏽‍♂️","🚶🏾‍♂️","🚶🏿‍♂️",
"👩‍🦯","👩🏻‍🦯","👩🏼‍🦯","👩🏽‍🦯","👩🏾‍🦯","👩🏿‍🦯",
"🧑‍🦯","🧑🏻‍🦯","🧑🏼‍🦯","🧑🏽‍🦯","🧑🏾‍🦯","🧑🏿‍🦯",
"👨‍🦯","👨🏻‍🦯","👨🏼‍🦯","👨🏽‍🦯","👨🏾‍🦯","👨🏿‍🦯",
"🧎‍♀️","🧎🏻‍♀️","🧎🏼‍♀️","🧎🏽‍♀️","🧎🏾‍♀️","🧎🏿‍♀️",
"🧎","🧎🏻","🧎🏼","🧎🏽","🧎🏾","🧎🏿",
"🧎‍♂️","🧎🏻‍♂️","🧎🏼‍♂️","🧎🏽‍♂️","🧎🏾‍♂️","🧎🏿‍♂️",
"🏃‍♀️","🏃🏻‍♀️","🏃🏼‍♀️","🏃🏽‍♀️","🏃🏾‍♀️","🏃🏿‍♀️",
"🏃","🏃🏻","🏃🏼","🏃🏽","🏃🏾","🏃🏿",
"🏃‍♂️","🏃🏻‍♂️","🏃🏼‍♂️","🏃🏽‍♂️","🏃🏾‍♂️","🏃🏿‍♂️",
"🧍‍♀️","🧍🏻‍♀️","🧍🏼‍♀️","🧍🏽‍♀️","🧍🏾‍♀️","🧍🏿‍♀️",
"🧍","🧍🏻","🧍🏼","🧍🏽","🧍🏾","🧍🏿",
"🧍‍♂️","🧍🏻‍♂️","🧍🏼‍♂️","🧍🏽‍♂️","🧍🏾‍♂️","🧍🏿‍♂️",
"👫","👫🏻","👩🏻‍🤝‍👨🏼","👩🏻‍🤝‍👨🏽","👩🏻‍🤝‍👨🏾","👩🏻‍🤝‍👨🏿","👩🏼‍🤝‍👨🏻","👫🏼","👩🏼‍🤝‍👨🏽","👩🏼‍🤝‍👨🏾","👩🏼‍🤝‍👨🏿","👩🏽‍🤝‍👨🏻","👩🏽‍🤝‍👨🏼","👫🏽","👩🏽‍🤝‍👨🏾","👩🏽‍🤝‍👨🏿","👩🏾‍🤝‍👨🏻","👩🏾‍🤝‍👨🏼","👩🏾‍🤝‍👨🏽","👫🏾","👩🏾‍🤝‍👨🏿","👩🏿‍🤝‍👨🏻","👩🏿‍🤝‍👨🏼","👩🏿‍🤝‍👨🏽","👩🏿‍🤝‍👨🏾","👫🏿",
"👭","👭🏻","👩🏻‍🤝‍👩🏼","👩🏻‍🤝‍👩🏽","👩🏻‍🤝‍👩🏾","👩🏻‍🤝‍👩🏿","👩🏼‍🤝‍👩🏻","👭🏼","👩🏼‍🤝‍👩🏽","👩🏼‍🤝‍👩🏾","👩🏼‍🤝‍👩🏿","👩🏽‍🤝‍👩🏻","👩🏽‍🤝‍👩🏼","👭🏽","👩🏽‍🤝‍👩🏾","👩🏽‍🤝‍👩🏿","👩🏾‍🤝‍👩🏻","👩🏾‍🤝‍👩🏼","👩🏾‍🤝‍👩🏽","👭🏾","👩🏾‍🤝‍👩🏿","👩🏿‍🤝‍👩🏻","👩🏿‍🤝‍👩🏼","👩🏿‍🤝‍👩🏽","👩🏿‍🤝‍👩🏾","👭🏿",
"👬","👬🏻","👨🏻‍🤝‍👨🏼","👨🏻‍🤝‍👨🏽","👨🏻‍🤝‍👨🏾","👨🏻‍🤝‍👨🏿","👨🏼‍🤝‍👨🏻","👬🏼","👨🏼‍🤝‍👨🏽","👨🏼‍🤝‍👨🏾","👨🏼‍🤝‍👨🏿","👨🏽‍🤝‍👨🏻","👨🏽‍🤝‍👨🏼","👬🏽","👨🏽‍🤝‍👨🏾","👨🏽‍🤝‍👨🏿","👨🏾‍🤝‍👨🏻","👨🏾‍🤝‍👨🏼","👨🏾‍🤝‍👨🏽","👬🏾","👨🏾‍🤝‍👨🏿","👨🏿‍🤝‍👨🏻","👨🏿‍🤝‍👨🏼","👨🏿‍🤝‍👨🏽","👨🏿‍🤝‍👨🏾","👬🏿",
"👩‍❤️‍👨","👩🏻‍❤️‍👨🏻","👩🏻‍❤️‍👨🏼","👩🏻‍❤️‍👨🏽","👩🏻‍❤️‍👨🏾","👩🏻‍❤️‍👨🏿","👩🏼‍❤️‍👨🏻","👩🏼‍❤️‍👨🏼","👩🏼‍❤️‍👨🏽","👩🏼‍❤️‍👨🏾","👩🏼‍❤️‍👨🏿","👩🏽‍❤️‍👨🏻","👩🏽‍❤️‍👨🏼","👩🏽‍❤️‍👨🏽","👩🏽‍❤️‍👨🏾","👩🏽‍❤️‍👨🏿","👩🏾‍❤️‍👨🏻","👩🏾‍❤️‍👨🏼","👩🏾‍❤️‍👨🏽","👩🏾‍❤️‍👨🏾","👩🏾‍❤️‍👨🏿","👩🏿‍❤️‍👨🏻","👩🏿‍❤️‍👨🏼","👩🏿‍❤️‍👨🏽","👩🏿‍❤️‍👨🏾","👩🏿‍❤️‍👨🏿",
"👩‍❤️‍👩","👩🏻‍❤️‍👩🏻","👩🏻‍❤️‍👩🏼","👩🏻‍❤️‍👩🏽","👩🏻‍❤️‍👩🏾","👩🏻‍❤️‍👩🏿","👩🏼‍❤️‍👩🏻","👩🏼‍❤️‍👩🏼","👩🏼‍❤️‍👩🏽","👩🏼‍❤️‍👩🏾","👩🏼‍❤️‍👩🏿","👩🏽‍❤️‍👩🏻","👩🏽‍❤️‍👩🏼","👩🏽‍❤️‍👩🏽","👩🏽‍❤️‍👩🏾","👩🏽‍❤️‍👩🏿","👩🏾‍❤️‍👩🏻","👩🏾‍❤️‍👩🏼","👩🏾‍❤️‍👩🏽","👩🏾‍❤️‍👩🏾","👩🏾‍❤️‍👩🏿","👩🏿‍❤️‍👩🏻","👩🏿‍❤️‍👩🏼","👩🏿‍❤️‍👩🏽","👩🏿‍❤️‍👩🏾","👩🏿‍❤️‍👩🏿",
"💑","🧑🏻‍❤️‍🧑🏻","🧑🏻‍❤️‍🧑🏼","🧑🏻‍❤️‍🧑🏽","🧑🏻‍❤️‍🧑🏾","🧑🏻‍❤️‍🧑🏿","🧑🏼‍❤️‍🧑🏻","🧑🏼‍❤️‍🧑🏼","🧑🏼‍❤️‍🧑🏽","🧑🏼‍❤️‍🧑🏾","🧑🏼‍❤️‍🧑🏿","🧑🏽‍❤️‍🧑🏻","🧑🏽‍❤️‍🧑🏼","🧑🏽‍❤️‍🧑🏽","🧑🏽‍❤️‍🧑🏾","🧑🏽‍❤️‍🧑🏿","🧑🏾‍❤️‍🧑🏻","🧑🏾‍❤️‍🧑🏼","🧑🏾‍❤️‍🧑🏽","🧑🏾‍❤️‍🧑🏾","🧑🏾‍❤️‍🧑🏿","🧑🏿‍❤️‍🧑🏻","🧑🏿‍❤️‍🧑🏼","🧑🏿‍❤️‍🧑🏽","🧑🏿‍❤️‍🧑🏾","🧑🏿‍❤️‍🧑🏿",
"👨‍❤️‍👨","👨🏻‍❤️‍👨🏻","👨🏻‍❤️‍👨🏼","👨🏻‍❤️‍👨🏽","👨🏻‍❤️‍👨🏾","👨🏻‍❤️‍👨🏿","👨🏼‍❤️‍👨🏻","👨🏼‍❤️‍👨🏼","👨🏼‍❤️‍👨🏽","👨🏼‍❤️‍👨🏾","👨🏼‍❤️‍👨🏿","👨🏽‍❤️‍👨🏻","👨🏽‍❤️‍👨🏼","👨🏽‍❤️‍👨🏽","👨🏽‍❤️‍👨🏾","👨🏽‍❤️‍👨🏿","👨🏾‍❤️‍👨🏻","👨🏾‍❤️‍👨🏼","👨🏾‍❤️‍👨🏽","👨🏾‍❤️‍👨🏾","👨🏾‍❤️‍👨🏿","👨🏿‍❤️‍👨🏻","👨🏿‍❤️‍👨🏼","👨🏿‍❤️‍👨🏽","👨🏿‍❤️‍👨🏾","👨🏿‍❤️‍👨🏿",
"👩‍❤️‍💋‍👨","👩🏻‍❤️‍💋‍👨🏻","👩🏻‍❤️‍💋‍👨🏼","👩🏻‍❤️‍💋‍👨🏽","👩🏻‍❤️‍💋‍👨🏾","👩🏻‍❤️‍💋‍👨🏿","👩🏼‍❤️‍💋‍👨🏻","👩🏼‍❤️‍💋‍👨🏼","👩🏼‍❤️‍💋‍👨🏽","👩🏼‍❤️‍💋‍👨🏾","👩🏼‍❤️‍💋‍👨🏿","👩🏽‍❤️‍💋‍👨🏻","👩🏽‍❤️‍💋‍👨🏼","👩🏽‍❤️‍💋‍👨🏽","👩🏽‍❤️‍💋‍👨🏾","👩🏽‍❤️‍💋‍👨🏿","👩🏾‍❤️‍💋‍👨🏻","👩🏾‍❤️‍💋‍👨🏼","👩🏾‍❤️‍💋‍👨🏽","👩🏾‍❤️‍💋‍👨🏾","👩🏾‍❤️‍💋‍👨🏿","👩🏿‍❤️‍💋‍👨🏻","👩🏿‍❤️‍💋‍👨🏼","👩🏿‍❤️‍💋‍👨🏽","👩🏿‍❤️‍💋‍👨🏾","👩🏿‍❤️‍💋‍👨🏿",
"👩‍❤️‍💋‍👩","👩🏻‍❤️‍💋‍👩🏻","👩🏻‍❤️‍💋‍👩🏼","👩🏻‍❤️‍💋‍👩🏽","👩🏻‍❤️‍💋‍👩🏾","👩🏻‍❤️‍💋‍👩🏿","👩🏼‍❤️‍💋‍👩🏻","👩🏼‍❤️‍💋‍👩🏼","👩🏼‍❤️‍💋‍👩🏽","👩🏼‍❤️‍💋‍👩🏾","👩🏼‍❤️‍💋‍👩🏿","👩🏽‍❤️‍💋‍👩🏻","👩🏽‍❤️‍💋‍👩🏼","👩🏽‍❤️‍💋‍👩🏽","👩🏽‍❤️‍💋‍👩🏾","👩🏽‍❤️‍💋‍👩🏿","👩🏾‍❤️‍💋‍👩🏻","👩🏾‍❤️‍💋‍👩🏼","👩🏾‍❤️‍💋‍👩🏽","👩🏾‍❤️‍💋‍👩🏾","👩🏾‍❤️‍💋‍👩🏿","👩🏿‍❤️‍💋‍👩🏻","👩🏿‍❤️‍💋‍👩🏼","👩🏿‍❤️‍💋‍👩🏽","👩🏿‍❤️‍💋‍👩🏾","👩🏿‍❤️‍💋‍👩🏿",
"💏","🧑🏻‍❤️‍💋‍🧑🏻","🧑🏻‍❤️‍💋‍🧑🏼","🧑🏻‍❤️‍💋‍🧑🏽","🧑🏻‍❤️‍💋‍🧑🏾","🧑🏻‍❤️‍💋‍🧑🏿","🧑🏼‍❤️‍💋‍🧑🏻","🧑🏼‍❤️‍💋‍🧑🏼","🧑🏼‍❤️‍💋‍🧑🏽","🧑🏼‍❤️‍💋‍🧑🏾","🧑🏼‍❤️‍💋‍🧑🏿","🧑🏽‍❤️‍💋‍🧑🏻","🧑🏽‍❤️‍💋‍🧑🏼","🧑🏽‍❤️‍💋‍🧑🏽","🧑🏽‍❤️‍💋‍🧑🏾","🧑🏽‍❤️‍💋‍🧑🏿","🧑🏾‍❤️‍💋‍🧑🏻","🧑🏾‍❤️‍💋‍🧑🏼","🧑🏾‍❤️‍💋‍🧑🏽","🧑🏾‍❤️‍💋‍🧑🏾","🧑🏾‍❤️‍💋‍🧑🏿","🧑🏿‍❤️‍💋‍🧑🏻","🧑🏿‍❤️‍💋‍🧑🏼","🧑🏿‍❤️‍💋‍🧑🏽","🧑🏿‍❤️‍💋‍🧑🏾","🧑🏿‍❤️‍💋‍🧑🏿",
"👨‍❤️‍💋‍👨","👨🏻‍❤️‍💋‍👨🏻","👨🏻‍❤️‍💋‍👨🏼","👨🏻‍❤️‍💋‍👨🏽","👨🏻‍❤️‍💋‍👨🏾","👨🏻‍❤️‍💋‍👨🏿","👨🏼‍❤️‍💋‍👨🏻","👨🏼‍❤️‍💋‍👨🏼","👨🏼‍❤️‍💋‍👨🏽","👨🏼‍❤️‍💋‍👨🏾","👨🏼‍❤️‍💋‍👨🏿","👨🏽‍❤️‍💋‍👨🏻","👨🏽‍❤️‍💋‍👨🏼","👨🏽‍❤️‍💋‍👨🏽","👨🏽‍❤️‍💋‍👨🏾","👨🏽‍❤️‍💋‍👨🏿","👨🏾‍❤️‍💋‍👨🏻","👨🏾‍❤️‍💋‍👨🏼","👨🏾‍❤️‍💋‍👨🏽","👨🏾‍❤️‍💋‍👨🏾","👨🏾‍❤️‍💋‍👨🏿","👨🏿‍❤️‍💋‍👨🏻","👨🏿‍❤️‍💋‍👨🏼","👨🏿‍❤️‍💋‍👨🏽","👨🏿‍❤️‍💋‍👨🏾","👨🏿‍❤️‍💋‍👨🏿",
"👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂"
"🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼","🐻‍❄️","🐨","🐯","🦁","🐮","🐷","🐽","🐸","🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦","🐤","🐣","🐥","🦆","🦅","🦉","🦇","🐺","🐗","🐴","🦄","🐝","🪱","🐛","🦋","🐌","🐞","🐜","🪰","🪲","🪳","🦟","🦗","🕷","🕸","🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑","🦐","🦞","🦀","🐡","🐠","🐟","🐬","🐳","🐋","🦈","🦭","🐊","🐅","🐆","🦓","🦍","🦧","🦣","🐘","🦛","🦏","🐪","🐫","🦒","🦘","🦬","🐃","🐂","🐄","🐎","🐖","🐏","🐑","🦙","🐐","🦌","🐕","🐩","🦮","🐕‍🦺","🐈","🐈‍⬛","🪶","🐓","🦃","🦤","🦚","🦜","🦢","🦩","🕊","🐇","🦝","🦨","🦡","🦫","🦦","🦥","🐁","🐀","🐿","🦔","🐾","🐉","🐲","🌵","🎄","🌲","🌳","🌴","🪵","🌱","🌿","☘️","🍀","🎍","🪴","🎋","🍃","🍂","🍁","🍄","🐚","🪨","🌾","💐","🌷","🌹","🥀","🌺","🌸","🌼","🌻","🌞","🌝","🌛","🌜","🌚","🌕","🌖","🌗","🌘","🌑","🌒","🌓","🌔","🌙","🌎","🌍","🌏","🪐","💫","⭐️","🌟","✨","⚡️","☄️","💥","🔥","🌪","🌈","☀️","🌤","⛅️","🌥","☁️","🌦","🌧","⛈","🌩","🌨","❄️","☃️","⛄️","🌬","💨","💧","💦","☔️","☂️","🌊","🌫"
"🍏","🍎","🍐","🍊","🍋","🍌","🍉","🍇","🍓","🫐","🍈","🍒","🍑","🥭","🍍","🥥","🥝","🍅","🍆","🥑","🥦","🥬","🥒","🌶","🫑","🌽","🥕","🫒","🧄","🧅","🥔","🍠","🥐","🥯","🍞","🥖","🥨","🧀","🥚","🍳","🧈","🥞","🧇","🥓","🥩","🍗","🍖","🦴","🌭","🍔","🍟","🍕","🫓","🥪","🥙","🧆","🌮","🌯","🫔","🥗","🥘","🫕","🥫","🍝","🍜","🍲","🍛","🍣","🍱","🥟","🦪","🍤","🍙","🍚","🍘","🍥","🥠","🥮","🍢","🍡","🍧","🍨","🍦","🥧","🧁","🍰","🎂","🍮","🍭","🍬","🍫","🍿","🍩","🍪","🌰","🥜","🍯","🥛","🍼","🫖","☕️","🍵","🧃","🥤","🧋","🍶","🍺","🍻","🥂","🍷","🥃","🍸","🍹","🧉","🍾","🧊","🥄","🍴","🍽","🥣","🥡","🥢","🧂"
"⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂",
"🏋️‍♀️","🏋🏻‍♀️","🏋🏼‍♀️","🏋🏽‍♀️","🏋🏾‍♀️","🏋🏿‍♀️",
"🏋️","🏋🏻","🏋🏼","🏋🏽","🏋🏾","🏋🏿",
"🏋️‍♂️","🏋🏻‍♂️","🏋🏼‍♂️","🏋🏽‍♂️","🏋🏾‍♂️","🏋🏿‍♂️",
"🤼‍♀️","🤼","🤼‍♂️",
"🤸‍♀️","🤸🏻‍♀️","🤸🏼‍♀️","🤸🏽‍♀️","🤸🏾‍♀️","🤸🏿‍♀️",
"🤸","🤸🏻","🤸🏼","🤸🏽","🤸🏾","🤸🏿",
"🤸‍♂️","🤸🏻‍♂️","🤸🏼‍♂️","🤸🏽‍♂️","🤸🏾‍♂️","🤸🏿‍♂️",
"⛹️‍♀️","⛹🏻‍♀️","⛹🏼‍♀️","⛹🏽‍♀️","⛹🏾‍♀️","⛹🏿‍♀️",
"⛹️","⛹🏻","⛹🏼","⛹🏽","⛹🏾","⛹🏿",
"⛹️‍♂️","⛹🏻‍♂️","⛹🏼‍♂️","⛹🏽‍♂️","⛹🏾‍♂️","⛹🏿‍♂️",
"🤺",
"🤾‍♀️","🤾🏻‍♀️","🤾🏼‍♀️","🤾🏽‍♀️","🤾🏾‍♀️","🤾🏿‍♀️",
"🤾","🤾🏻","🤾🏼","🤾🏽","🤾🏾","🤾🏿",
"🤾‍♂️","🤾🏻‍♂️","🤾🏼‍♂️","🤾🏽‍♂️","🤾🏾‍♂️","🤾🏿‍♂️",
"🏌️‍♀️","🏌🏻‍♀️","🏌🏼‍♀️","🏌🏽‍♀️","🏌🏾‍♀️","🏌🏿‍♀️",
"🏌️","🏌🏻","🏌🏼","🏌🏽","🏌🏾","🏌🏿",
"🏌️‍♂️","🏌🏻‍♂️","🏌🏼‍♂️","🏌🏽‍♂️","🏌🏾‍♂️","🏌🏿‍♂️",
"🏇","🏇🏻","🏇🏼","🏇🏽","🏇🏾","🏇🏿",
"🧘‍♀️","🧘🏻‍♀️","🧘🏼‍♀️","🧘🏽‍♀️","🧘🏾‍♀️","🧘🏿‍♀️",
"🧘","🧘🏻","🧘🏼","🧘🏽","🧘🏾","🧘🏿",
"🧘‍♂️","🧘🏻‍♂️","🧘🏼‍♂️","🧘🏽‍♂️","🧘🏾‍♂️","🧘🏿‍♂️",
"🏄‍♀️","🏄🏻‍♀️","🏄🏼‍♀️","🏄🏽‍♀️","🏄🏾‍♀️","🏄🏿‍♀️",
"🏄","🏄🏻","🏄🏼","🏄🏽","🏄🏾","🏄🏿",
"🏄‍♂️","🏄🏻‍♂️","🏄🏼‍♂️","🏄🏽‍♂️","🏄🏾‍♂️","🏄🏿‍♂️",
"🏊‍♀️","🏊🏻‍♀️","🏊🏼‍♀️","🏊🏽‍♀️","🏊🏾‍♀️","🏊🏿‍♀️",
"🏊","🏊🏻","🏊🏼","🏊🏽","🏊🏾","🏊🏿",
"🏊‍♂️","🏊🏻‍♂️","🏊🏼‍♂️","🏊🏽‍♂️","🏊🏾‍♂️","🏊🏿‍♂️",
"🤽‍♀️","🤽🏻‍♀️","🤽🏼‍♀️","🤽🏽‍♀️","🤽🏾‍♀️","🤽🏿‍♀️",
"🤽","🤽🏻","🤽🏼","🤽🏽","🤽🏾","🤽🏿",
"🤽‍♂️","🤽🏻‍♂️","🤽🏼‍♂️","🤽🏽‍♂️","🤽🏾‍♂️","🤽🏿‍♂️",
"🚣‍♀️","🚣🏻‍♀️","🚣🏼‍♀️","🚣🏽‍♀️","🚣🏾‍♀️","🚣🏿‍♀️",
"🚣","🚣🏻","🚣🏼","🚣🏽","🚣🏾","🚣🏿",
"🚣‍♂️","🚣🏻‍♂️","🚣🏼‍♂️","🚣🏽‍♂️","🚣🏾‍♂️","🚣🏿‍♂️",
"🧗‍♀️","🧗🏻‍♀️","🧗🏼‍♀️","🧗🏽‍♀️","🧗🏾‍♀️","🧗🏿‍♀️",
"🧗","🧗🏻","🧗🏼","🧗🏽","🧗🏾","🧗🏿",
"🧗‍♂️","🧗🏻‍♂️","🧗🏼‍♂️","🧗🏽‍♂️","🧗🏾‍♂️","🧗🏿‍♂️",
"🚵‍♀️","🚵🏻‍♀️","🚵🏼‍♀️","🚵🏽‍♀️","🚵🏾‍♀️","🚵🏿‍♀️",
"🚵","🚵🏻","🚵🏼","🚵🏽","🚵🏾","🚵🏿",
"🚵‍♂️","🚵🏻‍♂️","🚵🏼‍♂️","🚵🏽‍♂️","🚵🏾‍♂️","🚵🏿‍♂️",
"🚴‍♀️","🚴🏻‍♀️","🚴🏼‍♀️","🚴🏽‍♀️","🚴🏾‍♀️","🚴🏿‍♀️",
"🚴","🚴🏻","🚴🏼","🚴🏽","🚴🏾","🚴🏿",
"🚴‍♂️","🚴🏻‍♂️","🚴🏼‍♂️","🚴🏽‍♂️","🚴🏾‍♂️","🚴🏿‍♂️",
"🏆","🥇","🥈","🥉","🏅","🎖","🏵","🎗","🎫","🎟","🎪",
"🤹‍♀️","🤹🏻‍♀️","🤹🏼‍♀️","🤹🏽‍♀️","🤹🏾‍♀️","🤹🏿‍♀️",
"🤹","🤹🏻","🤹🏼","🤹🏽","🤹🏾","🤹🏿",
"🤹‍♂️","🤹🏻‍♂️","🤹🏼‍♂️","🤹🏽‍♂️","🤹🏾‍♂️","🤹🏿‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"🚗","🚕","🚙","🚌","🚎","🏎","🚓","🚑","🚒","🚐","🛻","🚚","🚛","🚜","🦯","🦽","🦼","🛴","🚲","🛵","🏍","🛺","🚨","🚔","🚍","🚘","🚖","🚡","🚠","🚟","🚃","🚋","🚞","🚝","🚄","🚅","🚈","🚂","🚆","🚇","🚊","🚉","✈️","🛫","🛬","🛩","💺","🛰","🚀","🛸","🚁","🛶","⛵️","🚤","🛥","🛳","⛴","🚢","⚓️","🪝","⛽️","🚧","🚦","🚥","🚏","🗺","🗿","🗽","🗼","🏰","🏯","🏟","🎡","🎢","🎠","⛲️","⛱","🏖","🏝","🏜","🌋","⛰","🏔","🗻","🏕","⛺️","🛖","🏠","🏡","🏘","🏚","🏗","🏭","🏢","🏬","🏣","🏤","🏥","🏦","🏨","🏪","🏫","🏩","💒","🏛","⛪️","🕌","🕍","🛕","🕋","⛩","🛤","🛣","🗾","🎑","🏞","🌅","🌄","🌠","🎇","🎆","🌇","🌆","🏙","🌃","🌌","🌉","🌁"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","💈","⚗️","🔭","🔬","🕳","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀","🛀🏻","🛀🏼","🛀🏽","🛀🏾","🛀🏿",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
"❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎","💔","❤️‍🔥","❤️‍🩹","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✝️","☪️","🕉","☸️","✡️","🔯","🕎","☯️","☦️","🛐","⛎","♈️","♉️","♊️","♋️","♌️","♍️","♎️","♏️","♐️","♑️","♒️","♓️","🆔","⚛️","🉑","☢️","☣️","📴","📳","🈶","🈚️","🈸","🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️","🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑","🅾️","🆘","❌","⭕️","🛑","⛔️","📛","🚫","💯","💢","♨️","🚷","🚯","🚳","🚱","🔞","📵","🚭","❗️","❕","❓","❔","‼️","⁉️","🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰","♻️","✅","🈯️","💹","❇️","✳️","❎","🌐","💠","Ⓜ️","🌀","💤","🏧","🚾","♿️","🅿️","🛗","🈳","🈂️","🛂","🛃","🛄","🛅","🚹","🚺","🚼","⚧","🚻","🚮","🎦","📶","🈁","🔣","","🔤","🔡","🔠","🆖","🆗","🆙","🆒","🆕","🆓","0⃣","1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣","🔟","🔢","#️⃣","*️⃣","⏏️","▶️","⏸","⏯","⏹","⏺","⏭","⏮","⏩","⏪","⏫","⏬","◀️","🔼","🔽","➡️","⬅️","⬆️","⬇️","↗️","↘️","↙️","↖️","↕️","↔️","↪️","↩️","⤴️","⤵️","🔀","🔁","🔂","🔄","🔃","🎵","🎶","","","➗","✖️","♾","💲","💱","™️","©️","®️","👁‍🗨","🔚","🔙","🔛","🔝","🔜","〰️","➰","➿","✔️","☑️","🔘","🔴","🟠","🟡","🟢","🔵","🟣","⚫️","⚪️","🟤","🔺","🔻","🔸","🔹","🔶","🔷","🔳","🔲","▪️","▫️","◾️","◽️","◼️","◻️","🟥","🟧","🟨","🟩","🟦","🟪","⬛️","⬜️","🟫","🔈","🔇","🔉","🔊","🔔","🔕","📣","📢","💬","💭","🗯","♠️","♣️","♥️","♦️","🃏","🎴","🀄️","🕐","🕑","🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚","🕛","🕜","🕝","🕞","🕟","🕠","🕡","🕢","🕣","🕤","🕥","🕦","🕧"
"🏳️","🏴","🏴‍☠️","🏁","🚩","🏳️‍🌈","🏳️‍⚧️","🇺🇳","🇦🇫","🇦🇽","🇦🇱","🇩🇿","🇦🇸","🇦🇩","🇦🇴","🇦🇮","🇦🇶","🇦🇬","🇦🇷","🇦🇲","🇦🇼","🇦🇺","🇦🇹","🇦🇿","🇧🇸","🇧🇭","🇧🇩","🇧🇧","🇧🇾","🇧🇪","🇧🇿","🇧🇯","🇧🇲","🇧🇹","🇧🇴","🇧🇦","🇧🇼","🇧🇷","🇻🇬","🇧🇳","🇧🇬","🇧🇫","🇧🇮","🇰🇭","🇨🇲","🇨🇦","🇮🇨","🇨🇻","🇧🇶","🇰🇾","🇨🇫","🇹🇩","🇮🇴","🇨🇱","🇨🇳","🇨🇽","🇨🇨","🇨🇴","🇰🇲","🇨🇬","🇨🇩","🇨🇰","🇨🇷","🇨🇮","🇭🇷","🇨🇺","🇨🇼","🇨🇾","🇨🇿","🇩🇰","🇩🇯","🇩🇲","🇩🇴","🇪🇨","🇪🇬","🇸🇻","🇬🇶","🇪🇷","🇪🇪","🇸🇿","🇪🇹","🇪🇺","🇫🇰","🇫🇴","🇫🇯","🇫🇮","🇫🇷","🇬🇫","🇵🇫","🇹🇫","🇬🇦","🇬🇲","🇬🇪","🇩🇪","🇬🇭","🇬🇮","🇬🇷","🇬🇱","🇬🇩","🇬🇵","🇬🇺","🇬🇹","🇬🇬","🇬🇳","🇬🇼","🇬🇾","🇭🇹","🇭🇳","🇭🇰","🇭🇺","🇮🇸","🇮🇳","🇮🇩","🇮🇷","🇮🇶","🇮🇪","🇮🇲","🇮🇱","🇮🇹","🇯🇲","🇯🇵","🎌","🇯🇪","🇯🇴","🇰🇿","🇰🇪","🇰🇮","🇽🇰","🇰🇼","🇰🇬","🇱🇦","🇱🇻","🇱🇧","🇱🇸","🇱🇷","🇱🇾","🇱🇮","🇱🇹","🇱🇺","🇲🇴","🇲🇬","🇲🇼","🇲🇾","🇲🇻","🇲🇱","🇲🇹","🇲🇭","🇲🇶","🇲🇷","🇲🇺","🇾🇹","🇲🇽","🇫🇲","🇲🇩","🇲🇨","🇲🇳","🇲🇪","🇲🇸","🇲🇦","🇲🇿","🇲🇲","🇳🇦","🇳🇷","🇳🇵","🇳🇱","🇳🇨","🇳🇿","🇳🇮","🇳🇪","🇳🇬","🇳🇺","🇳🇫","🇰🇵","🇲🇰","🇲🇵","🇳🇴","🇴🇲","🇵🇰","🇵🇼","🇵🇸","🇵🇦","🇵🇬","🇵🇾","🇵🇪","🇵🇭","🇵🇳","🇵🇱","🇵🇹","🇵🇷","🇶🇦","🇷🇪","🇷🇴","🇷🇺","🇷🇼","🇼🇸","🇸🇲","🇸🇹","🇸🇦","🇸🇳","🇷🇸","🇸🇨","🇸🇱","🇸🇬","🇸🇽","🇸🇰","🇸🇮","🇬🇸","🇸🇧","🇸🇴","🇿🇦","🇰🇷","🇸🇸","🇪🇸","🇱🇰","🇧🇱","🇸🇭","🇰🇳","🇱🇨","🇵🇲","🇻🇨","🇸🇩","🇸🇷","🇸🇪","🇨🇭","🇸🇾","🇹🇼","🇹🇯","🇹🇿","🇹🇭","🇹🇱","🇹🇬","🇹🇰","🇹🇴","🇹🇹","🇹🇳","🇹🇷","🇹🇲","🇹🇨","🇹🇻","🇺🇬","🇺🇦","🇦🇪","🇬🇧","🏴󠁧󠁢󠁥󠁮󠁧󠁿","🏴󠁧󠁢󠁳󠁣󠁴󠁿","🏴󠁧󠁢󠁷󠁬󠁳󠁿","🇺🇸","🇺🇾","🇻🇮","🇺🇿","🇻🇺","🇻🇦","🇻🇪","🇻🇳","🇼🇫","🇪🇭","🇾🇪","🇿🇲","🇿🇼"
=========================================
"😀","😃","😄","😁","😆","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🤭","🤫","🤥","😶","😐","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🤲",
"👐",
"🙌",
"👏",
"🤝",
"👍",
"👎",
"👊",
"✊",
"🤛",
"🤜",
"🤞",
"✌️",
"🤟",
"🤘",
"👌",
"🤌",
"🤏",
"👈",
"👉",
"👆",
"👇",
"☝️",
"✋",
"🤚",
"🖐",
"🖖",
"👋",
"🤙",
"💪",
"🦾",
"🖕",
"✍️",
"🙏",
"🦶",
"🦵",
"🦿","💄","💋","👄","🦷","👅",
"👂",
"🦻",
"👃",
"👣","👁","👀","🫀","🫁","🧠","🗣","👤","👥","🫂",
"👶",
"👧",
"🧒",
"👦",
"👩",
"🧑",
"👨",
"👩‍🦱",
"🧑‍🦱",
"👨‍🦱",
"👩‍🦰",
"🧑‍🦰",
"👨‍🦰",
"👱‍♀️",
"👱",
"👱‍♂️",
"👩‍🦳",
"🧑‍🦳",
"👨‍🦳",
"👩‍🦲",
"🧑‍🦲",
"👨‍🦲",
"🧔‍♀️",
"🧔",
"🧔‍♂️",
"👵",
"🧓",
"👴",
"👲",
"👳‍♀️",
"👳",
"👳‍♂️",
"🧕",
"👮‍♀️",
"👮",
"👮‍♂️",
"👷‍♀️",
"👷",
"👷‍♂️",
"💂‍♀️",
"💂",
"💂‍♂️",
"🕵️‍♀️",
"🕵️",
"🕵️‍♂️",
"👩‍⚕️",
"🧑‍⚕️",
"👨‍⚕️",
"👩‍🌾",
"🧑‍🌾",
"👨‍🌾",
"👩‍🍳",
"🧑‍🍳",
"👨‍🍳",
"👩‍🎓",
"🧑‍🎓",
"👨‍🎓",
"👩‍🎤",
"🧑‍🎤",
"👨‍🎤",
"👩‍🏫",
"🧑‍🏫",
"👨‍🏫",
"👩‍🏭",
"🧑‍🏭",
"👨‍🏭",
"👩‍💻",
"🧑‍💻",
"👨‍💻",
"👩‍💼",
"🧑‍💼",
"👨‍💼",
"👩‍🔧",
"🧑‍🔧",
"👨‍🔧",
"👩‍🔬",
"🧑‍🔬",
"👨‍🔬",
"👩‍🎨",
"🧑‍🎨",
"👨‍🎨",
"👩‍🚒",
"🧑‍🚒",
"👨‍🚒",
"👩‍✈️",
"🧑‍✈️",
"👨‍✈️",
"👩‍🚀",
"🧑‍🚀",
"👨‍🚀",
"👩‍⚖️",
"🧑‍⚖️",
"👨‍⚖️",
"👰‍♀️",
"👰",
"👰‍♂️",
"🤵‍♀️",
"🤵",
"🤵‍♂️",
"👸",
"🤴",
"🥷",
"🦸‍♀️",
"🦸",
"🦸‍♂️",
"🦹‍♀️",
"🦹",
"🦹‍♂️",
"🤶",
"🧑‍🎄",
"🎅",
"🧙‍♀️",
"🧙",
"🧙‍♂️",
"🧝‍♀️",
"🧝",
"🧝‍♂️",
"🧛‍♀️",
"🧛",
"🧛‍♂️",
"🧟‍♀️","🧟","🧟‍♂️","🧞‍♀️","🧞","🧞‍♂️",
"🧜‍♀️",
"🧜",
"🧜‍♂️",
"🧚‍♀️",
"🧚",
"🧚‍♂️",
"👼",
"🤰",
"🤱",
"👩‍🍼",
"🧑‍🍼",
"👨‍🍼",
"🙇‍♀️",
"🙇",
"🙇‍♂️",
"💁‍♀️",
"💁",
"💁‍♂️",
"🙅‍♀️",
"🙅",
"🙅‍♂️",
"🙆‍♀️",
"🙆",
"🙆‍♂️",
"🙋‍♀️",
"🙋",
"🙋‍♂️",
"🧏‍♀️",
"🧏",
"🧏‍♂️",
"🤦‍♀️",
"🤦",
"🤦‍♂️",
"🤷‍♀️",
"🤷",
"🤷‍♂️",
"🙎‍♀️",
"🙎",
"🙎‍♂️",
"🙍‍♀️",
"🙍",
"🙍‍♂️",
"💇‍♀️",
"💇",
"💇‍♂️",
"💆‍♀️",
"💆",
"💆‍♂️",
"🧖‍♀️",
"🧖",
"🧖‍♂️",
"💅",
"🤳",
"💃",
"🕺",
"👯‍♀️","👯","👯‍♂️",
"🕴",
"👩‍🦽",
"🧑‍🦽",
"👨‍🦽",
"👩‍🦼",
"🧑‍🦼",
"👨‍🦼",
"🚶‍♀️",
"🚶",
"🚶‍♂️",
"👩‍🦯",
"🧑‍🦯",
"👨‍🦯",
"🧎‍♀️",
"🧎",
"🧎‍♂️",
"🏃‍♀️",
"🏃",
"🏃‍♂️",
"🧍‍♀️",
"🧍",
"🧍‍♂️",
"👫",
"👭",
"👬",
"👩‍❤️‍👨",
"👩‍❤️‍👩",
"💑",
"👨‍❤️‍👨",
"👩‍❤️‍💋‍👨",
"👩‍❤️‍💋‍👩",
"💏",
"👨‍❤️‍💋‍👨",
"👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂"
"⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂",
"🏋️‍♀️",
"🏋️",
"🏋️‍♂️",
"🤼‍♀️","🤼","🤼‍♂️",
"🤸‍♀️",
"🤸",
"🤸‍♂️",
"⛹️‍♀️",
"⛹️",
"⛹️‍♂️",
"🤺",
"🤾‍♀️",
"🤾",
"🤾‍♂️",
"🏌️‍♀️",
"🏌️",
"🏌️‍♂️",
"🏇",
"🧘‍♀️",
"🧘",
"🧘‍♂️",
"🏄‍♀️",
"🏄",
"🏄‍♂️",
"🏊‍♀️",
"🏊",
"🏊‍♂️",
"🤽‍♀️",
"🤽",
"🤽‍♂️",
"🚣‍♀️",
"🚣",
"🚣‍♂️",
"🧗‍♀️",
"🧗",
"🧗‍♂️",
"🚵‍♀️",
"🚵",
"🚵‍♂️",
"🚴‍♀️",
"🚴",
"🚴‍♂️",
"🏆","🥇","🥈","🥉","🏅","🎖","🏵","🎗","🎫","🎟","🎪",
"🤹‍♀️",
"🤹",
"🤹‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","💈","⚗️","🔭","🔬","🕳","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
=========================================
"🤲","👐","🙌","👏","👍","👎","👊","✊","🤛","🤜","🤞","✌️","🤟","🤘","👌","🤌","🤏","👈","👉","👆","👇","☝️","✋","🤚","🖐","🖖","👋","🤙","💪","🖕","✍️","🙏","🦶","🦵","👂","🦻","👃","👶","👧","🧒","👦","👩","🧑","👨","👩‍🦱","🧑‍🦱","👨‍🦱","👩‍🦰","🧑‍🦰","👨‍🦰","👱‍♀️","👱","👱‍♂️","👩‍🦳","🧑‍🦳","👨‍🦳","👩‍🦲","🧑‍🦲","👨‍🦲","🧔‍♀️","🧔","🧔‍♂️","👵","🧓","👴","👲","👳‍♀️","👳","👳‍♂️","🧕","👮‍♀️","👮","👮‍♂️","👷‍♀️","👷","👷‍♂️","💂‍♀️","💂","💂‍♂️","🕵️‍♀️","🕵️","🕵️‍♂️","👩‍⚕️","🧑‍⚕️","👨‍⚕️","👩‍🌾","🧑‍🌾","👨‍🌾","👩‍🍳","🧑‍🍳","👨‍🍳","👩‍🎓","🧑‍🎓","👨‍🎓","👩‍🎤","🧑‍🎤","👨‍🎤","👩‍🏫","🧑‍🏫","👨‍🏫","👩‍🏭","🧑‍🏭","👨‍🏭","👩‍💻","🧑‍💻","👨‍💻","👩‍💼","🧑‍💼","👨‍💼","👩‍🔧","🧑‍🔧","👨‍🔧","👩‍🔬","🧑‍🔬","👨‍🔬","👩‍🎨","🧑‍🎨","👨‍🎨","👩‍🚒","🧑‍🚒","👨‍🚒","👩‍✈️","🧑‍✈️","👨‍✈️","👩‍🚀","🧑‍🚀","👨‍🚀","👩‍⚖️","🧑‍⚖️","👨‍⚖️","👰‍♀️","👰","👰‍♂️","🤵‍♀️","🤵","🤵‍♂️","👸","🤴","🥷","🦸‍♀️","🦸","🦸‍♂️","🦹‍♀️","🦹","🦹‍♂️","🤶","🧑‍🎄","🎅","🧙‍♀️","🧙","🧙‍♂️","🧝‍♀️","🧝","🧝‍♂️","🧛‍♀️","🧛","🧛‍♂️","🧜‍♀️","🧜","🧜‍♂️","🧚‍♀️","🧚","🧚‍♂️","👼","🤰","🤱","👩‍🍼","🧑‍🍼","👨‍🍼","🙇‍♀️","🙇","🙇‍♂️","💁‍♀️","💁","💁‍♂️","🙅‍♀️","🙅","🙅‍♂️","🙆‍♀️","🙆","🙆‍♂️","🙋‍♀️","🙋","🙋‍♂️","🧏‍♀️","🧏","🧏‍♂️","🤦‍♀️","🤦","🤦‍♂️","🤷‍♀️","🤷","🤷‍♂️","🙎‍♀️","🙎","🙎‍♂️","🙍‍♀️","🙍","🙍‍♂️","💇‍♀️","💇","💇‍♂️","💆‍♀️","💆","💆‍♂️","🧖‍♀️","🧖","🧖‍♂️","💅","🤳","💃","🕺","🕴","👩‍🦽","🧑‍🦽","👨‍🦽","👩‍🦼","🧑‍🦼","👨‍🦼","🚶‍♀️","🚶","🚶‍♂️","👩‍🦯","🧑‍🦯","👨‍🦯","🧎‍♀️","🧎","🧎‍♂️","🏃‍♀️","🏃","🏃‍♂️","🧍‍♀️","🧍","🧍‍♂️","🏋️‍♀️","🏋️","🏋️‍♂️","🤸‍♀️","🤸","🤸‍♂️","⛹️‍♀️","⛹️","⛹️‍♂️","🤾‍♀️","🤾","🤾‍♂️","🏌️‍♀️","🏌️","🏌️‍♂️","🏇","🧘‍♀️","🧘","🧘‍♂️","🏄‍♀️","🏄","🏄‍♂️","🏊‍♀️","🏊","🏊‍♂️","🤽‍♀️","🤽","🤽‍♂️","🚣‍♀️","🚣","🚣‍♂️","🧗‍♀️","🧗","🧗‍♂️","🚵‍♀️","🚵","🚵‍♂️","🚴‍♀️","🚴","🚴‍♂️","🤹‍♀️","🤹","🤹‍♂️","🛀"
"👫","👭","👬","👩‍❤️‍👨","👩‍❤️‍👩","💑","👨‍❤️‍👨","👩‍❤️‍💋‍👨","👩‍❤️‍💋‍👩","💏","👨‍❤️‍💋‍👨"
=========================================
"🗨", "👪"

676
emoji_old/data5.txt Normal file
View file

@ -0,0 +1,676 @@
"😀","😃","😄","😁","😆","🥹","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🫣","🤭","🫢","🫡","🤫","🫠","🤥","😶","🫥","😐","🫤","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🫶","🫶🏻","🫶🏼","🫶🏽","🫶🏾","🫶🏿",
"🤲","🤲🏻","🤲🏼","🤲🏽","🤲🏾","🤲🏿",
"👐","👐🏻","👐🏼","👐🏽","👐🏾","👐🏿",
"🙌","🙌🏻","🙌🏼","🙌🏽","🙌🏾","🙌🏿",
"👏","👏🏻","👏🏼","👏🏽","👏🏾","👏🏿",
"🤝","🤝🏻","🫱🏻‍🫲🏼","🫱🏻‍🫲🏽","🫱🏻‍🫲🏾","🫱🏻‍🫲🏿","🫱🏼‍🫲🏻","🤝🏼","🫱🏼‍🫲🏽","🫱🏼‍🫲🏾","🫱🏼‍🫲🏿","🫱🏽‍🫲🏻","🫱🏽‍🫲🏼","🤝🏽","🫱🏽‍🫲🏾","🫱🏽‍🫲🏿","🫱🏾‍🫲🏻","🫱🏾‍🫲🏼","🫱🏾‍🫲🏽","🤝🏾","🫱🏾‍🫲🏿","🫱🏿‍🫲🏻","🫱🏿‍🫲🏼","🫱🏿‍🫲🏽","🫱🏿‍🫲🏾","🤝🏿",
"👍","👍🏻","👍🏼","👍🏽","👍🏾","👍🏿",
"👎","👎🏻","👎🏼","👎🏽","👎🏾","👎🏿",
"👊","👊🏻","👊🏼","👊🏽","👊🏾","👊🏿",
"✊","✊🏻","✊🏼","✊🏽","✊🏾","✊🏿",
"🤛","🤛🏻","🤛🏼","🤛🏽","🤛🏾","🤛🏿",
"🤜","🤜🏻","🤜🏼","🤜🏽","🤜🏾","🤜🏿",
"🤞","🤞🏻","🤞🏼","🤞🏽","🤞🏾","🤞🏿",
"✌️","✌🏻","✌🏼","✌🏽","✌🏾","✌🏿",
"🫰","🫰🏻","🫰🏼","🫰🏽","🫰🏾","🫰🏿",
"🤟","🤟🏻","🤟🏼","🤟🏽","🤟🏾","🤟🏿",
"🤘","🤘🏻","🤘🏼","🤘🏽","🤘🏾","🤘🏿",
"👌","👌🏻","👌🏼","👌🏽","👌🏾","👌🏿",
"🤌","🤌🏻","🤌🏼","🤌🏽","🤌🏾","🤌🏿",
"🤏","🤏🏻","🤏🏼","🤏🏽","🤏🏾","🤏🏿",
"🫳","🫳🏻","🫳🏼","🫳🏽","🫳🏾","🫳🏿",
"🫴","🫴🏻","🫴🏼","🫴🏽","🫴🏾","🫴🏿",
"👈","👈🏻","👈🏼","👈🏽","👈🏾","👈🏿",
"👉","👉🏻","👉🏼","👉🏽","👉🏾","👉🏿",
"👆","👆🏻","👆🏼","👆🏽","👆🏾","👆🏿",
"👇","👇🏻","👇🏼","👇🏽","👇🏾","👇🏿",
"☝️","☝🏻","☝🏼","☝🏽","☝🏾","☝🏿",
"✋","✋🏻","✋🏼","✋🏽","✋🏾","✋🏿",
"🤚","🤚🏻","🤚🏼","🤚🏽","🤚🏾","🤚🏿",
"🖐","🖐🏻","🖐🏼","🖐🏽","🖐🏾","🖐🏿",
"🖖","🖖🏻","🖖🏼","🖖🏽","🖖🏾","🖖🏿",
"👋","👋🏻","👋🏼","👋🏽","👋🏾","👋🏿",
"🤙","🤙🏻","🤙🏼","🤙🏽","🤙🏾","🤙🏿",
"🫲","🫲🏻","🫲🏼","🫲🏽","🫲🏾","🫲🏿",
"🫱","🫱🏻","🫱🏼","🫱🏽","🫱🏾","🫱🏿",
"💪","💪🏻","💪🏼","💪🏽","💪🏾","💪🏿",
"🦾",
"🖕","🖕🏻","🖕🏼","🖕🏽","🖕🏾","🖕🏿",
"✍️","✍🏻","✍🏼","✍🏽","✍🏾","✍🏿",
"🙏","🙏🏻","🙏🏼","🙏🏽","🙏🏾","🙏🏿",
"🫵","🫵🏻","🫵🏼","🫵🏽","🫵🏾","🫵🏿",
"🦶","🦶🏻","🦶🏼","🦶🏽","🦶🏾","🦶🏿",
"🦵","🦵🏻","🦵🏼","🦵🏽","🦵🏾","🦵🏿",
"🦿","💄","💋","👄","🫦","🦷","👅",
"👂","👂🏻","👂🏼","👂🏽","👂🏾","👂🏿",
"🦻","🦻🏻","🦻🏼","🦻🏽","🦻🏾","🦻🏿",
"👃","👃🏻","👃🏼","👃🏽","👃🏾","👃🏿",
"👣","👁","👀","🫀","🫁","🧠","🗣","👤","👥","🫂",
"👶","👶🏻","👶🏼","👶🏽","👶🏾","👶🏿",
"👧","👧🏻","👧🏼","👧🏽","👧🏾","👧🏿",
"🧒","🧒🏻","🧒🏼","🧒🏽","🧒🏾","🧒🏿",
"👦","👦🏻","👦🏼","👦🏽","👦🏾","👦🏿",
"👩","👩🏻","👩🏼","👩🏽","👩🏾","👩🏿",
"🧑","🧑🏻","🧑🏼","🧑🏽","🧑🏾","🧑🏿",
"👨","👨🏻","👨🏼","👨🏽","👨🏾","👨🏿",
"👩‍🦱","👩🏻‍🦱","👩🏼‍🦱","👩🏽‍🦱","👩🏾‍🦱","👩🏿‍🦱",
"🧑‍🦱","🧑🏻‍🦱","🧑🏼‍🦱","🧑🏽‍🦱","🧑🏾‍🦱","🧑🏿‍🦱",
"👨‍🦱","👨🏻‍🦱","👨🏼‍🦱","👨🏽‍🦱","👨🏾‍🦱","👨🏿‍🦱",
"👩‍🦰","👩🏻‍🦰","👩🏼‍🦰","👩🏽‍🦰","👩🏾‍🦰","👩🏿‍🦰",
"🧑‍🦰","🧑🏻‍🦰","🧑🏼‍🦰","🧑🏽‍🦰","🧑🏾‍🦰","🧑🏿‍🦰",
"👨‍🦰","👨🏻‍🦰","👨🏼‍🦰","👨🏽‍🦰","👨🏾‍🦰","👨🏿‍🦰",
"👱‍♀️","👱🏻‍♀️","👱🏼‍♀️","👱🏽‍♀️","👱🏾‍♀️","👱🏿‍♀️",
"👱","👱🏻","👱🏼","👱🏽","👱🏾","👱🏿",
"👱‍♂️","👱🏻‍♂️","👱🏼‍♂️","👱🏽‍♂️","👱🏾‍♂️","👱🏿‍♂️",
"👩‍🦳","👩🏻‍🦳","👩🏼‍🦳","👩🏽‍🦳","👩🏾‍🦳","👩🏿‍🦳",
"🧑‍🦳","🧑🏻‍🦳","🧑🏼‍🦳","🧑🏽‍🦳","🧑🏾‍🦳","🧑🏿‍🦳",
"👨‍🦳","👨🏻‍🦳","👨🏼‍🦳","👨🏽‍🦳","👨🏾‍🦳","👨🏿‍🦳",
"👩‍🦲","👩🏻‍🦲","👩🏼‍🦲","👩🏽‍🦲","👩🏾‍🦲","👩🏿‍🦲",
"🧑‍🦲","🧑🏻‍🦲","🧑🏼‍🦲","🧑🏽‍🦲","🧑🏾‍🦲","🧑🏿‍🦲",
"👨‍🦲","👨🏻‍🦲","👨🏼‍🦲","👨🏽‍🦲","👨🏾‍🦲","👨🏿‍🦲",
"🧔‍♀️","🧔🏻‍♀️","🧔🏼‍♀️","🧔🏽‍♀️","🧔🏾‍♀️","🧔🏿‍♀️",
"🧔","🧔🏻","🧔🏼","🧔🏽","🧔🏾","🧔🏿",
"🧔‍♂️","🧔🏻‍♂️","🧔🏼‍♂️","🧔🏽‍♂️","🧔🏾‍♂️","🧔🏿‍♂️",
"👵","👵🏻","👵🏼","👵🏽","👵🏾","👵🏿",
"🧓","🧓🏻","🧓🏼","🧓🏽","🧓🏾","🧓🏿",
"👴","👴🏻","👴🏼","👴🏽","👴🏾","👴🏿",
"👲","👲🏻","👲🏼","👲🏽","👲🏾","👲🏿",
"👳‍♀️","👳🏻‍♀️","👳🏼‍♀️","👳🏽‍♀️","👳🏾‍♀️","👳🏿‍♀️",
"👳","👳🏻","👳🏼","👳🏽","👳🏾","👳🏿",
"👳‍♂️","👳🏻‍♂️","👳🏼‍♂️","👳🏽‍♂️","👳🏾‍♂️","👳🏿‍♂️",
"🧕","🧕🏻","🧕🏼","🧕🏽","🧕🏾","🧕🏿",
"👮‍♀️","👮🏻‍♀️","👮🏼‍♀️","👮🏽‍♀️","👮🏾‍♀️","👮🏿‍♀️",
"👮","👮🏻","👮🏼","👮🏽","👮🏾","👮🏿",
"👮‍♂️","👮🏻‍♂️","👮🏼‍♂️","👮🏽‍♂️","👮🏾‍♂️","👮🏿‍♂️",
"👷‍♀️","👷🏻‍♀️","👷🏼‍♀️","👷🏽‍♀️","👷🏾‍♀️","👷🏿‍♀️",
"👷","👷🏻","👷🏼","👷🏽","👷🏾","👷🏿",
"👷‍♂️","👷🏻‍♂️","👷🏼‍♂️","👷🏽‍♂️","👷🏾‍♂️","👷🏿‍♂️",
"💂‍♀️","💂🏻‍♀️","💂🏼‍♀️","💂🏽‍♀️","💂🏾‍♀️","💂🏿‍♀️",
"💂","💂🏻","💂🏼","💂🏽","💂🏾","💂🏿",
"💂‍♂️","💂🏻‍♂️","💂🏼‍♂️","💂🏽‍♂️","💂🏾‍♂️","💂🏿‍♂️",
"🕵️‍♀️","🕵🏻‍♀️","🕵🏼‍♀️","🕵🏽‍♀️","🕵🏾‍♀️","🕵🏿‍♀️",
"🕵️","🕵🏻","🕵🏼","🕵🏽","🕵🏾","🕵🏿",
"🕵️‍♂️","🕵🏻‍♂️","🕵🏼‍♂️","🕵🏽‍♂️","🕵🏾‍♂️","🕵🏿‍♂️",
"👩‍⚕️","👩🏻‍⚕️","👩🏼‍⚕️","👩🏽‍⚕️","👩🏾‍⚕️","👩🏿‍⚕️",
"🧑‍⚕️","🧑🏻‍⚕️","🧑🏼‍⚕️","🧑🏽‍⚕️","🧑🏾‍⚕️","🧑🏿‍⚕️",
"👨‍⚕️","👨🏻‍⚕️","👨🏼‍⚕️","👨🏽‍⚕️","👨🏾‍⚕️","👨🏿‍⚕️",
"👩‍🌾","👩🏻‍🌾","👩🏼‍🌾","👩🏽‍🌾","👩🏾‍🌾","👩🏿‍🌾",
"🧑‍🌾","🧑🏻‍🌾","🧑🏼‍🌾","🧑🏽‍🌾","🧑🏾‍🌾","🧑🏿‍🌾",
"👨‍🌾","👨🏻‍🌾","👨🏼‍🌾","👨🏽‍🌾","👨🏾‍🌾","👨🏿‍🌾",
"👩‍🍳","👩🏻‍🍳","👩🏼‍🍳","👩🏽‍🍳","👩🏾‍🍳","👩🏿‍🍳",
"🧑‍🍳","🧑🏻‍🍳","🧑🏼‍🍳","🧑🏽‍🍳","🧑🏾‍🍳","🧑🏿‍🍳",
"👨‍🍳","👨🏻‍🍳","👨🏼‍🍳","👨🏽‍🍳","👨🏾‍🍳","👨🏿‍🍳",
"👩‍🎓","👩🏻‍🎓","👩🏼‍🎓","👩🏽‍🎓","👩🏾‍🎓","👩🏿‍🎓",
"🧑‍🎓","🧑🏻‍🎓","🧑🏼‍🎓","🧑🏽‍🎓","🧑🏾‍🎓","🧑🏿‍🎓",
"👨‍🎓","👨🏻‍🎓","👨🏼‍🎓","👨🏽‍🎓","👨🏾‍🎓","👨🏿‍🎓",
"👩‍🎤","👩🏻‍🎤","👩🏼‍🎤","👩🏽‍🎤","👩🏾‍🎤","👩🏿‍🎤",
"🧑‍🎤","🧑🏻‍🎤","🧑🏼‍🎤","🧑🏽‍🎤","🧑🏾‍🎤","🧑🏿‍🎤",
"👨‍🎤","👨🏻‍🎤","👨🏼‍🎤","👨🏽‍🎤","👨🏾‍🎤","👨🏿‍🎤",
"👩‍🏫","👩🏻‍🏫","👩🏼‍🏫","👩🏽‍🏫","👩🏾‍🏫","👩🏿‍🏫",
"🧑‍🏫","🧑🏻‍🏫","🧑🏼‍🏫","🧑🏽‍🏫","🧑🏾‍🏫","🧑🏿‍🏫",
"👨‍🏫","👨🏻‍🏫","👨🏼‍🏫","👨🏽‍🏫","👨🏾‍🏫","👨🏿‍🏫",
"👩‍🏭","👩🏻‍🏭","👩🏼‍🏭","👩🏽‍🏭","👩🏾‍🏭","👩🏿‍🏭",
"🧑‍🏭","🧑🏻‍🏭","🧑🏼‍🏭","🧑🏽‍🏭","🧑🏾‍🏭","🧑🏿‍🏭",
"👨‍🏭","👨🏻‍🏭","👨🏼‍🏭","👨🏽‍🏭","👨🏾‍🏭","👨🏿‍🏭",
"👩‍💻","👩🏻‍💻","👩🏼‍💻","👩🏽‍💻","👩🏾‍💻","👩🏿‍💻",
"🧑‍💻","🧑🏻‍💻","🧑🏼‍💻","🧑🏽‍💻","🧑🏾‍💻","🧑🏿‍💻",
"👨‍💻","👨🏻‍💻","👨🏼‍💻","👨🏽‍💻","👨🏾‍💻","👨🏿‍💻",
"👩‍💼","👩🏻‍💼","👩🏼‍💼","👩🏽‍💼","👩🏾‍💼","👩🏿‍💼",
"🧑‍💼","🧑🏻‍💼","🧑🏼‍💼","🧑🏽‍💼","🧑🏾‍💼","🧑🏿‍💼",
"👨‍💼","👨🏻‍💼","👨🏼‍💼","👨🏽‍💼","👨🏾‍💼","👨🏿‍💼",
"👩‍🔧","👩🏻‍🔧","👩🏼‍🔧","👩🏽‍🔧","👩🏾‍🔧","👩🏿‍🔧",
"🧑‍🔧","🧑🏻‍🔧","🧑🏼‍🔧","🧑🏽‍🔧","🧑🏾‍🔧","🧑🏿‍🔧",
"👨‍🔧","👨🏻‍🔧","👨🏼‍🔧","👨🏽‍🔧","👨🏾‍🔧","👨🏿‍🔧",
"👩‍🔬","👩🏻‍🔬","👩🏼‍🔬","👩🏽‍🔬","👩🏾‍🔬","👩🏿‍🔬",
"🧑‍🔬","🧑🏻‍🔬","🧑🏼‍🔬","🧑🏽‍🔬","🧑🏾‍🔬","🧑🏿‍🔬",
"👨‍🔬","👨🏻‍🔬","👨🏼‍🔬","👨🏽‍🔬","👨🏾‍🔬","👨🏿‍🔬",
"👩‍🎨","👩🏻‍🎨","👩🏼‍🎨","👩🏽‍🎨","👩🏾‍🎨","👩🏿‍🎨",
"🧑‍🎨","🧑🏻‍🎨","🧑🏼‍🎨","🧑🏽‍🎨","🧑🏾‍🎨","🧑🏿‍🎨",
"👨‍🎨","👨🏻‍🎨","👨🏼‍🎨","👨🏽‍🎨","👨🏾‍🎨","👨🏿‍🎨",
"👩‍🚒","👩🏻‍🚒","👩🏼‍🚒","👩🏽‍🚒","👩🏾‍🚒","👩🏿‍🚒",
"🧑‍🚒","🧑🏻‍🚒","🧑🏼‍🚒","🧑🏽‍🚒","🧑🏾‍🚒","🧑🏿‍🚒",
"👨‍🚒","👨🏻‍🚒","👨🏼‍🚒","👨🏽‍🚒","👨🏾‍🚒","👨🏿‍🚒",
"👩‍✈️","👩🏻‍✈️","👩🏼‍✈️","👩🏽‍✈️","👩🏾‍✈️","👩🏿‍✈️",
"🧑‍✈️","🧑🏻‍✈️","🧑🏼‍✈️","🧑🏽‍✈️","🧑🏾‍✈️","🧑🏿‍✈️",
"👨‍✈️","👨🏻‍✈️","👨🏼‍✈️","👨🏽‍✈️","👨🏾‍✈️","👨🏿‍✈️",
"👩‍🚀","👩🏻‍🚀","👩🏼‍🚀","👩🏽‍🚀","👩🏾‍🚀","👩🏿‍🚀",
"🧑‍🚀","🧑🏻‍🚀","🧑🏼‍🚀","🧑🏽‍🚀","🧑🏾‍🚀","🧑🏿‍🚀",
"👨‍🚀","👨🏻‍🚀","👨🏼‍🚀","👨🏽‍🚀","👨🏾‍🚀","👨🏿‍🚀",
"👩‍⚖️","👩🏻‍⚖️","👩🏼‍⚖️","👩🏽‍⚖️","👩🏾‍⚖️","👩🏿‍⚖️",
"🧑‍⚖️","🧑🏻‍⚖️","🧑🏼‍⚖️","🧑🏽‍⚖️","🧑🏾‍⚖️","🧑🏿‍⚖️",
"👨‍⚖️","👨🏻‍⚖️","👨🏼‍⚖️","👨🏽‍⚖️","👨🏾‍⚖️","👨🏿‍⚖️",
"👰‍♀️","👰🏻‍♀️","👰🏼‍♀️","👰🏽‍♀️","👰🏾‍♀️","👰🏿‍♀️",
"👰","👰🏻","👰🏼","👰🏽","👰🏾","👰🏿",
"👰‍♂️","👰🏻‍♂️","👰🏼‍♂️","👰🏽‍♂️","👰🏾‍♂️","👰🏿‍♂️",
"🤵‍♀️","🤵🏻‍♀️","🤵🏼‍♀️","🤵🏽‍♀️","🤵🏾‍♀️","🤵🏿‍♀️",
"🤵","🤵🏻","🤵🏼","🤵🏽","🤵🏾","🤵🏿",
"🤵‍♂️","🤵🏻‍♂️","🤵🏼‍♂️","🤵🏽‍♂️","🤵🏾‍♂️","🤵🏿‍♂️",
"👸","👸🏻","👸🏼","👸🏽","👸🏾","👸🏿",
"🫅","🫅🏻","🫅🏼","🫅🏽","🫅🏾","🫅🏿",
"🤴","🤴🏻","🤴🏼","🤴🏽","🤴🏾","🤴🏿",
"🥷","🥷🏻","🥷🏼","🥷🏽","🥷🏾","🥷🏿",
"🦸‍♀️","🦸🏻‍♀️","🦸🏼‍♀️","🦸🏽‍♀️","🦸🏾‍♀️","🦸🏿‍♀️",
"🦸","🦸🏻","🦸🏼","🦸🏽","🦸🏾","🦸🏿",
"🦸‍♂️","🦸🏻‍♂️","🦸🏼‍♂️","🦸🏽‍♂️","🦸🏾‍♂️","🦸🏿‍♂️",
"🦹‍♀️","🦹🏻‍♀️","🦹🏼‍♀️","🦹🏽‍♀️","🦹🏾‍♀️","🦹🏿‍♀️",
"🦹","🦹🏻","🦹🏼","🦹🏽","🦹🏾","🦹🏿",
"🦹‍♂️","🦹🏻‍♂️","🦹🏼‍♂️","🦹🏽‍♂️","🦹🏾‍♂️","🦹🏿‍♂️",
"🤶","🤶🏻","🤶🏼","🤶🏽","🤶🏾","🤶🏿",
"🧑‍🎄","🧑🏻‍🎄","🧑🏼‍🎄","🧑🏽‍🎄","🧑🏾‍🎄","🧑🏿‍🎄",
"🎅","🎅🏻","🎅🏼","🎅🏽","🎅🏾","🎅🏿",
"🧙‍♀️","🧙🏻‍♀️","🧙🏼‍♀️","🧙🏽‍♀️","🧙🏾‍♀️","🧙🏿‍♀️",
"🧙","🧙🏻","🧙🏼","🧙🏽","🧙🏾","🧙🏿",
"🧙‍♂️","🧙🏻‍♂️","🧙🏼‍♂️","🧙🏽‍♂️","🧙🏾‍♂️","🧙🏿‍♂️",
"🧝‍♀️","🧝🏻‍♀️","🧝🏼‍♀️","🧝🏽‍♀️","🧝🏾‍♀️","🧝🏿‍♀️",
"🧝","🧝🏻","🧝🏼","🧝🏽","🧝🏾","🧝🏿",
"🧝‍♂️","🧝🏻‍♂️","🧝🏼‍♂️","🧝🏽‍♂️","🧝🏾‍♂️","🧝🏿‍♂️",
"🧌",
"🧛‍♀️","🧛🏻‍♀️","🧛🏼‍♀️","🧛🏽‍♀️","🧛🏾‍♀️","🧛🏿‍♀️",
"🧛","🧛🏻","🧛🏼","🧛🏽","🧛🏾","🧛🏿",
"🧛‍♂️","🧛🏻‍♂️","🧛🏼‍♂️","🧛🏽‍♂️","🧛🏾‍♂️","🧛🏿‍♂️",
"🧟‍♀️","🧟","🧟‍♂️","🧞‍♀️","🧞","🧞‍♂️",
"🧜‍♀️","🧜🏻‍♀️","🧜🏼‍♀️","🧜🏽‍♀️","🧜🏾‍♀️","🧜🏿‍♀️",
"🧜","🧜🏻","🧜🏼","🧜🏽","🧜🏾","🧜🏿",
"🧜‍♂️","🧜🏻‍♂️","🧜🏼‍♂️","🧜🏽‍♂️","🧜🏾‍♂️","🧜🏿‍♂️",
"🧚‍♀️","🧚🏻‍♀️","🧚🏼‍♀️","🧚🏽‍♀️","🧚🏾‍♀️","🧚🏿‍♀️",
"🧚","🧚🏻","🧚🏼","🧚🏽","🧚🏾","🧚🏿",
"🧚‍♂️","🧚🏻‍♂️","🧚🏼‍♂️","🧚🏽‍♂️","🧚🏾‍♂️","🧚🏿‍♂️",
"👼","👼🏻","👼🏼","👼🏽","👼🏾","👼🏿",
"🤰","🤰🏻","🤰🏼","🤰🏽","🤰🏾","🤰🏿",
"🫄","🫄🏻","🫄🏼","🫄🏽","🫄🏾","🫄🏿",
"🫃","🫃🏻","🫃🏼","🫃🏽","🫃🏾","🫃🏿",
"🤱","🤱🏻","🤱🏼","🤱🏽","🤱🏾","🤱🏿",
"👩‍🍼","👩🏻‍🍼","👩🏼‍🍼","👩🏽‍🍼","👩🏾‍🍼","👩🏿‍🍼",
"🧑‍🍼","🧑🏻‍🍼","🧑🏼‍🍼","🧑🏽‍🍼","🧑🏾‍🍼","🧑🏿‍🍼",
"👨‍🍼","👨🏻‍🍼","👨🏼‍🍼","👨🏽‍🍼","👨🏾‍🍼","👨🏿‍🍼",
"🙇‍♀️","🙇🏻‍♀️","🙇🏼‍♀️","🙇🏽‍♀️","🙇🏾‍♀️","🙇🏿‍♀️",
"🙇","🙇🏻","🙇🏼","🙇🏽","🙇🏾","🙇🏿",
"🙇‍♂️","🙇🏻‍♂️","🙇🏼‍♂️","🙇🏽‍♂️","🙇🏾‍♂️","🙇🏿‍♂️",
"💁‍♀️","💁🏻‍♀️","💁🏼‍♀️","💁🏽‍♀️","💁🏾‍♀️","💁🏿‍♀️",
"💁","💁🏻","💁🏼","💁🏽","💁🏾","💁🏿",
"💁‍♂️","💁🏻‍♂️","💁🏼‍♂️","💁🏽‍♂️","💁🏾‍♂️","💁🏿‍♂️",
"🙅‍♀️","🙅🏻‍♀️","🙅🏼‍♀️","🙅🏽‍♀️","🙅🏾‍♀️","🙅🏿‍♀️",
"🙅","🙅🏻","🙅🏼","🙅🏽","🙅🏾","🙅🏿",
"🙅‍♂️","🙅🏻‍♂️","🙅🏼‍♂️","🙅🏽‍♂️","🙅🏾‍♂️","🙅🏿‍♂️",
"🙆‍♀️","🙆🏻‍♀️","🙆🏼‍♀️","🙆🏽‍♀️","🙆🏾‍♀️","🙆🏿‍♀️",
"🙆","🙆🏻","🙆🏼","🙆🏽","🙆🏾","🙆🏿",
"🙆‍♂️","🙆🏻‍♂️","🙆🏼‍♂️","🙆🏽‍♂️","🙆🏾‍♂️","🙆🏿‍♂️",
"🙋‍♀️","🙋🏻‍♀️","🙋🏼‍♀️","🙋🏽‍♀️","🙋🏾‍♀️","🙋🏿‍♀️",
"🙋","🙋🏻","🙋🏼","🙋🏽","🙋🏾","🙋🏿",
"🙋‍♂️","🙋🏻‍♂️","🙋🏼‍♂️","🙋🏽‍♂️","🙋🏾‍♂️","🙋🏿‍♂️",
"🧏‍♀️","🧏🏻‍♀️","🧏🏼‍♀️","🧏🏽‍♀️","🧏🏾‍♀️","🧏🏿‍♀️",
"🧏","🧏🏻","🧏🏼","🧏🏽","🧏🏾","🧏🏿",
"🧏‍♂️","🧏🏻‍♂️","🧏🏼‍♂️","🧏🏽‍♂️","🧏🏾‍♂️","🧏🏿‍♂️",
"🤦‍♀️","🤦🏻‍♀️","🤦🏼‍♀️","🤦🏽‍♀️","🤦🏾‍♀️","🤦🏿‍♀️",
"🤦","🤦🏻","🤦🏼","🤦🏽","🤦🏾","🤦🏿",
"🤦‍♂️","🤦🏻‍♂️","🤦🏼‍♂️","🤦🏽‍♂️","🤦🏾‍♂️","🤦🏿‍♂️",
"🤷‍♀️","🤷🏻‍♀️","🤷🏼‍♀️","🤷🏽‍♀️","🤷🏾‍♀️","🤷🏿‍♀️",
"🤷","🤷🏻","🤷🏼","🤷🏽","🤷🏾","🤷🏿",
"🤷‍♂️","🤷🏻‍♂️","🤷🏼‍♂️","🤷🏽‍♂️","🤷🏾‍♂️","🤷🏿‍♂️",
"🙎‍♀️","🙎🏻‍♀️","🙎🏼‍♀️","🙎🏽‍♀️","🙎🏾‍♀️","🙎🏿‍♀️",
"🙎","🙎🏻","🙎🏼","🙎🏽","🙎🏾","🙎🏿",
"🙎‍♂️","🙎🏻‍♂️","🙎🏼‍♂️","🙎🏽‍♂️","🙎🏾‍♂️","🙎🏿‍♂️",
"🙍‍♀️","🙍🏻‍♀️","🙍🏼‍♀️","🙍🏽‍♀️","🙍🏾‍♀️","🙍🏿‍♀️",
"🙍","🙍🏻","🙍🏼","🙍🏽","🙍🏾","🙍🏿",
"🙍‍♂️","🙍🏻‍♂️","🙍🏼‍♂️","🙍🏽‍♂️","🙍🏾‍♂️","🙍🏿‍♂️",
"💇‍♀️","💇🏻‍♀️","💇🏼‍♀️","💇🏽‍♀️","💇🏾‍♀️","💇🏿‍♀️",
"💇","💇🏻","💇🏼","💇🏽","💇🏾","💇🏿",
"💇‍♂️","💇🏻‍♂️","💇🏼‍♂️","💇🏽‍♂️","💇🏾‍♂️","💇🏿‍♂️",
"💆‍♀️","💆🏻‍♀️","💆🏼‍♀️","💆🏽‍♀️","💆🏾‍♀️","💆🏿‍♀️",
"💆","💆🏻","💆🏼","💆🏽","💆🏾","💆🏿",
"💆‍♂️","💆🏻‍♂️","💆🏼‍♂️","💆🏽‍♂️","💆🏾‍♂️","💆🏿‍♂️",
"🧖‍♀️","🧖🏻‍♀️","🧖🏼‍♀️","🧖🏽‍♀️","🧖🏾‍♀️","🧖🏿‍♀️",
"🧖","🧖🏻","🧖🏼","🧖🏽","🧖🏾","🧖🏿",
"🧖‍♂️","🧖🏻‍♂️","🧖🏼‍♂️","🧖🏽‍♂️","🧖🏾‍♂️","🧖🏿‍♂️",
"💅","💅🏻","💅🏼","💅🏽","💅🏾","💅🏿",
"🤳","🤳🏻","🤳🏼","🤳🏽","🤳🏾","🤳🏿",
"💃","💃🏻","💃🏼","💃🏽","💃🏾","💃🏿",
"🕺","🕺🏻","🕺🏼","🕺🏽","🕺🏾","🕺🏿",
"👯‍♀️","👯","👯‍♂️",
"🕴","🕴🏻","🕴🏼","🕴🏽","🕴🏾","🕴🏿",
"👩‍🦽","👩🏻‍🦽","👩🏼‍🦽","👩🏽‍🦽","👩🏾‍🦽","👩🏿‍🦽",
"🧑‍🦽","🧑🏻‍🦽","🧑🏼‍🦽","🧑🏽‍🦽","🧑🏾‍🦽","🧑🏿‍🦽",
"👨‍🦽","👨🏻‍🦽","👨🏼‍🦽","👨🏽‍🦽","👨🏾‍🦽","👨🏿‍🦽",
"👩‍🦼","👩🏻‍🦼","👩🏼‍🦼","👩🏽‍🦼","👩🏾‍🦼","👩🏿‍🦼",
"🧑‍🦼","🧑🏻‍🦼","🧑🏼‍🦼","🧑🏽‍🦼","🧑🏾‍🦼","🧑🏿‍🦼",
"👨‍🦼","👨🏻‍🦼","👨🏼‍🦼","👨🏽‍🦼","👨🏾‍🦼","👨🏿‍🦼",
"🚶‍♀️","🚶🏻‍♀️","🚶🏼‍♀️","🚶🏽‍♀️","🚶🏾‍♀️","🚶🏿‍♀️",
"🚶","🚶🏻","🚶🏼","🚶🏽","🚶🏾","🚶🏿",
"🚶‍♂️","🚶🏻‍♂️","🚶🏼‍♂️","🚶🏽‍♂️","🚶🏾‍♂️","🚶🏿‍♂️",
"👩‍🦯","👩🏻‍🦯","👩🏼‍🦯","👩🏽‍🦯","👩🏾‍🦯","👩🏿‍🦯",
"🧑‍🦯","🧑🏻‍🦯","🧑🏼‍🦯","🧑🏽‍🦯","🧑🏾‍🦯","🧑🏿‍🦯",
"👨‍🦯","👨🏻‍🦯","👨🏼‍🦯","👨🏽‍🦯","👨🏾‍🦯","👨🏿‍🦯",
"🧎‍♀️","🧎🏻‍♀️","🧎🏼‍♀️","🧎🏽‍♀️","🧎🏾‍♀️","🧎🏿‍♀️",
"🧎","🧎🏻","🧎🏼","🧎🏽","🧎🏾","🧎🏿",
"🧎‍♂️","🧎🏻‍♂️","🧎🏼‍♂️","🧎🏽‍♂️","🧎🏾‍♂️","🧎🏿‍♂️",
"🏃‍♀️","🏃🏻‍♀️","🏃🏼‍♀️","🏃🏽‍♀️","🏃🏾‍♀️","🏃🏿‍♀️",
"🏃","🏃🏻","🏃🏼","🏃🏽","🏃🏾","🏃🏿",
"🏃‍♂️","🏃🏻‍♂️","🏃🏼‍♂️","🏃🏽‍♂️","🏃🏾‍♂️","🏃🏿‍♂️",
"🧍‍♀️","🧍🏻‍♀️","🧍🏼‍♀️","🧍🏽‍♀️","🧍🏾‍♀️","🧍🏿‍♀️",
"🧍","🧍🏻","🧍🏼","🧍🏽","🧍🏾","🧍🏿",
"🧍‍♂️","🧍🏻‍♂️","🧍🏼‍♂️","🧍🏽‍♂️","🧍🏾‍♂️","🧍🏿‍♂️",
"👫","👫🏻","👩🏻‍🤝‍👨🏼","👩🏻‍🤝‍👨🏽","👩🏻‍🤝‍👨🏾","👩🏻‍🤝‍👨🏿","👩🏼‍🤝‍👨🏻","👫🏼","👩🏼‍🤝‍👨🏽","👩🏼‍🤝‍👨🏾","👩🏼‍🤝‍👨🏿","👩🏽‍🤝‍👨🏻","👩🏽‍🤝‍👨🏼","👫🏽","👩🏽‍🤝‍👨🏾","👩🏽‍🤝‍👨🏿","👩🏾‍🤝‍👨🏻","👩🏾‍🤝‍👨🏼","👩🏾‍🤝‍👨🏽","👫🏾","👩🏾‍🤝‍👨🏿","👩🏿‍🤝‍👨🏻","👩🏿‍🤝‍👨🏼","👩🏿‍🤝‍👨🏽","👩🏿‍🤝‍👨🏾","👫🏿",
"👭","👭🏻","👩🏻‍🤝‍👩🏼","👩🏻‍🤝‍👩🏽","👩🏻‍🤝‍👩🏾","👩🏻‍🤝‍👩🏿","👩🏼‍🤝‍👩🏻","👭🏼","👩🏼‍🤝‍👩🏽","👩🏼‍🤝‍👩🏾","👩🏼‍🤝‍👩🏿","👩🏽‍🤝‍👩🏻","👩🏽‍🤝‍👩🏼","👭🏽","👩🏽‍🤝‍👩🏾","👩🏽‍🤝‍👩🏿","👩🏾‍🤝‍👩🏻","👩🏾‍🤝‍👩🏼","👩🏾‍🤝‍👩🏽","👭🏾","👩🏾‍🤝‍👩🏿","👩🏿‍🤝‍👩🏻","👩🏿‍🤝‍👩🏼","👩🏿‍🤝‍👩🏽","👩🏿‍🤝‍👩🏾","👭🏿",
"👬","👬🏻","👨🏻‍🤝‍👨🏼","👨🏻‍🤝‍👨🏽","👨🏻‍🤝‍👨🏾","👨🏻‍🤝‍👨🏿","👨🏼‍🤝‍👨🏻","👬🏼","👨🏼‍🤝‍👨🏽","👨🏼‍🤝‍👨🏾","👨🏼‍🤝‍👨🏿","👨🏽‍🤝‍👨🏻","👨🏽‍🤝‍👨🏼","👬🏽","👨🏽‍🤝‍👨🏾","👨🏽‍🤝‍👨🏿","👨🏾‍🤝‍👨🏻","👨🏾‍🤝‍👨🏼","👨🏾‍🤝‍👨🏽","👬🏾","👨🏾‍🤝‍👨🏿","👨🏿‍🤝‍👨🏻","👨🏿‍🤝‍👨🏼","👨🏿‍🤝‍👨🏽","👨🏿‍🤝‍👨🏾","👬🏿",
"👩‍❤️‍👨","👩🏻‍❤️‍👨🏻","👩🏻‍❤️‍👨🏼","👩🏻‍❤️‍👨🏽","👩🏻‍❤️‍👨🏾","👩🏻‍❤️‍👨🏿","👩🏼‍❤️‍👨🏻","👩🏼‍❤️‍👨🏼","👩🏼‍❤️‍👨🏽","👩🏼‍❤️‍👨🏾","👩🏼‍❤️‍👨🏿","👩🏽‍❤️‍👨🏻","👩🏽‍❤️‍👨🏼","👩🏽‍❤️‍👨🏽","👩🏽‍❤️‍👨🏾","👩🏽‍❤️‍👨🏿","👩🏾‍❤️‍👨🏻","👩🏾‍❤️‍👨🏼","👩🏾‍❤️‍👨🏽","👩🏾‍❤️‍👨🏾","👩🏾‍❤️‍👨🏿","👩🏿‍❤️‍👨🏻","👩🏿‍❤️‍👨🏼","👩🏿‍❤️‍👨🏽","👩🏿‍❤️‍👨🏾","👩🏿‍❤️‍👨🏿",
"👩‍❤️‍👩","👩🏻‍❤️‍👩🏻","👩🏻‍❤️‍👩🏼","👩🏻‍❤️‍👩🏽","👩🏻‍❤️‍👩🏾","👩🏻‍❤️‍👩🏿","👩🏼‍❤️‍👩🏻","👩🏼‍❤️‍👩🏼","👩🏼‍❤️‍👩🏽","👩🏼‍❤️‍👩🏾","👩🏼‍❤️‍👩🏿","👩🏽‍❤️‍👩🏻","👩🏽‍❤️‍👩🏼","👩🏽‍❤️‍👩🏽","👩🏽‍❤️‍👩🏾","👩🏽‍❤️‍👩🏿","👩🏾‍❤️‍👩🏻","👩🏾‍❤️‍👩🏼","👩🏾‍❤️‍👩🏽","👩🏾‍❤️‍👩🏾","👩🏾‍❤️‍👩🏿","👩🏿‍❤️‍👩🏻","👩🏿‍❤️‍👩🏼","👩🏿‍❤️‍👩🏽","👩🏿‍❤️‍👩🏾","👩🏿‍❤️‍👩🏿",
"💑","🧑🏻‍❤️‍🧑🏻","🧑🏻‍❤️‍🧑🏼","🧑🏻‍❤️‍🧑🏽","🧑🏻‍❤️‍🧑🏾","🧑🏻‍❤️‍🧑🏿","🧑🏼‍❤️‍🧑🏻","🧑🏼‍❤️‍🧑🏼","🧑🏼‍❤️‍🧑🏽","🧑🏼‍❤️‍🧑🏾","🧑🏼‍❤️‍🧑🏿","🧑🏽‍❤️‍🧑🏻","🧑🏽‍❤️‍🧑🏼","🧑🏽‍❤️‍🧑🏽","🧑🏽‍❤️‍🧑🏾","🧑🏽‍❤️‍🧑🏿","🧑🏾‍❤️‍🧑🏻","🧑🏾‍❤️‍🧑🏼","🧑🏾‍❤️‍🧑🏽","🧑🏾‍❤️‍🧑🏾","🧑🏾‍❤️‍🧑🏿","🧑🏿‍❤️‍🧑🏻","🧑🏿‍❤️‍🧑🏼","🧑🏿‍❤️‍🧑🏽","🧑🏿‍❤️‍🧑🏾","🧑🏿‍❤️‍🧑🏿",
"👨‍❤️‍👨","👨🏻‍❤️‍👨🏻","👨🏻‍❤️‍👨🏼","👨🏻‍❤️‍👨🏽","👨🏻‍❤️‍👨🏾","👨🏻‍❤️‍👨🏿","👨🏼‍❤️‍👨🏻","👨🏼‍❤️‍👨🏼","👨🏼‍❤️‍👨🏽","👨🏼‍❤️‍👨🏾","👨🏼‍❤️‍👨🏿","👨🏽‍❤️‍👨🏻","👨🏽‍❤️‍👨🏼","👨🏽‍❤️‍👨🏽","👨🏽‍❤️‍👨🏾","👨🏽‍❤️‍👨🏿","👨🏾‍❤️‍👨🏻","👨🏾‍❤️‍👨🏼","👨🏾‍❤️‍👨🏽","👨🏾‍❤️‍👨🏾","👨🏾‍❤️‍👨🏿","👨🏿‍❤️‍👨🏻","👨🏿‍❤️‍👨🏼","👨🏿‍❤️‍👨🏽","👨🏿‍❤️‍👨🏾","👨🏿‍❤️‍👨🏿",
"👩‍❤️‍💋‍👨","👩🏻‍❤️‍💋‍👨🏻","👩🏻‍❤️‍💋‍👨🏼","👩🏻‍❤️‍💋‍👨🏽","👩🏻‍❤️‍💋‍👨🏾","👩🏻‍❤️‍💋‍👨🏿","👩🏼‍❤️‍💋‍👨🏻","👩🏼‍❤️‍💋‍👨🏼","👩🏼‍❤️‍💋‍👨🏽","👩🏼‍❤️‍💋‍👨🏾","👩🏼‍❤️‍💋‍👨🏿","👩🏽‍❤️‍💋‍👨🏻","👩🏽‍❤️‍💋‍👨🏼","👩🏽‍❤️‍💋‍👨🏽","👩🏽‍❤️‍💋‍👨🏾","👩🏽‍❤️‍💋‍👨🏿","👩🏾‍❤️‍💋‍👨🏻","👩🏾‍❤️‍💋‍👨🏼","👩🏾‍❤️‍💋‍👨🏽","👩🏾‍❤️‍💋‍👨🏾","👩🏾‍❤️‍💋‍👨🏿","👩🏿‍❤️‍💋‍👨🏻","👩🏿‍❤️‍💋‍👨🏼","👩🏿‍❤️‍💋‍👨🏽","👩🏿‍❤️‍💋‍👨🏾","👩🏿‍❤️‍💋‍👨🏿",
"👩‍❤️‍💋‍👩","👩🏻‍❤️‍💋‍👩🏻","👩🏻‍❤️‍💋‍👩🏼","👩🏻‍❤️‍💋‍👩🏽","👩🏻‍❤️‍💋‍👩🏾","👩🏻‍❤️‍💋‍👩🏿","👩🏼‍❤️‍💋‍👩🏻","👩🏼‍❤️‍💋‍👩🏼","👩🏼‍❤️‍💋‍👩🏽","👩🏼‍❤️‍💋‍👩🏾","👩🏼‍❤️‍💋‍👩🏿","👩🏽‍❤️‍💋‍👩🏻","👩🏽‍❤️‍💋‍👩🏼","👩🏽‍❤️‍💋‍👩🏽","👩🏽‍❤️‍💋‍👩🏾","👩🏽‍❤️‍💋‍👩🏿","👩🏾‍❤️‍💋‍👩🏻","👩🏾‍❤️‍💋‍👩🏼","👩🏾‍❤️‍💋‍👩🏽","👩🏾‍❤️‍💋‍👩🏾","👩🏾‍❤️‍💋‍👩🏿","👩🏿‍❤️‍💋‍👩🏻","👩🏿‍❤️‍💋‍👩🏼","👩🏿‍❤️‍💋‍👩🏽","👩🏿‍❤️‍💋‍👩🏾","👩🏿‍❤️‍💋‍👩🏿",
"💏","🧑🏻‍❤️‍💋‍🧑🏻","🧑🏻‍❤️‍💋‍🧑🏼","🧑🏻‍❤️‍💋‍🧑🏽","🧑🏻‍❤️‍💋‍🧑🏾","🧑🏻‍❤️‍💋‍🧑🏿","🧑🏼‍❤️‍💋‍🧑🏻","🧑🏼‍❤️‍💋‍🧑🏼","🧑🏼‍❤️‍💋‍🧑🏽","🧑🏼‍❤️‍💋‍🧑🏾","🧑🏼‍❤️‍💋‍🧑🏿","🧑🏽‍❤️‍💋‍🧑🏻","🧑🏽‍❤️‍💋‍🧑🏼","🧑🏽‍❤️‍💋‍🧑🏽","🧑🏽‍❤️‍💋‍🧑🏾","🧑🏽‍❤️‍💋‍🧑🏿","🧑🏾‍❤️‍💋‍🧑🏻","🧑🏾‍❤️‍💋‍🧑🏼","🧑🏾‍❤️‍💋‍🧑🏽","🧑🏾‍❤️‍💋‍🧑🏾","🧑🏾‍❤️‍💋‍🧑🏿","🧑🏿‍❤️‍💋‍🧑🏻","🧑🏿‍❤️‍💋‍🧑🏼","🧑🏿‍❤️‍💋‍🧑🏽","🧑🏿‍❤️‍💋‍🧑🏾","🧑🏿‍❤️‍💋‍🧑🏿",
"👨‍❤️‍💋‍👨","👨🏻‍❤️‍💋‍👨🏻","👨🏻‍❤️‍💋‍👨🏼","👨🏻‍❤️‍💋‍👨🏽","👨🏻‍❤️‍💋‍👨🏾","👨🏻‍❤️‍💋‍👨🏿","👨🏼‍❤️‍💋‍👨🏻","👨🏼‍❤️‍💋‍👨🏼","👨🏼‍❤️‍💋‍👨🏽","👨🏼‍❤️‍💋‍👨🏾","👨🏼‍❤️‍💋‍👨🏿","👨🏽‍❤️‍💋‍👨🏻","👨🏽‍❤️‍💋‍👨🏼","👨🏽‍❤️‍💋‍👨🏽","👨🏽‍❤️‍💋‍👨🏾","👨🏽‍❤️‍💋‍👨🏿","👨🏾‍❤️‍💋‍👨🏻","👨🏾‍❤️‍💋‍👨🏼","👨🏾‍❤️‍💋‍👨🏽","👨🏾‍❤️‍💋‍👨🏾","👨🏾‍❤️‍💋‍👨🏿","👨🏿‍❤️‍💋‍👨🏻","👨🏿‍❤️‍💋‍👨🏼","👨🏿‍❤️‍💋‍👨🏽","👨🏿‍❤️‍💋‍👨🏾","👨🏿‍❤️‍💋‍👨🏿",
"👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂"
"🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼","🐻‍❄️","🐨","🐯","🦁","🐮","🐷","🐽","🐸","🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦","🐤","🐣","🐥","🦆","🦅","🦉","🦇","🐺","🐗","🐴","🦄","🐝","🪱","🐛","🦋","🐌","🐞","🐜","🪰","🪲","🪳","🦟","🦗","🕷","🕸","🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑","🦐","🦞","🦀","🐡","🐠","🐟","🐬","🐳","🐋","🦈","🦭","🐊","🐅","🐆","🦓","🦍","🦧","🦣","🐘","🦛","🦏","🐪","🐫","🦒","🦘","🦬","🐃","🐂","🐄","🐎","🐖","🐏","🐑","🦙","🐐","🦌","🐕","🐩","🦮","🐕‍🦺","🐈","🐈‍⬛","🪶","🐓","🦃","🦤","🦚","🦜","🦢","🦩","🕊","🐇","🦝","🦨","🦡","🦫","🦦","🦥","🐁","🐀","🐿","🦔","🐾","🐉","🐲","🌵","🎄","🌲","🌳","🌴","🪵","🌱","🌿","☘️","🍀","🎍","🪴","🎋","🍃","🍂","🍁","🪺","🪹","🍄","🐚","🪸","🪨","🌾","💐","🌷","🌹","🥀","🪷","🌺","🌸","🌼","🌻","🌞","🌝","🌛","🌜","🌚","🌕","🌖","🌗","🌘","🌑","🌒","🌓","🌔","🌙","🌎","🌍","🌏","🪐","💫","⭐️","🌟","✨","⚡️","☄️","💥","🔥","🌪","🌈","☀️","🌤","⛅️","🌥","☁️","🌦","🌧","⛈","🌩","🌨","❄️","☃️","⛄️","🌬","💨","💧","💦","🫧","☔️","☂️","🌊","🌫"
"🍏","🍎","🍐","🍊","🍋","🍌","🍉","🍇","🍓","🫐","🍈","🍒","🍑","🥭","🍍","🥥","🥝","🍅","🍆","🥑","🥦","🥬","🥒","🌶","🫑","🌽","🥕","🫒","🧄","🧅","🥔","🍠","🥐","🥯","🍞","🥖","🥨","🧀","🥚","🍳","🧈","🥞","🧇","🥓","🥩","🍗","🍖","🦴","🌭","🍔","🍟","🍕","🫓","🥪","🥙","🧆","🌮","🌯","🫔","🥗","🥘","🫕","🥫","🍝","🍜","🍲","🫙","🍛","🍣","🍱","🥟","🦪","🍤","🍙","🍚","🍘","🍥","🥠","🥮","🍢","🍡","🍧","🍨","🍦","🥧","🧁","🍰","🎂","🍮","🍭","🍬","🍫","🍿","🍩","🍪","🌰","🥜","🫘","🍯","🥛","🫗","🍼","🫖","☕️","🍵","🧃","🥤","🧋","🍶","🍺","🍻","🥂","🍷","🥃","🍸","🍹","🧉","🍾","🧊","🥄","🍴","🍽","🥣","🥡","🥢","🧂"
"⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🛝","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂",
"🏋️‍♀️","🏋🏻‍♀️","🏋🏼‍♀️","🏋🏽‍♀️","🏋🏾‍♀️","🏋🏿‍♀️",
"🏋️","🏋🏻","🏋🏼","🏋🏽","🏋🏾","🏋🏿",
"🏋️‍♂️","🏋🏻‍♂️","🏋🏼‍♂️","🏋🏽‍♂️","🏋🏾‍♂️","🏋🏿‍♂️",
"🤼‍♀️","🤼","🤼‍♂️",
"🤸‍♀️","🤸🏻‍♀️","🤸🏼‍♀️","🤸🏽‍♀️","🤸🏾‍♀️","🤸🏿‍♀️",
"🤸","🤸🏻","🤸🏼","🤸🏽","🤸🏾","🤸🏿",
"🤸‍♂️","🤸🏻‍♂️","🤸🏼‍♂️","🤸🏽‍♂️","🤸🏾‍♂️","🤸🏿‍♂️",
"⛹️‍♀️","⛹🏻‍♀️","⛹🏼‍♀️","⛹🏽‍♀️","⛹🏾‍♀️","⛹🏿‍♀️",
"⛹️","⛹🏻","⛹🏼","⛹🏽","⛹🏾","⛹🏿",
"⛹️‍♂️","⛹🏻‍♂️","⛹🏼‍♂️","⛹🏽‍♂️","⛹🏾‍♂️","⛹🏿‍♂️",
"🤺",
"🤾‍♀️","🤾🏻‍♀️","🤾🏼‍♀️","🤾🏽‍♀️","🤾🏾‍♀️","🤾🏿‍♀️",
"🤾","🤾🏻","🤾🏼","🤾🏽","🤾🏾","🤾🏿",
"🤾‍♂️","🤾🏻‍♂️","🤾🏼‍♂️","🤾🏽‍♂️","🤾🏾‍♂️","🤾🏿‍♂️",
"🏌️‍♀️","🏌🏻‍♀️","🏌🏼‍♀️","🏌🏽‍♀️","🏌🏾‍♀️","🏌🏿‍♀️",
"🏌️","🏌🏻","🏌🏼","🏌🏽","🏌🏾","🏌🏿",
"🏌️‍♂️","🏌🏻‍♂️","🏌🏼‍♂️","🏌🏽‍♂️","🏌🏾‍♂️","🏌🏿‍♂️",
"🏇","🏇🏻","🏇🏼","🏇🏽","🏇🏾","🏇🏿",
"🧘‍♀️","🧘🏻‍♀️","🧘🏼‍♀️","🧘🏽‍♀️","🧘🏾‍♀️","🧘🏿‍♀️",
"🧘","🧘🏻","🧘🏼","🧘🏽","🧘🏾","🧘🏿",
"🧘‍♂️","🧘🏻‍♂️","🧘🏼‍♂️","🧘🏽‍♂️","🧘🏾‍♂️","🧘🏿‍♂️",
"🏄‍♀️","🏄🏻‍♀️","🏄🏼‍♀️","🏄🏽‍♀️","🏄🏾‍♀️","🏄🏿‍♀️",
"🏄","🏄🏻","🏄🏼","🏄🏽","🏄🏾","🏄🏿",
"🏄‍♂️","🏄🏻‍♂️","🏄🏼‍♂️","🏄🏽‍♂️","🏄🏾‍♂️","🏄🏿‍♂️",
"🏊‍♀️","🏊🏻‍♀️","🏊🏼‍♀️","🏊🏽‍♀️","🏊🏾‍♀️","🏊🏿‍♀️",
"🏊","🏊🏻","🏊🏼","🏊🏽","🏊🏾","🏊🏿",
"🏊‍♂️","🏊🏻‍♂️","🏊🏼‍♂️","🏊🏽‍♂️","🏊🏾‍♂️","🏊🏿‍♂️",
"🤽‍♀️","🤽🏻‍♀️","🤽🏼‍♀️","🤽🏽‍♀️","🤽🏾‍♀️","🤽🏿‍♀️",
"🤽","🤽🏻","🤽🏼","🤽🏽","🤽🏾","🤽🏿",
"🤽‍♂️","🤽🏻‍♂️","🤽🏼‍♂️","🤽🏽‍♂️","🤽🏾‍♂️","🤽🏿‍♂️",
"🚣‍♀️","🚣🏻‍♀️","🚣🏼‍♀️","🚣🏽‍♀️","🚣🏾‍♀️","🚣🏿‍♀️",
"🚣","🚣🏻","🚣🏼","🚣🏽","🚣🏾","🚣🏿",
"🚣‍♂️","🚣🏻‍♂️","🚣🏼‍♂️","🚣🏽‍♂️","🚣🏾‍♂️","🚣🏿‍♂️",
"🧗‍♀️","🧗🏻‍♀️","🧗🏼‍♀️","🧗🏽‍♀️","🧗🏾‍♀️","🧗🏿‍♀️",
"🧗","🧗🏻","🧗🏼","🧗🏽","🧗🏾","🧗🏿",
"🧗‍♂️","🧗🏻‍♂️","🧗🏼‍♂️","🧗🏽‍♂️","🧗🏾‍♂️","🧗🏿‍♂️",
"🚵‍♀️","🚵🏻‍♀️","🚵🏼‍♀️","🚵🏽‍♀️","🚵🏾‍♀️","🚵🏿‍♀️",
"🚵","🚵🏻","🚵🏼","🚵🏽","🚵🏾","🚵🏿",
"🚵‍♂️","🚵🏻‍♂️","🚵🏼‍♂️","🚵🏽‍♂️","🚵🏾‍♂️","🚵🏿‍♂️",
"🚴‍♀️","🚴🏻‍♀️","🚴🏼‍♀️","🚴🏽‍♀️","🚴🏾‍♀️","🚴🏿‍♀️",
"🚴","🚴🏻","🚴🏼","🚴🏽","🚴🏾","🚴🏿",
"🚴‍♂️","🚴🏻‍♂️","🚴🏼‍♂️","🚴🏽‍♂️","🚴🏾‍♂️","🚴🏿‍♂️",
"🏆","🥇","🥈","🥉","🏅","🎖","🏵","🎗","🎫","🎟","🎪",
"🤹‍♀️","🤹🏻‍♀️","🤹🏼‍♀️","🤹🏽‍♀️","🤹🏾‍♀️","🤹🏿‍♀️",
"🤹","🤹🏻","🤹🏼","🤹🏽","🤹🏾","🤹🏿",
"🤹‍♂️","🤹🏻‍♂️","🤹🏼‍♂️","🤹🏽‍♂️","🤹🏾‍♂️","🤹🏿‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"🚗","🚕","🚙","🚌","🚎","🏎","🚓","🚑","🚒","🚐","🛻","🚚","🚛","🚜","🦯","🦽","🦼","🩼","🛴","🚲","🛵","🏍","🛺","🛞","🚨","🚔","🚍","🚘","🚖","🚡","🚠","🚟","🚃","🚋","🚞","🚝","🚄","🚅","🚈","🚂","🚆","🚇","🚊","🚉","✈️","🛫","🛬","🛩","💺","🛰","🚀","🛸","🚁","🛶","⛵️","🚤","🛥","🛳","⛴","🚢","🛟","⚓️","🪝","⛽️","🚧","🚦","🚥","🚏","🗺","🗿","🗽","🗼","🏰","🏯","🏟","🎡","🎢","🎠","⛲️","⛱","🏖","🏝","🏜","🌋","⛰","🏔","🗻","🏕","⛺️","🛖","🏠","🏡","🏘","🏚","🏗","🏭","🏢","🏬","🏣","🏤","🏥","🏦","🏨","🏪","🏫","🏩","💒","🏛","⛪️","🕌","🕍","🛕","🕋","⛩","🛤","🛣","🗾","🎑","🏞","🌅","🌄","🌠","🎇","🎆","🌇","🌆","🏙","🌃","🌌","🌉","🌁"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🪫","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","🪪","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","🪬","💈","⚗️","🔭","🔬","🕳","🩻","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀","🛀🏻","🛀🏼","🛀🏽","🛀🏾","🛀🏿",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🪩","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
"❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎","💔","❤️‍🔥","❤️‍🩹","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✝️","☪️","🕉","☸️","✡️","🔯","🕎","☯️","☦️","🛐","⛎","♈️","♉️","♊️","♋️","♌️","♍️","♎️","♏️","♐️","♑️","♒️","♓️","🆔","⚛️","🉑","☢️","☣️","📴","📳","🈶","🈚️","🈸","🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️","🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑","🅾️","🆘","❌","⭕️","🛑","⛔️","📛","🚫","💯","💢","♨️","🚷","🚯","🚳","🚱","🔞","📵","🚭","❗️","❕","❓","❔","‼️","⁉️","🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰","♻️","✅","🈯️","💹","❇️","✳️","❎","🌐","💠","Ⓜ️","🌀","💤","🏧","🚾","♿️","🅿️","🛗","🈳","🈂️","🛂","🛃","🛄","🛅","🚹","🚺","🚼","⚧","🚻","🚮","🎦","📶","🈁","🔣","","🔤","🔡","🔠","🆖","🆗","🆙","🆒","🆕","🆓","0⃣","1⃣","2⃣","3⃣","4⃣","5⃣","6⃣","7⃣","8⃣","9⃣","🔟","🔢","#️⃣","*️⃣","⏏️","▶️","⏸","⏯","⏹","⏺","⏭","⏮","⏩","⏪","⏫","⏬","◀️","🔼","🔽","➡️","⬅️","⬆️","⬇️","↗️","↘️","↙️","↖️","↕️","↔️","↪️","↩️","⤴️","⤵️","🔀","🔁","🔂","🔄","🔃","🎵","🎶","","","➗", "✖️", "🟰", "♾", "💲","💱","™️","©️","®️","👁‍🗨","🔚","🔙","🔛","🔝","🔜","〰️","➰","➿","✔️","☑️","🔘","🔴","🟠","🟡","🟢","🔵","🟣","⚫️","⚪️","🟤","🔺","🔻","🔸","🔹","🔶","🔷","🔳","🔲","▪️","▫️","◾️","◽️","◼️","◻️","🟥","🟧","🟨","🟩","🟦","🟪","⬛️","⬜️","🟫","🔈","🔇","🔉","🔊","🔔","🔕","📣","📢","💬","💭","🗯","♠️","♣️","♥️","♦️","🃏","🎴","🀄️","🕐","🕑","🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚","🕛","🕜","🕝","🕞","🕟","🕠","🕡","🕢","🕣","🕤","🕥","🕦","🕧"
"🏳️","🏴","🏴‍☠️","🏁","🚩","🏳️‍🌈","🏳️‍⚧️","🇺🇳","🇦🇫","🇦🇽","🇦🇱","🇩🇿","🇦🇸","🇦🇩","🇦🇴","🇦🇮","🇦🇶","🇦🇬","🇦🇷","🇦🇲","🇦🇼","🇦🇺","🇦🇹","🇦🇿","🇧🇸","🇧🇭","🇧🇩","🇧🇧","🇧🇾","🇧🇪","🇧🇿","🇧🇯","🇧🇲","🇧🇹","🇧🇴","🇧🇦","🇧🇼","🇧🇷","🇻🇬","🇧🇳","🇧🇬","🇧🇫","🇧🇮","🇰🇭","🇨🇲","🇨🇦","🇮🇨","🇨🇻","🇧🇶","🇰🇾","🇨🇫","🇹🇩","🇮🇴","🇨🇱","🇨🇳","🇨🇽","🇨🇨","🇨🇴","🇰🇲","🇨🇬","🇨🇩","🇨🇰","🇨🇷","🇨🇮","🇭🇷","🇨🇺","🇨🇼","🇨🇾","🇨🇿","🇩🇰","🇩🇯","🇩🇲","🇩🇴","🇪🇨","🇪🇬","🇸🇻","🇬🇶","🇪🇷","🇪🇪","🇸🇿","🇪🇹","🇪🇺","🇫🇰","🇫🇴","🇫🇯","🇫🇮","🇫🇷","🇬🇫","🇵🇫","🇹🇫","🇬🇦","🇬🇲","🇬🇪","🇩🇪","🇬🇭","🇬🇮","🇬🇷","🇬🇱","🇬🇩","🇬🇵","🇬🇺","🇬🇹","🇬🇬","🇬🇳","🇬🇼","🇬🇾","🇭🇹","🇭🇳","🇭🇰","🇭🇺","🇮🇸","🇮🇳","🇮🇩","🇮🇷","🇮🇶","🇮🇪","🇮🇲","🇮🇱","🇮🇹","🇯🇲","🇯🇵","🎌","🇯🇪","🇯🇴","🇰🇿","🇰🇪","🇰🇮","🇽🇰","🇰🇼","🇰🇬","🇱🇦","🇱🇻","🇱🇧","🇱🇸","🇱🇷","🇱🇾","🇱🇮","🇱🇹","🇱🇺","🇲🇴","🇲🇬","🇲🇼","🇲🇾","🇲🇻","🇲🇱","🇲🇹","🇲🇭","🇲🇶","🇲🇷","🇲🇺","🇾🇹","🇲🇽","🇫🇲","🇲🇩","🇲🇨","🇲🇳","🇲🇪","🇲🇸","🇲🇦","🇲🇿","🇲🇲","🇳🇦","🇳🇷","🇳🇵","🇳🇱","🇳🇨","🇳🇿","🇳🇮","🇳🇪","🇳🇬","🇳🇺","🇳🇫","🇰🇵","🇲🇰","🇲🇵","🇳🇴","🇴🇲","🇵🇰","🇵🇼","🇵🇸","🇵🇦","🇵🇬","🇵🇾","🇵🇪","🇵🇭","🇵🇳","🇵🇱","🇵🇹","🇵🇷","🇶🇦","🇷🇪","🇷🇴","🇷🇺","🇷🇼","🇼🇸","🇸🇲","🇸🇹","🇸🇦","🇸🇳","🇷🇸","🇸🇨","🇸🇱","🇸🇬","🇸🇽","🇸🇰","🇸🇮","🇬🇸","🇸🇧","🇸🇴","🇿🇦","🇰🇷","🇸🇸","🇪🇸","🇱🇰","🇧🇱","🇸🇭","🇰🇳","🇱🇨","🇵🇲","🇻🇨","🇸🇩","🇸🇷","🇸🇪","🇨🇭","🇸🇾","🇹🇼","🇹🇯","🇹🇿","🇹🇭","🇹🇱","🇹🇬","🇹🇰","🇹🇴","🇹🇹","🇹🇳","🇹🇷","🇹🇲","🇹🇨","🇹🇻","🇺🇬","🇺🇦","🇦🇪","🇬🇧","🏴󠁧󠁢󠁥󠁮󠁧󠁿","🏴󠁧󠁢󠁳󠁣󠁴󠁿","🏴󠁧󠁢󠁷󠁬󠁳󠁿","🇺🇸","🇺🇾","🇻🇮","🇺🇿","🇻🇺","🇻🇦","🇻🇪","🇻🇳","🇼🇫","🇪🇭","🇾🇪","🇿🇲","🇿🇼"
=========================================
"😀","😃","😄","😁","😆","🥹","😅","😂","🤣","🥲","☺️","😊","😇","🙂","🙃","😉","😌","😍","🥰","😘","😗","😙","😚","😋","😛","😝","😜","🤪","🤨","🧐","🤓","😎","🥸","🤩","🥳","😏","😒","😞","😔","😟","😕","🙁","☹️","😣","😖","😫","😩","🥺","😢","😭","😤","😠","😡","🤬","🤯","😳","🥵","🥶","😶‍🌫️","😱","😨","😰","😥","😓","🤗","🤔","🫣","🤭","🫢","🫡","🤫","🫠","🤥","😶","🫥","😐","🫤","😑","😬","🙄","😯","😦","😧","😮","😲","🥱","😴","🤤","😪","😮‍💨","😵","😵‍💫","🤐","🥴","🤢","🤮","🤧","😷","🤒","🤕","🤑","🤠","😈","👿","👹","👺","🤡","💩","👻","💀","☠️","👽","👾","🤖","🎃","😺","😸","😹","😻","😼","😽","🙀","😿","😾",
"🫶",
"🤲",
"👐",
"🙌",
"👏",
"🤝",
"👍",
"👎",
"👊",
"✊",
"🤛",
"🤜",
"🤞",
"✌️",
"🫰",
"🤟",
"🤘",
"👌",
"🤌",
"🤏",
"🫳",
"🫴",
"👈",
"👉",
"👆",
"👇",
"☝️",
"✋",
"🤚",
"🖐",
"🖖",
"👋",
"🤙",
"🫲",
"🫱",
"💪",
"🦾",
"🖕",
"✍️",
"🙏",
"🫵",
"🦶",
"🦵",
"🦿","💄","💋","👄","🫦","🦷","👅",
"👂",
"🦻",
"👃",
"👣","👁","👀","🫀","🫁","🧠","🗣","👤","👥","🫂",
"👶",
"👧",
"🧒",
"👦",
"👩",
"🧑",
"👨",
"👩‍🦱",
"🧑‍🦱",
"👨‍🦱",
"👩‍🦰",
"🧑‍🦰",
"👨‍🦰",
"👱‍♀️",
"👱",
"👱‍♂️",
"👩‍🦳",
"🧑‍🦳",
"👨‍🦳",
"👩‍🦲",
"🧑‍🦲",
"👨‍🦲",
"🧔‍♀️",
"🧔",
"🧔‍♂️",
"👵",
"🧓",
"👴",
"👲",
"👳‍♀️",
"👳",
"👳‍♂️",
"🧕",
"👮‍♀️",
"👮",
"👮‍♂️",
"👷‍♀️",
"👷",
"👷‍♂️",
"💂‍♀️",
"💂",
"💂‍♂️",
"🕵️‍♀️",
"🕵️",
"🕵️‍♂️",
"👩‍⚕️",
"🧑‍⚕️",
"👨‍⚕️",
"👩‍🌾",
"🧑‍🌾",
"👨‍🌾",
"👩‍🍳",
"🧑‍🍳",
"👨‍🍳",
"👩‍🎓",
"🧑‍🎓",
"👨‍🎓",
"👩‍🎤",
"🧑‍🎤",
"👨‍🎤",
"👩‍🏫",
"🧑‍🏫",
"👨‍🏫",
"👩‍🏭",
"🧑‍🏭",
"👨‍🏭",
"👩‍💻",
"🧑‍💻",
"👨‍💻",
"👩‍💼",
"🧑‍💼",
"👨‍💼",
"👩‍🔧",
"🧑‍🔧",
"👨‍🔧",
"👩‍🔬",
"🧑‍🔬",
"👨‍🔬",
"👩‍🎨",
"🧑‍🎨",
"👨‍🎨",
"👩‍🚒",
"🧑‍🚒",
"👨‍🚒",
"👩‍✈️",
"🧑‍✈️",
"👨‍✈️",
"👩‍🚀",
"🧑‍🚀",
"👨‍🚀",
"👩‍⚖️",
"🧑‍⚖️",
"👨‍⚖️",
"👰‍♀️",
"👰",
"👰‍♂️",
"🤵‍♀️",
"🤵",
"🤵‍♂️",
"👸",
"🫅",
"🤴",
"🥷",
"🦸‍♀️",
"🦸",
"🦸‍♂️",
"🦹‍♀️",
"🦹",
"🦹‍♂️",
"🤶",
"🧑‍🎄",
"🎅",
"🧙‍♀️",
"🧙",
"🧙‍♂️",
"🧝‍♀️",
"🧝",
"🧝‍♂️",
"🧌",
"🧛‍♀️",
"🧛",
"🧛‍♂️",
"🧟‍♀️","🧟","🧟‍♂️","🧞‍♀️","🧞","🧞‍♂️",
"🧜‍♀️",
"🧜",
"🧜‍♂️",
"🧚‍♀️",
"🧚",
"🧚‍♂️",
"👼",
"🤰",
"🫄",
"🫃",
"🤱",
"👩‍🍼",
"🧑‍🍼",
"👨‍🍼",
"🙇‍♀️",
"🙇",
"🙇‍♂️",
"💁‍♀️",
"💁",
"💁‍♂️",
"🙅‍♀️",
"🙅",
"🙅‍♂️",
"🙆‍♀️",
"🙆",
"🙆‍♂️",
"🙋‍♀️",
"🙋",
"🙋‍♂️",
"🧏‍♀️",
"🧏",
"🧏‍♂️",
"🤦‍♀️",
"🤦",
"🤦‍♂️",
"🤷‍♀️",
"🤷",
"🤷‍♂️",
"🙎‍♀️",
"🙎",
"🙎‍♂️",
"🙍‍♀️",
"🙍",
"🙍‍♂️",
"💇‍♀️",
"💇",
"💇‍♂️",
"💆‍♀️",
"💆",
"💆‍♂️",
"🧖‍♀️",
"🧖",
"🧖‍♂️",
"💅",
"🤳",
"💃",
"🕺",
"👯‍♀️","👯","👯‍♂️",
"🕴",
"👩‍🦽",
"🧑‍🦽",
"👨‍🦽",
"👩‍🦼",
"🧑‍🦼",
"👨‍🦼",
"🚶‍♀️",
"🚶",
"🚶‍♂️",
"👩‍🦯",
"🧑‍🦯",
"👨‍🦯",
"🧎‍♀️",
"🧎",
"🧎‍♂️",
"🏃‍♀️",
"🏃",
"🏃‍♂️",
"🧍‍♀️",
"🧍",
"🧍‍♂️",
"👫",
"👭",
"👬",
"👩‍❤️‍👨",
"👩‍❤️‍👩",
"💑",
"👨‍❤️‍👨",
"👩‍❤️‍💋‍👨",
"👩‍❤️‍💋‍👩",
"💏",
"👨‍❤️‍💋‍👨",
"👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👦","👩‍👧","👩‍👧‍👦","👩‍👦‍👦","👩‍👧‍👧","👨‍👦","👨‍👧","👨‍👧‍👦","👨‍👦‍👦","👨‍👧‍👧","🪢","🧶","🧵","🪡","🧥","🥼","🦺","👚","👕","👖","🩲","🩳","👔","👗","👙","🩱","👘","🥻","🩴","🥿","👠","👡","👢","👞","👟","🥾","🧦","🧤","🧣","🎩","🧢","👒","🎓","⛑","🪖","👑","💍","👝","👛","👜","💼","🎒","🧳","👓","🕶","🥽","🌂"
"⚽️","🏀","🏈","⚾️","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍","🏏","🪃","🥅","⛳️","🪁","🏹","🎣","🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸","🥌","🎿","⛷","🏂","🪂",
"🏋️‍♀️",
"🏋️",
"🏋️‍♂️",
"🤼‍♀️","🤼","🤼‍♂️",
"🤸‍♀️",
"🤸",
"🤸‍♂️",
"⛹️‍♀️",
"⛹️",
"⛹️‍♂️",
"🤺",
"🤾‍♀️",
"🤾",
"🤾‍♂️",
"🏌️‍♀️",
"🏌️",
"🏌️‍♂️",
"🏇",
"🧘‍♀️",
"🧘",
"🧘‍♂️",
"🏄‍♀️",
"🏄",
"🏄‍♂️",
"🏊‍♀️",
"🏊",
"🏊‍♂️",
"🤽‍♀️",
"🤽",
"🤽‍♂️",
"🚣‍♀️",
"🚣",
"🚣‍♂️",
"🧗‍♀️",
"🧗",
"🧗‍♂️",
"🚵‍♀️",
"🚵",
"🚵‍♂️",
"🚴‍♀️",
"🚴",
"🚴‍♂️",
"🏆","🥇","🥈","🥉","🏅","🎖","🏵","🎗","🎫","🎟","🎪",
"🤹‍♀️",
"🤹",
"🤹‍♂️",
"🎭","🩰","🎨","🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷","🎺","🪗","🎸","🪕","🎻","🎲","♟","🎯","🎳","🎮","🎰","🧩"
"⌚️","📱","📲","💻","⌨️","🖥","🖨","🖱","🖲","🕹","🗜","💽","💾","💿","📀","📼","📷","📸","📹","🎥","📽","🎞","📞","☎️","📟","📠","📺","📻","🎙","🎚","🎛","🧭","⏱","⏲","⏰","🕰","⌛️","⏳","📡","🔋","🪫","🔌","💡","🔦","🕯","🪔","🧯","🛢","💸","💵","💴","💶","💷","🪙","💰","💳","🪪","💎","⚖️","🪜","🧰","🪛","🔧","🔨","⚒","🛠","⛏","🪚","🔩","⚙️","🪤","🧱","⛓","🧲","🔫","💣","🧨","🪓","🔪","🗡","⚔️","🛡","🚬","⚰️","🪦","⚱️","🏺","🔮","📿","🧿","🪬","💈","⚗️","🔭","🔬","🕳","🩻","🩹","🩺","💊","💉","🩸","🧬","🦠","🧫","🧪","🌡","🧹","🪠","🧺","🧻","🚽","🚰","🚿","🛁",
"🛀",
"🧼","🪥","🪒","🧽","🪣","🧴","🛎","🔑","🗝","🚪","🪑","🛋","🛏","🛌","🧸","🪆","🖼","🪞","🪟","🛍","🛒","🎁","🎈","🎏","🎀","🪄","🪅","🎊","🎉","🎎","🏮","🎐","🪩","🧧","✉️","📩","📨","📧","💌","📥","📤","📦","🏷","🪧","📪","📫","📬","📭","📮","📯","📜","📃","📄","📑","🧾","📊","📈","📉","🗒","🗓","📆","📅","🗑","📇","🗃","🗳","🗄","📋","📁","📂","🗂","🗞","📰","📓","📔","📒","📕","📗","📘","📙","📚","📖","🔖","🧷","🔗","📎","🖇","📐","📏","🧮","📌","📍","✂️","🖊","🖋","✒️","🖌","🖍","📝","✏️","🔍","🔎","🔏","🔐","🔒","🔓"
=========================================
"🫶","🤲","👐","🙌","👏","👍","👎","👊","✊","🤛","🤜","🤞","✌️","🫰","🤟","🤘","👌","🤌","🤏","🫳","🫴","👈","👉","👆","👇","☝️","✋","🤚","🖐","🖖","👋","🤙","🫲","🫱","💪","🖕","✍️","🙏","🫵","🦶","🦵","👂","🦻","👃","👶","👧","🧒","👦","👩","🧑","👨","👩‍🦱","🧑‍🦱","👨‍🦱","👩‍🦰","🧑‍🦰","👨‍🦰","👱‍♀️","👱","👱‍♂️","👩‍🦳","🧑‍🦳","👨‍🦳","👩‍🦲","🧑‍🦲","👨‍🦲","🧔‍♀️","🧔","🧔‍♂️","👵","🧓","👴","👲","👳‍♀️","👳","👳‍♂️","🧕","👮‍♀️","👮","👮‍♂️","👷‍♀️","👷","👷‍♂️","💂‍♀️","💂","💂‍♂️","🕵️‍♀️","🕵️","🕵️‍♂️","👩‍⚕️","🧑‍⚕️","👨‍⚕️","👩‍🌾","🧑‍🌾","👨‍🌾","👩‍🍳","🧑‍🍳","👨‍🍳","👩‍🎓","🧑‍🎓","👨‍🎓","👩‍🎤","🧑‍🎤","👨‍🎤","👩‍🏫","🧑‍🏫","👨‍🏫","👩‍🏭","🧑‍🏭","👨‍🏭","👩‍💻","🧑‍💻","👨‍💻","👩‍💼","🧑‍💼","👨‍💼","👩‍🔧","🧑‍🔧","👨‍🔧","👩‍🔬","🧑‍🔬","👨‍🔬","👩‍🎨","🧑‍🎨","👨‍🎨","👩‍🚒","🧑‍🚒","👨‍🚒","👩‍✈️","🧑‍✈️","👨‍✈️","👩‍🚀","🧑‍🚀","👨‍🚀","👩‍⚖️","🧑‍⚖️","👨‍⚖️","👰‍♀️","👰","👰‍♂️","🤵‍♀️","🤵","🤵‍♂️","👸","🫅","🤴","🥷","🦸‍♀️","🦸","🦸‍♂️","🦹‍♀️","🦹","🦹‍♂️","🤶","🧑‍🎄","🎅","🧙‍♀️","🧙","🧙‍♂️","🧝‍♀️","🧝","🧝‍♂️","🧛‍♀️","🧛","🧛‍♂️","🧜‍♀️","🧜","🧜‍♂️","🧚‍♀️","🧚","🧚‍♂️","👼","🤰","🫄","🫃","🤱","👩‍🍼","🧑‍🍼","👨‍🍼","🙇‍♀️","🙇","🙇‍♂️","💁‍♀️","💁","💁‍♂️","🙅‍♀️","🙅","🙅‍♂️","🙆‍♀️","🙆","🙆‍♂️","🙋‍♀️","🙋","🙋‍♂️","🧏‍♀️","🧏","🧏‍♂️","🤦‍♀️","🤦","🤦‍♂️","🤷‍♀️","🤷","🤷‍♂️","🙎‍♀️","🙎","🙎‍♂️","🙍‍♀️","🙍","🙍‍♂️","💇‍♀️","💇","💇‍♂️","💆‍♀️","💆","💆‍♂️","🧖‍♀️","🧖","🧖‍♂️","💅","🤳","💃","🕺","🕴","👩‍🦽","🧑‍🦽","👨‍🦽","👩‍🦼","🧑‍🦼","👨‍🦼","🚶‍♀️","🚶","🚶‍♂️","👩‍🦯","🧑‍🦯","👨‍🦯","🧎‍♀️","🧎","🧎‍♂️","🏃‍♀️","🏃","🏃‍♂️","🧍‍♀️","🧍","🧍‍♂️","🏋️‍♀️","🏋️","🏋️‍♂️","🤸‍♀️","🤸","🤸‍♂️","⛹️‍♀️","⛹️","⛹️‍♂️","🤾‍♀️","🤾","🤾‍♂️","🏌️‍♀️","🏌️","🏌️‍♂️","🏇","🧘‍♀️","🧘","🧘‍♂️","🏄‍♀️","🏄","🏄‍♂️","🏊‍♀️","🏊","🏊‍♂️","🤽‍♀️","🤽","🤽‍♂️","🚣‍♀️","🚣","🚣‍♂️","🧗‍♀️","🧗","🧗‍♂️","🚵‍♀️","🚵","🚵‍♂️","🚴‍♀️","🚴","🚴‍♂️","🤹‍♀️","🤹","🤹‍♂️","🛀"
"🤝","👫","👭","👬","👩‍❤️‍👨","👩‍❤️‍👩","💑","👨‍❤️‍👨","👩‍❤️‍💋‍👨","👩‍❤️‍💋‍👩","💏","👨‍❤️‍💋‍👨"
=========================================
"🗨", "👪"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 B

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 B

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,006 B

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 890 B

View file

@ -104,18 +104,20 @@ void AbstractButton::setPointerCursor(bool enablePointerCursor) {
} }
void AbstractButton::setOver(bool over, StateChangeSource source) { void AbstractButton::setOver(bool over, StateChangeSource source) {
if (over && !(_state & StateFlag::Over)) { if (over == isOver()) {
auto was = _state; return;
}
const auto was = _state;
if (over) {
_state |= StateFlag::Over; _state |= StateFlag::Over;
Integration::Instance().registerLeaveSubscription(this); Integration::Instance().registerLeaveSubscription(this);
onStateChanged(was, source); } else {
} else if (!over && (_state & StateFlag::Over)) {
auto was = _state;
_state &= ~State(StateFlag::Over); _state &= ~State(StateFlag::Over);
Integration::Instance().unregisterLeaveSubscription(this); Integration::Instance().unregisterLeaveSubscription(this);
onStateChanged(was, source);
} }
onStateChanged(was, source);
updateCursor(); updateCursor();
update();
} }
bool AbstractButton::setDown( bool AbstractButton::setDown(
@ -149,8 +151,11 @@ bool AbstractButton::setDown(
} }
void AbstractButton::updateCursor() { void AbstractButton::updateCursor() {
auto pointerCursor = _enablePointerCursor && (_state & StateFlag::Over); const auto pointerCursor = _enablePointerCursor && isOver();
setCursor(pointerCursor ? style::cur_pointer : style::cur_default); if (_pointerCursor != pointerCursor) {
_pointerCursor = pointerCursor;
setCursor(_pointerCursor ? style::cur_pointer : style::cur_default);
}
} }
void AbstractButton::setDisabled(bool disabled) { void AbstractButton::setDisabled(bool disabled) {

View file

@ -16,22 +16,29 @@ class AbstractButton : public RpWidget {
public: public:
AbstractButton(QWidget *parent); AbstractButton(QWidget *parent);
Qt::KeyboardModifiers clickModifiers() const { [[nodiscard]] Qt::KeyboardModifiers clickModifiers() const {
return _modifiers; return _modifiers;
} }
void setDisabled(bool disabled = true); void setDisabled(bool disabled = true);
virtual void clearState(); virtual void clearState();
bool isOver() const { [[nodiscard]] bool isOver() const {
return _state & StateFlag::Over; return _state & StateFlag::Over;
} }
bool isDown() const { [[nodiscard]] bool isDown() const {
return _state & StateFlag::Down; return _state & StateFlag::Down;
} }
bool isDisabled() const { [[nodiscard]] bool isDisabled() const {
return _state & StateFlag::Disabled; return _state & StateFlag::Disabled;
} }
void setSynteticOver(bool over) {
setOver(over, StateChangeSource::ByPress);
}
void setSynteticDown(bool down, Qt::MouseButton button = Qt::LeftButton) {
setDown(down, StateChangeSource::ByPress, {}, button);
}
void setPointerCursor(bool enablePointerCursor); void setPointerCursor(bool enablePointerCursor);
void setAcceptBoth(bool acceptBoth = true); void setAcceptBoth(bool acceptBoth = true);
@ -95,9 +102,10 @@ private:
State _state = StateFlag::None; State _state = StateFlag::None;
bool _acceptBoth = false;
Qt::KeyboardModifiers _modifiers; Qt::KeyboardModifiers _modifiers;
bool _enablePointerCursor = true; bool _enablePointerCursor = true;
bool _pointerCursor = false;
bool _acceptBoth = false;
Fn<void()> _clickedCallback; Fn<void()> _clickedCallback;

400
ui/animated_icon.cpp Normal file
View file

@ -0,0 +1,400 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/animated_icon.h"
#include "ui/image/image_prepare.h"
#include "ui/style/style_core.h"
#include "ui/effects/frame_generator.h"
#include <QtGui/QPainter>
#include <crl/crl_async.h>
#include <crl/crl_semaphore.h>
#include <crl/crl_on_main.h>
namespace Ui {
namespace {
constexpr auto kDefaultDuration = crl::time(800);
} // namespace
struct AnimatedIcon::Frame {
FrameGenerator::Frame generated;
QImage resizedImage;
int index = 0;
};
class AnimatedIcon::Impl final : public std::enable_shared_from_this<Impl> {
public:
explicit Impl(base::weak_ptr<AnimatedIcon> weak);
void prepareFromAsync(
FnMut<std::unique_ptr<FrameGenerator>()> factory,
QSize sizeOverride);
void waitTillPrepared() const;
[[nodiscard]] bool valid() const;
[[nodiscard]] QSize size() const;
[[nodiscard]] int framesCount() const;
[[nodiscard]] double frameRate() const;
[[nodiscard]] Frame &frame();
[[nodiscard]] const Frame &frame() const;
[[nodiscard]] crl::time animationDuration() const;
void moveToFrame(int frame, QSize updatedDesiredSize);
private:
enum class PreloadState {
None,
Preloading,
Ready,
};
// Called from crl::async.
void renderPreloadFrame();
std::unique_ptr<FrameGenerator> _generator;
Frame _current;
QSize _desiredSize;
std::atomic<PreloadState> _preloadState = PreloadState::None;
Frame _preloaded; // Changed on main or async depending on _preloadState.
QSize _preloadImageSize;
base::weak_ptr<AnimatedIcon> _weak;
int _framesCount = 0;
double _frameRate = 0.;
mutable crl::semaphore _semaphore;
mutable bool _ready = false;
};
AnimatedIcon::Impl::Impl(base::weak_ptr<AnimatedIcon> weak)
: _weak(weak) {
}
void AnimatedIcon::Impl::prepareFromAsync(
FnMut<std::unique_ptr<FrameGenerator>()> factory,
QSize sizeOverride) {
const auto guard = gsl::finally([&] { _semaphore.release(); });
if (!_weak) {
return;
}
auto generator = factory ? factory() : nullptr;
if (!generator || !_weak) {
return;
}
_framesCount = generator->count();
_frameRate = generator->rate();
_current.generated = generator->renderNext(QImage(), sizeOverride);
if (_current.generated.image.isNull()) {
return;
}
_generator = std::move(generator);
_desiredSize = sizeOverride.isEmpty()
? style::ConvertScale(_current.generated.image.size())
: sizeOverride;
}
void AnimatedIcon::Impl::waitTillPrepared() const {
if (!_ready) {
_semaphore.acquire();
_ready = true;
}
}
bool AnimatedIcon::Impl::valid() const {
waitTillPrepared();
return (_generator != nullptr);
}
QSize AnimatedIcon::Impl::size() const {
waitTillPrepared();
return _desiredSize;
}
int AnimatedIcon::Impl::framesCount() const {
waitTillPrepared();
return _framesCount;
}
double AnimatedIcon::Impl::frameRate() const {
waitTillPrepared();
return _frameRate;
}
AnimatedIcon::Frame &AnimatedIcon::Impl::frame() {
waitTillPrepared();
return _current;
}
const AnimatedIcon::Frame &AnimatedIcon::Impl::frame() const {
waitTillPrepared();
return _current;
}
crl::time AnimatedIcon::Impl::animationDuration() const {
waitTillPrepared();
const auto rate = _generator ? _generator->rate() : 0.;
const auto frames = _generator ? _generator->count() : 0;
return (frames && rate >= 1.)
? crl::time(base::SafeRound(frames / rate * 1000.))
: 0;
}
void AnimatedIcon::Impl::moveToFrame(int frame, QSize updatedDesiredSize) {
waitTillPrepared();
const auto state = _preloadState.load();
const auto shown = _current.index;
if (!updatedDesiredSize.isEmpty()) {
_desiredSize = updatedDesiredSize;
}
const auto desiredImageSize = _desiredSize * style::DevicePixelRatio();
if (!_generator
|| state == PreloadState::Preloading
|| (shown == frame
&& (_current.generated.image.size() == desiredImageSize))) {
return;
} else if (state == PreloadState::Ready) {
if (_preloaded.index == frame
&& (shown != frame
|| _preloaded.generated.image.size() == desiredImageSize)) {
std::swap(_current, _preloaded);
if (_current.generated.image.size() == desiredImageSize) {
return;
}
} else if ((shown < _preloaded.index && _preloaded.index < frame)
|| (shown > _preloaded.index && _preloaded.index > frame)) {
std::swap(_current, _preloaded);
}
}
_preloadImageSize = desiredImageSize;
_preloaded.index = frame;
_preloadState = PreloadState::Preloading;
crl::async([guard = shared_from_this()] {
guard->renderPreloadFrame();
});
}
void AnimatedIcon::Impl::renderPreloadFrame() {
if (!_weak) {
return;
}
if (_preloaded.index == 0) {
_generator->jumpToStart();
}
_preloaded.generated = (_preloaded.index && _preloaded.index == _current.index)
? _generator->renderCurrent(
std::move(_preloaded.generated.image),
_preloadImageSize)
: _generator->renderNext(
std::move(_preloaded.generated.image),
_preloadImageSize);
_preloaded.resizedImage = QImage();
_preloadState = PreloadState::Ready;
crl::on_main(_weak, [=] {
_weak->frameJumpFinished();
});
}
AnimatedIcon::AnimatedIcon(AnimatedIconDescriptor &&descriptor)
: _impl(std::make_shared<Impl>(base::make_weak(this)))
, _colorized(descriptor.colorized) {
crl::async([
impl = _impl,
factory = std::move(descriptor.generator),
sizeOverride = descriptor.sizeOverride
]() mutable {
impl->prepareFromAsync(std::move(factory), sizeOverride);
});
}
void AnimatedIcon::wait() const {
_impl->waitTillPrepared();
}
bool AnimatedIcon::valid() const {
return _impl->valid();
}
int AnimatedIcon::frameIndex() const {
return _impl->frame().index;
}
int AnimatedIcon::framesCount() const {
return _impl->framesCount();
}
double AnimatedIcon::frameRate() const {
return _impl->frameRate();
}
QImage AnimatedIcon::frame(const QColor &textColor) const {
return frame(textColor, QSize(), nullptr).image;
}
QImage AnimatedIcon::notColorizedFrame() const {
return notColorizedFrame(QSize(), nullptr).image;
}
AnimatedIcon::ResizedFrame AnimatedIcon::frame(
const QColor &textColor,
QSize desiredSize,
Fn<void()> updateWithPerfect) const {
auto result = notColorizedFrame(
desiredSize,
std::move(updateWithPerfect));
if (_colorized) {
auto &image = result.image;
style::colorizeImage(image, textColor, &image, {}, {}, true);
}
return result;
}
AnimatedIcon::ResizedFrame AnimatedIcon::notColorizedFrame(
QSize desiredSize,
Fn<void()> updateWithPerfect) const {
auto &frame = _impl->frame();
preloadNextFrame(crl::now(), &frame, desiredSize);
const auto desired = size() * style::DevicePixelRatio();
if (frame.generated.image.isNull()) {
return { frame.generated.image };
} else if (frame.generated.image.size() == desired) {
return { frame.generated.image };
} else if (frame.resizedImage.size() != desired) {
frame.resizedImage = frame.generated.image.scaled(
desired,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
if (updateWithPerfect) {
_repaint = std::move(updateWithPerfect);
}
return { frame.resizedImage, true };
}
int AnimatedIcon::width() const {
return size().width();
}
int AnimatedIcon::height() const {
return size().height();
}
QSize AnimatedIcon::size() const {
return _impl->size();
}
void AnimatedIcon::paint(QPainter &p, int x, int y) {
auto &frame = _impl->frame();
preloadNextFrame(crl::now(), &frame);
if (frame.generated.image.isNull()) {
return;
}
const auto rect = QRect{ QPoint(x, y), size() };
p.drawImage(rect, frame.generated.image);
}
void AnimatedIcon::paintInCenter(QPainter &p, QRect rect) {
const auto my = size();
paint(
p,
rect.x() + (rect.width() - my.width()) / 2,
rect.y() + (rect.height() - my.height()) / 2);
}
void AnimatedIcon::animate(Fn<void()> update) {
if (framesCount() != 1 && !anim::Disabled()) {
jumpToStart(std::move(update));
_animationDuration = _impl->animationDuration();
_animationCurrentStart = _animationStarted = crl::now();
continueAnimation(_animationCurrentStart);
}
}
void AnimatedIcon::continueAnimation(crl::time now) {
const auto callback = [=](float64 value) {
if (anim::Disabled()) {
return;
}
const auto elapsed = int(value);
const auto now = _animationStartTime + elapsed;
if (!_animationDuration && elapsed > kDefaultDuration / 2) {
auto animation = std::move(_animation);
continueAnimation(now);
}
preloadNextFrame(now);
if (_repaint) _repaint();
};
const auto duration = _animationDuration
? _animationDuration
: kDefaultDuration;
_animationStartTime = now;
_animation.start(callback, 0., 1. * duration, duration);
}
void AnimatedIcon::jumpToStart(Fn<void()> update) {
_repaint = std::move(update);
_animation.stop();
_animationCurrentIndex = 0;
_impl->moveToFrame(0, QSize());
}
void AnimatedIcon::frameJumpFinished() {
if (_repaint && !animating()) {
_repaint();
_repaint = nullptr;
}
}
int AnimatedIcon::wantedFrameIndex(
crl::time now,
const Frame *resolvedCurrent) const {
const auto frame = resolvedCurrent ? resolvedCurrent : &_impl->frame();
if (frame->index == _animationCurrentIndex + 1) {
++_animationCurrentIndex;
_animationCurrentStart = _animationNextStart;
}
if (!_animation.animating()) {
return _animationCurrentIndex;
}
if (frame->index == _animationCurrentIndex) {
const auto duration = frame->generated.duration;
const auto next = _animationCurrentStart + duration;
if (frame->generated.last) {
_animation.stop();
if (_repaint) _repaint();
return _animationCurrentIndex;
} else if (now < next) {
return _animationCurrentIndex;
}
_animationNextStart = next;
return _animationCurrentIndex + 1;
}
Assert(!_animationCurrentIndex);
return 0;
}
void AnimatedIcon::preloadNextFrame(
crl::time now,
const Frame *resolvedCurrent,
QSize updatedDesiredSize) const {
_impl->moveToFrame(
wantedFrameIndex(now, resolvedCurrent),
updatedDesiredSize);
}
bool AnimatedIcon::animating() const {
return _animation.animating();
}
std::unique_ptr<AnimatedIcon> MakeAnimatedIcon(
AnimatedIconDescriptor &&descriptor) {
return std::make_unique<AnimatedIcon>(std::move(descriptor));
}
} // namespace Lottie

97
ui/animated_icon.h Normal file
View file

@ -0,0 +1,97 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "ui/style/style_core_types.h"
#include "ui/effects/animations.h"
#include "base/weak_ptr.h"
#include <crl/crl_time.h>
#include <QtCore/QByteArray>
#include <optional>
namespace Ui {
class FrameGenerator;
struct AnimatedIconDescriptor {
FnMut<std::unique_ptr<FrameGenerator>()> generator;
QSize sizeOverride;
bool colorized = false;
};
class AnimatedIcon final : public base::has_weak_ptr {
public:
explicit AnimatedIcon(AnimatedIconDescriptor &&descriptor);
AnimatedIcon(const AnimatedIcon &other) = delete;
AnimatedIcon &operator=(const AnimatedIcon &other) = delete;
AnimatedIcon(AnimatedIcon &&other) = delete; // _animation captures this.
AnimatedIcon &operator=(AnimatedIcon &&other) = delete;
[[nodiscard]] bool valid() const;
[[nodiscard]] int frameIndex() const;
[[nodiscard]] int framesCount() const;
[[nodiscard]] double frameRate() const;
[[nodiscard]] QImage frame(const QColor &textColor) const;
[[nodiscard]] QImage notColorizedFrame() const;
[[nodiscard]] int width() const;
[[nodiscard]] int height() const;
[[nodiscard]] QSize size() const;
struct ResizedFrame {
QImage image;
bool scaled = false;
};
[[nodiscard]] ResizedFrame frame(
const QColor &textColor,
QSize desiredSize,
Fn<void()> updateWithPerfect) const;
[[nodiscard]] ResizedFrame notColorizedFrame(
QSize desiredSize,
Fn<void()> updateWithPerfect) const;
void animate(Fn<void()> update);
void jumpToStart(Fn<void()> update);
void paint(QPainter &p, int x, int y);
void paintInCenter(QPainter &p, QRect rect);
[[nodiscard]] bool animating() const;
private:
struct Frame;
class Impl;
friend class Impl;
void wait() const;
[[nodiscard]] int wantedFrameIndex(
crl::time now,
const Frame *resolvedCurrent = nullptr) const;
void preloadNextFrame(
crl::time now,
const Frame *resolvedCurrent = nullptr,
QSize updatedDesiredSize = QSize()) const;
void frameJumpFinished();
void continueAnimation(crl::time now);
std::shared_ptr<Impl> _impl;
crl::time _animationStartTime = 0;
crl::time _animationStarted = 0;
mutable Animations::Simple _animation;
mutable Fn<void()> _repaint;
mutable crl::time _animationDuration = 0;
mutable crl::time _animationCurrentStart = 0;
mutable crl::time _animationNextStart = 0;
mutable int _animationCurrentIndex = 0;
bool _colorized = false;
};
[[nodiscard]] std::unique_ptr<AnimatedIcon> MakeAnimatedIcon(
AnimatedIconDescriptor &&descriptor);
} // namespace Ui

View file

@ -9,31 +9,47 @@ using "ui/colors.palette";
TextPalette { TextPalette {
linkFg: color; linkFg: color;
monoFg: color; monoFg: color;
spoilerFg: color;
selectBg: color; selectBg: color;
selectFg: color; selectFg: color;
selectLinkFg: color; selectLinkFg: color;
selectMonoFg: color; selectMonoFg: color;
selectSpoilerFg: color;
selectOverlay: color; selectOverlay: color;
linkAlwaysActive: int; linkAlwaysActive: bool;
spoilerBg: color; }
spoilerActiveBg: color;
spoilerActiveFg: color; QuoteStyle {
padding: margins;
verticalSkip: pixels;
header: pixels;
headerPosition: point;
icon: icon;
iconPosition: point;
outline: pixels;
outlineShift: pixels;
radius: pixels;
scrollable: bool;
} }
TextStyle { TextStyle {
font: font; font: font;
linkFont: font; linkUnderline: int;
linkFontOver: font;
lineHeight: pixels; lineHeight: pixels;
blockquote: QuoteStyle;
pre: QuoteStyle;
} }
kLinkUnderlineNever: 0;
kLinkUnderlineActive: 1;
kLinkUnderlineAlways: 2;
fsize: 13px; fsize: 13px;
normalFont: font(fsize); normalFont: font(fsize);
semiboldFont: font(fsize semibold); semiboldFont: font(fsize semibold);
boxFontSize: 14px; boxFontSize: 14px;
boxTextFont: font(boxFontSize); boxTextFont: font(boxFontSize);
emojiImgSize: 18px; // exceptional value for retina
emojiSize: 18px; emojiSize: 18px;
emojiPadding: 1px; emojiPadding: 1px;
@ -42,29 +58,27 @@ lineWidth: 1px;
defaultTextPalette: TextPalette { defaultTextPalette: TextPalette {
linkFg: windowActiveTextFg; linkFg: windowActiveTextFg;
monoFg: msgInMonoFg; monoFg: msgInMonoFg;
spoilerFg: msgInDateFg;
selectBg: msgInBgSelected; selectBg: msgInBgSelected;
selectFg: transparent; // use painter current pen instead selectFg: transparent; // use painter current pen instead
selectLinkFg: historyLinkInFgSelected; selectLinkFg: historyLinkInFgSelected;
selectMonoFg: msgInMonoFgSelected; selectMonoFg: msgInMonoFgSelected;
selectSpoilerFg: msgInDateFgSelected;
selectOverlay: msgSelectOverlay; selectOverlay: msgSelectOverlay;
spoilerBg: msgInDateFg; }
spoilerActiveBg: msgInDateFg; defaultQuoteStyle: QuoteStyle {
spoilerActiveFg: msgInBg;
} }
defaultTextStyle: TextStyle { defaultTextStyle: TextStyle {
font: normalFont; font: normalFont;
linkFont: normalFont; linkUnderline: kLinkUnderlineActive;
linkFontOver: font(fsize underline);
lineHeight: 0px; lineHeight: 0px;
blockquote: defaultQuoteStyle;
pre: defaultQuoteStyle;
} }
semiboldTextStyle: TextStyle(defaultTextStyle) { semiboldTextStyle: TextStyle(defaultTextStyle) {
font: semiboldFont; font: semiboldFont;
linkFont: semiboldFont;
linkFontOver: font(fsize semibold underline);
} }
shadowToggleDuration: 200;
slideDuration: 240; slideDuration: 240;
slideShift: 100px; slideShift: 100px;
slideShadow: icon {{ "slide_shadow", slideFadeOutShadowFg }}; slideShadow: icon {{ "slide_shadow", slideFadeOutShadowFg }};
@ -74,15 +88,13 @@ fadeWrapDuration: 200;
linkCropLimit: 360px; linkCropLimit: 360px;
linkFont: normalFont; linkFont: normalFont;
linkOverFont: font(fsize underline); linkFontOver: font(fsize underline);
roundRadiusLarge: 6px; roundRadiusLarge: 6px;
roundRadiusSmall: 3px; roundRadiusSmall: 3px;
dateRadius: roundRadiusLarge; dateRadius: roundRadiusLarge;
setLittleSkip: 9px;
noContactsHeight: 100px; noContactsHeight: 100px;
noContactsFont: font(fsize); noContactsFont: font(fsize);
noContactsColor: windowSubTextFg; noContactsColor: windowSubTextFg;
@ -90,170 +102,8 @@ noContactsColor: windowSubTextFg;
activeFadeInDuration: 500; activeFadeInDuration: 500;
activeFadeOutDuration: 3000; 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;
spoilerBg: msgInDateFg;
spoilerActiveBg: msgInDateFg;
spoilerActiveFg: msgInBg;
}
inTextPaletteSelected: TextPalette(inTextPalette) {
linkFg: historyLinkInFgSelected;
monoFg: msgInMonoFgSelected;
}
outTextPalette: TextPalette(defaultTextPalette) {
linkFg: historyLinkOutFg;
monoFg: msgOutMonoFg;
selectBg: msgOutBgSelected;
selectFg: historyTextOutFgSelected;
selectLinkFg: historyLinkOutFgSelected;
selectMonoFg: msgOutMonoFgSelected;
selectOverlay: msgSelectOverlay;
spoilerBg: msgOutDateFg;
spoilerActiveBg: msgOutDateFg;
spoilerActiveFg: msgOutBg;
}
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 }}; smallCloseIcon: icon {{ "simple_close", smallCloseIconFg }};
smallCloseIconOver: icon {{ "simple_close", smallCloseIconFgOver }}; smallCloseIconOver: icon {{ "simple_close", smallCloseIconFgOver }};
dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }};
emojiTextFont: font(15px);
emojiReplaceWidth: 52px;
emojiReplaceHeight: 56px;
emojiReplaceInnerHeight: 42px;
emojiReplacePadding: 14px;
dragFont: font(27px semibold);
dragSubfont: font(19px semibold);
dragColor: windowSubTextFg;
dragDropColor: windowActiveTextFg;
dragMargin: margins(0px, 10px, 0px, 10px);
dragPadding: margins(20px, 10px, 20px, 10px);
dragHeight: 72px;
radialSize: size(50px, 50px); radialSize: size(50px, 50px);
radialLine: 3px; radialLine: 3px;
@ -261,40 +111,13 @@ radialDuration: 350;
radialPeriod: 3000; radialPeriod: 3000;
locationSize: size(320px, 240px); 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;
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; transparentPlaceholderSize: 4px;
defaultVerticalListSkip: 6px;
shakeShift: 4px;
shakeDuration: 300;
// floating badge colors // floating badge colors
roundedFg: radialFg; roundedFg: radialFg;
roundedBg: radialBg; // closest to #00000066 roundedBg: radialBg; // closest to #00000066

View file

@ -64,11 +64,13 @@ QString UrlClickHandler::EncodeForOpening(const QString &originalUrl) {
? QString::fromUtf8(good.toEncoded()) ? QString::fromUtf8(good.toEncoded())
: originalUrl; : originalUrl;
static const auto RegExp = QRegularExpression(
QStringLiteral("^[a-zA-Z]+:"));
if (!result.isEmpty() if (!result.isEmpty()
&& !QRegularExpression( && !RegExp.match(result).hasMatch()) {
QStringLiteral("^[a-zA-Z]+:")).match(result).hasMatch()) {
// No protocol. // No protocol.
return QStringLiteral("http://") + result; return QStringLiteral("https://") + result;
} }
return result; return result;
} }

View file

@ -28,7 +28,6 @@ public:
} }
protected: protected:
virtual QString url() const = 0;
virtual QString readable() const; virtual QString readable() const;
bool _fullDisplayed; bool _fullDisplayed;
@ -39,6 +38,10 @@ class UrlClickHandler : public TextClickHandler {
public: public:
UrlClickHandler(const QString &url, bool fullDisplayed = true); UrlClickHandler(const QString &url, bool fullDisplayed = true);
[[nodiscard]] QString originalUrl() const {
return _originalUrl;
}
QString copyToClipboardContextItemText() const override; QString copyToClipboardContextItemText() const override;
QString dragText() const override { QString dragText() const override {
@ -64,9 +67,6 @@ public:
[[nodiscard]] static QString ShowEncoded(const QString &url); [[nodiscard]] static QString ShowEncoded(const QString &url);
protected: protected:
[[nodiscard]] QString originalUrl() const {
return _originalUrl;
}
QString url() const override { QString url() const override {
return EncodeForOpening(_originalUrl); return EncodeForOpening(_originalUrl);
} }

View file

@ -0,0 +1,71 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/cached_special_layer_shadow_corners.h"
#include "ui/effects/ripple_animation.h"
#include "styles/style_layers.h"
namespace Ui {
namespace {
[[nodiscard]] std::array<QImage, 4> PrepareSpecialLayerShadowCorners() {
const auto &st = st::boxRoundShadow;
const auto s = QSize(
st::boxRadius * 2 + st.extend.left(),
st::boxRadius * 2 + st.extend.right());
const auto mask = Ui::RippleAnimation::MaskByDrawer(s, false, [&](
QPainter &p) {
p.drawRoundedRect(QRect(QPoint(), s), st::boxRadius, st::boxRadius);
});
struct Corner {
const style::icon &icon;
QPoint factor;
};
const auto corners = std::vector<Corner>{
Corner{ st.topLeft, QPoint(1, 1) },
Corner{ st.bottomLeft, QPoint(1, 0) },
Corner{ st.topRight, QPoint(0, 1) },
Corner{ st.bottomRight, QPoint(0, 0) },
};
const auto processCorner = [&](int i) {
const auto &corner = corners[i];
auto result = QImage(
corner.icon.size() * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(style::DevicePixelRatio());
result.fill(Qt::transparent);
{
QPainter p(&result);
corner.icon.paint(p, 0, 0, corner.icon.width());
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.drawImage(
corner.icon.width() * corner.factor.x()
- mask.width() / style::DevicePixelRatio() / 2,
corner.icon.height() * corner.factor.y()
- mask.height() / style::DevicePixelRatio() / 2,
mask);
}
return result;
};
return std::array<QImage, 4>{ {
processCorner(0),
processCorner(1),
processCorner(2),
processCorner(3),
} };
}
} // namespace
const std::array<QImage, 4> &SpecialLayerShadowCorners() {
static const auto custom = PrepareSpecialLayerShadowCorners();
return custom;
}
} // namespace Ui

View file

@ -0,0 +1,13 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace Ui {
[[nodiscard]] const std::array<QImage, 4> &SpecialLayerShadowCorners();
} // namespace Ui

View file

@ -8,6 +8,7 @@
#include "base/algorithm.h" #include "base/algorithm.h"
#include "ui/text/text_entity.h" #include "ui/text/text_entity.h"
#include "ui/integration.h"
#include <QtCore/QPointer> #include <QtCore/QPointer>
@ -170,7 +171,9 @@ void ActivateClickHandler(
ClickContext context) { ClickContext context) {
crl::on_main(guard, [=, weak = std::weak_ptr<ClickHandler>(handler)] { crl::on_main(guard, [=, weak = std::weak_ptr<ClickHandler>(handler)] {
if (const auto strong = weak.lock()) { if (const auto strong = weak.lock()) {
strong->onClick(context); if (Ui::Integration::Instance().allowClickHandlerActivation(strong, context)) {
strong->onClick(context);
}
} }
}); });
} }

View file

@ -38,6 +38,11 @@ public:
virtual void onClick(ClickContext context) const = 0; virtual void onClick(ClickContext context) const = 0;
// Some sort of `id`, for text links contains urls.
virtual QString url() const {
return QString();
}
// What text to show in a tooltip when mouse is over that click handler as a link in Text. // What text to show in a tooltip when mouse is over that click handler as a link in Text.
virtual QString tooltip() const { virtual QString tooltip() const {
return QString(); return QString();
@ -109,16 +114,16 @@ protected:
}; };
class LambdaClickHandler : public ClickHandler { class GenericClickHandler : public ClickHandler {
public: public:
LambdaClickHandler(Fn<void()> handler) GenericClickHandler(Fn<void()> handler)
: _handler([handler = std::move(handler)](ClickContext) { handler(); }) { : _handler([handler = std::move(handler)](ClickContext) { handler(); }) {
} }
LambdaClickHandler(Fn<void(ClickContext)> handler) GenericClickHandler(Fn<void(ClickContext)> handler)
: _handler(std::move(handler)) { : _handler(std::move(handler)) {
} }
void onClick(ClickContext context) const override final { void onClick(ClickContext context) const override {
if (context.button == Qt::LeftButton && _handler) { if (_handler) {
_handler(context); _handler(context);
} }
} }
@ -128,6 +133,18 @@ private:
}; };
class LambdaClickHandler : public GenericClickHandler {
public:
using GenericClickHandler::GenericClickHandler;
void onClick(ClickContext context) const override final {
if (context.button == Qt::LeftButton) {
GenericClickHandler::onClick(std::move(context));
}
}
};
void ActivateClickHandler( void ActivateClickHandler(
not_null<QWidget*> guard, not_null<QWidget*> guard,
ClickHandlerPtr handler, ClickHandlerPtr handler,

View file

@ -53,9 +53,9 @@ attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
menuBg: windowBg; // default popup menu background menuBg: windowBg; // default popup menu background
menuBgOver: windowBgOver; // default popup menu item background with mouse over menuBgOver: windowBgOver; // default popup menu item background with mouse over
menuBgRipple: windowBgRipple; // default popup menu item ripple effect menuBgRipple: windowBgRipple; // default popup menu item ripple effect
menuIconFg: #a8a8a8; // default popup menu item icon (like main menu) menuIconFg: #999999; // default popup menu item icon (like main menu)
menuIconFgOver: #999999; // default popup menu item icon with mouse over menuIconFgOver: #8a8a8a; // default popup menu item icon with mouse over
menuSubmenuArrowFg: #666B72; // default popup menu submenu arrow icon (like in message field context menu in case of RTL system language) 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) 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) menuSeparatorFg: #f1f1f1; // default popup menu separator (like in message field context menu)
@ -161,7 +161,6 @@ callArrowMissedFg: #dd5b4a | boxTextFgError; // missed phone call arrow (in call
introBg: windowBg; // login background introBg: windowBg; // login background
introTitleFg: windowBoldFg; // login title text introTitleFg: windowBoldFg; // login title text
introDescriptionFg: windowSubTextFg; // login description 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) introCoverTopBg: #0f89d0; // intro gradient top (from)
introCoverBottomBg: #39b0f0; // intro gradient bottom (to) introCoverBottomBg: #39b0f0; // intro gradient bottom (to)
@ -230,9 +229,6 @@ dialogsScamFgActive: dialogsDraftFgActive; // chat list scam label for current (
dialogsRippleBg: windowBgRipple; // chat list background ripple effect dialogsRippleBg: windowBgRipple; // chat list background ripple effect
dialogsRippleBgActive: activeButtonBgRipple; // chat list background ripple effect for current (active) chat 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..) searchedBarBg: windowBgOver; // search results bar background (in chats list, contacts box..)
searchedBarFg: windowSubTextFgOver; // search results bar text (in chats list, contacts box..) searchedBarFg: windowSubTextFgOver; // search results bar text (in chats list, contacts box..)
@ -243,11 +239,13 @@ emojiPanBg: windowBg; // emoji panel background
emojiPanCategories: #f7f7f7 | windowBg; // emoji panel categories background emojiPanCategories: #f7f7f7 | windowBg; // emoji panel categories background
emojiPanHeaderFg: windowSubTextFg; // emoji panel section header text emojiPanHeaderFg: windowSubTextFg; // emoji panel section header text
emojiPanHeaderBg: #fffffff2 | emojiPanBg; // emoji panel section header background emojiPanHeaderBg: #fffffff2 | emojiPanBg; // emoji panel section header background
emojiIconFg: checkboxFg; // emoji category icon emojiIconFg: #999999; // emoji category icon
emojiIconFgActive: windowBgActive; // active emoji category icon emojiSubIconFgActive: #666666 | windowBoldFg; // active emoji subcategory icon
stickerPanDeleteBg: #000000ff; // delete X button background for custom sent stickers in stickers panel (legacy) 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) 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) stickerPreviewBg: #ffffffb0; // sticker and GIF preview background (when you press and hold on a sticker)
stickerPanPremium1: #5a99ff; // premium sticker pack icon gradient 1
stickerPanPremium2: #45b9f3; // premium sticker pack icon gradient 2
historyTextInFg: windowFg; // inbox message text historyTextInFg: windowFg; // inbox message text
historyTextInFgSelected: historyTextInFg; // inbox message selected text or text in a selected message historyTextInFgSelected: historyTextInFg; // inbox message selected text or text in a selected message
@ -283,32 +281,52 @@ historyForwardChooseFg: windowFgActive; // forwarding messages in a large window
historyPeer1NameFg: #c03d33; // red group member name historyPeer1NameFg: #c03d33; // red group member name
historyPeer1NameFgSelected: historyPeer1NameFg; // red group member name in a selected message historyPeer1NameFgSelected: historyPeer1NameFg; // red group member name in a selected message
historyPeer1UserpicBg: #e17076; // red userpic background historyPeer1UserpicBg: #ff845e; // red userpic background
historyPeer2NameFg: #4fad2d; // green group member name historyPeer2NameFg: #4fad2d; // green group member name
historyPeer2NameFgSelected: historyPeer2NameFg; // green group member name in a selected message historyPeer2NameFgSelected: historyPeer2NameFg; // green group member name in a selected message
historyPeer2UserpicBg: #7bc862; // green userpic background historyPeer2UserpicBg: #9ad164; // green userpic background
historyPeer3NameFg: #d09306; // yellow group member name historyPeer3NameFg: #d09306; // yellow group member name (actually unused)
historyPeer3NameFgSelected: historyPeer3NameFg; // yellow group member name in a selected message historyPeer3NameFgSelected: historyPeer3NameFg; // yellow group member name in a selected message (actually unused)
historyPeer3UserpicBg: #e5ca77; // yellow userpic background historyPeer3UserpicBg: #e5ca77; // yellow userpic background (actually unused)
historyPeer4NameFg: windowActiveTextFg; // blue group member name historyPeer4NameFg: windowActiveTextFg; // blue group member name
historyPeer4NameFgSelected: historyPeer4NameFg; // blue group member name in a selected message historyPeer4NameFgSelected: historyPeer4NameFg; // blue group member name in a selected message
historyPeer4UserpicBg: #65aadd; // blue userpic background historyPeer4UserpicBg: #5caffa; // blue userpic background
historyPeer5NameFg: #8544d6; // purple group member name historyPeer5NameFg: #8544d6; // purple group member name
historyPeer5NameFgSelected: historyPeer5NameFg; // purple group member name in a selected message historyPeer5NameFgSelected: historyPeer5NameFg; // purple group member name in a selected message
historyPeer5UserpicBg: #a695e7; // purple userpic background historyPeer5UserpicBg: #b694f9; // purple userpic background
historyPeer6NameFg: #cd4073; // pink group member name historyPeer6NameFg: #cd4073; // pink group member name
historyPeer6NameFgSelected: historyPeer6NameFg; // pink group member name in a selected message historyPeer6NameFgSelected: historyPeer6NameFg; // pink group member name in a selected message
historyPeer6UserpicBg: #ee7aae; // pink userpic background historyPeer6UserpicBg: #ff8aac; // pink userpic background
historyPeer7NameFg: #2996ad; // sea group member name historyPeer7NameFg: #2996ad; // sea group member name
historyPeer7NameFgSelected: historyPeer7NameFg; // sea group member name in a selected message historyPeer7NameFgSelected: historyPeer7NameFg; // sea group member name in a selected message
historyPeer7UserpicBg: #6ec9cb; // sea userpic background historyPeer7UserpicBg: #5bcbe3; // sea userpic background
historyPeer8NameFg: #ce671b; // orange group member name historyPeer8NameFg: #ce671b; // orange group member name
historyPeer8NameFgSelected: historyPeer8NameFg; // orange group member name in a selected message historyPeer8NameFgSelected: historyPeer8NameFg; // orange group member name in a selected message
historyPeer8UserpicBg: #faa774; // orange userpic background historyPeer8UserpicBg: #febb5b; // orange userpic background
historyPeerUserpicFg: windowFgActive; // default userpic initials historyPeerUserpicFg: windowFgActive; // default userpic initials
historyPeerSavedMessagesBg: historyPeer4UserpicBg; // saved messages userpic background historyPeerSavedMessagesBg: historyPeer4UserpicBg; // saved messages userpic background
historyPeerArchiveUserpicBg: dialogsUnreadBgMuted; // archive folder userpic background historyPeerArchiveUserpicBg: dialogsUnreadBgMuted; // archive folder userpic background
historyPeer1UserpicBg2: #d45246 | historyPeer1UserpicBg; // the second red userpic background
historyPeer2UserpicBg2: #46ba43 | historyPeer2UserpicBg; // the second green userpic background
historyPeer3UserpicBg2: #e5ca77 | historyPeer3UserpicBg; // the second yellow userpic background (actually unused)
historyPeer4UserpicBg2: #408acf | historyPeer4UserpicBg; // the second blue userpic background
historyPeer5UserpicBg2: #6c61df | historyPeer5UserpicBg; // the second purple userpic background
historyPeer6UserpicBg2: #d95574 | historyPeer6UserpicBg; // the second pink userpic background
historyPeer7UserpicBg2: #359ad4 | historyPeer7UserpicBg; // the second sea userpic background
historyPeer8UserpicBg2: #f68136 | historyPeer8UserpicBg; // the second orange userpic background
historyPeerSavedMessagesBg2: historyPeer4UserpicBg2; // the second saved messages userpic background
settingsIconBg1: #f06964; // red settings icon background
settingsIconBg2: #6dc534; // green settings icon background
settingsIconBg3: #ed9f20; // light-orange settings icon background
settingsIconBg4: #56b3f5; // light-blue settings icon background
settingsIconBg5: #7595ff; // dark-blue settings icon background
settingsIconBg6: #b580e2; // purple settings icon background
settingsIconBg8: #f2925b; // dark-orange settings icon background
settingsIconBgArchive: #9da2b0; // archive main menu icon background
settingsIconFg: #ffffff; // settings icon shape
// Some values are marked as (adjusted), it means they're adjusted by // Some values are marked as (adjusted), it means they're adjusted by
// hue and saturation of the average background color if user chooses // hue and saturation of the average background color if user chooses
// some other (not bundled to this color theme) background. If the // some other (not bundled to this color theme) background. If the
@ -361,7 +379,6 @@ msgFileInBg: windowBgActive; // inbox audio file download circle background
msgFileInBgOver: #4eade3; // inbox audio file download circle background with mouse over msgFileInBgOver: #4eade3; // inbox audio file download circle background with mouse over
msgFileInBgSelected: #51a3d3; // inbox selected audio file download circle background msgFileInBgSelected: #51a3d3; // inbox selected audio file download circle background
msgFileOutBg: #5fbe67; // outbox audio file download circle background msgFileOutBg: #5fbe67; // outbox audio file download circle background
msgFileOutBgOver: #5ab15f; // outbox audio file download circle background with mouse over
msgFileOutBgSelected: #50ac9b; // outbox selected audio file download circle background msgFileOutBgSelected: #50ac9b; // outbox selected audio file download circle background
msgFile1Bg: #72b1df; // blue shared links / files without image square thumbnail msgFile1Bg: #72b1df; // blue shared links / files without image square thumbnail
@ -418,11 +435,8 @@ youtubePlayIconBg: #e83131c8; // youtube play icon background (when a link to a
youtubePlayIconFg: windowFgActive; // youtube play icon arrow (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) 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) 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) toastBg: #2c3033e5; // 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) toastFg: #ffffff; // 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) historyToDownBg: windowBg; // arrow button background (to scroll to the end of the viewed chat)
historyToDownBgOver: windowBgOver; // arrow button background with mouse over historyToDownBgOver: windowBgOver; // arrow button background with mouse over
@ -455,7 +469,6 @@ mapPointDot: #ffffff; // geo location marker point
overviewCheckBg: #00000040; // shared media / files / links checkbox background for not selected rows when some rows are selected 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 overviewCheckBgActive: windowBgActive; // shared media / files / links checkbox background for selected rows
overviewCheckBorder: windowBg; // shared media round checkbox border 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 overviewCheckFgActive: windowBg; // shared files / links checkbox icon for selected rows
overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill for selected rows overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill for selected rows
@ -464,9 +477,6 @@ profileStatusFgOver: #7c99b2; // group members list in group profile user last s
profileVerifiedCheckBg: windowBgActive; // profile verified check icon background profileVerifiedCheckBg: windowBgActive; // profile verified check icon background
profileVerifiedCheckFg: windowFgActive; // profile verified check icon tick profileVerifiedCheckFg: windowFgActive; // profile verified check icon tick
profileAdminStartFg: windowBgActive; // group members list creator star icon 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 // settings
notificationsBoxMonitorFg: windowFg; // custom notifications settings box monitor color notificationsBoxMonitorFg: windowFg; // custom notifications settings box monitor color
@ -477,12 +487,8 @@ notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; // custom notifications se
notificationSampleTextFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample text placeholder notificationSampleTextFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample text placeholder
notificationSampleNameFg: #939393 | windowSubTextFg; // custom notifications settings box small sample name 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 mainMenuBg: windowBg; // main menu background
mainMenuCoverBg: dialogsBgActive; // main menu top cover background mainMenuCoverBg: dialogsBgActive; // main menu top cover background
mainMenuCoverFg: windowFgActive; // main menu top cover text
mainMenuCloudFg: activeButtonFg; // main menu top cover saved messages / archive button icon mainMenuCloudFg: activeButtonFg; // main menu top cover saved messages / archive button icon
mainMenuCloudBg: #2785bf | activeButtonBgRipple; // main menu top cover saved messages / archive button background mainMenuCloudBg: #2785bf | activeButtonBgRipple; // main menu top cover saved messages / archive button background
@ -509,10 +515,10 @@ mediaviewMenuFg: windowFgActive; // context menu item text
mediaviewBg: #222222eb; // Media Viewer background mediaviewBg: #222222eb; // Media Viewer background
mediaviewVideoBg: imageBg; // Media Viewer background when viewing a video in full screen mediaviewVideoBg: imageBg; // Media Viewer background when viewing a video in full screen
mediaviewControlBg: #0000003c; // controls background (like next photo / previous photo) mediaviewControlBg: #0000003c; // controls background (like next photo / previous photo)
mediaviewControlFg: windowFgActive; // controls icon (like next photo / previous photo) mediaviewControlFg: #ffffff; // controls icon (like next photo / previous photo)
mediaviewCaptionBg: #11111180; // caption text background (when viewing photo with caption) mediaviewCaptionBg: #11111180; // caption text background (when viewing photo with caption)
mediaviewCaptionFg: mediaviewControlFg; // caption text mediaviewCaptionFg: mediaviewControlFg; // caption text
mediaviewTextLinkFg: #91d9ff; // caption text link mediaviewTextLinkFg: #4db8ff; // caption text link
mediaviewSaveMsgBg: toastBg; // save to file toast message background in Media Viewer mediaviewSaveMsgBg: toastBg; // save to file toast message background in Media Viewer
mediaviewSaveMsgFg: toastFg; // save to file toast message text mediaviewSaveMsgFg: toastFg; // save to file toast message text
@ -527,7 +533,6 @@ mediaviewPlaybackIconRipple: #ffffff14; // video playback controls ripple effect
mediaviewPipControlsFg: #ffffffd9; // picture-in-picture controls mediaviewPipControlsFg: #ffffffd9; // picture-in-picture controls
mediaviewPipControlsFgOver: #ffffff; // picture-in-picture controls with mouse over mediaviewPipControlsFgOver: #ffffff; // picture-in-picture controls with mouse over
mediaviewPipFade: #00000095; // picture-in-picture gradient fade controls background
mediaviewPipPlaybackActive: #ffffffda; // picture-in-picture playback progress already played part mediaviewPipPlaybackActive: #ffffffda; // picture-in-picture playback progress already played part
mediaviewPipPlaybackInactive: #ffffff26; // picture-in-picture playback progress upcoming (not played yet) part mediaviewPipPlaybackInactive: #ffffff26; // picture-in-picture playback progress upcoming (not played yet) part
@ -589,7 +594,6 @@ groupCallVideoSubTextFg: #ffffffc0; // group call additional text over video
callBarBg: dialogsBgActive; // active phone call bar background callBarBg: dialogsBgActive; // active phone call bar background
callBarMuteRipple: dialogsRippleBgActive; // active phone call bar mute and hangup button ripple effect callBarMuteRipple: dialogsRippleBgActive; // active phone call bar mute and hangup button ripple effect
callBarBgMuted: #8f8f8f | dialogsUnreadBgMuted; // phone call bar with muted mic background 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 callBarFg: dialogsNameFgActive; // phone call bar text and icons
importantTooltipBg: toastBg; // group call important tooltip background color importantTooltipBg: toastBg; // group call important tooltip background color
@ -641,9 +645,31 @@ sideBarBadgeFg: #ffffff; // filters side bar badge text
songCoverOverlayFg: #00000066; // song cover overlay songCoverOverlayFg: #00000066; // song cover overlay
photoEditorBg: boxTitleAdditionalFg; // photo editor background
photoEditorItemBaseHandleFg: #3ccaef; // photo editor handle circle photoEditorItemBaseHandleFg: #3ccaef; // photo editor handle circle
premiumButtonBg1: #55a5ff; // upgrade to premium button gradient 1
premiumButtonBg2: #a767ff; // upgrade to premium button gradient 2
premiumButtonBg3: #db5c9d; // upgrade to premium button gradient 3
premiumButtonFg: #ffffff; // upgrade to premium button text
premiumIconBg1: #f38926; // icon in premium settings gradient 1
premiumIconBg2: #e44456; // icon in premium settings gradient 2
premiumIconBg3: #4acd43; // icon in premium settings gradient 3
statisticsChartInactive: #e2eef999; // inactive area in footer of statistic charts
statisticsChartActive: #baccd9d8; // sides in footer of statistic charts
statisticsChartLineBlue: #327fe5; // represents blue color on statistical charts
statisticsChartLineGreen: #61c752; // represents green color on statistical charts
statisticsChartLineRed: #e05356; // represents red color on statistical charts
statisticsChartLineGolden: #eba52d; // represents golden color on statistical charts
statisticsChartLineLightblue: #58a8ed; // represents lightblue color on statistical charts
statisticsChartLineLightgreen: #8fcf39; // represents lightgreen color on statistical charts
statisticsChartLineOrange: #f28c39; // represents orange color on statistical charts
statisticsChartLineIndigo: #7f79f3; // represents indigo color on statistical charts
statisticsChartLinePurple: #9f79e8; // represents purple color on statistical charts
statisticsChartLineCyan: #40d0ca; // represents cyan color on statistical charts
// kotatogram // kotatogram
ktgTopBarBg: topBarBg; // Kotatogram: top bar background ktgTopBarBg: topBarBg; // Kotatogram: top bar background
ktgTopBarNameFg: dialogsNameFg; // Kotatogram: top bar name text ktgTopBarNameFg: dialogsNameFg; // Kotatogram: top bar name text

View file

@ -7,32 +7,80 @@
#include "ui/delayed_activation.h" #include "ui/delayed_activation.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "base/call_delayed.h"
#include "base/invoke_queued.h"
#include "base/platform/base_platform_info.h"
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
#include "base/platform/linux/base_linux_xcb_utilities.h"
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtWidgets/QApplication>
namespace Ui { namespace Ui {
namespace { namespace {
auto Paused = false; constexpr auto kPreventTimeout = crl::time(100);
bool Paused/* = false*/;
bool Attempted/* = false*/;
auto Window = QPointer<QWidget>(); auto Window = QPointer<QWidget>();
bool Unpause(bool force = false) {
if (force || Attempted) {
Attempted = false;
Paused = false;
return true;
}
return false;
}
} // namespace } // namespace
void ActivateWindow(not_null<QWidget*> widget) {
const auto window = widget->window();
window->raise();
window->activateWindow();
ActivateWindowDelayed(window);
}
void ActivateWindowDelayed(not_null<QWidget*> widget) { void ActivateWindowDelayed(not_null<QWidget*> widget) {
if (Paused) { if (Paused) {
Attempted = true;
return; return;
} else if (std::exchange(Window, widget.get())) { } else if (std::exchange(Window, widget.get())) {
return; return;
} }
crl::on_main(Window, [=] { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
if (const auto widget = base::take(Window)) { const auto focusAncestor = [&] {
if (const auto window = widget->window()) { const auto focusWidget = QApplication::focusWidget();
if (!window->isHidden()) { if (!focusWidget || !widget->window()) {
window->raise(); return false;
window->activateWindow();
}
}
} }
return widget->window()->isAncestorOf(focusWidget);
}();
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
crl::on_main(Window, [=] {
const auto widget = base::take(Window);
if (!widget) {
return;
}
const auto window = widget->window();
if (!window || window->isHidden()) {
return;
}
window->raise();
window->activateWindow();
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
if (::Platform::IsX11() && focusAncestor) {
xcb_set_input_focus(
base::Platform::XCB::GetConnectionFromQt(),
XCB_INPUT_FOCUS_PARENT,
window->winId(),
XCB_CURRENT_TIME);
}
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
}); });
} }
@ -40,7 +88,22 @@ void PreventDelayedActivation() {
Window = nullptr; Window = nullptr;
Paused = true; Paused = true;
PostponeCall([] { PostponeCall([] {
Paused = false; if (Unpause()) {
return;
}
InvokeQueued(qApp, [] {
if (Unpause()) {
return;
}
crl::on_main([] {
if (Unpause()) {
return;
}
base::call_delayed(kPreventTimeout, [] {
Unpause(true);
});
});
});
}); });
} }

View file

@ -8,6 +8,7 @@
namespace Ui { namespace Ui {
void ActivateWindow(not_null<QWidget*> widget);
void ActivateWindowDelayed(not_null<QWidget*> widget); void ActivateWindowDelayed(not_null<QWidget*> widget);
void PreventDelayedActivation(); void PreventDelayedActivation();

32
ui/dpr/dpr_icon.cpp Normal file
View file

@ -0,0 +1,32 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/dpr/dpr_icon.h"
namespace dpr {
QImage IconFrame(
const style::icon &icon,
const QColor &color,
double ratio) {
const auto scale = style::Scale() * ratio;
const auto use = (scale > 200. || style::DevicePixelRatio() > 2)
? (300 / style::DevicePixelRatio())
: (scale > 100.)
? (200 / style::DevicePixelRatio())
: (100 / style::DevicePixelRatio());
auto image = icon.instance(color, use);
image.setDevicePixelRatio(1.);
const auto desired = icon.size() * ratio;
return (image.size() == desired)
? image
: image.scaled(
desired,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
} // namespace dpr

20
ui/dpr/dpr_icon.h Normal file
View file

@ -0,0 +1,20 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "ui/style/style_core.h"
class QImage;
namespace dpr {
[[nodiscard]] QImage IconFrame(
const style::icon &icon,
const QColor &color,
double ratio);
} // namespace dpr

50
ui/dpr/dpr_image.h Normal file
View file

@ -0,0 +1,50 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <rpl/details/callable.h>
#include <QtGui/QImage>
#include <QtGui/QPainter>
namespace dpr {
// Validate(_cache, devicePixelRatioF(), size, [&](QPainter &p, QSize size) {
// ... paint using p ...
// }, (_cacheKey != cacheKey()), Qt::transparent);
template <typename Generator>
void Validate(
QImage &image,
double ratio,
QSize size,
Generator &&generator,
bool force,
std::optional<QColor> fill = {},
bool setResultRatio = true) {
size *= ratio;
const auto sizeChanged = (image.size() != size);
if (sizeChanged || force) {
if (sizeChanged) {
image = QImage(size, QImage::Format_ARGB32_Premultiplied);
}
if (fill) {
image.fill(*fill);
}
image.setDevicePixelRatio(1.);
auto p = QPainter(&image);
using namespace rpl::details;
if constexpr (is_callable_plain_v<Generator, QPainter&, QSize>) {
generator(p, size);
} else {
generator(p);
}
}
image.setDevicePixelRatio(setResultRatio ? ratio : 1.);
}
} // namespace dpr

View file

@ -0,0 +1,63 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/dragging_scroll_manager.h"
#include "base/timer.h"
namespace Ui {
namespace {
// 37px per 15ms while select-by-drag.
inline constexpr auto kMaxScrollSpeed = 37;
} // namespace
DraggingScrollManager::DraggingScrollManager() = default;
void DraggingScrollManager::scrollByTimer() {
const auto d = (_delta > 0)
? std::min(_delta * 3 / 20 + 1, kMaxScrollSpeed)
: std::max(_delta * 3 / 20 - 1, -kMaxScrollSpeed);
_scrolls.fire_copy(d);
}
void DraggingScrollManager::checkDeltaScroll(int delta) {
_delta = delta;
if (_delta) {
if (!_timer) {
_timer = std::make_unique<base::Timer>([=] { scrollByTimer(); });
}
_timer->callEach(15);
} else {
cancel();
}
}
void DraggingScrollManager::checkDeltaScroll(
const QPoint &point,
int top,
int bottom) {
const auto diff = point.y() - top;
checkDeltaScroll((diff < 0)
? diff
: (point.y() >= bottom)
? (point.y() - bottom + 1)
: 0);
}
void DraggingScrollManager::cancel() {
if (_timer) {
_timer->cancel();
_timer = nullptr;
}
}
rpl::producer<int> DraggingScrollManager::scrolls() const {
return _scrolls.events();
}
} // namespace Ui

View file

@ -0,0 +1,34 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace base {
class Timer;
} // namespace base
namespace Ui {
class DraggingScrollManager final {
public:
DraggingScrollManager();
void checkDeltaScroll(int delta);
void checkDeltaScroll(const QPoint &point, int top, int bottom);
void cancel();
[[nodiscard]] rpl::producer<int> scrolls() const;
private:
void scrollByTimer();
std::unique_ptr<base::Timer> _timer;
int _delta = 0;
rpl::event_stream<int> _scrolls;
};
} // namespace Ui

19
ui/dynamic_image.h Normal file
View file

@ -0,0 +1,19 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace Ui {
class DynamicImage {
public:
virtual ~DynamicImage() = default;
[[nodiscard]] virtual QImage image(int size) = 0;
virtual void subscribeToUpdates(Fn<void()> callback) = 0;
};
} // namespace Ui

View file

@ -19,50 +19,113 @@ int SlowMultiplierMinusOne/* = 0*/;
} // namespace } // namespace
transition linear = [](const float64 &delta, const float64 &dt) { transition linear = [](const float64 &delta, const float64 &dt) {
return delta * dt; Expects(!std::isnan(delta));
Expects(!std::isnan(dt));
const auto result = delta * dt;
Ensures(!std::isnan(result));
return result;
}; };
transition sineInOut = [](const float64 &delta, const float64 &dt) { transition sineInOut = [](const float64 &delta, const float64 &dt) {
return -(delta / 2) * (cos(M_PI * dt) - 1); Expects(!std::isnan(delta));
Expects(!std::isnan(dt));
const auto result = -(delta / 2) * (cos(M_PI * dt) - 1);
Ensures(!std::isnan(result));
return result;
}; };
transition halfSine = [](const float64 &delta, const float64 &dt) { transition halfSine = [](const float64 &delta, const float64 &dt) {
return delta * sin(M_PI * dt / 2); Expects(!std::isnan(delta));
Expects(!std::isnan(dt));
const auto result = delta * sin(M_PI * dt / 2);
Ensures(!std::isnan(result));
return result;
}; };
transition easeOutBack = [](const float64 &delta, const float64 &dt) { transition easeOutBack = [](const float64 &delta, const float64 &dt) {
Expects(!std::isnan(delta));
Expects(!std::isnan(dt));
static constexpr auto s = 1.70158; static constexpr auto s = 1.70158;
const float64 t = dt - 1; const auto t = dt - 1;
return delta * (t * t * ((s + 1) * t + s) + 1); Assert(!std::isnan(t));
const auto result = delta * (t * t * ((s + 1) * t + s) + 1);
Ensures(!std::isnan(result));
return result;
}; };
transition easeInCirc = [](const float64 &delta, const float64 &dt) { transition easeInCirc = [](const float64 &delta, const float64 &dt) {
return -delta * (sqrt(1 - dt * dt) - 1); Expects(!std::isnan(delta));
Expects(!std::isnan(dt));
const auto result = -delta * (sqrt(1 - dt * dt) - 1);
Ensures(!std::isnan(result));
return result;
}; };
transition easeOutCirc = [](const float64 &delta, const float64 &dt) { transition easeOutCirc = [](const float64 &delta, const float64 &dt) {
const float64 t = dt - 1; Expects(!std::isnan(delta));
return delta * sqrt(1 - t * t); Expects(!std::isnan(dt));
const auto t = dt - 1;
Assert(!std::isnan(t));
const auto result = delta * sqrt(1 - t * t);
Ensures(!std::isnan(result));
return result;
}; };
transition easeInCubic = [](const float64 &delta, const float64 &dt) { transition easeInCubic = [](const float64 &delta, const float64 &dt) {
return delta * dt * dt * dt; const auto result = delta * dt * dt * dt;
Ensures(!std::isnan(result));
return result;
}; };
transition easeOutCubic = [](const float64 &delta, const float64 &dt) { transition easeOutCubic = [](const float64 &delta, const float64 &dt) {
const float64 t = dt - 1; Expects(!std::isnan(delta));
return delta * (t * t * t + 1); Expects(!std::isnan(dt));
const auto t = dt - 1;
Assert(!std::isnan(t));
const auto result = delta * (t * t * t + 1);
Ensures(!std::isnan(result));
return result;
}; };
transition easeInQuint = [](const float64 &delta, const float64 &dt) { transition easeInQuint = [](const float64 &delta, const float64 &dt) {
const float64 t2 = dt * dt; Expects(!std::isnan(delta));
return delta * t2 * t2 * dt; Expects(!std::isnan(dt));
const auto t2 = dt * dt;
Assert(!std::isnan(t2));
const auto result = delta * t2 * t2 * dt;
Ensures(!std::isnan(result));
return result;
}; };
transition easeOutQuint = [](const float64 &delta, const float64 &dt) { transition easeOutQuint = [](const float64 &delta, const float64 &dt) {
const float64 t = dt - 1, t2 = t * t; Expects(!std::isnan(delta));
return delta * (t2 * t2 * t + 1); Expects(!std::isnan(dt));
const auto t = dt - 1, t2 = t * t;
Assert(!std::isnan(t));
Assert(!std::isnan(t2));
const auto result = delta * (t2 * t2 * t + 1);
Ensures(!std::isnan(result));
return result;
}; };
rpl::producer<bool> Disables() { rpl::producer<bool> Disables() {
@ -90,7 +153,7 @@ void SetSlowMultiplier(int multiplier) {
void DrawStaticLoading( void DrawStaticLoading(
QPainter &p, QPainter &p,
QRectF rect, QRectF rect,
int stroke, float64 stroke,
QPen pen, QPen pen,
QBrush brush) { QBrush brush) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);

View file

@ -14,16 +14,21 @@
namespace anim { namespace anim {
enum class type { enum class type : uchar {
normal, normal,
instant, instant,
}; };
enum class activation { enum class activation : uchar {
normal, normal,
background, background,
}; };
enum class repeat : uchar {
loop,
once,
};
using transition = Fn<float64(float64 delta, float64 dt)>; using transition = Fn<float64(float64 delta, float64 dt)>;
extern transition linear; extern transition linear;
@ -94,12 +99,12 @@ private:
}; };
TG_FORCE_INLINE float64 interpolateF(int a, int b, float64 b_ratio) { TG_FORCE_INLINE float64 interpolateToF(int a, int b, float64 b_ratio) {
return a + float64(b - a) * b_ratio; return a + float64(b - a) * b_ratio;
} }
TG_FORCE_INLINE int interpolate(int a, int b, float64 b_ratio) { TG_FORCE_INLINE int interpolate(int a, int b, float64 b_ratio) {
return base::SafeRound(interpolateF(a, b, b_ratio)); return base::SafeRound(interpolateToF(a, b, b_ratio));
} }
#ifdef ARCH_CPU_32_BITS #ifdef ARCH_CPU_32_BITS
@ -317,6 +322,11 @@ 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; return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a;
} }
TG_FORCE_INLINE QColor with_alpha(QColor color, float64 alpha) {
color.setAlphaF(color.alphaF() * alpha);
return color;
}
template <int N> template <int N>
QPainterPath interpolate(QPointF (&from)[N], QPointF (&to)[N], float64 k) { QPainterPath interpolate(QPointF (&from)[N], QPointF (&to)[N], float64 k) {
static_assert(N > 1, "Wrong points count in path!"); static_assert(N > 1, "Wrong points count in path!");
@ -357,7 +367,7 @@ void SetSlowMultiplier(int multiplier); // 1 - default, 10 - slow x10.
void DrawStaticLoading( void DrawStaticLoading(
QPainter &p, QPainter &p,
QRectF rect, QRectF rect,
int stroke, float64 stroke,
QPen pen, QPen pen,
QBrush brush = Qt::NoBrush); QBrush brush = Qt::NoBrush);

View file

@ -0,0 +1,26 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace anim {
TG_FORCE_INLINE float64 interpolateF(float a, float b, float64 b_ratio) {
return a + float64(b - a) * b_ratio;
};
TG_FORCE_INLINE QRectF interpolatedRectF(
const QRectF &r1,
const QRectF &r2,
float64 ratio) {
return QRectF(
interpolateF(r1.x(), r2.x(), ratio),
interpolateF(r1.y(), r2.y(), ratio),
interpolateF(r1.width(), r2.width(), ratio),
interpolateF(r1.height(), r2.height(), ratio));
}
} // namespace anim

View file

@ -10,6 +10,7 @@
#include <crl/crl_time.h> #include <crl/crl_time.h>
#include <rpl/lifetime.h> #include <rpl/lifetime.h>
#include <QtCore/QObject>
namespace Ui { namespace Ui {
namespace Animations { namespace Animations {
@ -337,14 +338,24 @@ inline void Simple::start(
that = _data.get(), that = _data.get(),
callback = Prepare(std::forward<Callback>(callback)) callback = Prepare(std::forward<Callback>(callback))
](crl::time now) { ](crl::time now) {
Assert(!std::isnan(double(now - that->animation.started())));
const auto time = anim::Disabled() const auto time = anim::Disabled()
? that->duration ? that->duration
: (now - that->animation.started()); : (now - that->animation.started());
Assert(!std::isnan(time));
Assert(!std::isnan(that->delta));
Assert(!std::isnan(that->duration));
const auto finished = (time >= that->duration); const auto finished = (time >= that->duration);
Assert(finished || that->duration > 0);
const auto progressRatio = finished ? 1. : time / that->duration;
Assert(!std::isnan(progressRatio));
const auto progress = finished const auto progress = finished
? that->delta ? that->delta
: that->transition(that->delta, time / that->duration); : that->transition(that->delta, progressRatio);
Assert(!std::isnan(that->from));
Assert(!std::isnan(progress));
that->value = that->from + progress; that->value = that->from + progress;
Assert(!std::isnan(that->value));
if (finished) { if (finished) {
that->animation.stop(); that->animation.stop();
@ -401,7 +412,12 @@ inline bool Simple::animating() const {
} }
TG_FORCE_INLINE float64 Simple::value(float64 final) const { TG_FORCE_INLINE float64 Simple::value(float64 final) const {
return animating() ? _data->value : final; if (animating()) {
Assert(!std::isnan(_data->value));
return _data->value;
}
Assert(!std::isnan(final));
return final;
} }
inline void Simple::startPrepared( inline void Simple::startPrepared(

View file

@ -99,7 +99,7 @@ void transformLoadingCross(float64 loading, std::array<QPointF, kPointCount> &po
} // namespace } // namespace
void CrossAnimation::paintStaticLoading( void CrossAnimation::paintStaticLoading(
Painter &p, QPainter &p,
const style::CrossAnimation &st, const style::CrossAnimation &st,
style::color color, style::color color,
int x, int x,
@ -110,7 +110,7 @@ void CrossAnimation::paintStaticLoading(
} }
void CrossAnimation::paint( void CrossAnimation::paint(
Painter &p, QPainter &p,
const style::CrossAnimation &st, const style::CrossAnimation &st,
style::color color, style::color color,
int x, int x,
@ -120,14 +120,17 @@ void CrossAnimation::paint(
float64 loading) { float64 loading) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
auto sqrt2 = sqrt(2.); const auto stroke = style::ConvertScaleExact(st.stroke);
auto deleteScale = shown + st.minScale * (1. - shown);
auto deleteSkip = (deleteScale * st.skip) + (1. - deleteScale) * (st.size / 2); const auto deleteScale = shown + st.minScale * (1. - shown);
auto deleteLeft = style::rtlpoint(x + deleteSkip, 0, outerWidth).x() + 0.; const auto deleteSkip = (deleteScale * st.skip)
auto deleteTop = y + deleteSkip + 0.; + (1. - deleteScale) * (st.size / 2);
auto deleteWidth = st.size - 2 * deleteSkip; const auto deleteLeft = 0.
auto deleteHeight = st.size - 2 * deleteSkip; + style::rtlpoint(x + deleteSkip, 0, outerWidth).x();
auto deleteStroke = st.stroke / sqrt2; const auto deleteTop = y + deleteSkip + 0.;
const auto deleteWidth = st.size - 2 * deleteSkip;
const auto deleteHeight = st.size - 2 * deleteSkip;
const auto deleteStroke = stroke / M_SQRT2;
std::array<QPointF, kPointCount> pathDelete = { { std::array<QPointF, kPointCount> pathDelete = { {
{ deleteLeft, deleteTop + deleteStroke }, { deleteLeft, deleteTop + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop }, { deleteLeft + deleteStroke, deleteTop },
@ -176,10 +179,10 @@ void CrossAnimation::paint(
p.fillPath(path, color); p.fillPath(path, color);
} }
if (loadingArcLength != 0) { if (loadingArcLength != 0) {
auto roundSkip = (st.size * (1 - sqrt2) + 2 * sqrt2 * deleteSkip + st.stroke) / 2; auto roundSkip = (st.size * (1 - M_SQRT2) + 2 * M_SQRT2 * deleteSkip + stroke) / 2;
auto roundPart = QRectF(x + roundSkip, y + roundSkip, st.size - 2 * roundSkip, st.size - 2 * roundSkip); auto roundPart = QRectF(x + roundSkip, y + roundSkip, st.size - 2 * roundSkip, st.size - 2 * roundSkip);
if (staticLoading) { if (staticLoading) {
anim::DrawStaticLoading(p, roundPart, st.stroke, color); anim::DrawStaticLoading(p, roundPart, stroke, color);
} else { } else {
auto loadingArcStart = kFullArcLength / 8; auto loadingArcStart = kFullArcLength / 8;
if (shown < 1.) { if (shown < 1.) {
@ -192,7 +195,7 @@ void CrossAnimation::paint(
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
auto pen = color->p; auto pen = color->p;
pen.setWidthF(st.stroke); pen.setWidthF(stroke);
pen.setCapStyle(Qt::RoundCap); pen.setCapStyle(Qt::RoundCap);
p.setPen(pen); p.setPen(pen);
p.drawArc(roundPart, loadingArcStart, loadingArcLength); p.drawArc(roundPart, loadingArcStart, loadingArcLength);

View file

@ -15,7 +15,7 @@ namespace Ui {
class CrossAnimation { class CrossAnimation {
public: public:
static void paint( static void paint(
Painter &p, QPainter &p,
const style::CrossAnimation &st, const style::CrossAnimation &st,
style::color color, style::color color,
int x, int x,
@ -24,7 +24,7 @@ public:
float64 shown, float64 shown,
float64 loading = 0.); float64 loading = 0.);
static void paintStaticLoading( static void paintStaticLoading(
Painter &p, QPainter &p,
const style::CrossAnimation &st, const style::CrossAnimation &st,
style::color color, style::color color,
int x, int x,

View file

@ -36,7 +36,7 @@ CrossLineAnimation::CrossLineAnimation(
} }
void CrossLineAnimation::paint( void CrossLineAnimation::paint(
Painter &p, QPainter &p,
QPoint position, QPoint position,
float64 progress, float64 progress,
std::optional<QColor> colorOverride) { std::optional<QColor> colorOverride) {
@ -44,7 +44,7 @@ void CrossLineAnimation::paint(
} }
void CrossLineAnimation::paint( void CrossLineAnimation::paint(
Painter &p, QPainter &p,
int left, int left,
int top, int top,
float64 progress, float64 progress,
@ -86,7 +86,7 @@ void CrossLineAnimation::fillFrame(
topLine.setLength(topLine.length() * progress); topLine.setLength(topLine.length() * progress);
auto bottomLine = topLine.translated(0, _strokePen.widthF() + 1); auto bottomLine = topLine.translated(0, _strokePen.widthF() + 1);
Painter q(&_frame); auto q = QPainter(&_frame);
PainterHighQualityEnabler hq(q); PainterHighQualityEnabler hq(q);
const auto colorize = ((colorOverride && colorOverride->alpha() != 255) const auto colorize = ((colorOverride && colorOverride->alpha() != 255)
|| (!colorOverride && _st.fg->c.alpha() != 255)); || (!colorOverride && _st.fg->c.alpha() != 255));

View file

@ -8,8 +8,6 @@
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
class Painter;
namespace Ui { namespace Ui {
class CrossLineAnimation { class CrossLineAnimation {
@ -20,12 +18,12 @@ public:
float angle = 315); float angle = 315);
void paint( void paint(
Painter &p, QPainter &p,
QPoint position, QPoint position,
float64 progress, float64 progress,
std::optional<QColor> colorOverride = std::nullopt); std::optional<QColor> colorOverride = std::nullopt);
void paint( void paint(
Painter &p, QPainter &p,
int left, int left,
int top, int top,
float64 progress, float64 progress,

View file

@ -0,0 +1,79 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/effects/frame_generator.h"
#include "ui/image/image_prepare.h"
namespace Ui {
ImageFrameGenerator::ImageFrameGenerator(const QByteArray &bytes)
: _bytes(bytes) {
}
ImageFrameGenerator::ImageFrameGenerator(const QImage &image)
: _image(image) {
}
int ImageFrameGenerator::count() {
return 1;
}
double ImageFrameGenerator::rate() {
return 1.;
}
FrameGenerator::Frame ImageFrameGenerator::renderNext(
QImage storage,
QSize size,
Qt::AspectRatioMode mode) {
return renderCurrent(std::move(storage), size, mode);
}
FrameGenerator::Frame ImageFrameGenerator::renderCurrent(
QImage storage,
QSize size,
Qt::AspectRatioMode mode) {
if (_image.isNull() && !_bytes.isEmpty()) {
_image = Images::Read({
.content = _bytes,
}).image;
_bytes = QByteArray();
}
if (_image.isNull()) {
return {};
}
auto scaled = _image.scaled(
size,
mode,
Qt::SmoothTransformation
).convertToFormat(QImage::Format_ARGB32_Premultiplied);
if (scaled.size() == size) {
return { .image = std::move(scaled) };
}
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
const auto skipx = (size.width() - scaled.width()) / 2;
const auto skipy = (size.height() - scaled.height()) / 2;
const auto srcPerLine = scaled.bytesPerLine();
const auto dstPerLine = result.bytesPerLine();
const auto lineBytes = scaled.width() * 4;
auto src = scaled.constBits();
auto dst = result.bits() + (skipx * 4) + (skipy * srcPerLine);
for (auto y = 0, height = scaled.height(); y != height; ++y) {
memcpy(dst, src, lineBytes);
src += srcPerLine;
dst += dstPerLine;
}
return { .image = std::move(result), .last = true };
}
void ImageFrameGenerator::jumpToStart() {
}
} // namespace Ui

View file

@ -0,0 +1,69 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <QtGui/QImage>
#include <crl/crl_time.h>
namespace Ui {
class FrameGenerator {
public:
virtual ~FrameGenerator() = default;
// 0 means unknown.
[[nodiscard]] virtual int count() = 0;
// 0. means unknown.
[[nodiscard]] virtual double rate() = 0;
struct Frame {
crl::time duration = 0;
QImage image;
bool last = false;
};
[[nodiscard]] virtual Frame renderNext(
QImage storage,
QSize size,
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) = 0;
[[nodiscard]] virtual Frame renderCurrent(
QImage storage,
QSize size,
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) = 0;
virtual void jumpToStart() = 0;
};
class ImageFrameGenerator final : public Ui::FrameGenerator {
public:
explicit ImageFrameGenerator(const QByteArray &bytes);
explicit ImageFrameGenerator(const QImage &image);
int count() override;
double rate() override;
Frame renderNext(
QImage storage,
QSize size,
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) override;
Frame renderCurrent(
QImage storage,
QSize size,
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) override;
void jumpToStart() override;
private:
QByteArray _bytes;
QImage _image;
};
[[nodiscard]] bool GoodStorageForFrame(const QImage &storage, QSize size);
[[nodiscard]] QImage CreateFrameStorage(QSize size);
} // namespace Ui

30
ui/effects/gradient.cpp Normal file
View file

@ -0,0 +1,30 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/effects/gradient.h"
namespace anim {
QColor gradient_color_at(const QGradientStops & stops, float64 ratio) {
for (auto i = 1; i < stops.size(); i++) {
const auto currentPoint = stops[i].first;
const auto previousPoint = stops[i - 1].first;
if ((ratio <= currentPoint) && (ratio >= previousPoint)) {
return anim::color(
stops[i - 1].second,
stops[i].second,
(ratio - previousPoint) / (currentPoint - previousPoint));
}
}
return QColor();
}
QColor gradient_color_at(const QGradient &gradient, float64 ratio) {
return gradient_color_at(gradient.stops(), ratio);
}
} // namespace anim

View file

@ -14,6 +14,14 @@
namespace anim { namespace anim {
[[nodiscard]] QColor gradient_color_at(
const QGradientStops &stops,
float64 ratio);
[[nodiscard]] QColor gradient_color_at(
const QGradient &gradient,
float64 ratio);
struct gradient_colors { struct gradient_colors {
explicit gradient_colors(QColor color) { explicit gradient_colors(QColor color) {
stops.push_back({ 0., color }); stops.push_back({ 0., color });

View file

@ -17,12 +17,21 @@ NumbersAnimation::NumbersAnimation(
const style::font &font, const style::font &font,
Fn<void()> animationCallback) Fn<void()> animationCallback)
: _font(font) : _font(font)
, _duration(st::slideWrapDuration)
, _animationCallback(std::move(animationCallback)) { , _animationCallback(std::move(animationCallback)) {
for (auto ch = '0'; ch != '9'; ++ch) { for (auto ch = '0'; ch != '9'; ++ch) {
accumulate_max(_digitWidth, _font->m.horizontalAdvance(ch)); accumulate_max(_digitWidth, _font->width(ch));
} }
} }
void NumbersAnimation::setDuration(int duration) {
_duration = duration;
}
void NumbersAnimation::setDisabledMonospace(bool value) {
_disabledMonospace = value;
}
void NumbersAnimation::setText(const QString &text, int value) { void NumbersAnimation::setText(const QString &text, int value) {
if (_a_ready.animating()) { if (_a_ready.animating()) {
_delayedText = text; _delayedText = text;
@ -67,7 +76,7 @@ void NumbersAnimation::realSetText(QString text, int value) {
digit.from = digit.to; digit.from = digit.to;
digit.fromWidth = digit.toWidth; digit.fromWidth = digit.toWidth;
digit.to = (newSize + i < size) ? QChar(0) : text[newSize + i - size]; digit.to = (newSize + i < size) ? QChar(0) : text[newSize + i - size];
digit.toWidth = digit.to.unicode() ? _font->m.horizontalAdvance(digit.to) : 0; digit.toWidth = digit.to.unicode() ? _font->width(digit.to) : 0;
if (digit.from != digit.to) { if (digit.from != digit.to) {
animating = true; animating = true;
} }
@ -75,14 +84,19 @@ void NumbersAnimation::realSetText(QString text, int value) {
--oldSize; --oldSize;
} }
} }
_fromWidth = oldSize * _digitWidth; if (_disabledMonospace) {
_toWidth = newSize * _digitWidth; _fromWidth = _toWidth;
_toWidth = _font->width(text);
} else {
_fromWidth = oldSize * _digitWidth;
_toWidth = newSize * _digitWidth;
}
if (animating) { if (animating) {
_a_ready.start( _a_ready.start(
[this] { animationCallback(); }, [this] { animationCallback(); },
0., 0.,
1., 1.,
st::slideWrapDuration); _duration);
} }
} }
@ -117,30 +131,37 @@ void NumbersAnimation::paint(QPainter &p, int x, int y, int outerWidth) {
QString singleChar('0'); QString singleChar('0');
if (style::RightToLeft()) x = outerWidth - x - width; if (style::RightToLeft()) x = outerWidth - x - width;
x += width - _digits.size() * _digitWidth; x += width
- (_disabledMonospace ? _toWidth : _digits.size() * _digitWidth);
auto fromTop = anim::interpolate(0, _font->height, progress) * (_growing ? 1 : -1); auto fromTop = anim::interpolate(0, _font->height, progress) * (_growing ? 1 : -1);
auto toTop = anim::interpolate(_font->height, 0, progress) * (_growing ? -1 : 1); auto toTop = anim::interpolate(_font->height, 0, progress) * (_growing ? -1 : 1);
for (auto i = 0; i != digitsCount; ++i) { for (auto i = 0; i != digitsCount; ++i) {
auto &digit = _digits[i]; auto &digit = _digits[i];
auto from = digit.from; auto from = digit.from;
auto to = digit.to; auto to = digit.to;
const auto toCharWidth = (!_disabledMonospace || to.isDigit())
? _digitWidth
: _font->width(to);
const auto fromCharWidth = (!_disabledMonospace || from.isDigit())
? _digitWidth
: _font->width(from);
if (from == to) { if (from == to) {
p.setOpacity(1.); p.setOpacity(1.);
singleChar[0] = from; singleChar[0] = from;
p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + _font->ascent, singleChar); p.drawText(x + (toCharWidth - digit.fromWidth) / 2, y + _font->ascent, singleChar);
} else { } else {
if (from.unicode()) { if (from.unicode()) {
p.setOpacity(1. - progress); p.setOpacity(1. - progress);
singleChar[0] = from; singleChar[0] = from;
p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + fromTop + _font->ascent, singleChar); p.drawText(x + (fromCharWidth - digit.fromWidth) / 2, y + fromTop + _font->ascent, singleChar);
} }
if (to.unicode()) { if (to.unicode()) {
p.setOpacity(progress); p.setOpacity(progress);
singleChar[0] = to; singleChar[0] = to;
p.drawText(x + (_digitWidth - digit.toWidth) / 2, y + toTop + _font->ascent, singleChar); p.drawText(x + (toCharWidth - digit.toWidth) / 2, y + toTop + _font->ascent, singleChar);
} }
} }
x += _digitWidth; x += std::max(toCharWidth, fromCharWidth);
} }
p.setOpacity(1.); p.setOpacity(1.);
} }

View file

@ -25,6 +25,8 @@ public:
_widthChangedCallback = std::move(callback); _widthChangedCallback = std::move(callback);
} }
void setText(const QString &text, int value); void setText(const QString &text, int value);
void setDuration(int duration);
void setDisabledMonospace(bool value);
void finishAnimating(); void finishAnimating();
void paint(QPainter &p, int x, int y, int outerWidth); void paint(QPainter &p, int x, int y, int outerWidth);
@ -44,6 +46,8 @@ private:
const style::font &_font; const style::font &_font;
int _duration;
QList<Digit> _digits; QList<Digit> _digits;
int _digitWidth = 0; int _digitWidth = 0;
@ -57,6 +61,8 @@ private:
int _value = 0; int _value = 0;
bool _growing = false; bool _growing = false;
bool _disabledMonospace = false;
Fn<void()> _animationCallback; Fn<void()> _animationCallback;
Fn<void()> _widthChangedCallback; Fn<void()> _widthChangedCallback;

View file

@ -361,30 +361,59 @@ void PanelAnimation::start() {
checkCorner(_bottomRight); checkCorner(_bottomRight);
} }
void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity) { auto PanelAnimation::computeState(float64 dt, float64 opacity) const
-> PaintState {
auto &transition = anim::easeOutCirc;
if (dt < _alphaDuration) {
opacity *= transition(1., dt / _alphaDuration);
}
const auto widthProgress = (_startWidth < 0 || dt >= _widthDuration)
? 1.
: transition(1., dt / _widthDuration);
const auto heightProgress = (_startHeight < 0 || dt >= _heightDuration)
? 1.
: transition(1., dt / _heightDuration);
const auto frameWidth = (widthProgress < 1.)
? anim::interpolate(_startWidth, _finalInnerWidth, widthProgress)
: _finalInnerWidth;
const auto frameHeight = (heightProgress < 1.)
? anim::interpolate(_startHeight, _finalInnerHeight, heightProgress)
: _finalInnerHeight;
const auto ratio = style::DevicePixelRatio();
return {
.opacity = opacity,
.widthProgress = widthProgress,
.heightProgress = heightProgress,
.fade = transition(1., dt),
.width = frameWidth / ratio,
.height = frameHeight / ratio,
};
}
auto PanelAnimation::paintFrame(
QPainter &p,
int x,
int y,
int outerWidth,
float64 dt,
float64 opacity)
-> PaintState {
Assert(started()); Assert(started());
Assert(dt >= 0.); Assert(dt >= 0.);
const auto pixelRatio = style::DevicePixelRatio(); const auto pixelRatio = style::DevicePixelRatio();
auto &transition = anim::easeOutCirc; const auto state = computeState(dt, opacity);
if (dt < _alphaDuration) opacity *= transition(1., dt / _alphaDuration); opacity = state.opacity;
_frameAlpha = anim::interpolate(1, 256, opacity); _frameAlpha = anim::interpolate(1, 256, opacity);
const auto frameWidth = state.width * pixelRatio;
auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration)); const auto frameHeight = state.height * pixelRatio;
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 frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth);
auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight); auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight);
auto frameRight = frameLeft + frameWidth; auto frameRight = frameLeft + frameWidth;
auto frameBottom = frameTop + frameHeight; auto frameBottom = frameTop + frameHeight;
auto fadeTop = (_fadeHeight > 0) ? std::clamp(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight; auto fadeTop = (_fadeHeight > 0) ? std::clamp(anim::interpolate(_startFadeTop, _finalInnerHeight, state.fade), 0, frameHeight) : frameHeight;
if (auto decrease = (fadeTop % pixelRatio)) { if (auto decrease = (fadeTop % pixelRatio)) {
fadeTop -= decrease; fadeTop -= decrease;
} }
@ -503,6 +532,8 @@ void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float
//} //}
p.drawImage(style::rtlpoint(x + (outerLeft / pixelRatio), y + (outerTop / pixelRatio), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop)); p.drawImage(style::rtlpoint(x + (outerLeft / pixelRatio), y + (outerTop / pixelRatio), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop));
return state;
} }
} // namespace Ui } // namespace Ui

View file

@ -79,11 +79,27 @@ public:
PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) { PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) {
} }
struct PaintState {
float64 opacity = 0.;
float64 widthProgress = 0.;
float64 heightProgress = 0.;
float64 fade = 0.;
int width = 0;
int height = 0;
};
void setFinalImage(QImage &&finalImage, QRect inner); void setFinalImage(QImage &&finalImage, QRect inner);
void setSkipShadow(bool skipShadow); void setSkipShadow(bool skipShadow);
void start(); void start();
void paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity); [[nodiscard]] PaintState computeState(float64 dt, float64 opacity) const;
PaintState paintFrame(
QPainter &p,
int x,
int y,
int outerWidth,
float64 dt,
float64 opacity);
private: private:
void setStartWidth(); void setStartWidth();

View file

@ -112,13 +112,25 @@ RadialState RadialAnimation::computeState() const {
return { _opacity, from, length }; return { _opacity, from, length };
} }
void InfiniteRadialAnimation::init() {
anim::Disables() | rpl::filter([=] {
return animating();
}) | rpl::start_with_next([=](bool disabled) {
if (!disabled && !_animation.animating()) {
_animation.start();
} else if (disabled && _animation.animating()) {
_animation.stop();
}
}, _lifetime);
}
void InfiniteRadialAnimation::start(crl::time skip) { void InfiniteRadialAnimation::start(crl::time skip) {
const auto now = crl::now(); if (!animating()) {
if (_workFinished <= now && (_workFinished || !_workStarted)) { const auto now = crl::now();
_workStarted = std::max(now + _st.sineDuration - skip, crl::time(1)); _workStarted = std::max(now + _st.sineDuration - skip, crl::time(1));
_workFinished = 0; _workFinished = 0;
} }
if (!_animation.animating()) { if (!anim::Disabled() && !_animation.animating()) {
_animation.start(); _animation.start();
} }
} }
@ -213,7 +225,7 @@ RadialState InfiniteRadialAnimation::computeState() {
const auto now = crl::now(); const auto now = crl::now();
const auto linear = kFullArcLength const auto linear = kFullArcLength
- int(((now * kFullArcLength) / _st.linearPeriod) % kFullArcLength); - int(((now * kFullArcLength) / _st.linearPeriod) % kFullArcLength);
if (!_workStarted || (_workFinished && _workFinished <= now)) { if (!animating()) {
const auto shown = 0.; const auto shown = 0.;
_animation.stop(); _animation.stop();
return { return {

View file

@ -73,7 +73,7 @@ public:
const style::InfiniteRadialAnimation &st); const style::InfiniteRadialAnimation &st);
[[nodiscard]] bool animating() const { [[nodiscard]] bool animating() const {
return _animation.animating(); return _workStarted && (!_workFinished || _workFinished > crl::now());
} }
void start(crl::time skip = 0); void start(crl::time skip = 0);
@ -101,10 +101,13 @@ public:
[[nodiscard]] RadialState computeState(); [[nodiscard]] RadialState computeState();
private: private:
void init();
const style::InfiniteRadialAnimation &_st; const style::InfiniteRadialAnimation &_st;
crl::time _workStarted = 0; crl::time _workStarted = 0;
crl::time _workFinished = 0; crl::time _workFinished = 0;
Ui::Animations::Basic _animation; Ui::Animations::Basic _animation;
rpl::lifetime _lifetime;
}; };
@ -114,6 +117,7 @@ inline InfiniteRadialAnimation::InfiniteRadialAnimation(
const style::InfiniteRadialAnimation &st) const style::InfiniteRadialAnimation &st)
: _st(st) : _st(st)
, _animation(std::forward<Callback>(callback)) { , _animation(std::forward<Callback>(callback)) {
init();
} }
} // namespace Ui } // namespace Ui

View file

@ -9,15 +9,28 @@
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/image/image_prepare.h"
#include "styles/style_widgets.h"
namespace Ui { namespace Ui {
class RippleAnimation::Ripple { class RippleAnimation::Ripple {
public: public:
Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update); Ripple(
Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn<void()> update); const style::RippleAnimation &st,
QPoint origin,
int startRadius,
const QPixmap &mask,
Fn<void()> update);
Ripple(
const style::RippleAnimation &st,
const QPixmap &mask,
Fn<void()> update);
void paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride); void paint(
QPainter &p,
const QPixmap &mask,
const QColor *colorOverride);
void stop(); void stop();
void unstop(); void unstop();
@ -43,7 +56,12 @@ private:
}; };
RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update) RippleAnimation::Ripple::Ripple(
const style::RippleAnimation &st,
QPoint origin,
int startRadius,
const QPixmap &mask,
Fn<void()> update)
: _st(st) : _st(st)
, _update(update) , _update(update)
, _origin(origin) , _origin(origin)
@ -59,7 +77,9 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin,
{ 0, _frame.height() / pixelRatio }, { 0, _frame.height() / pixelRatio },
}; };
for (auto point : points) { for (auto point : points) {
accumulate_max(_radiusTo, style::point::dotProduct(_origin - point, _origin - point)); accumulate_max(
_radiusTo,
style::point::dotProduct(_origin - point, _origin - point));
} }
_radiusTo = qRound(sqrt(_radiusTo)); _radiusTo = qRound(sqrt(_radiusTo));
@ -79,7 +99,10 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap
_hide.start(_update, 0., 1., _st.hideDuration); _hide.start(_update, 0., 1., _st.hideDuration);
} }
void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride) { void RippleAnimation::Ripple::paint(
QPainter &p,
const QPixmap &mask,
const QColor *colorOverride) {
auto opacity = _hide.value(_hiding ? 0. : 1.); auto opacity = _hide.value(_hiding ? 0. : 1.);
if (opacity == 0.) { if (opacity == 0.) {
return; return;
@ -92,9 +115,11 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QCol
Assert(!std::isnan(diff)); Assert(!std::isnan(diff));
const auto mult = diff * shown; const auto mult = diff * shown;
Assert(!std::isnan(mult)); Assert(!std::isnan(mult));
const auto interpolated = _radiusFrom + mult;//anim::interpolateF(_radiusFrom, _radiusTo, shown); const auto interpolated = _radiusFrom + mult;
//anim::interpolateF(_radiusFrom, _radiusTo, shown);
Assert(!std::isnan(interpolated)); Assert(!std::isnan(interpolated));
auto radius = int(base::SafeRound(interpolated));//anim::interpolate(_radiusFrom, _radiusTo, _show.value(1.)); auto radius = int(base::SafeRound(interpolated));
//anim::interpolate(_radiusFrom, _radiusTo, _show.value(1.));
_frame.fill(Qt::transparent); _frame.fill(Qt::transparent);
{ {
QPainter p(&_frame); QPainter p(&_frame);
@ -151,7 +176,10 @@ void RippleAnimation::Ripple::clearCache() {
_cache = QPixmap(); _cache = QPixmap();
} }
RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> callback) RippleAnimation::RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> callback)
: _st(st) : _st(st)
, _mask(PixmapFromImage(std::move(mask))) , _mask(PixmapFromImage(std::move(mask)))
, _update(callback) { , _update(callback) {
@ -160,7 +188,8 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask,
void RippleAnimation::add(QPoint origin, int startRadius) { void RippleAnimation::add(QPoint origin, int startRadius) {
lastStop(); lastStop();
_ripples.push_back(std::make_unique<Ripple>(_st, origin, startRadius, _mask, _update)); _ripples.push_back(
std::make_unique<Ripple>(_st, origin, startRadius, _mask, _update));
} }
void RippleAnimation::addFading() { void RippleAnimation::addFading() {
@ -195,7 +224,12 @@ void RippleAnimation::forceRepaint() {
} }
} }
void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride) { void RippleAnimation::paint(
QPainter &p,
int x,
int y,
int outerWidth,
const QColor *colorOverride) {
if (_ripples.empty()) { if (_ripples.empty()) {
return; return;
} }
@ -211,8 +245,13 @@ void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QCo
clearFinished(); clearFinished();
} }
QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer) { QImage RippleAnimation::MaskByDrawer(
auto result = QImage(size * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied); QSize size,
bool filled,
Fn<void(QPainter &p)> drawer) {
auto result = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(style::DevicePixelRatio()); result.setDevicePixelRatio(style::DevicePixelRatio());
result.fill(filled ? QColor(255, 255, 255) : Qt::transparent); result.fill(filled ? QColor(255, 255, 255) : Qt::transparent);
if (drawer) { if (drawer) {
@ -226,18 +265,46 @@ QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn<void(QPainter &
return result; return result;
} }
QImage RippleAnimation::rectMask(QSize size) { QImage RippleAnimation::RectMask(QSize size) {
return maskByDrawer(size, true, Fn<void(QPainter&)>()); return MaskByDrawer(size, true, nullptr);
} }
QImage RippleAnimation::roundRectMask(QSize size, int radius) { QImage RippleAnimation::RoundRectMask(QSize size, int radius) {
return maskByDrawer(size, false, [size, radius](QPainter &p) { return MaskByDrawer(size, false, [&](QPainter &p) {
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius); p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
}); });
} }
QImage RippleAnimation::ellipseMask(QSize size) { QImage RippleAnimation::RoundRectMask(
return maskByDrawer(size, false, [size](QPainter &p) { QSize size,
Images::CornersMaskRef corners) {
return MaskByDrawer(size, true, [&](QPainter &p) {
p.setCompositionMode(QPainter::CompositionMode_Source);
const auto ratio = style::DevicePixelRatio();
const auto corner = [&](int index, bool right, bool bottom) {
if (const auto image = corners.p[index]) {
if (!image->isNull()) {
const auto width = image->width() / ratio;
const auto height = image->height() / ratio;
p.drawImage(
QRect(
right ? (size.width() - width) : 0,
bottom ? (size.height() - height) : 0,
width,
height),
*image);
}
}
};
corner(0, false, false);
corner(1, true, false);
corner(2, false, true);
corner(3, true, true);
});
}
QImage RippleAnimation::EllipseMask(QSize size) {
return MaskByDrawer(size, false, [&](QPainter &p) {
p.drawEllipse(0, 0, size.width(), size.height()); p.drawEllipse(0, 0, size.width(), size.height());
}); });
} }

View file

@ -6,16 +6,26 @@
// //
#pragma once #pragma once
#include "styles/style_widgets.h"
#include <deque> #include <deque>
namespace Images {
struct CornersMaskRef;
} // namespace Images
namespace style {
struct RippleAnimation;
} // namespace style
namespace Ui { namespace Ui {
class RippleAnimation { class RippleAnimation {
public: public:
// White upon transparent mask, like colorizeImage(black-white-mask, white). // White upon transparent mask,
RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> update); // like colorizeImage(black-white-mask, white).
RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> update);
void add(QPoint origin, int startRadius = 0); void add(QPoint origin, int startRadius = 0);
void addFading(); void addFading();
@ -24,16 +34,25 @@ public:
void lastFinish(); void lastFinish();
void forceRepaint(); void forceRepaint();
void paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride = nullptr); void paint(
QPainter &p,
int x,
int y,
int outerWidth,
const QColor *colorOverride = nullptr);
bool empty() const { bool empty() const {
return _ripples.empty(); return _ripples.empty();
} }
static QImage maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer); static QImage MaskByDrawer(
static QImage rectMask(QSize size); QSize size,
static QImage roundRectMask(QSize size, int radius); bool filled,
static QImage ellipseMask(QSize size); Fn<void(QPainter &p)> drawer);
static QImage RectMask(QSize size);
static QImage RoundRectMask(QSize size, int radius);
static QImage RoundRectMask(QSize size, Images::CornersMaskRef corners);
static QImage EllipseMask(QSize size);
~RippleAnimation(); ~RippleAnimation();

View file

@ -0,0 +1,471 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/effects/round_area_with_shadow.h"
#include "ui/style/style_core.h"
#include "ui/image/image_prepare.h"
#include "ui/painter.h"
namespace Ui {
namespace {
constexpr auto kBgCacheIndex = 0;
constexpr auto kShadowCacheIndex = 0;
constexpr auto kOverlayMaskCacheIndex = 0;
constexpr auto kOverlayShadowCacheIndex = 1;
constexpr auto kOverlayCacheColumsCount = 2;
constexpr auto kDivider = 4;
} // namespace
[[nodiscard]] QImage RoundAreaWithShadow::PrepareImage(QSize size) {
const auto ratio = style::DevicePixelRatio();
auto result = QImage(
size * ratio,
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(ratio);
return result;
}
[[nodiscard]] QImage RoundAreaWithShadow::PrepareFramesCache(
QSize frame,
int columns) {
static_assert(!(kFramesCount % kDivider));
return PrepareImage(QSize(
frame.width() * kDivider * columns,
frame.height() * kFramesCount / kDivider));
}
[[nodiscard]] QRect RoundAreaWithShadow::FrameCacheRect(
int frameIndex,
int column,
QSize frame) {
const auto ratio = style::DevicePixelRatio();
const auto origin = QPoint(
frame.width() * (kDivider * column + (frameIndex % kDivider)),
frame.height() * (frameIndex / kDivider));
return QRect(ratio * origin, ratio * frame);
}
RoundAreaWithShadow::RoundAreaWithShadow(
QSize inner,
QMargins shadow,
int twiceRadiusMax)
: _inner({}, inner)
, _outer(_inner.marginsAdded(shadow).size())
, _overlay(QRect(
0,
0,
std::max(inner.width(), twiceRadiusMax),
std::max(inner.height(), twiceRadiusMax)).marginsAdded(shadow).size())
, _cacheBg(PrepareFramesCache(_outer))
, _shadowParts(PrepareFramesCache(_outer))
, _overlayCacheParts(PrepareFramesCache(_overlay, kOverlayCacheColumsCount))
, _overlayMaskScaled(PrepareImage(_overlay))
, _overlayShadowScaled(PrepareImage(_overlay))
, _shadowBuffer(PrepareImage(_outer)) {
_inner.translate(QRect({}, _outer).center() - _inner.center());
}
ImageSubrect RoundAreaWithShadow::validateOverlayMask(
int frameIndex,
QSize innerSize,
float64 radius,
int twiceRadius,
float64 scale) {
const auto ratio = style::DevicePixelRatio();
const auto cached = (scale == 1.);
const auto full = cached
? FrameCacheRect(frameIndex, kOverlayMaskCacheIndex, _overlay)
: QRect(QPoint(), _overlay * ratio);
const auto minWidth = twiceRadius + _outer.width() - _inner.width();
const auto minHeight = twiceRadius + _outer.height() - _inner.height();
const auto maskSize = QSize(
std::max(_outer.width(), minWidth),
std::max(_outer.height(), minHeight));
const auto result = ImageSubrect{
cached ? &_overlayCacheParts : &_overlayMaskScaled,
QRect(full.topLeft(), maskSize * ratio),
};
if (cached && _validOverlayMask[frameIndex]) {
return result;
}
auto p = QPainter(result.image.get());
const auto position = full.topLeft() / ratio;
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(QRect(position, maskSize), Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
auto hq = PainterHighQualityEnabler(p);
const auto inner = QRect(position + _inner.topLeft(), innerSize);
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
if (scale != 1.) {
const auto center = inner.center();
p.save();
p.translate(center);
p.scale(scale, scale);
p.translate(-center);
}
p.drawRoundedRect(inner, radius, radius);
if (scale != 1.) {
p.restore();
}
if (cached) {
_validOverlayMask[frameIndex] = true;
}
return result;
}
ImageSubrect RoundAreaWithShadow::validateOverlayShadow(
int frameIndex,
QSize innerSize,
float64 radius,
int twiceRadius,
float64 scale,
const ImageSubrect &mask) {
const auto ratio = style::DevicePixelRatio();
const auto cached = (scale == 1.);
const auto full = cached
? FrameCacheRect(frameIndex, kOverlayShadowCacheIndex, _overlay)
: QRect(QPoint(), _overlay * ratio);
const auto minWidth = twiceRadius + _outer.width() - _inner.width();
const auto minHeight = twiceRadius + _outer.height() - _inner.height();
const auto maskSize = QSize(
std::max(_outer.width(), minWidth),
std::max(_outer.height(), minHeight));
const auto result = ImageSubrect{
cached ? &_overlayCacheParts : &_overlayShadowScaled,
QRect(full.topLeft(), maskSize * ratio),
};
if (cached && _validOverlayShadow[frameIndex]) {
return result;
}
const auto position = full.topLeft() / ratio;
_overlayShadowScaled.fill(Qt::transparent);
const auto inner = QRect(_inner.topLeft(), innerSize);
const auto add = style::ConvertScale(2.5);
const auto shift = style::ConvertScale(0.5);
const auto extended = QRectF(inner).marginsAdded({ add, add, add, add });
{
auto p = QPainter(&_overlayShadowScaled);
p.setCompositionMode(QPainter::CompositionMode_Source);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(_shadow);
if (scale != 1.) {
const auto center = inner.center();
p.translate(center);
p.scale(scale, scale);
p.translate(-center);
}
p.drawRoundedRect(extended.translated(0, shift), radius, radius);
p.end();
}
_overlayShadowScaled = Images::Blur(std::move(_overlayShadowScaled));
auto q = Painter(result.image);
if (result.image != &_overlayShadowScaled) {
q.setCompositionMode(QPainter::CompositionMode_Source);
q.drawImage(
QRect(position, maskSize),
_overlayShadowScaled,
QRect(QPoint(), maskSize * ratio));
}
q.setCompositionMode(QPainter::CompositionMode_DestinationOut);
q.drawImage(QRect(position, maskSize), *mask.image, mask.rect);
if (cached) {
_validOverlayShadow[frameIndex] = true;
}
return result;
}
void RoundAreaWithShadow::overlayExpandedBorder(
QPainter &p,
QSize size,
float64 expandRatio,
float64 radiusFrom,
float64 radiusTill,
float64 scale) {
const auto progress = expandRatio;
const auto frame = int(base::SafeRound(progress * (kFramesCount - 1)));
const auto cacheRatio = frame / float64(kFramesCount - 1);
const auto radius = radiusFrom + (radiusTill - radiusFrom) * cacheRatio;
const auto twiceRadius = int(base::SafeRound(radius * 2));
const auto innerSize = QSize(
std::max(_inner.width(), twiceRadius),
std::max(_inner.height(), twiceRadius));
const auto overlayMask = validateOverlayMask(
frame,
innerSize,
radius,
twiceRadius,
scale);
const auto overlayShadow = validateOverlayShadow(
frame,
innerSize,
radius,
twiceRadius,
scale,
overlayMask);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
FillWithImage(p, QRect(QPoint(), size), overlayMask);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
FillWithImage(p, QRect(QPoint(), size), overlayShadow);
}
QRect RoundAreaWithShadow::FillWithImage(
QPainter &p,
QRect geometry,
const ImageSubrect &pattern) {
const auto factor = style::DevicePixelRatio();
const auto &image = *pattern.image;
const auto source = pattern.rect;
const auto sourceWidth = (source.width() / factor);
const auto sourceHeight = (source.height() / factor);
if (geometry.width() == sourceWidth) {
const auto part = (sourceHeight / 2) - 1;
const auto fill = geometry.height() - 2 * part;
const auto half = part * factor;
const auto top = source.height() - half;
p.drawImage(
geometry.topLeft(),
image,
QRect(source.x(), source.y(), source.width(), half));
if (fill > 0) {
p.drawImage(
QRect(
geometry.topLeft() + QPoint(0, part),
QSize(sourceWidth, fill)),
image,
QRect(
source.x(),
source.y() + half,
source.width(),
top - half));
}
p.drawImage(
geometry.topLeft() + QPoint(0, part + fill),
image,
QRect(source.x(), source.y() + top, source.width(), half));
return QRect();
} else if (geometry.height() == sourceHeight) {
const auto part = (sourceWidth / 2) - 1;
const auto fill = geometry.width() - 2 * part;
const auto half = part * factor;
const auto left = source.width() - half;
p.drawImage(
geometry.topLeft(),
image,
QRect(source.x(), source.y(), half, source.height()));
if (fill > 0) {
p.drawImage(
QRect(
geometry.topLeft() + QPoint(part, 0),
QSize(fill, sourceHeight)),
image,
QRect(
source.x() + half,
source.y(),
left - half,
source.height()));
}
p.drawImage(
geometry.topLeft() + QPoint(part + fill, 0),
image,
QRect(source.x() + left, source.y(), half, source.height()));
return QRect();
} else if (geometry.width() > sourceWidth
&& geometry.height() > sourceHeight) {
const auto xpart = (sourceWidth / 2) - 1;
const auto xfill = geometry.width() - 2 * xpart;
const auto xhalf = xpart * factor;
const auto left = source.width() - xhalf;
const auto ypart = (sourceHeight / 2) - 1;
const auto yfill = geometry.height() - 2 * ypart;
const auto yhalf = ypart * factor;
const auto top = source.height() - yhalf;
p.drawImage(
geometry.topLeft(),
image,
QRect(source.x(), source.y(), xhalf, yhalf));
if (xfill > 0) {
p.drawImage(
QRect(
geometry.topLeft() + QPoint(xpart, 0),
QSize(xfill, ypart)),
image,
QRect(
source.x() + xhalf,
source.y(),
left - xhalf,
yhalf));
}
p.drawImage(
geometry.topLeft() + QPoint(xpart + xfill, 0),
image,
QRect(source.x() + left, source.y(), xhalf, yhalf));
if (yfill > 0) {
p.drawImage(
QRect(
geometry.topLeft() + QPoint(0, ypart),
QSize(xpart, yfill)),
image,
QRect(
source.x(),
source.y() + yhalf,
xhalf,
top - yhalf));
p.drawImage(
QRect(
geometry.topLeft() + QPoint(xpart + xfill, ypart),
QSize(xpart, yfill)),
image,
QRect(
source.x() + left,
source.y() + yhalf,
xhalf,
top - yhalf));
}
p.drawImage(
geometry.topLeft() + QPoint(0, ypart + yfill),
image,
QRect(source.x(), source.y() + top, xhalf, yhalf));
if (xfill > 0) {
p.drawImage(
QRect(
geometry.topLeft() + QPoint(xpart, ypart + yfill),
QSize(xfill, ypart)),
image,
QRect(
source.x() + xhalf,
source.y() + top,
left - xhalf,
yhalf));
}
p.drawImage(
geometry.topLeft() + QPoint(xpart + xfill, ypart + yfill),
image,
QRect(source.x() + left, source.y() + top, xhalf, yhalf));
return QRect(
geometry.topLeft() + QPoint(xpart, ypart),
QSize(xfill, yfill));
} else {
Unexpected("Values in RoundAreaWithShadow::fillWithImage.");
}
}
void RoundAreaWithShadow::setShadowColor(const QColor &shadow) {
if (_shadow == shadow) {
return;
}
_shadow = shadow;
ranges::fill(_validBg, false);
ranges::fill(_validShadow, false);
ranges::fill(_validOverlayShadow, false);
}
QRect RoundAreaWithShadow::validateShadow(
int frameIndex,
float64 scale,
float64 radius) {
const auto rect = FrameCacheRect(frameIndex, kShadowCacheIndex, _outer);
if (_validShadow[frameIndex]) {
return rect;
}
_shadowBuffer.fill(Qt::transparent);
auto p = QPainter(&_shadowBuffer);
auto hq = PainterHighQualityEnabler(p);
const auto center = _inner.center();
const auto add = style::ConvertScale(2.5);
const auto shift = style::ConvertScale(0.5);
const auto big = QRectF(_inner).marginsAdded({ add, add, add, add });
p.setPen(Qt::NoPen);
p.setBrush(_shadow);
if (scale != 1.) {
p.translate(center);
p.scale(scale, scale);
p.translate(-center);
}
p.drawRoundedRect(big.translated(0, shift), radius, radius);
p.end();
_shadowBuffer = Images::Blur(std::move(_shadowBuffer));
auto q = QPainter(&_shadowParts);
q.setCompositionMode(QPainter::CompositionMode_Source);
q.drawImage(rect.topLeft() / style::DevicePixelRatio(), _shadowBuffer);
_validShadow[frameIndex] = true;
return rect;
}
void RoundAreaWithShadow::setBackgroundColor(const QColor &background) {
if (_background == background) {
return;
}
_background = background;
ranges::fill(_validBg, false);
}
ImageSubrect RoundAreaWithShadow::validateFrame(
int frameIndex,
float64 scale,
float64 radius) {
const auto result = ImageSubrect{
&_cacheBg,
FrameCacheRect(frameIndex, kBgCacheIndex, _outer)
};
if (_validBg[frameIndex]) {
return result;
}
const auto position = result.rect.topLeft() / style::DevicePixelRatio();
const auto inner = _inner.translated(position);
const auto shadowSource = validateShadow(frameIndex, scale, radius);
auto p = QPainter(&_cacheBg);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.drawImage(position, _shadowParts, shadowSource);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(_background);
if (scale != 1.) {
const auto center = inner.center();
p.save();
p.translate(center);
p.scale(scale, scale);
p.translate(-center);
}
p.drawRoundedRect(inner, radius, radius);
if (scale != 1.) {
p.restore();
}
_validBg[frameIndex] = true;
return result;
}
} // namespace Ui

View file

@ -0,0 +1,92 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace Ui {
struct ImageSubrect {
not_null<QImage*> image;
QRect rect;
};
class RoundAreaWithShadow final {
public:
static constexpr auto kFramesCount = 32;
[[nodiscard]] static QImage PrepareImage(QSize size);
[[nodiscard]] static QImage PrepareFramesCache(
QSize frame,
int columns = 1);
[[nodiscard]] static QRect FrameCacheRect(
int frameIndex,
int column,
QSize frame);
// Returns center area which could be just filled with a solid color.
static QRect FillWithImage(
QPainter &p,
QRect geometry,
const ImageSubrect &pattern);
RoundAreaWithShadow(QSize inner, QMargins shadow, int twiceRadiusMax);
void setBackgroundColor(const QColor &background);
void setShadowColor(const QColor &shadow);
[[nodiscard]] ImageSubrect validateFrame(
int frameIndex,
float64 scale,
float64 radius);
[[nodiscard]] ImageSubrect validateOverlayMask(
int frameIndex,
QSize innerSize,
float64 radius,
int twiceRadius,
float64 scale);
[[nodiscard]] ImageSubrect validateOverlayShadow(
int frameIndex,
QSize innerSize,
float64 radius,
int twiceRadius,
float64 scale,
const ImageSubrect &mask);
void overlayExpandedBorder(
QPainter &p,
QSize size,
float64 expandRatio,
float64 radiusFrom,
float64 radiusTill,
float64 scale);
private:
[[nodiscard]] QRect validateShadow(
int frameIndex,
float64 scale,
float64 radius);
QRect _inner;
QSize _outer;
QSize _overlay;
std::array<bool, kFramesCount> _validBg = { { false } };
std::array<bool, kFramesCount> _validShadow = { { false } };
std::array<bool, kFramesCount> _validOverlayMask = { { false } };
std::array<bool, kFramesCount> _validOverlayShadow = { { false } };
QColor _background;
QColor _gradient;
QColor _shadow;
QImage _cacheBg;
QImage _shadowParts;
QImage _overlayCacheParts;
QImage _overlayMaskScaled;
QImage _overlayShadowScaled;
QImage _shadowBuffer;
};
} // namespace Ui

View file

@ -0,0 +1,110 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/effects/show_animation.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
#include "styles/style_widgets.h"
namespace Ui::Animations {
namespace {
void AnimateWidgets(const Widgets &targets, bool show) {
enum class Finish {
Bad,
Good,
};
struct Object {
base::unique_qptr<Ui::RpWidget> container;
QPointer<Ui::RpWidget> weakTarget;
};
struct State {
rpl::event_stream<Finish> destroy;
Ui::Animations::Simple animation;
std::vector<Object> objects;
};
auto lifetime = std::make_shared<rpl::lifetime>();
const auto state = lifetime->make_state<State>();
const auto from = show ? 0. : 1.;
const auto to = show ? 1. : 0.;
for (const auto &target : targets) {
state->objects.push_back({
base::make_unique_q<Ui::RpWidget>(target->parentWidget()),
Ui::MakeWeak(target),
});
const auto pixmap = Ui::GrabWidget(target);
const auto raw = state->objects.back().container.get();
raw->paintRequest(
) | rpl::start_with_next([=] {
QPainter p(raw);
p.setOpacity(state->animation.value(to));
p.drawPixmap(QPoint(), pixmap);
}, raw->lifetime());
target->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
raw->setGeometry(r);
}, raw->lifetime());
raw->show();
if (!show) {
target->hide();
}
}
state->destroy.events(
) | rpl::take(
1
) | rpl::start_with_next([=](Finish type) mutable {
if (type == Finish::Good && show) {
for (const auto &object : state->objects) {
if (object.weakTarget) {
object.weakTarget->show();
}
}
}
if (lifetime) {
base::take(lifetime)->destroy();
}
}, *lifetime);
state->animation.start(
[=](auto value) {
for (const auto &object : state->objects) {
object.container->update();
if (!object.weakTarget && show) {
state->destroy.fire(Finish::Bad);
return;
}
}
if (value == to) {
state->destroy.fire(Finish::Good);
}
},
from,
to,
st::defaultToggle.duration);
}
} // namespace
void ShowWidgets(const Widgets &targets) {
AnimateWidgets(targets, true);
}
void HideWidgets(const Widgets &targets) {
AnimateWidgets(targets, false);
}
} // namespace Ui::Animations

View file

@ -0,0 +1,20 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace Ui {
class RpWidget;
} // namespace Ui
namespace Ui::Animations {
using Widgets = std::vector<not_null<Ui::RpWidget*>>;
void ShowWidgets(const Widgets &targets);
void HideWidgets(const Widgets &targets);
} // namespace Ui::Animations

878
ui/effects/spoiler_mess.cpp Normal file
View file

@ -0,0 +1,878 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/effects/spoiler_mess.h"
#include "ui/effects/animations.h"
#include "ui/image/image_prepare.h"
#include "ui/painter.h"
#include "ui/integration.h"
#include "base/random.h"
#include "base/flags.h"
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <crl/crl_async.h>
#include <xxhash.h>
#include <mutex>
#include <condition_variable>
namespace Ui {
namespace {
constexpr auto kVersion = 2;
constexpr auto kFramesPerRow = 10;
constexpr auto kImageSpoilerDarkenAlpha = 32;
constexpr auto kMaxCacheSize = 5 * 1024 * 1024;
constexpr auto kDefaultFrameDuration = crl::time(33);
constexpr auto kDefaultFramesCount = 60;
constexpr auto kAutoPauseTimeout = crl::time(1000);
[[nodiscard]] SpoilerMessDescriptor DefaultDescriptorText() {
const auto ratio = style::DevicePixelRatio();
const auto size = style::ConvertScale(128) * ratio;
return {
.particleFadeInDuration = crl::time(200),
.particleShownDuration = crl::time(200),
.particleFadeOutDuration = crl::time(200),
.particleSizeMin = style::ConvertScaleExact(1.5) * ratio,
.particleSizeMax = style::ConvertScaleExact(2.) * ratio,
.particleSpeedMin = style::ConvertScaleExact(4.),
.particleSpeedMax = style::ConvertScaleExact(8.),
.particleSpritesCount = 5,
.particlesCount = 9000,
.canvasSize = size,
.framesCount = kDefaultFramesCount,
.frameDuration = kDefaultFrameDuration,
};
}
[[nodiscard]] SpoilerMessDescriptor DefaultDescriptorImage() {
const auto ratio = style::DevicePixelRatio();
const auto size = style::ConvertScale(128) * ratio;
return {
.particleFadeInDuration = crl::time(300),
.particleShownDuration = crl::time(0),
.particleFadeOutDuration = crl::time(300),
.particleSizeMin = style::ConvertScaleExact(1.5) * ratio,
.particleSizeMax = style::ConvertScaleExact(2.) * ratio,
.particleSpeedMin = style::ConvertScaleExact(10.),
.particleSpeedMax = style::ConvertScaleExact(20.),
.particleSpritesCount = 5,
.particlesCount = 3000,
.canvasSize = size,
.framesCount = kDefaultFramesCount,
.frameDuration = kDefaultFrameDuration,
};
}
} // namespace
class SpoilerAnimationManager final {
public:
explicit SpoilerAnimationManager(not_null<SpoilerAnimation*> animation);
void add(not_null<SpoilerAnimation*> animation);
void remove(not_null<SpoilerAnimation*> animation);
private:
void destroyIfEmpty();
Ui::Animations::Basic _animation;
base::flat_set<not_null<SpoilerAnimation*>> _list;
};
namespace {
struct DefaultSpoilerWaiter {
std::condition_variable variable;
std::mutex mutex;
};
struct DefaultSpoiler {
std::atomic<const SpoilerMessCached*> cached/* = nullptr*/;
std::atomic<DefaultSpoilerWaiter*> waiter/* = nullptr*/;
};
DefaultSpoiler DefaultTextMask;
DefaultSpoiler DefaultImageCached;
SpoilerAnimationManager *DefaultAnimationManager/* = nullptr*/;
struct Header {
uint32 version = 0;
uint32 dataLength = 0;
uint32 dataHash = 0;
int32 framesCount = 0;
int32 canvasSize = 0;
int32 frameDuration = 0;
};
struct Particle {
crl::time start = 0;
int spriteIndex = 0;
int x = 0;
int y = 0;
float64 dx = 0.;
float64 dy = 0.;
};
[[nodiscard]] std::pair<float64, float64> RandomSpeed(
const SpoilerMessDescriptor &descriptor,
base::BufferedRandom<uint32> &random) {
const auto count = descriptor.particlesCount;
const auto speedMax = descriptor.particleSpeedMax;
const auto speedMin = descriptor.particleSpeedMin;
const auto value = RandomIndex(2 * count + 2, random);
const auto negative = (value < count + 1);
const auto module = (negative ? value : (value - count - 1));
const auto speed = speedMin + (((speedMax - speedMin) * module) / count);
const auto lifetime = descriptor.particleFadeInDuration
+ descriptor.particleShownDuration
+ descriptor.particleFadeOutDuration;
const auto max = int(std::ceil(speedMax * lifetime));
const auto k = speed / lifetime;
const auto x = (speedMax > 0)
? ((RandomIndex(2 * max + 1, random) - max) / float64(max))
: 0.;
const auto y = (speedMax > 0)
? (sqrt(1 - x * x) * (negative ? -1 : 1))
: 0.;
return { k * x, k * y };
}
[[nodiscard]] Particle GenerateParticle(
const SpoilerMessDescriptor &descriptor,
int index,
base::BufferedRandom<uint32> &random) {
const auto speed = RandomSpeed(descriptor, random);
return {
.start = (index * descriptor.framesCount * descriptor.frameDuration
/ descriptor.particlesCount),
.spriteIndex = RandomIndex(descriptor.particleSpritesCount, random),
.x = RandomIndex(descriptor.canvasSize, random),
.y = RandomIndex(descriptor.canvasSize, random),
.dx = speed.first,
.dy = speed.second,
};
}
[[nodiscard]] QImage GenerateSprite(
const SpoilerMessDescriptor &descriptor,
int index,
int size,
base::BufferedRandom<uint32> &random) {
Expects(index >= 0 && index < descriptor.particleSpritesCount);
const auto count = descriptor.particleSpritesCount;
const auto middle = count / 2;
const auto min = descriptor.particleSizeMin;
const auto delta = descriptor.particleSizeMax - min;
const auto width = (index < middle)
? (min + delta * (middle - index) / float64(middle))
: min;
const auto height = (index > middle)
? (min + delta * (index - middle) / float64(count - 1 - middle))
: min;
const auto radius = min / 2.;
auto result = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
auto p = QPainter(&result);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
QPainterPath path;
path.addRoundedRect(1., 1., width, height, radius, radius);
p.drawPath(path);
p.end();
return result;
}
[[nodiscard]] QString DefaultMaskCacheFolder() {
const auto base = Integration::Instance().emojiCacheFolder();
return base.isEmpty() ? QString() : (base + "/spoiler");
}
[[nodiscard]] std::optional<SpoilerMessCached> ReadDefaultMask(
const QString &name,
std::optional<SpoilerMessCached::Validator> validator) {
const auto folder = DefaultMaskCacheFolder();
if (folder.isEmpty()) {
return {};
}
auto file = QFile(folder + '/' + name);
return (file.open(QIODevice::ReadOnly) && file.size() <= kMaxCacheSize)
? SpoilerMessCached::FromSerialized(file.readAll(), validator)
: std::nullopt;
}
void WriteDefaultMask(
const QString &name,
const SpoilerMessCached &mask) {
const auto folder = DefaultMaskCacheFolder();
if (!QDir().mkpath(folder)) {
return;
}
const auto bytes = mask.serialize();
auto file = QFile(folder + '/' + name);
if (file.open(QIODevice::WriteOnly) && bytes.size() <= kMaxCacheSize) {
file.write(bytes);
}
}
void Register(not_null<SpoilerAnimation*> animation) {
if (DefaultAnimationManager) {
DefaultAnimationManager->add(animation);
} else {
new SpoilerAnimationManager(animation);
}
}
void Unregister(not_null<SpoilerAnimation*> animation) {
Expects(DefaultAnimationManager != nullptr);
DefaultAnimationManager->remove(animation);
}
// DescriptorFactory: (void) -> SpoilerMessDescriptor.
// Postprocess: (unique_ptr<MessCached>) -> unique_ptr<MessCached>.
template <typename DescriptorFactory, typename Postprocess>
void PrepareDefaultSpoiler(
DefaultSpoiler &spoiler,
const char *nameFactory,
DescriptorFactory descriptorFactory,
Postprocess postprocess) {
if (spoiler.waiter.load()) {
return;
}
const auto waiter = new DefaultSpoilerWaiter();
auto expected = (DefaultSpoilerWaiter*)nullptr;
if (!spoiler.waiter.compare_exchange_strong(expected, waiter)) {
delete waiter;
return;
}
const auto name = QString::fromUtf8(nameFactory);
crl::async([=, &spoiler] {
const auto descriptor = descriptorFactory();
auto cached = ReadDefaultMask(name, SpoilerMessCached::Validator{
.frameDuration = descriptor.frameDuration,
.framesCount = descriptor.framesCount,
.canvasSize = descriptor.canvasSize,
});
spoiler.cached = postprocess(cached
? std::make_unique<SpoilerMessCached>(std::move(*cached))
: std::make_unique<SpoilerMessCached>(
GenerateSpoilerMess(descriptor))
).release();
auto lock = std::unique_lock(waiter->mutex);
waiter->variable.notify_all();
if (!cached) {
WriteDefaultMask(name, *spoiler.cached);
}
});
}
[[nodiscard]] const SpoilerMessCached &WaitDefaultSpoiler(
DefaultSpoiler &spoiler) {
const auto &cached = spoiler.cached;
if (const auto result = cached.load()) {
return *result;
}
const auto waiter = spoiler.waiter.load();
Assert(waiter != nullptr);
while (true) {
auto lock = std::unique_lock(waiter->mutex);
if (const auto result = cached.load()) {
return *result;
}
waiter->variable.wait(lock);
}
}
} // namespace
SpoilerAnimationManager::SpoilerAnimationManager(
not_null<SpoilerAnimation*> animation)
: _animation([=](crl::time now) {
for (auto i = begin(_list); i != end(_list);) {
if ((*i)->repaint(now)) {
++i;
} else {
i = _list.erase(i);
}
}
destroyIfEmpty();
})
, _list{ { animation } } {
Expects(!DefaultAnimationManager);
DefaultAnimationManager = this;
_animation.start();
}
void SpoilerAnimationManager::add(not_null<SpoilerAnimation*> animation) {
_list.emplace(animation);
}
void SpoilerAnimationManager::remove(not_null<SpoilerAnimation*> animation) {
_list.remove(animation);
destroyIfEmpty();
}
void SpoilerAnimationManager::destroyIfEmpty() {
if (_list.empty()) {
Assert(DefaultAnimationManager == this);
delete base::take(DefaultAnimationManager);
}
}
SpoilerMessCached GenerateSpoilerMess(
const SpoilerMessDescriptor &descriptor) {
Expects(descriptor.framesCount > 0);
Expects(descriptor.frameDuration > 0);
Expects(descriptor.particlesCount > 0);
Expects(descriptor.canvasSize > 0);
Expects(descriptor.particleSizeMax >= descriptor.particleSizeMin);
Expects(descriptor.particleSizeMin > 0.);
const auto frames = descriptor.framesCount;
const auto rows = (frames + kFramesPerRow - 1) / kFramesPerRow;
const auto columns = std::min(frames, kFramesPerRow);
const auto size = descriptor.canvasSize;
const auto count = descriptor.particlesCount;
const auto width = size * columns;
const auto height = size * rows;
const auto spriteSize = 2 + int(std::ceil(descriptor.particleSizeMax));
const auto singleDuration = descriptor.particleFadeInDuration
+ descriptor.particleShownDuration
+ descriptor.particleFadeOutDuration;
const auto fullDuration = frames * descriptor.frameDuration;
Assert(fullDuration > singleDuration);
auto random = base::BufferedRandom<uint32>(count * 5);
auto particles = std::vector<Particle>();
particles.reserve(descriptor.particlesCount);
for (auto i = 0; i != descriptor.particlesCount; ++i) {
particles.push_back(GenerateParticle(descriptor, i, random));
}
auto sprites = std::vector<QImage>();
sprites.reserve(descriptor.particleSpritesCount);
for (auto i = 0; i != descriptor.particleSpritesCount; ++i) {
sprites.push_back(GenerateSprite(descriptor, i, spriteSize, random));
}
auto frame = 0;
auto image = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
auto p = QPainter(&image);
const auto paintOneAt = [&](const Particle &particle, crl::time now) {
if (now <= 0 || now >= singleDuration) {
return;
}
const auto clamp = [&](int value) {
return ((value % size) + size) % size;
};
const auto x = clamp(
particle.x + int(base::SafeRound(now * particle.dx)));
const auto y = clamp(
particle.y + int(base::SafeRound(now * particle.dy)));
const auto opacity = (now < descriptor.particleFadeInDuration)
? (now / float64(descriptor.particleFadeInDuration))
: (now > singleDuration - descriptor.particleFadeOutDuration)
? ((singleDuration - now)
/ float64(descriptor.particleFadeOutDuration))
: 1.;
p.setOpacity(opacity);
const auto &sprite = sprites[particle.spriteIndex];
p.drawImage(x, y, sprite);
if (x + spriteSize > size) {
p.drawImage(x - size, y, sprite);
if (y + spriteSize > size) {
p.drawImage(x, y - size, sprite);
p.drawImage(x - size, y - size, sprite);
}
} else if (y + spriteSize > size) {
p.drawImage(x, y - size, sprite);
}
};
const auto paintOne = [&](const Particle &particle, crl::time now) {
paintOneAt(particle, now - particle.start);
paintOneAt(particle, now + fullDuration - particle.start);
};
for (auto y = 0; y != rows; ++y) {
for (auto x = 0; x != columns; ++x) {
const auto rect = QRect(x * size, y * size, size, size);
p.setClipRect(rect);
p.translate(rect.topLeft());
const auto time = frame * descriptor.frameDuration;
for (auto index = 0; index != count; ++index) {
paintOne(particles[index], time);
}
p.translate(-rect.topLeft());
if (++frame >= frames) {
break;
}
}
}
return SpoilerMessCached(
std::move(image),
frames,
descriptor.frameDuration,
size);
}
void FillSpoilerRect(
QPainter &p,
QRect rect,
const SpoilerMessFrame &frame,
QPoint originShift) {
if (rect.isEmpty()) {
return;
}
const auto &image = *frame.image;
const auto source = frame.source;
const auto ratio = style::DevicePixelRatio();
const auto origin = rect.topLeft() + originShift;
const auto size = source.width() / ratio;
const auto xSkipFrames = (origin.x() <= rect.x())
? ((rect.x() - origin.x()) / size)
: -((origin.x() - rect.x() + size - 1) / size);
const auto ySkipFrames = (origin.y() <= rect.y())
? ((rect.y() - origin.y()) / size)
: -((origin.y() - rect.y() + size - 1) / size);
const auto xFrom = origin.x() + size * xSkipFrames;
const auto yFrom = origin.y() + size * ySkipFrames;
Assert((xFrom <= rect.x())
&& (yFrom <= rect.y())
&& (xFrom + size > rect.x())
&& (yFrom + size > rect.y()));
const auto xTill = rect.x() + rect.width();
const auto yTill = rect.y() + rect.height();
const auto xCount = (xTill - xFrom + size - 1) / size;
const auto yCount = (yTill - yFrom + size - 1) / size;
Assert(xCount > 0 && yCount > 0);
const auto xFullFrom = (xFrom < rect.x()) ? 1 : 0;
const auto yFullFrom = (yFrom < rect.y()) ? 1 : 0;
const auto xFullTill = xCount - (xFrom + xCount * size > xTill ? 1 : 0);
const auto yFullTill = yCount - (yFrom + yCount * size > yTill ? 1 : 0);
const auto targetRect = [&](int x, int y) {
return QRect(xFrom + x * size, yFrom + y * size, size, size);
};
const auto drawFull = [&](int x, int y) {
p.drawImage(targetRect(x, y), image, source);
};
const auto drawPart = [&](int x, int y) {
const auto target = targetRect(x, y);
const auto fill = target.intersected(rect);
Assert(!fill.isEmpty());
p.drawImage(fill, image, QRect(
source.topLeft() + ((fill.topLeft() - target.topLeft()) * ratio),
fill.size() * ratio));
};
if (yFullFrom) {
for (auto x = 0; x != xCount; ++x) {
drawPart(x, 0);
}
}
if (yFullFrom < yFullTill) {
if (xFullFrom) {
for (auto y = yFullFrom; y != yFullTill; ++y) {
drawPart(0, y);
}
}
if (xFullFrom < xFullTill) {
for (auto y = yFullFrom; y != yFullTill; ++y) {
for (auto x = xFullFrom; x != xFullTill; ++x) {
drawFull(x, y);
}
}
}
if (xFullFrom <= xFullTill && xFullTill < xCount) {
for (auto y = yFullFrom; y != yFullTill; ++y) {
drawPart(xFullTill, y);
}
}
}
if (yFullFrom <= yFullTill && yFullTill < yCount) {
for (auto x = 0; x != xCount; ++x) {
drawPart(x, yFullTill);
}
}
}
void FillSpoilerRect(
QPainter &p,
QRect rect,
Images::CornersMaskRef mask,
const SpoilerMessFrame &frame,
QImage &cornerCache,
QPoint originShift) {
using namespace Images;
if ((!mask.p[kTopLeft] || mask.p[kTopLeft]->isNull())
&& (!mask.p[kTopRight] || mask.p[kTopRight]->isNull())
&& (!mask.p[kBottomLeft] || mask.p[kBottomLeft]->isNull())
&& (!mask.p[kBottomRight] || mask.p[kBottomRight]->isNull())) {
FillSpoilerRect(p, rect, frame, originShift);
return;
}
const auto ratio = style::DevicePixelRatio();
const auto cornerSize = [&](int index) {
const auto corner = mask.p[index];
return (!corner || corner->isNull()) ? 0 : (corner->width() / ratio);
};
const auto verticalSkip = [&](int left, int right) {
return std::max(cornerSize(left), cornerSize(right));
};
const auto fillBg = [&](QRect part) {
FillSpoilerRect(
p,
part.translated(rect.topLeft()),
frame,
originShift - rect.topLeft() - part.topLeft());
};
const auto fillCorner = [&](int x, int y, int index) {
const auto position = QPoint(x, y);
const auto corner = mask.p[index];
if (!corner || corner->isNull()) {
return;
}
if (cornerCache.width() < corner->width()
|| cornerCache.height() < corner->height()) {
cornerCache = QImage(
std::max(cornerCache.width(), corner->width()),
std::max(cornerCache.height(), corner->height()),
QImage::Format_ARGB32_Premultiplied);
cornerCache.setDevicePixelRatio(ratio);
}
const auto size = corner->size() / ratio;
const auto target = QRect(QPoint(), size);
auto q = QPainter(&cornerCache);
q.setCompositionMode(QPainter::CompositionMode_Source);
FillSpoilerRect(
q,
target,
frame,
originShift - rect.topLeft() - position);
q.setCompositionMode(QPainter::CompositionMode_DestinationIn);
q.drawImage(target, *corner);
q.end();
p.drawImage(
QRect(rect.topLeft() + position, size),
cornerCache,
QRect(QPoint(), corner->size()));
};
const auto top = verticalSkip(kTopLeft, kTopRight);
const auto bottom = verticalSkip(kBottomLeft, kBottomRight);
if (top) {
const auto left = cornerSize(kTopLeft);
const auto right = cornerSize(kTopRight);
if (left) {
fillCorner(0, 0, kTopLeft);
if (const auto add = top - left) {
fillBg({ 0, left, left, add });
}
}
if (const auto fill = rect.width() - left - right; fill > 0) {
fillBg({ left, 0, fill, top });
}
if (right) {
fillCorner(rect.width() - right, 0, kTopRight);
if (const auto add = top - right) {
fillBg({ rect.width() - right, right, right, add });
}
}
}
if (const auto h = rect.height() - top - bottom; h > 0) {
fillBg({ 0, top, rect.width(), h });
}
if (bottom) {
const auto left = cornerSize(kBottomLeft);
const auto right = cornerSize(kBottomRight);
if (left) {
fillCorner(0, rect.height() - left, kBottomLeft);
if (const auto add = bottom - left) {
fillBg({ 0, rect.height() - bottom, left, add });
}
}
if (const auto fill = rect.width() - left - right; fill > 0) {
fillBg({ left, rect.height() - bottom, fill, bottom });
}
if (right) {
fillCorner(
rect.width() - right,
rect.height() - right,
kBottomRight);
if (const auto add = bottom - right) {
fillBg({
rect.width() - right,
rect.height() - bottom,
right,
add,
});
}
}
}
}
SpoilerMessCached::SpoilerMessCached(
QImage image,
int framesCount,
crl::time frameDuration,
int canvasSize)
: _image(std::move(image))
, _frameDuration(frameDuration)
, _framesCount(framesCount)
, _canvasSize(canvasSize) {
Expects(_frameDuration > 0);
Expects(_framesCount > 0);
Expects(_canvasSize > 0);
Expects(_image.size() == QSize(
std::min(_framesCount, kFramesPerRow) * _canvasSize,
((_framesCount + kFramesPerRow - 1) / kFramesPerRow) * _canvasSize));
}
SpoilerMessCached::SpoilerMessCached(
const SpoilerMessCached &mask,
const QColor &color)
: SpoilerMessCached(
style::colorizeImage(*mask.frame(0).image, color),
mask.framesCount(),
mask.frameDuration(),
mask.canvasSize()) {
}
SpoilerMessFrame SpoilerMessCached::frame(int index) const {
const auto row = index / kFramesPerRow;
const auto column = index - row * kFramesPerRow;
return {
.image = &_image,
.source = QRect(
column * _canvasSize,
row * _canvasSize,
_canvasSize,
_canvasSize),
};
}
SpoilerMessFrame SpoilerMessCached::frame() const {
return frame((crl::now() / _frameDuration) % _framesCount);
}
crl::time SpoilerMessCached::frameDuration() const {
return _frameDuration;
}
int SpoilerMessCached::framesCount() const {
return _framesCount;
}
int SpoilerMessCached::canvasSize() const {
return _canvasSize;
}
QByteArray SpoilerMessCached::serialize() const {
Expects(_frameDuration < std::numeric_limits<int32>::max());
const auto skip = sizeof(Header);
auto result = QByteArray(skip, Qt::Uninitialized);
auto header = Header{
.version = kVersion,
.framesCount = _framesCount,
.canvasSize = _canvasSize,
.frameDuration = int32(_frameDuration),
};
const auto width = int(_image.width());
const auto height = int(_image.height());
auto grayscale = QImage(width, height, QImage::Format_Grayscale8);
{
auto tobytes = grayscale.bits();
auto frombytes = _image.constBits();
const auto toadd = grayscale.bytesPerLine() - width;
const auto fromadd = _image.bytesPerLine() - (width * 4);
for (auto y = 0; y != height; ++y) {
for (auto x = 0; x != width; ++x) {
*tobytes++ = *frombytes;
frombytes += 4;
}
tobytes += toadd;
frombytes += fromadd;
}
}
auto device = QBuffer(&result);
device.open(QIODevice::WriteOnly);
device.seek(skip);
grayscale.save(&device, "PNG");
device.close();
header.dataLength = result.size() - skip;
header.dataHash = XXH32(result.data() + skip, header.dataLength, 0);
memcpy(result.data(), &header, skip);
return result;
}
std::optional<SpoilerMessCached> SpoilerMessCached::FromSerialized(
QByteArray data,
std::optional<Validator> validator) {
const auto skip = sizeof(Header);
const auto length = data.size();
const auto bytes = reinterpret_cast<const uchar*>(data.constData());
if (length <= skip) {
return {};
}
auto header = Header();
memcpy(&header, bytes, skip);
if (header.version != kVersion
|| header.canvasSize <= 0
|| header.framesCount <= 0
|| header.frameDuration <= 0
|| (validator
&& (validator->frameDuration != header.frameDuration
|| validator->framesCount != header.framesCount
|| validator->canvasSize != header.canvasSize))
|| (skip + header.dataLength != length)
|| (XXH32(bytes + skip, header.dataLength, 0) != header.dataHash)) {
return {};
}
auto grayscale = QImage();
if (!grayscale.loadFromData(bytes + skip, header.dataLength, "PNG")
|| (grayscale.format() != QImage::Format_Grayscale8)) {
return {};
}
const auto count = header.framesCount;
const auto rows = (count + kFramesPerRow - 1) / kFramesPerRow;
const auto columns = std::min(count, kFramesPerRow);
const auto width = grayscale.width();
const auto height = grayscale.height();
if (QSize(width, height) != QSize(columns, rows) * header.canvasSize) {
return {};
}
auto image = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
{
Assert(image.bytesPerLine() % 4 == 0);
auto toints = reinterpret_cast<uint32*>(image.bits());
auto frombytes = grayscale.constBits();
const auto toadd = (image.bytesPerLine() / 4) - width;
const auto fromadd = grayscale.bytesPerLine() - width;
for (auto y = 0; y != height; ++y) {
for (auto x = 0; x != width; ++x) {
const auto byte = uint32(*frombytes++);
*toints++ = (byte << 24) | (byte << 16) | (byte << 8) | byte;
}
toints += toadd;
frombytes += fromadd;
}
}
return SpoilerMessCached(
std::move(image),
count,
header.frameDuration,
header.canvasSize);
}
SpoilerAnimation::SpoilerAnimation(Fn<void()> repaint)
: _repaint(std::move(repaint)) {
Expects(_repaint != nullptr);
}
SpoilerAnimation::~SpoilerAnimation() {
if (_animating) {
_animating = false;
Unregister(this);
}
}
int SpoilerAnimation::index(crl::time now, bool paused) {
_scheduled = false;
const auto add = std::min(now - _last, kDefaultFrameDuration);
if (anim::Disabled()) {
paused = true;
}
if (!paused || _last) {
_accumulated += add;
_last = paused ? 0 : now;
}
const auto absolute = (_accumulated / kDefaultFrameDuration);
if (!paused && !_animating) {
_animating = true;
Register(this);
} else if (paused && _animating) {
_animating = false;
Unregister(this);
}
return absolute % kDefaultFramesCount;
}
Fn<void()> SpoilerAnimation::repaintCallback() const {
return _repaint;
}
bool SpoilerAnimation::repaint(crl::time now) {
if (!_scheduled) {
_scheduled = true;
_repaint();
} else if (_animating && _last && _last + kAutoPauseTimeout <= now) {
_animating = false;
return false;
}
return true;
}
void PreloadTextSpoilerMask() {
PrepareDefaultSpoiler(
DefaultTextMask,
"text",
DefaultDescriptorText,
[](std::unique_ptr<SpoilerMessCached> cached) { return cached; });
}
const SpoilerMessCached &DefaultTextSpoilerMask() {
[[maybe_unused]] static const auto once = [&] {
PreloadTextSpoilerMask();
return 0;
}();
return WaitDefaultSpoiler(DefaultTextMask);
}
void PreloadImageSpoiler() {
const auto postprocess = [](std::unique_ptr<SpoilerMessCached> cached) {
Expects(cached != nullptr);
const auto frame = cached->frame(0);
auto image = QImage(
frame.image->size(),
QImage::Format_ARGB32_Premultiplied);
image.fill(QColor(0, 0, 0, kImageSpoilerDarkenAlpha));
auto p = QPainter(&image);
p.drawImage(0, 0, *frame.image);
p.end();
return std::make_unique<SpoilerMessCached>(
std::move(image),
cached->framesCount(),
cached->frameDuration(),
cached->canvasSize());
};
PrepareDefaultSpoiler(
DefaultImageCached,
"image",
DefaultDescriptorImage,
postprocess);
}
const SpoilerMessCached &DefaultImageSpoiler() {
[[maybe_unused]] static const auto once = [&] {
PreloadImageSpoiler();
return 0;
}();
return WaitDefaultSpoiler(DefaultImageCached);
}
} // namespace Ui

Some files were not shown because too many files have changed in this diff Show more