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