From 849deb57e2d0ba317a0d98f9fd26fe7330f0c4cb Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 16 Sep 2019 14:14:06 +0300 Subject: [PATCH] Move many widget classes to lib_ui. --- Telegram/Resources/basic.style | 7 +- Telegram/SourceFiles/api/api_sending.cpp | 7 +- .../SourceFiles/api/api_text_entities.cpp | 129 +++ Telegram/SourceFiles/api/api_text_entities.h | 23 + Telegram/SourceFiles/apiwrap.cpp | 23 +- Telegram/SourceFiles/app.cpp | 33 - Telegram/SourceFiles/app.h | 3 - Telegram/SourceFiles/base/algorithm.h | 17 + Telegram/SourceFiles/base/crc32hash.cpp | 61 ++ Telegram/SourceFiles/base/crc32hash.h | 16 + Telegram/SourceFiles/base/object_ptr.h | 2 + Telegram/SourceFiles/base/openssl_help.h | 11 + Telegram/SourceFiles/base/qthelp_url.h | 4 + Telegram/SourceFiles/base/timer.cpp | 4 +- Telegram/SourceFiles/base/unique_qptr.h | 2 + Telegram/SourceFiles/boxes/abstract_box.cpp | 18 +- Telegram/SourceFiles/boxes/abstract_box.h | 11 +- .../SourceFiles/boxes/add_contact_box.cpp | 2 +- Telegram/SourceFiles/boxes/add_contact_box.h | 10 +- Telegram/SourceFiles/boxes/background_box.cpp | 1 + .../boxes/background_preview_box.h | 3 +- Telegram/SourceFiles/boxes/calendar_box.h | 2 +- .../SourceFiles/boxes/change_phone_box.cpp | 1 + Telegram/SourceFiles/boxes/confirm_box.cpp | 3 +- Telegram/SourceFiles/boxes/confirm_box.h | 7 +- .../SourceFiles/boxes/edit_caption_box.cpp | 15 +- Telegram/SourceFiles/boxes/edit_caption_box.h | 5 +- Telegram/SourceFiles/boxes/edit_color_box.h | 2 +- .../boxes/peers/edit_peer_type_box.cpp | 1 + .../SourceFiles/boxes/sticker_set_box.cpp | 2 +- Telegram/SourceFiles/boxes/stickers_box.h | 7 +- Telegram/SourceFiles/boxes/username_box.cpp | 2 +- Telegram/SourceFiles/calls/calls_panel.cpp | 18 +- .../SourceFiles/chat_helpers/bot_keyboard.cpp | 6 +- .../SourceFiles/chat_helpers/bot_keyboard.h | 1 + .../chat_helpers/emoji_list_widget.cpp | 4 + .../chat_helpers/emoji_list_widget.h | 1 + .../chat_helpers/gifs_list_widget.cpp | 2 +- .../chat_helpers/message_field.cpp | 134 +-- .../SourceFiles/chat_helpers/message_field.h | 9 - .../SourceFiles/chat_helpers/tabbed_panel.cpp | 3 +- .../chat_helpers/tabbed_selector.cpp | 3 +- .../SourceFiles/codegen/style/generator.cpp | 52 +- Telegram/SourceFiles/config.h | 5 - Telegram/SourceFiles/core/application.cpp | 39 +- Telegram/SourceFiles/core/application.h | 4 - Telegram/SourceFiles/core/click_handler.cpp | 55 - .../SourceFiles/core/click_handler_types.cpp | 65 +- .../SourceFiles/core/click_handler_types.h | 71 +- .../SourceFiles/core/core_ui_integration.cpp | 217 ++++ .../SourceFiles/core/core_ui_integration.h | 55 + Telegram/SourceFiles/core/file_utilities.cpp | 17 +- Telegram/SourceFiles/core/launcher.cpp | 2 +- .../SourceFiles/core/local_url_handlers.cpp | 4 +- Telegram/SourceFiles/core/sandbox.cpp | 24 - Telegram/SourceFiles/core/sandbox.h | 7 - Telegram/SourceFiles/core/utils.cpp | 44 - Telegram/SourceFiles/core/utils.h | 15 - Telegram/SourceFiles/data/data_drafts.cpp | 6 +- Telegram/SourceFiles/data/data_peer.cpp | 4 +- .../data/data_scheduled_messages.cpp | 4 +- Telegram/SourceFiles/data/data_session.cpp | 5 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 2 +- Telegram/SourceFiles/facades.cpp | 10 - Telegram/SourceFiles/facades.h | 3 - .../admin_log/history_admin_log_inner.cpp | 22 +- .../admin_log/history_admin_log_inner.h | 1 + .../admin_log/history_admin_log_item.cpp | 3 +- Telegram/SourceFiles/history/history.style | 2 +- .../history/history_inner_widget.cpp | 38 +- .../history/history_inner_widget.h | 1 + .../SourceFiles/history/history_message.cpp | 5 +- .../SourceFiles/history/history_widget.cpp | 20 +- .../view/history_view_context_menu.cpp | 7 +- .../history/view/history_view_list_widget.cpp | 21 +- .../history/view/history_view_list_widget.h | 1 + .../history/view/media/history_view_gif.cpp | 1 + .../view/media/history_view_sticker.cpp | 1 + .../info/media/info_media_list_widget.cpp | 14 +- .../inline_bots/inline_bot_result.cpp | 5 +- .../inline_bots/inline_bot_send_data.cpp | 3 +- .../inline_bots/inline_results_widget.cpp | 9 +- .../inline_bots/inline_results_widget.h | 1 + Telegram/SourceFiles/intro/introphone.cpp | 1 + Telegram/SourceFiles/mainwidget.cpp | 5 +- Telegram/SourceFiles/mainwindow.cpp | 6 + .../media/view/media_view_overlay_widget.cpp | 21 +- .../passport/passport_panel_edit_contact.cpp | 1 + .../platform/linux/specific_linux.cpp | 37 - .../platform/linux/specific_linux.h | 20 +- .../SourceFiles/platform/mac/mac_touchbar.mm | 2 +- .../SourceFiles/platform/mac/specific_mac.h | 8 - .../SourceFiles/platform/mac/specific_mac.mm | 23 +- .../SourceFiles/platform/mac/specific_mac_p.h | 2 - .../platform/mac/specific_mac_p.mm | 64 -- .../SourceFiles/platform/platform_specific.h | 8 +- .../platform/win/main_window_win.cpp | 3 +- .../SourceFiles/platform/win/specific_win.cpp | 21 - .../SourceFiles/platform/win/specific_win.h | 24 +- .../platform/win/windows_event_filter.cpp | 3 +- .../SourceFiles/storage/localimageloader.cpp | 5 +- .../SourceFiles/support/support_helper.cpp | 11 +- Telegram/SourceFiles/ui/abstract_button.cpp | 4 +- .../SourceFiles/ui/basic_click_handlers.cpp | 68 ++ .../SourceFiles/ui/basic_click_handlers.h | 79 ++ Telegram/SourceFiles/ui/click_handler.cpp | 172 +++ .../SourceFiles/{core => ui}/click_handler.h | 92 +- .../SourceFiles/ui/delayed_activation.cpp | 45 + Telegram/SourceFiles/ui/delayed_activation.h | 15 + .../SourceFiles/ui/effects/animations.cpp | 2 +- .../SourceFiles/ui/effects/fade_animation.cpp | 3 +- .../SourceFiles/ui/effects/fade_animation.h | 2 +- .../ui/effects/panel_animation.cpp | 11 +- .../SourceFiles/ui/effects/panel_animation.h | 2 +- Telegram/SourceFiles/ui/emoji_config.cpp | 45 +- Telegram/SourceFiles/ui/emoji_config.h | 2 - .../SourceFiles/ui/image/image_prepare.cpp | 82 +- Telegram/SourceFiles/ui/image/image_prepare.h | 11 +- Telegram/SourceFiles/ui/inactive_press.cpp | 54 + Telegram/SourceFiles/ui/inactive_press.h | 15 + .../linux/ui_platform_utility_linux.cpp | 48 + .../linux/ui_platform_utility_linux.h | 38 + .../ui/platform/mac/ui_platform_utility_mac.h | 23 + .../platform/mac/ui_platform_utility_mac.mm | 88 ++ .../ui/platform/ui_platform_utility.h | 41 + .../platform/win/ui_platform_utility_win.cpp | 32 + .../ui/platform/win/ui_platform_utility_win.h | 41 + Telegram/SourceFiles/ui/round_rect.cpp | 83 ++ Telegram/SourceFiles/ui/round_rect.h | 39 + Telegram/SourceFiles/ui/special_buttons.cpp | 4 + Telegram/SourceFiles/ui/special_buttons.h | 1 + Telegram/SourceFiles/ui/special_fields.cpp | 413 ++++++++ Telegram/SourceFiles/ui/special_fields.h | 121 +++ Telegram/SourceFiles/ui/style/style_core.cpp | 45 +- Telegram/SourceFiles/ui/style/style_core.h | 2 + .../ui/style/style_core_direction.h | 4 + .../SourceFiles/ui/style/style_core_font.cpp | 14 +- Telegram/SourceFiles/ui/text/text.cpp | 109 +- Telegram/SourceFiles/ui/text/text.h | 20 +- Telegram/SourceFiles/ui/text/text_block.cpp | 10 +- Telegram/SourceFiles/ui/text/text_block.h | 3 + Telegram/SourceFiles/ui/text/text_entity.cpp | 988 +++++++++--------- Telegram/SourceFiles/ui/text/text_entity.h | 21 +- .../SourceFiles/ui/text/text_utilities.cpp | 4 + Telegram/SourceFiles/ui/text/text_utilities.h | 2 + Telegram/SourceFiles/ui/text_options.h | 2 + Telegram/SourceFiles/ui/ui_integration.cpp | 120 +++ Telegram/SourceFiles/ui/ui_integration.h | 60 +- Telegram/SourceFiles/ui/ui_log.cpp | 18 + Telegram/SourceFiles/ui/ui_log.h | 16 + Telegram/SourceFiles/ui/ui_pch.h | 10 + Telegram/SourceFiles/ui/ui_utility.cpp | 21 +- Telegram/SourceFiles/ui/ui_utility.h | 11 + Telegram/SourceFiles/ui/widgets/checkbox.h | 3 + .../SourceFiles/ui/widgets/dropdown_menu.cpp | 18 +- .../SourceFiles/ui/widgets/dropdown_menu.h | 17 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 23 +- .../SourceFiles/ui/widgets/inner_dropdown.h | 10 +- .../SourceFiles/ui/widgets/input_fields.cpp | 610 ++--------- .../SourceFiles/ui/widgets/input_fields.h | 148 +-- Telegram/SourceFiles/ui/widgets/labels.cpp | 96 +- Telegram/SourceFiles/ui/widgets/labels.h | 18 +- Telegram/SourceFiles/ui/widgets/menu.cpp | 6 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 63 +- Telegram/SourceFiles/ui/widgets/popup_menu.h | 29 +- .../SourceFiles/ui/widgets/scroll_area.cpp | 23 +- Telegram/SourceFiles/ui/widgets/scroll_area.h | 21 +- .../SourceFiles/ui/widgets/separate_panel.cpp | 9 +- Telegram/SourceFiles/ui/widgets/shadow.cpp | 20 +- Telegram/SourceFiles/ui/widgets/shadow.h | 6 +- Telegram/SourceFiles/ui/widgets/tooltip.cpp | 37 +- Telegram/SourceFiles/ui/widgets/tooltip.h | 5 +- Telegram/SourceFiles/ui/wrap/fade_wrap.cpp | 2 + Telegram/SourceFiles/ui/wrap/slide_wrap.cpp | 2 + Telegram/SourceFiles/ui/wrap/slide_wrap.h | 6 +- Telegram/SourceFiles/window/layer_widget.cpp | 10 + Telegram/SourceFiles/window/layer_widget.h | 12 +- Telegram/SourceFiles/window/main_window.cpp | 16 +- Telegram/SourceFiles/window/main_window.h | 6 - .../window/notifications_manager_default.cpp | 5 +- .../window/themes/window_theme.cpp | 7 +- .../window/themes/window_theme_editor_box.cpp | 1 + .../window/window_lock_widgets.cpp | 3 +- .../emoji_suggestions/emoji_suggestions.cpp | 2 +- Telegram/gyp/codegen.gyp | 4 +- Telegram/gyp/lib_base.gyp | 2 + Telegram/gyp/lib_ui.gyp | 35 +- Telegram/gyp/lib_ui_sources.txt | 91 ++ Telegram/gyp/telegram_sources.txt | 55 +- 189 files changed, 3750 insertions(+), 2572 deletions(-) create mode 100644 Telegram/SourceFiles/api/api_text_entities.cpp create mode 100644 Telegram/SourceFiles/api/api_text_entities.h create mode 100644 Telegram/SourceFiles/base/crc32hash.cpp create mode 100644 Telegram/SourceFiles/base/crc32hash.h delete mode 100644 Telegram/SourceFiles/core/click_handler.cpp create mode 100644 Telegram/SourceFiles/core/core_ui_integration.cpp create mode 100644 Telegram/SourceFiles/core/core_ui_integration.h create mode 100644 Telegram/SourceFiles/ui/basic_click_handlers.cpp create mode 100644 Telegram/SourceFiles/ui/basic_click_handlers.h create mode 100644 Telegram/SourceFiles/ui/click_handler.cpp rename Telegram/SourceFiles/{core => ui}/click_handler.h (57%) create mode 100644 Telegram/SourceFiles/ui/delayed_activation.cpp create mode 100644 Telegram/SourceFiles/ui/delayed_activation.h create mode 100644 Telegram/SourceFiles/ui/inactive_press.cpp create mode 100644 Telegram/SourceFiles/ui/inactive_press.h create mode 100644 Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.cpp create mode 100644 Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.h create mode 100644 Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.h create mode 100644 Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.mm create mode 100644 Telegram/SourceFiles/ui/platform/ui_platform_utility.h create mode 100644 Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.cpp create mode 100644 Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.h create mode 100644 Telegram/SourceFiles/ui/round_rect.cpp create mode 100644 Telegram/SourceFiles/ui/round_rect.h create mode 100644 Telegram/SourceFiles/ui/special_fields.cpp create mode 100644 Telegram/SourceFiles/ui/special_fields.h create mode 100644 Telegram/SourceFiles/ui/ui_integration.cpp create mode 100644 Telegram/SourceFiles/ui/ui_log.cpp create mode 100644 Telegram/SourceFiles/ui/ui_log.h create mode 100644 Telegram/gyp/lib_ui_sources.txt diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 3c03e95ce..a6cd8f0b2 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -72,8 +72,11 @@ linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); -dateRadius: 6px; -buttonRadius: 3px; +roundRadiusLarge: 6px; +roundRadiusSmall: 3px; + +dateRadius: roundRadiusLarge; +buttonRadius: roundRadiusSmall; setLittleSkip: 9px; diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 64ca0da43..45476bfd5 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_sending.h" +#include "api/api_text_entities.h" #include "base/unixtime.h" #include "data/data_document.h" #include "data/data_photo.h" @@ -73,12 +74,12 @@ void SendExistingMedia( auto caption = TextWithEntities{ message.textWithTags.text, - ConvertTextTagsToEntities(message.textWithTags.tags) + TextUtilities::ConvertTextTagsToEntities(message.textWithTags.tags) }; TextUtilities::Trim(caption); - auto sentEntities = TextUtilities::EntitiesToMTP( + auto sentEntities = EntitiesToMTP( caption.entities, - TextUtilities::ConvertOption::SkipLocal); + ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_SendMedia::Flag::f_entities; } diff --git a/Telegram/SourceFiles/api/api_text_entities.cpp b/Telegram/SourceFiles/api/api_text_entities.cpp new file mode 100644 index 000000000..e67b86dcc --- /dev/null +++ b/Telegram/SourceFiles/api/api_text_entities.cpp @@ -0,0 +1,129 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "api/api_text_entities.h" + +#include "main/main_session.h" +#include "data/data_session.h" +#include "data/data_user.h" + +namespace Api { +namespace { + +using namespace TextUtilities; + +} // namespace + +EntitiesInText EntitiesFromMTP(const QVector &entities) { + auto result = EntitiesInText(); + if (!entities.isEmpty()) { + result.reserve(entities.size()); + for_const (auto &entity, entities) { + switch (entity.type()) { + case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break; + case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityPhone: break; // Skipping phones. + case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityMentionName: { + auto &d = entity.c_messageEntityMentionName(); + auto data = [&d] { + if (auto user = Auth().data().userLoaded(d.vuser_id().v)) { + return MentionNameDataFromFields({ + d.vuser_id().v, + user->accessHash() }); + } + return MentionNameDataFromFields(d.vuser_id().v); + }; + result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data() }); + } break; + case mtpc_inputMessageEntityMentionName: { + auto &d = entity.c_inputMessageEntityMentionName(); + auto data = ([&d]() -> QString { + if (d.vuser_id().type() == mtpc_inputUserSelf) { + return MentionNameDataFromFields(Auth().userId()); + } else if (d.vuser_id().type() == mtpc_inputUser) { + auto &user = d.vuser_id().c_inputUser(); + return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v }); + } + return QString(); + })(); + if (!data.isEmpty()) { + result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); + } + } break; + case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break; + case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break; + // #TODO entities + } + } + } + return result; +} + +MTPVector EntitiesToMTP( + const EntitiesInText &entities, + ConvertOption option) { + auto v = QVector(); + v.reserve(entities.size()); + for_const (auto &entity, entities) { + if (entity.length() <= 0) continue; + if (option == ConvertOption::SkipLocal + && entity.type() != EntityType::Bold + && entity.type() != EntityType::Italic + && entity.type() != EntityType::Underline + && entity.type() != EntityType::StrikeOut + && entity.type() != EntityType::Code // #TODO entities + && entity.type() != EntityType::Pre + && entity.type() != EntityType::MentionName + && entity.type() != EntityType::CustomUrl) { + continue; + } + + auto offset = MTP_int(entity.offset()); + auto length = MTP_int(entity.length()); + switch (entity.type()) { + case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break; + case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break; + case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break; + case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break; + case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break; + case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break; + case EntityType::MentionName: { + auto inputUser = ([](const QString &data) -> MTPInputUser { + auto fields = MentionNameDataToFields(data); + if (fields.userId == Auth().userId()) { + return MTP_inputUserSelf(); + } else if (fields.userId) { + return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash)); + } + return MTP_inputUserEmpty(); + })(entity.data()); + if (inputUser.type() != mtpc_inputUserEmpty) { + v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser)); + } + } break; + case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break; + case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break; + case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break; + case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break; + case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break; + case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities + case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break; + } + } + return MTP_vector(std::move(v)); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_text_entities.h b/Telegram/SourceFiles/api/api_text_entities.h new file mode 100644 index 000000000..836a3d77a --- /dev/null +++ b/Telegram/SourceFiles/api/api_text_entities.h @@ -0,0 +1,23 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/text/text_entity.h" + +namespace Api { + +EntitiesInText EntitiesFromMTP(const QVector &entities); +enum class ConvertOption { + WithLocal, + SkipLocal, +}; +MTPVector EntitiesToMTP( + const EntitiesInText &entities, + ConvertOption option = ConvertOption::WithLocal); + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 9cf5ab3e3..e5ad1c9a6 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "apiwrap.h" +#include "api/api_text_entities.h" #include "data/data_drafts.h" #include "data/data_photo.h" #include "data/data_web_page.h" @@ -2562,9 +2563,9 @@ void ApiWrap::saveDraftsToCloud() { if (!textWithTags.tags.isEmpty()) { flags |= MTPmessages_SaveDraft::Flag::f_entities; } - auto entities = TextUtilities::EntitiesToMTP( - ConvertTextTagsToEntities(textWithTags.tags), - TextUtilities::ConvertOption::SkipLocal); + auto entities = Api::EntitiesToMTP( + TextUtilities::ConvertTextTagsToEntities(textWithTags.tags), + Api::ConvertOption::SkipLocal); const auto draftText = textWithTags.text; history->setSentDraftText(draftText); @@ -4830,9 +4831,9 @@ void ApiWrap::editUploadedFile( return; } - auto sentEntities = TextUtilities::EntitiesToMTP( + auto sentEntities = Api::EntitiesToMTP( item->originalText().entities, - TextUtilities::ConvertOption::SkipLocal); + Api::ConvertOption::SkipLocal); auto flagsEditMsg = MTPmessages_EditMessage::Flag::f_message | 0; flagsEditMsg |= MTPmessages_EditMessage::Flag::f_no_webpage; @@ -4934,7 +4935,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { auto sending = TextWithEntities(); auto left = TextWithEntities { textWithTags.text, - ConvertTextTagsToEntities(textWithTags.tags) + TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) }; auto prepareFlags = Ui::ItemTextOptions( history, @@ -4988,8 +4989,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) { if (silentPost) { sendFlags |= MTPmessages_SendMessage::Flag::f_silent; } - auto localEntities = TextUtilities::EntitiesToMTP(sending.entities); - auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal); + auto localEntities = Api::EntitiesToMTP(sending.entities); + auto sentEntities = Api::EntitiesToMTP( + sending.entities, + Api::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_SendMessage::Flag::f_entities; } @@ -5274,9 +5277,9 @@ void ApiWrap::sendMediaWithRandomId( auto caption = item->originalText(); TextUtilities::Trim(caption); - auto sentEntities = TextUtilities::EntitiesToMTP( + auto sentEntities = Api::EntitiesToMTP( caption.entities, - TextUtilities::ConvertOption::SkipLocal); + Api::ConvertOption::SkipLocal); const auto flags = MTPmessages_SendMedia::Flags(0) | (replyTo diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 0dc9dbf19..916b0ed02 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -63,8 +63,6 @@ namespace { *pressedLinkItem = nullptr, *mousedItem = nullptr; - style::font monofont; - struct CornersPixmaps { QPixmap p[4]; }; @@ -138,14 +136,6 @@ namespace App { } } - void tryFontFamily(QString &family, const QString &tryFamily) { - if (family.isEmpty()) { - if (!QFontInfo(QFont(tryFamily)).family().trimmed().compare(tryFamily, Qt::CaseInsensitive)) { - family = tryFamily; - } - } - } - void createMaskCorners() { QImage mask[4]; prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask); @@ -204,16 +194,6 @@ namespace App { } void initMedia() { - if (!::monofont) { - QString family; - tryFontFamily(family, qsl("Consolas")); - tryFontFamily(family, qsl("Liberation Mono")); - tryFontFamily(family, qsl("Menlo")); - tryFontFamily(family, qsl("Courier")); - if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family(); - ::monofont = style::font(st::normalFont->f.pixelSize(), 0, family); - } - createCorners(); using Update = Window::Theme::BackgroundUpdate; @@ -293,10 +273,6 @@ namespace App { mousedItem(nullptr); } - const style::font &monofont() { - return ::monofont; - } - void quit() { if (quitting()) { return; @@ -451,15 +427,6 @@ namespace App { rectWithCorners(p, rect, st::msgInBg, MessageInCorners, corners); } - QImage *cornersMask(ImageRoundRadius radius) { - switch (radius) { - case ImageRoundRadius::Large: return ::cornersMaskLarge; - case ImageRoundRadius::Small: - default: break; - } - return ::cornersMaskSmall; - } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) { auto cornerWidth = corner.p[0].width() / cIntRetinaFactor(); auto cornerHeight = corner.p[0].height() / cIntRetinaFactor(); diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index cb764c410..5f7173781 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -81,8 +81,6 @@ namespace App { HistoryView::Element *mousedItem(); void clearMousedItems(); - const style::font &monofont(); - void initMedia(); void deinitMedia(); @@ -106,7 +104,6 @@ namespace App { void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); - QImage *cornersMask(ImageRoundRadius radius); void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); inline void roundRect(Painter &p, const QRect &rect, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) { return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts); diff --git a/Telegram/SourceFiles/base/algorithm.h b/Telegram/SourceFiles/base/algorithm.h index edcf7684f..fbf53e94c 100644 --- a/Telegram/SourceFiles/base/algorithm.h +++ b/Telegram/SourceFiles/base/algorithm.h @@ -81,6 +81,23 @@ struct pointer_comparator { }; +inline QString FromUtf8Safe(const char *string, int size = -1) { + if (!string || !size) { + return QString(); + } else if (size < 0) { + size = strlen(string); + } + const auto result = QString::fromUtf8(string, size); + const auto back = result.toUtf8(); + return (back.size() != size || memcmp(back.constData(), string, size)) + ? QString::fromLocal8Bit(string, size) + : result; +} + +inline QString FromUtf8Safe(const QByteArray &string) { + return FromUtf8Safe(string.constData(), string.size()); +} + } // namespace base template diff --git a/Telegram/SourceFiles/base/crc32hash.cpp b/Telegram/SourceFiles/base/crc32hash.cpp new file mode 100644 index 000000000..91cbf9920 --- /dev/null +++ b/Telegram/SourceFiles/base/crc32hash.cpp @@ -0,0 +1,61 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "base/crc32hash.h" + +namespace base { +namespace { + +class Crc32Table { +public: + Crc32Table() { + auto poly = std::uint32_t(0x04c11db7); + for (auto i = 0; i != 256; ++i) { + _data[i] = reflect(i, 8) << 24; + for (auto j = 0; j != 8; ++j) { + _data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0); + } + _data[i] = reflect(_data[i], 32); + } + } + + std::uint32_t operator[](int index) const { + return _data[index]; + } + +private: + std::uint32_t reflect(std::uint32_t val, char ch) { + auto result = std::uint32_t(0); + for (int i = 1; i < (ch + 1); ++i) { + if (val & 1) { + result |= 1 << (ch - i); + } + val >>= 1; + } + return result; + } + + std::uint32_t _data[256]; + +}; + +} // namespace + +std::int32_t crc32(const void *data, int len) { + static const auto kTable = Crc32Table(); + + const auto buffer = static_cast(data); + + auto crc = std::uint32_t(0xffffffff); + for (auto i = 0; i != len; ++i) { + crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]]; + } + + return static_cast(crc ^ 0xffffffff); +} + +} // namespace base diff --git a/Telegram/SourceFiles/base/crc32hash.h b/Telegram/SourceFiles/base/crc32hash.h new file mode 100644 index 000000000..dac0b5886 --- /dev/null +++ b/Telegram/SourceFiles/base/crc32hash.h @@ -0,0 +1,16 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +namespace base { + +std::int32_t crc32(const void *data, int len); + +} // namespace base diff --git a/Telegram/SourceFiles/base/object_ptr.h b/Telegram/SourceFiles/base/object_ptr.h index 92479f4db..714ee600a 100644 --- a/Telegram/SourceFiles/base/object_ptr.h +++ b/Telegram/SourceFiles/base/object_ptr.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include + // Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent. template class object_ptr { diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h index bb9aae7f4..f9ca3ccfb 100644 --- a/Telegram/SourceFiles/base/openssl_help.h +++ b/Telegram/SourceFiles/base/openssl_help.h @@ -524,6 +524,17 @@ inline void AddRandomSeed(bytes::const_span data) { RAND_seed(data.data(), data.size()); } +template >> +[[nodiscard]] inline T RandomValue() { + unsigned char buffer[sizeof(T)]; + if (!RAND_bytes(buffer, sizeof(T))) { + Unexpected("Could not generate random bytes!"); + } + auto result = T(); + memcpy(&result, buffer, sizeof(T)); + return result; +} + inline bytes::vector Pbkdf2Sha512( bytes::const_span password, bytes::const_span salt, diff --git a/Telegram/SourceFiles/base/qthelp_url.h b/Telegram/SourceFiles/base/qthelp_url.h index c0e10bc6d..fd8e460be 100644 --- a/Telegram/SourceFiles/base/qthelp_url.h +++ b/Telegram/SourceFiles/base/qthelp_url.h @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include +#include +#include + namespace qthelp { const QRegularExpression &RegExpDomain(); diff --git a/Telegram/SourceFiles/base/timer.cpp b/Telegram/SourceFiles/base/timer.cpp index 07d8c2424..00204ad43 100644 --- a/Telegram/SourceFiles/base/timer.cpp +++ b/Telegram/SourceFiles/base/timer.cpp @@ -108,8 +108,8 @@ void Timer::timerEvent(QTimerEvent *e) { cancel(); } - if (_callback) { - _callback(); + if (const auto onstack = _callback) { + onstack(); } } diff --git a/Telegram/SourceFiles/base/unique_qptr.h b/Telegram/SourceFiles/base/unique_qptr.h index 7a1c1de3f..89bd2dc58 100644 --- a/Telegram/SourceFiles/base/unique_qptr.h +++ b/Telegram/SourceFiles/base/unique_qptr.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include + namespace base { template diff --git a/Telegram/SourceFiles/boxes/abstract_box.cpp b/Telegram/SourceFiles/boxes/abstract_box.cpp index 994c290d5..f4869e7d2 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.cpp +++ b/Telegram/SourceFiles/boxes/abstract_box.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/wrap/fade_wrap.h" #include "ui/text/text_utilities.h" +#include "ui/painter.h" #include "base/timer.h" #include "mainwidget.h" #include "mainwindow.h" @@ -135,7 +136,7 @@ void BoxContent::onDraggingScrollDelta(int delta) { } void BoxContent::onDraggingScrollTimer() { - auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); + auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed)); _scroll->scrollToY(_scroll->scrollTop() + delta); } @@ -276,7 +277,6 @@ AbstractBox::AbstractBox( : LayerWidget(layer) , _layer(layer) , _content(std::move(content)) { - subscribe(Lang::Current().updated(), [=] { refreshLang(); }); _content->setParent(this); _content->setDelegate(this); @@ -398,10 +398,6 @@ bool AbstractBox::closeByOutsideClick() const { return _closeByOutsideClick; } -void AbstractBox::refreshLang() { - InvokeQueued(this, [this] { updateButtonsPositions(); }); -} - bool AbstractBox::hasTitle() const { return (_title != nullptr) || !_additionalTitle.current().isEmpty(); } @@ -464,7 +460,10 @@ QPointer AbstractBox::addButton( auto result = QPointer(_buttons.back()); result->setClickedCallback(std::move(clickCallback)); result->show(); - updateButtonsPositions(); + result->widthValue( + ) | rpl::start_with_next([=] { + updateButtonsPositions(); + }, result->lifetime()); return result; } @@ -476,7 +475,10 @@ QPointer AbstractBox::addLeftButton( auto result = QPointer(_leftButton); result->setClickedCallback(std::move(clickCallback)); result->show(); - updateButtonsPositions(); + result->widthValue( + ) | rpl::start_with_next([=] { + updateButtonsPositions(); + }, result->lifetime()); return result; } diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h index 64930ccc7..0a38b109d 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.h +++ b/Telegram/SourceFiles/boxes/abstract_box.h @@ -11,8 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "base/flags.h" #include "ui/effects/animation_value.h" +#include "ui/text/text_entity.h" #include "ui/rp_widget.h" +class Painter; + namespace style { struct RoundButton; struct IconButton; @@ -77,7 +80,7 @@ public: }; -class BoxContent : public Ui::RpWidget, protected base::Subscriber { +class BoxContent : public Ui::RpWidget { Q_OBJECT public: @@ -270,10 +273,7 @@ private: }; -class AbstractBox - : public Window::LayerWidget - , public BoxContentDelegate - , protected base::Subscriber { +class AbstractBox : public Window::LayerWidget, public BoxContentDelegate { public: AbstractBox( not_null layer, @@ -345,7 +345,6 @@ private: void paintAdditionalTitle(Painter &p); void updateTitlePosition(); - void refreshLang(); [[nodiscard]] bool hasTitle() const; [[nodiscard]] int titleHeight() const; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index b7ddecd53..728afe6ed 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -25,10 +25,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/toast/toast.h" #include "ui/special_buttons.h" +#include "ui/special_fields.h" #include "ui/text_options.h" #include "ui/unread_badge.h" #include "ui/ui_utility.h" diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index ccdcbc3b6..76231b4be 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -139,7 +139,10 @@ private: }; -class SetupChannelBox : public BoxContent, public RPCSender { +class SetupChannelBox + : public BoxContent + , public RPCSender + , private base::Subscriber { public: SetupChannelBox( QWidget*, @@ -234,7 +237,10 @@ private: }; -class RevokePublicLinkBox : public BoxContent, public RPCSender { +class RevokePublicLinkBox + : public BoxContent + , public RPCSender + , private base::Subscriber { public: RevokePublicLinkBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index cec8274bb..7dd55753b 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "mtproto/sender.h" #include "data/data_session.h" +#include "data/data_file_origin.h" #include "boxes/background_preview_box.h" #include "boxes/confirm_box.h" #include "app.h" diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h index 6d97e2f18..879cc0d02 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.h +++ b/Telegram/SourceFiles/boxes/background_preview_box.h @@ -25,7 +25,8 @@ class Checkbox; class BackgroundPreviewBox : public BoxContent - , private HistoryView::SimpleElementDelegate { + , private HistoryView::SimpleElementDelegate + , private base::Subscriber { public: BackgroundPreviewBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/calendar_box.h b/Telegram/SourceFiles/boxes/calendar_box.h index 0f265617b..b718fcf0a 100644 --- a/Telegram/SourceFiles/boxes/calendar_box.h +++ b/Telegram/SourceFiles/boxes/calendar_box.h @@ -17,7 +17,7 @@ namespace Ui { class IconButton; } // namespace Ui -class CalendarBox : public BoxContent { +class CalendarBox : public BoxContent, private base::Subscriber { public: CalendarBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp index 0af50feed..6b50813b7 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" +#include "ui/special_fields.h" #include "boxes/confirm_phone_box.h" #include "boxes/confirm_box.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index dedb0c829..1e6532f69 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -254,8 +254,9 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); if (const auto activated = ClickHandler::unpressed()) { + const auto guard = window(); Ui::hideLayer(); - App::activateClickHandler(activated, e->button()); + ActivateClickHandler(guard, activated, e->button()); return; } BoxContent::mouseReleaseEvent(e); diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index 3d21f5bc5..ce31a2078 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -95,7 +95,7 @@ public: }; -class MaxInviteBox : public BoxContent { +class MaxInviteBox : public BoxContent, private base::Subscriber { public: MaxInviteBox(QWidget*, not_null channel); @@ -201,7 +201,10 @@ private: }; -class ConfirmInviteBox : public BoxContent, public RPCSender { +class ConfirmInviteBox + : public BoxContent + , public RPCSender + , private base::Subscriber { public: ConfirmInviteBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 5d23f0351..36868c467 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/edit_caption_box.h" #include "apiwrap.h" +#include "api/api_text_entities.h" #include "main/main_session.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" @@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_user.h" #include "data/data_session.h" +#include "data/data_file_origin.h" #include "history/history.h" #include "history/history_item.h" #include "lang/lang_keys.h" @@ -31,11 +33,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat_helpers.h" #include "styles/style_history.h" #include "ui/image/image.h" +#include "ui/widgets/input_fields.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/checkbox.h" #include "ui/special_buttons.h" #include "ui/text_options.h" -#include "ui/widgets/input_fields.h" #include "window/window_session_controller.h" -#include "ui/widgets/checkbox.h" #include "confirm_box.h" #include "facades.h" #include "app.h" @@ -898,7 +901,7 @@ void EditCaptionBox::save() { const auto textWithTags = _field->getTextWithAppliedMarkdown(); auto sending = TextWithEntities{ textWithTags.text, - ConvertTextTagsToEntities(textWithTags.tags) + TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) }; const auto prepareFlags = Ui::ItemTextOptions( item->history(), @@ -906,9 +909,9 @@ void EditCaptionBox::save() { TextUtilities::PrepareForSending(sending, prepareFlags); TextUtilities::Trim(sending); - const auto sentEntities = TextUtilities::EntitiesToMTP( + const auto sentEntities = Api::EntitiesToMTP( sending.entities, - TextUtilities::ConvertOption::SkipLocal); + Api::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { flags |= MTPmessages_EditMessage::Flag::f_entities; } @@ -917,7 +920,7 @@ void EditCaptionBox::save() { const auto textWithTags = _field->getTextWithAppliedMarkdown(); auto sending = TextWithEntities{ textWithTags.text, - ConvertTextTagsToEntities(textWithTags.tags) + TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) }; item->setText(sending); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 186fd8a59..5dde6f010 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -35,7 +35,10 @@ namespace Window { class SessionController; } // namespace Window -class EditCaptionBox : public BoxContent, public RPCSender { +class EditCaptionBox + : public BoxContent + , public RPCSender + , private base::Subscriber { public: EditCaptionBox( QWidget*, diff --git a/Telegram/SourceFiles/boxes/edit_color_box.h b/Telegram/SourceFiles/boxes/edit_color_box.h index 60ea019f8..46b8ccf20 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.h +++ b/Telegram/SourceFiles/boxes/edit_color_box.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" -class EditColorBox : public BoxContent { +class EditColorBox : public BoxContent, private base::Subscriber { public: enum class Mode { RGBA, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index a0b6b8eef..b00af3ac3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "ui/special_fields.h" #include "window/window_session_controller.h" #include diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index fab66ef90..bd0ce7590 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -441,7 +441,7 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) { const auto index = stickerFromGlobalPos(e->globalPos()); if (index >= 0 && index < _pack.size() && !isMasksSet()) { const auto sticker = _pack[index]; - Core::App().postponeCall(crl::guard(App::main(), [=] { + Ui::PostponeCall(crl::guard(App::main(), [=] { if (App::main()->onSendSticker(sticker)) { Ui::hideSettingsAndLayer(); } diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index 6bbb4173a..1436e3b5a 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/sender.h" #include "chat_helpers/stickers.h" #include "ui/effects/animations.h" -#include "ui/widgets/input_fields.h" +#include "ui/special_fields.h" class ConfirmBox; @@ -32,7 +32,10 @@ namespace Main { class Session; } // namespace Main -class StickersBox : public BoxContent, public RPCSender { +class StickersBox final + : public BoxContent + , public RPCSender + , private base::Subscriber { public: enum class Section { Installed, diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp index 3f17bb19c..55db8b294 100644 --- a/Telegram/SourceFiles/boxes/username_box.cpp +++ b/Telegram/SourceFiles/boxes/username_box.cpp @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/input_fields.h" +#include "ui/special_fields.h" #include "ui/toast/toast.h" #include "core/application.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 053d602fb..ea0e5147a 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -10,15 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_session.h" #include "data/data_user.h" +#include "data/data_file_origin.h" #include "calls/calls_emoji_fingerprint.h" -#include "styles/style_calls.h" -#include "styles/style_history.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/shadow.h" #include "ui/effects/ripple_animation.h" #include "ui/image/image.h" #include "ui/wrap/fade_wrap.h" +#include "ui/platform/ui_platform_utility.h" #include "ui/empty_userpic.h" #include "ui/emoji_config.h" #include "core/application.h" @@ -31,6 +31,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/main_window.h" #include "layout.h" #include "app.h" +#include "styles/style_calls.h" +#include "styles/style_history.h" #include #include @@ -440,7 +442,7 @@ void Panel::initLayout() { }); createDefaultCacheImage(); - Platform::InitOnTopPanel(this); + Ui::Platform::InitOnTopPanel(this); } void Panel::toggleOpacityAnimation(bool visible) { @@ -592,7 +594,7 @@ bool Panel::isGoodUserPhoto(PhotoData *photo) { void Panel::initGeometry() { auto center = Core::App().getPointForCallPanelCenter(); - _useTransparency = Platform::TranslucentWindowsSupported(center); + _useTransparency = Ui::Platform::TranslucentWindowsSupported(center); setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); _padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); _contentTop = _padding.top() + st::callWidth; @@ -704,7 +706,7 @@ void Panel::paintEvent(QPaintEvent *e) { finishAnimating(); if (!_call || isHidden()) return; } else { - Platform::StartTranslucentPaint(p, e); + Ui::Platform::StartTranslucentPaint(p, e); p.setOpacity(opacity); PainterHighQualityEnabler hq(p); @@ -717,7 +719,7 @@ void Panel::paintEvent(QPaintEvent *e) { } if (_useTransparency) { - Platform::StartTranslucentPaint(p, e); + Ui::Platform::StartTranslucentPaint(p, e); p.drawPixmapLeft(0, 0, width(), _cache); } else { p.drawPixmapLeft(_padding.left(), _padding.top(), width(), _userPhoto); @@ -864,9 +866,9 @@ void Panel::stateChanged(State state) { if (windowHandle()) { // First stateChanged() is called before the first Platform::InitOnTopPanel(this). if ((state == State::Starting) || (state == State::WaitingIncoming)) { - Platform::ReInitOnTopPanel(this); + Ui::Platform::ReInitOnTopPanel(this); } else { - Platform::DeInitOnTopPanel(this); + Ui::Platform::DeInitOnTopPanel(this); } } if (state == State::Established) { diff --git a/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp b/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp index 8eead3b4f..2668efcc6 100644 --- a/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp +++ b/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp @@ -135,7 +135,7 @@ void BotKeyboard::mouseReleaseEvent(QMouseEvent *e) { updateSelected(); if (ClickHandlerPtr activated = ClickHandler::unpressed()) { - App::activateClickHandler(activated, e->button()); + ActivateClickHandler(window(), activated, e->button()); } } @@ -270,6 +270,10 @@ QPoint BotKeyboard::tooltipPos() const { return _lastMousePos; } +bool BotKeyboard::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + QString BotKeyboard::tooltipText() const { if (ClickHandlerPtr lnk = ClickHandler::getActive()) { return lnk->tooltip(); diff --git a/Telegram/SourceFiles/chat_helpers/bot_keyboard.h b/Telegram/SourceFiles/chat_helpers/bot_keyboard.h index e8c0fa589..4b61a3b7f 100644 --- a/Telegram/SourceFiles/chat_helpers/bot_keyboard.h +++ b/Telegram/SourceFiles/chat_helpers/bot_keyboard.h @@ -46,6 +46,7 @@ public: // AbstractTooltipShower interface QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 03589d0b0..a9b4129f0 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -769,6 +769,10 @@ QPoint EmojiListWidget::tooltipPos() const { return _lastMousePos; } +bool EmojiListWidget::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + TabbedSelector::InnerFooter *EmojiListWidget::getFooter() const { return _footer; } diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 564b53e16..68f3ee93a 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -52,6 +52,7 @@ public: // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; rpl::producer chosen() const; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 33196bc3d..cd1dc1d38 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -356,7 +356,7 @@ void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) { int row = _selected / MatrixRowShift, column = _selected % MatrixRowShift; selectInlineResult(row, column); } else { - App::activateClickHandler(activated, e->button()); + ActivateClickHandler(window(), activated, e->button()); } } diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 3eddbe557..93e4400e7 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -39,21 +39,12 @@ using EditLinkAction = Ui::InputField::EditLinkAction; using EditLinkSelection = Ui::InputField::EditLinkSelection; constexpr auto kParseLinksTimeout = crl::time(1000); -const auto kMentionTagStart = qstr("mention://user."); - -bool IsMentionLink(const QString &link) { - return link.startsWith(kMentionTagStart); -} // For mention tags save and validate userId, ignore tags for different userId. class FieldTagMimeProcessor : public Ui::InputField::TagMimeProcessor { public: - QString mimeTagFromTag(const QString &tagId) override { - return ConvertTagToMimeTag(tagId); - } - QString tagFromMimeTag(const QString &mimeTag) override { - if (IsMentionLink(mimeTag)) { + if (TextUtilities::IsMentionLink(mimeTag)) { auto match = QRegularExpression(":(\\d+)$").match(mimeTag); if (!match.hasMatch() || match.capturedRef(1).toInt() != Auth().userId()) { @@ -216,135 +207,20 @@ TextWithEntities StripSupportHashtag(TextWithEntities &&text) { } // namespace -QString ConvertTagToMimeTag(const QString &tagId) { - if (IsMentionLink(tagId)) { - return tagId + ':' + QString::number(Auth().userId()); - } - return tagId; -} - QString PrepareMentionTag(not_null user) { - return kMentionTagStart + return TextUtilities::kMentionTagStart + QString::number(user->bareId()) + '.' + QString::number(user->accessHash()); } -EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) { - EntitiesInText result; - if (tags.isEmpty()) { - return result; - } - - result.reserve(tags.size()); - for (const auto &tag : tags) { - const auto push = [&]( - EntityType type, - const QString &data = QString()) { - result.push_back( - EntityInText(type, tag.offset, tag.length, data)); - }; - if (IsMentionLink(tag.id)) { - if (auto match = qthelp::regex_match("^(\\d+\\.\\d+)(/|$)", tag.id.midRef(kMentionTagStart.size()))) { - push(EntityType::MentionName, match->captured(1)); - } - } else if (tag.id == Ui::InputField::kTagBold) { - push(EntityType::Bold); - } else if (tag.id == Ui::InputField::kTagItalic) { - push(EntityType::Italic); - } else if (tag.id == Ui::InputField::kTagUnderline) { - push(EntityType::Underline); - } else if (tag.id == Ui::InputField::kTagStrikeOut) { - push(EntityType::StrikeOut); - } else if (tag.id == Ui::InputField::kTagCode) { - push(EntityType::Code); - } else if (tag.id == Ui::InputField::kTagPre) { // #TODO entities - push(EntityType::Pre); - } else /*if (ValidateUrl(tag.id)) */{ // We validate when we insert. - push(EntityType::CustomUrl, tag.id); - } - } - return result; -} - -TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) { - TextWithTags::Tags result; - if (entities.isEmpty()) { - return result; - } - - result.reserve(entities.size()); - for (const auto &entity : entities) { - const auto push = [&](const QString &tag) { - result.push_back({ entity.offset(), entity.length(), tag }); - }; - switch (entity.type()) { - case EntityType::MentionName: { - auto match = QRegularExpression(R"(^(\d+\.\d+)$)").match(entity.data()); - if (match.hasMatch()) { - push(kMentionTagStart + entity.data()); - } - } break; - case EntityType::CustomUrl: { - const auto url = entity.data(); - if (Ui::InputField::IsValidMarkdownLink(url) - && !IsMentionLink(url)) { - push(url); - } - } break; - case EntityType::Bold: push(Ui::InputField::kTagBold); break; - case EntityType::Italic: push(Ui::InputField::kTagItalic); break; - case EntityType::Underline: - push(Ui::InputField::kTagUnderline); - break; - case EntityType::StrikeOut: - push(Ui::InputField::kTagStrikeOut); - break; - case EntityType::Code: push(Ui::InputField::kTagCode); break; // #TODO entities - case EntityType::Pre: push(Ui::InputField::kTagPre); break; - } - } - return result; -} - -std::unique_ptr MimeDataFromText( - const TextForMimeData &text) { - if (text.empty()) { - return nullptr; - } - - auto result = std::make_unique(); - result->setText(text.expanded); - auto tags = ConvertEntitiesToTextTags(text.rich.entities); - if (!tags.isEmpty()) { - for (auto &tag : tags) { - tag.id = ConvertTagToMimeTag(tag.id); - } - result->setData( - TextUtilities::TagsTextMimeType(), - text.rich.text.toUtf8()); - result->setData( - TextUtilities::TagsMimeType(), - TextUtilities::SerializeTags(tags)); - } - return result; -} - -void SetClipboardText( - const TextForMimeData &text, - QClipboard::Mode mode) { - if (auto data = MimeDataFromText(text)) { - QGuiApplication::clipboard()->setMimeData(data.release(), mode); - } -} - TextWithTags PrepareEditText(not_null item) { const auto original = item->history()->session().supportMode() ? StripSupportHashtag(item->originalText()) : item->originalText(); return TextWithTags{ original.text, - ConvertEntitiesToTextTags(original.entities) + TextUtilities::ConvertEntitiesToTextTags(original.entities) }; } @@ -363,7 +239,7 @@ Fn(session, text, link, [=]( const QString &text, @@ -617,7 +493,7 @@ void MessageLinksParser::parse() { Expects(tag != tagsEnd); if (Ui::InputField::IsValidMarkdownLink(tag->id) - && !IsMentionLink(tag->id)) { + && !TextUtilities::IsMentionLink(tag->id)) { ranges.push_back({ tag->offset, tag->length, tag->id }); } ++tag; diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index a28632f00..8077c032b 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -21,16 +21,7 @@ namespace Window { class SessionController; } // namespace Window -QString ConvertTagToMimeTag(const QString &tagId); QString PrepareMentionTag(not_null user); - -EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags); -TextWithTags::Tags ConvertEntitiesToTextTags( - const EntitiesInText &entities); -std::unique_ptr MimeDataFromText(const TextForMimeData &text); -void SetClipboardText( - const TextForMimeData &text, - QClipboard::Mode mode = QClipboard::Clipboard); TextWithTags PrepareEditText(not_null item); Fn(st::emojiPanAnimation, Ui::PanelAnimation::Origin::BottomRight); auto inner = rect().marginsRemoved(st::emojiPanMargins); _showAnimation->setFinalImage(std::move(image), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); - auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); + _showAnimation->setCornerMasks(Images::CornersMask(ImageRoundRadius::Small)); _showAnimation->start(); } hideChildren(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 9f01b36f9..4a6c50e58 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -813,8 +813,7 @@ void TabbedSelector::switchTab() { _slideAnimation = std::make_unique(); auto slidingRect = QRect(0, _scroll->y() * cIntRetinaFactor(), width() * cIntRetinaFactor(), (height() - _scroll->y()) * cIntRetinaFactor()); _slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect, wasSectionIcons); - auto corners = App::cornersMask(ImageRoundRadius::Small); - _slideAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); + _slideAnimation->setCornerMasks(Images::CornersMask(ImageRoundRadius::Small)); _slideAnimation->start(); hideForSliding(); diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index d18a64962..385cc1140 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "codegen/style/generator.h" +#include "base/crc32hash.h" + #include #include #include @@ -33,54 +35,6 @@ const auto kMustBeContrast = std::map{ { "windowBoldFg", "windowBg" }, }; -// crc32 hash, taken somewhere from the internet - -class Crc32Table { -public: - Crc32Table() { - quint32 poly = 0x04c11db7; - for (auto i = 0; i != 256; ++i) { - _data[i] = reflect(i, 8) << 24; - for (auto j = 0; j != 8; ++j) { - _data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0); - } - _data[i] = reflect(_data[i], 32); - } - } - - inline quint32 operator[](int index) const { - return _data[index]; - } - -private: - quint32 reflect(quint32 val, char ch) { - quint32 result = 0; - for (int i = 1; i < (ch + 1); ++i) { - if (val & 1) { - result |= 1 << (ch - i); - } - val >>= 1; - } - return result; - } - - quint32 _data[256]; - -}; - -qint32 hashCrc32(const void *data, int len) { - static Crc32Table table; - - const uchar *buffer = static_cast(data); - - quint32 crc = 0xffffffff; - for (int i = 0; i != len; ++i) { - crc = (crc >> 8) ^ table[(crc & 0xFF) ^ buffer[i]]; - } - - return static_cast(crc ^ 0xffffffff); -} - char hexChar(uchar ch) { if (ch < 10) { return '0' + ch; @@ -844,7 +798,7 @@ void palette::finalize() {\n\ return false; } auto count = indexInPalette; - auto checksum = hashCrc32(checksumString.constData(), checksumString.size()); + auto checksum = base::crc32(checksumString.constData(), checksumString.size()); source_->stream() << "\n\n"; for (const auto &[over, under] : kMustBeContrast) { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 52d2662e3..d3460cc08 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -22,11 +22,6 @@ enum { MaxPhoneCodeLength = 4, // max length of country phone code MaxPhoneTailLength = 32, // rest of the phone number, without country code (seen 12 at least), need more for service numbers - MaxScrollSpeed = 37, // 37px per 15ms while select-by-drag - FingerAccuracyThreshold = 3, // touch flick ignore 3px - MaxScrollAccelerated = 4000, // 4000px per second - MaxScrollFlick = 2500, // 2500px per second - LocalEncryptIterCount = 4000, // key derivation iteration count LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway) LocalEncryptSaltSize = 32, // 256 bit diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 7739c2be0..a2b99e6cd 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/sandbox.h" #include "core/local_url_handlers.h" #include "core/launcher.h" +#include "core/core_ui_integration.h" #include "chat_helpers/emoji_keywords.h" #include "storage/localstorage.h" #include "platform/platform_specific.h" @@ -80,6 +81,7 @@ Application *Application::Instance = nullptr; struct Application::Private { base::Timer quitTimer; + UiIntegration uiIntegration; }; Application::Application(not_null launcher) @@ -98,6 +100,8 @@ Application::Application(not_null launcher) Expects(!_logo.isNull()); Expects(!_logoNoMargin.isNull()); + Ui::Integration::Set(&_private->uiIntegration); + activeAccount().sessionChanges( ) | rpl::start_with_next([=] { if (_mediaView) { @@ -778,25 +782,6 @@ void Application::refreshGlobalProxy() { Sandbox::Instance().refreshGlobalProxy(); } -void Application::activateWindowDelayed(not_null widget) { - Sandbox::Instance().activateWindowDelayed(widget); -} - -void Application::pauseDelayedWindowActivations() { - Sandbox::Instance().pauseDelayedWindowActivations(); -} - -void Application::resumeDelayedWindowActivations() { - Sandbox::Instance().resumeDelayedWindowActivations(); -} - -void Application::preventWindowActivation() { - pauseDelayedWindowActivations(); - postponeCall([=] { - resumeDelayedWindowActivations(); - }); -} - void Application::QuitAttempt() { auto prevents = false; if (IsAppLaunched() @@ -871,19 +856,3 @@ Application &App() { } } // namespace Core - -namespace Ui { - -void PostponeCall(FnMut &&callable) { - Core::App().postponeCall(std::move(callable)); -} - -void RegisterLeaveSubscription(not_null widget) { - Core::App().registerLeaveSubscription(widget); -} - -void UnregisterLeaveSubscription(not_null widget) { - Core::App().unregisterLeaveSubscription(widget); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 0435af5aa..4b90819bf 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -205,10 +205,6 @@ public: // Sandbox interface. void postponeCall(FnMut &&callable); void refreshGlobalProxy(); - void activateWindowDelayed(not_null widget); - void pauseDelayedWindowActivations(); - void resumeDelayedWindowActivations(); - void preventWindowActivation(); void quitPreventFinished(); diff --git a/Telegram/SourceFiles/core/click_handler.cpp b/Telegram/SourceFiles/core/click_handler.cpp deleted file mode 100644 index 023f55a4c..000000000 --- a/Telegram/SourceFiles/core/click_handler.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "core/click_handler.h" - -ClickHandlerHost::~ClickHandlerHost() { - ClickHandler::hostDestroyed(this); -} - -NeverFreedPointer ClickHandler::_active; -NeverFreedPointer ClickHandler::_pressed; -ClickHandlerHost *ClickHandler::_activeHost = nullptr; -ClickHandlerHost *ClickHandler::_pressedHost = nullptr; - -bool ClickHandler::setActive(const ClickHandlerPtr &p, ClickHandlerHost *host) { - if ((_active && (*_active == p)) || (!_active && !p)) { - return false; - } - - // emit clickHandlerActiveChanged only when there is no - // other pressed click handler currently, if there is - // this method will be called when it is unpressed - if (_active && *_active) { - const auto emitClickHandlerActiveChanged = false - || !_pressed - || !*_pressed - || (*_pressed == *_active); - const auto wasactive = base::take(*_active); - if (_activeHost) { - if (emitClickHandlerActiveChanged) { - _activeHost->clickHandlerActiveChanged(wasactive, false); - } - _activeHost = nullptr; - } - } - if (p) { - _active.createIfNull(); - *_active = p; - if ((_activeHost = host)) { - bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active); - if (emitClickHandlerActiveChanged) { - _activeHost->clickHandlerActiveChanged(*_active, true); - } - } - } - return true; -} - -auto ClickHandler::getTextEntity() const -> TextEntity { - return { EntityType::Invalid }; -} diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 92e9e8f2d..32828252a 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -10,17 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "core/application.h" #include "core/local_url_handlers.h" -#include "core/file_utilities.h" #include "mainwidget.h" #include "main/main_session.h" -#include "platform/platform_specific.h" -#include "history/view/history_view_element.h" -#include "history/history_item.h" #include "boxes/confirm_box.h" #include "base/qthelp_regex.h" -#include "base/qthelp_url.h" #include "storage/localstorage.h" -#include "ui/widgets/tooltip.h" +#include "history/view/history_view_element.h" +#include "history/history_item.h" #include "data/data_user.h" #include "data/data_session.h" #include "facades.h" @@ -38,63 +34,6 @@ bool UrlRequiresConfirmation(const QUrl &url) { } // namespace -UrlClickHandler::UrlClickHandler(const QString &url, bool fullDisplayed) -: TextClickHandler(fullDisplayed) -, _originalUrl(url) { - if (isEmail()) { - _readable = _originalUrl; - } else { - const auto original = QUrl(_originalUrl); - const auto good = QUrl(original.isValid() - ? original.toEncoded() - : QString()); - _readable = good.isValid() ? good.toDisplayString() : _originalUrl; - } -} - -QString UrlClickHandler::copyToClipboardContextItemText() const { - return isEmail() - ? tr::lng_context_copy_email(tr::now) - : tr::lng_context_copy_link(tr::now); -} - -QString UrlClickHandler::url() const { - if (isEmail()) { - return _originalUrl; - } - - QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString()); - QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl); - - if (!result.isEmpty() && !QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol - return qsl("http://") + result; - } - return result; -} - -void UrlClickHandler::Open(QString url, QVariant context) { - url = Core::TryConvertUrlToLocal(url); - if (Core::InternalPassportLink(url)) { - return; - } - - Ui::Tooltip::Hide(); - if (isEmail(url)) { - File::OpenEmailLink(url); - } else if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { - Core::App().openLocalUrl(url, context); - } else if (!url.isEmpty()) { - QDesktopServices::openUrl(url); - } -} - -auto UrlClickHandler::getTextEntity() const -> TextEntity { - const auto type = isEmail(_originalUrl) - ? EntityType::Email - : EntityType::Url; - return { type, _originalUrl }; -} - void HiddenUrlClickHandler::Open(QString url, QVariant context) { url = Core::TryConvertUrlToLocal(url); if (Core::InternalPassportLink(url)) { diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index a909b97d6..33ea64d0c 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -7,74 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "core/click_handler.h" - -class TextClickHandler : public ClickHandler { -public: - TextClickHandler(bool fullDisplayed = true) - : _fullDisplayed(fullDisplayed) { - } - - QString copyToClipboardText() const override { - return url(); - } - - QString tooltip() const override { - return _fullDisplayed ? QString() : readable(); - } - - void setFullDisplayed(bool full) { - _fullDisplayed = full; - } - -protected: - virtual QString url() const = 0; - virtual QString readable() const { - return url(); - } - - bool _fullDisplayed; - -}; - -class UrlClickHandler : public TextClickHandler { -public: - UrlClickHandler(const QString &url, bool fullDisplayed = true); - - QString copyToClipboardContextItemText() const override; - - QString dragText() const override { - return url(); - } - - TextEntity getTextEntity() const override; - - static void Open(QString url, QVariant context = {}); - void onClick(ClickContext context) const override { - const auto button = context.button; - if (button == Qt::LeftButton || button == Qt::MiddleButton) { - Open(url(), context.other); - } - } - -protected: - QString url() const override; - QString readable() const override { - return _readable; - } - -private: - static bool isEmail(const QString &url) { - int at = url.indexOf('@'), slash = url.indexOf('/'); - return ((at > 0) && (slash < 0 || slash > at)); - } - bool isEmail() const { - return isEmail(_originalUrl); - } - - QString _originalUrl, _readable; - -}; +#include "ui/basic_click_handlers.h" class HiddenUrlClickHandler : public UrlClickHandler { public: @@ -98,6 +31,7 @@ public: }; +class UserData; class BotGameUrlClickHandler : public UrlClickHandler { public: BotGameUrlClickHandler(UserData *bot, QString url) @@ -208,7 +142,6 @@ private: }; class PeerData; -class UserData; class BotCommandClickHandler : public TextClickHandler { public: BotCommandClickHandler(const QString &cmd) : _cmd(cmd) { diff --git a/Telegram/SourceFiles/core/core_ui_integration.cpp b/Telegram/SourceFiles/core/core_ui_integration.cpp new file mode 100644 index 000000000..a05808707 --- /dev/null +++ b/Telegram/SourceFiles/core/core_ui_integration.cpp @@ -0,0 +1,217 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "core/core_ui_integration.h" + +#include "core/local_url_handlers.h" +#include "core/file_utilities.h" +#include "core/application.h" +#include "core/sandbox.h" +#include "core/click_handler_types.h" +#include "ui/basic_click_handlers.h" +#include "ui/emoji_config.h" +#include "lang/lang_keys.h" +#include "platform/platform_specific.h" +#include "main/main_account.h" +#include "main/main_session.h" +#include "mainwindow.h" + +namespace Core { + +void UiIntegration::postponeCall(FnMut &&callable) { + Sandbox::Instance().postponeCall(std::move(callable)); +} + +void UiIntegration::registerLeaveSubscription(not_null widget) { + Core::App().registerLeaveSubscription(widget); +} + +void UiIntegration::unregisterLeaveSubscription(not_null widget) { + Core::App().unregisterLeaveSubscription(widget); +} + +void UiIntegration::writeLogEntry(const QString &entry) { + Logs::writeMain(entry); +} + +QString UiIntegration::emojiCacheFolder() { + return cWorkingDir() + "tdata/emoji"; +} + +void UiIntegration::textActionsUpdated() { + if (const auto window = App::wnd()) { + window->updateGlobalMenu(); + } +} + +void UiIntegration::activationFromTopPanel() { + Platform::IgnoreApplicationActivationRightNow(); +} + +std::shared_ptr UiIntegration::createLinkHandler( + EntityType type, + const QString &text, + const QString &data, + const TextParseOptions &options) { + switch (type) { + case EntityType::CustomUrl: + return !data.isEmpty() + ? std::make_shared(data) + : nullptr; + + case EntityType::BotCommand: + return std::make_shared(data); + + case EntityType::Hashtag: + if (options.flags & TextTwitterMentions) { + return std::make_shared( + (qsl("https://twitter.com/hashtag/") + + data.mid(1) + + qsl("?src=hash")), + true); + } else if (options.flags & TextInstagramMentions) { + return std::make_shared( + (qsl("https://instagram.com/explore/tags/") + + data.mid(1) + + '/'), + true); + } + return std::make_shared(data); + + case EntityType::Cashtag: + return std::make_shared(data); + + case EntityType::Mention: + if (options.flags & TextTwitterMentions) { + return std::make_shared( + qsl("https://twitter.com/") + data.mid(1), + true); + } else if (options.flags & TextInstagramMentions) { + return std::make_shared( + qsl("https://instagram.com/") + data.mid(1) + '/', + true); + } + return std::make_shared(data); + + case EntityType::MentionName: { + auto fields = TextUtilities::MentionNameDataToFields(data); + if (fields.userId) { + return std::make_shared( + text, + fields.userId, + fields.accessHash); + } else { + LOG(("Bad mention name: %1").arg(data)); + } + } break; + } + return nullptr; +} + +bool UiIntegration::handleUrlClick( + const QString &url, + const QVariant &context) { + auto local = Core::TryConvertUrlToLocal(url); + if (Core::InternalPassportLink(local)) { + return true; + } + + if (UrlClickHandler::IsEmail(url)) { + File::OpenEmailLink(url); + return true; + } else if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { + Core::App().openLocalUrl(url, context); + return true; + } + return false; + +} + +rpl::producer<> UiIntegration::forcePopupMenuHideRequests() { + return rpl::merge( + Core::App().passcodeLockChanges(), + Core::App().termsLockChanges() + ) | rpl::map([] { return rpl::empty_value(); }); +} + +QString UiIntegration::convertTagToMimeTag(const QString &tagId) { + if (TextUtilities::IsMentionLink(tagId)) { + const auto &account = Core::App().activeAccount(); + if (account.sessionExists()) { + return tagId + ':' + QString::number(account.session().userId()); + } + } + return tagId; +} + +const Ui::Emoji::One *UiIntegration::defaultEmojiVariant( + const Ui::Emoji::One *emoji) { + if (!emoji || !emoji->hasVariants()) { + return emoji; + } + const auto nonColored = emoji->nonColoredId(); + const auto it = cEmojiVariants().constFind(nonColored); + const auto result = (it != cEmojiVariants().cend()) + ? emoji->variant(it.value()) + : emoji; + AddRecentEmoji(result); + return result; +} + +QString UiIntegration::phraseContextCopyText() { + return tr::lng_context_copy_text(tr::now); +} + +QString UiIntegration::phraseContextCopyEmail() { + return tr::lng_context_copy_email(tr::now); +} + +QString UiIntegration::phraseContextCopyLink() { + return tr::lng_context_copy_link(tr::now); +} + +QString UiIntegration::phraseContextCopySelected() { + return tr::lng_context_copy_selected(tr::now); +} + +QString UiIntegration::phraseFormattingTitle() { + return tr::lng_menu_formatting(tr::now); +} + +QString UiIntegration::phraseFormattingLinkCreate() { + return tr::lng_menu_formatting_link_create(tr::now); +} + +QString UiIntegration::phraseFormattingLinkEdit() { + return tr::lng_menu_formatting_link_edit(tr::now); +} + +QString UiIntegration::phraseFormattingClear() { + return tr::lng_menu_formatting_clear(tr::now); +} + +QString UiIntegration::phraseFormattingBold() { + return tr::lng_menu_formatting_bold(tr::now); +} + +QString UiIntegration::phraseFormattingItalic() { + return tr::lng_menu_formatting_italic(tr::now); +} + +QString UiIntegration::phraseFormattingUnderline() { + return tr::lng_menu_formatting_underline(tr::now); +} + +QString UiIntegration::phraseFormattingStrikeOut() { + return tr::lng_menu_formatting_strike_out(tr::now); +} + +QString UiIntegration::phraseFormattingMonospace() { + return tr::lng_menu_formatting_monospace(tr::now); +} + +} // namespace Core diff --git a/Telegram/SourceFiles/core/core_ui_integration.h b/Telegram/SourceFiles/core/core_ui_integration.h new file mode 100644 index 000000000..e52bbb2ed --- /dev/null +++ b/Telegram/SourceFiles/core/core_ui_integration.h @@ -0,0 +1,55 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/ui_integration.h" + +namespace Core { + +class UiIntegration : public Ui::Integration { +public: + void postponeCall(FnMut &&callable) override; + void registerLeaveSubscription(not_null widget) override; + void unregisterLeaveSubscription(not_null widget) override; + + void writeLogEntry(const QString &entry) override; + QString emojiCacheFolder() override; + + void textActionsUpdated() override; + void activationFromTopPanel() override; + + std::shared_ptr createLinkHandler( + EntityType type, + const QString &text, + const QString &data, + const TextParseOptions &options) override; + bool handleUrlClick( + const QString &url, + const QVariant &context) override; + rpl::producer<> forcePopupMenuHideRequests() override; + QString convertTagToMimeTag(const QString &tagId) override; + const Ui::Emoji::One *defaultEmojiVariant( + const Ui::Emoji::One *emoji) override; + + QString phraseContextCopyText() override; + QString phraseContextCopyEmail() override; + QString phraseContextCopyLink() override; + QString phraseContextCopySelected() override; + QString phraseFormattingTitle() override; + QString phraseFormattingLinkCreate() override; + QString phraseFormattingLinkEdit() override; + QString phraseFormattingClear() override; + QString phraseFormattingBold() override; + QString phraseFormattingItalic() override; + QString phraseFormattingUnderline() override; + QString phraseFormattingStrikeOut() override; + QString phraseFormattingMonospace() override; + +}; + +} // namespace Core diff --git a/Telegram/SourceFiles/core/file_utilities.cpp b/Telegram/SourceFiles/core/file_utilities.cpp index dbd46ff07..0434d5599 100644 --- a/Telegram/SourceFiles/core/file_utilities.cpp +++ b/Telegram/SourceFiles/core/file_utilities.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_file_utilities.h" #include "core/application.h" #include "base/unixtime.h" +#include "ui/delayed_activation.h" #include "mainwindow.h" #include @@ -26,7 +27,7 @@ bool filedialogGetSaveFile( const QString &initialPath) { QStringList files; QByteArray remoteContent; - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); bool result = Platform::FileDialog::Get( parent, files, @@ -119,7 +120,7 @@ namespace File { void OpenEmailLink(const QString &email) { crl::on_main([=] { - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); Platform::File::UnsafeOpenEmailLink(email); }); } @@ -127,7 +128,7 @@ void OpenEmailLink(const QString &email) { void OpenWith(const QString &filepath, QPoint menuPosition) { InvokeQueued(QCoreApplication::instance(), [=] { if (!Platform::File::UnsafeShowOpenWithDropdown(filepath, menuPosition)) { - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); if (!Platform::File::UnsafeShowOpenWith(filepath)) { Platform::File::UnsafeLaunch(filepath); } @@ -137,14 +138,14 @@ void OpenWith(const QString &filepath, QPoint menuPosition) { void Launch(const QString &filepath) { crl::on_main([=] { - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); Platform::File::UnsafeLaunch(filepath); }); } void ShowInFolder(const QString &filepath) { crl::on_main([=] { - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); Platform::File::UnsafeShowInFolder(filepath); }); } @@ -231,7 +232,7 @@ void GetOpenPath( InvokeQueued(QCoreApplication::instance(), [=] { auto files = QStringList(); auto remoteContent = QByteArray(); - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); const auto success = Platform::FileDialog::Get( parent, files, @@ -265,7 +266,7 @@ void GetOpenPaths( InvokeQueued(QCoreApplication::instance(), [=] { auto files = QStringList(); auto remoteContent = QByteArray(); - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); const auto success = Platform::FileDialog::Get( parent, files, @@ -314,7 +315,7 @@ void GetFolder( InvokeQueued(QCoreApplication::instance(), [=] { auto files = QStringList(); auto remoteContent = QByteArray(); - Core::App().preventWindowActivation(); + Ui::PreventDelayedActivation(); const auto success = Platform::FileDialog::Get( parent, files, diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 702401a7c..a9190fc2b 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -323,7 +323,7 @@ QStringList Launcher::readArguments(int argc, char *argv[]) const { auto result = QStringList(); result.reserve(argc); for (auto i = 0; i != argc; ++i) { - result.push_back(fromUtf8Safe(argv[i])); + result.push_back(base::FromUtf8Safe(argv[i])); } return result; } diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index b56fd8afe..0799098dc 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "core/local_url_handlers.h" +#include "api/api_text_entities.h" #include "base/qthelp_regex.h" #include "base/qthelp_url.h" #include "lang/lang_cloud_manager.h" @@ -337,8 +338,7 @@ bool HandleUnknown( const auto callback = [=](const MTPDhelp_deepLinkInfo &result) { const auto text = TextWithEntities{ qs(result.vmessage()), - TextUtilities::EntitiesFromMTP( - result.ventities().value_or_empty()) + Api::EntitiesFromMTP(result.ventities().value_or_empty()) }; if (result.is_update_app()) { const auto box = std::make_shared>(); diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 5d9a4e303..638717db5 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -542,30 +542,6 @@ bool Sandbox::nativeEventFilter( return false; } -void Sandbox::activateWindowDelayed(not_null widget) { - if (_delayedActivationsPaused) { - return; - } else if (std::exchange(_windowForDelayedActivation, widget.get())) { - return; - } - crl::on_main(this, [=] { - if (const auto widget = base::take(_windowForDelayedActivation)) { - if (!widget->isHidden()) { - widget->activateWindow(); - } - } - }); -} - -void Sandbox::pauseDelayedWindowActivations() { - _windowForDelayedActivation = nullptr; - _delayedActivationsPaused = true; -} - -void Sandbox::resumeDelayedWindowActivations() { - _delayedActivationsPaused = false; -} - rpl::producer<> Sandbox::widgetUpdateRequests() const { return _widgetUpdateRequests.events(); } diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h index 3b31f878e..bf42c2267 100644 --- a/Telegram/SourceFiles/core/sandbox.h +++ b/Telegram/SourceFiles/core/sandbox.h @@ -48,10 +48,6 @@ public: return callable(); } - void activateWindowDelayed(not_null widget); - void pauseDelayedWindowActivations(); - void resumeDelayedWindowActivations(); - rpl::producer<> widgetUpdateRequests() const; ProxyData sandboxProxy() const; @@ -110,9 +106,6 @@ private: std::vector _previousLoopNestingLevels; std::vector _postponedCalls; - QPointer _windowForDelayedActivation; - bool _delayedActivationsPaused = false; - not_null _launcher; std::unique_ptr _application; diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp index e411864c5..0daea75a1 100644 --- a/Telegram/SourceFiles/core/utils.cpp +++ b/Telegram/SourceFiles/core/utils.cpp @@ -438,50 +438,6 @@ int GetNextRequestId() { return result; } -// crc32 hash, taken somewhere from the internet - -namespace { - uint32 _crc32Table[256]; - class _Crc32Initializer { - public: - _Crc32Initializer() { - uint32 poly = 0x04c11db7; - for (uint32 i = 0; i < 256; ++i) { - _crc32Table[i] = reflect(i, 8) << 24; - for (uint32 j = 0; j < 8; ++j) { - _crc32Table[i] = (_crc32Table[i] << 1) ^ (_crc32Table[i] & (1 << 31) ? poly : 0); - } - _crc32Table[i] = reflect(_crc32Table[i], 32); - } - } - - private: - uint32 reflect(uint32 val, char ch) { - uint32 result = 0; - for (int i = 1; i < (ch + 1); ++i) { - if (val & 1) { - result |= 1 << (ch - i); - } - val >>= 1; - } - return result; - } - }; -} - -int32 hashCrc32(const void *data, uint32 len) { - static _Crc32Initializer _crc32Initializer; - - const uchar *buf = (const uchar *)data; - - uint32 crc(0xffffffff); - for (uint32 i = 0; i < len; ++i) { - crc = (crc >> 8) ^ _crc32Table[(crc & 0xFF) ^ buf[i]]; - } - - return crc ^ 0xffffffff; -} - int32 *hashSha1(const void *data, uint32 len, void *dest) { return (int32*)SHA1((const uchar*)data, (size_t)len, (uchar*)dest); } diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 2484ceb91..c219f036e 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -127,8 +127,6 @@ private: }; -int32 hashCrc32(const void *data, uint32 len); - int32 *hashSha1(const void *data, uint32 len, void *dest); // dest - ptr to 20 bytes, returns (int32*)dest inline std::array hashSha1(const void *data, int size) { auto result = std::array(); @@ -198,19 +196,6 @@ private: }; -inline QString fromUtf8Safe(const char *str, int32 size = -1) { - if (!str || !size) return QString(); - if (size < 0) size = int32(strlen(str)); - QString result(QString::fromUtf8(str, size)); - QByteArray back = result.toUtf8(); - if (back.size() != size || memcmp(back.constData(), str, size)) return QString::fromLocal8Bit(str, size); - return result; -} - -inline QString fromUtf8Safe(const QByteArray &str) { - return fromUtf8Safe(str.constData(), str.size()); -} - static const QRegularExpression::PatternOptions reMultiline(QRegularExpression::DotMatchesEverythingOption | QRegularExpression::MultilineOption); template diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp index f6139b859..3e11daf77 100644 --- a/Telegram/SourceFiles/data/data_drafts.cpp +++ b/Telegram/SourceFiles/data/data_drafts.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_drafts.h" +#include "api/api_text_entities.h" #include "ui/widgets/input_fields.h" #include "chat_helpers/message_field.h" #include "history/history.h" @@ -48,9 +49,8 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { const auto history = Auth().data().history(peerId); const auto textWithTags = TextWithTags { qs(draft.vmessage()), - ConvertEntitiesToTextTags( - TextUtilities::EntitiesFromMTP( - draft.ventities().value_or_empty())) + TextUtilities::ConvertEntitiesToTextTags( + Api::EntitiesFromMTP(draft.ventities().value_or_empty())) }; auto replyTo = draft.vreply_to_msg_id().value_or_empty(); if (history->skipCloudDraft(textWithTags.text, replyTo, draft.vdate().v)) { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 624b0d661..de36eab49 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -13,7 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_folder.h" #include "data/data_session.h" +#include "data/data_file_origin.h" #include "base/unixtime.h" +#include "base/crc32hash.h" #include "lang/lang_keys.h" #include "observer_peer.h" #include "apiwrap.h" @@ -69,7 +71,7 @@ style::color PeerUserpicColor(PeerId peerId) { PeerId FakePeerIdForJustName(const QString &name) { return peerFromUser(name.isEmpty() ? 777 - : hashCrc32(name.constData(), name.size() * sizeof(QChar))); + : base::crc32(name.constData(), name.size() * sizeof(QChar))); } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 429d840e6..288359fee 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_session.h" #include "api/api_hash.h" +#include "api/api_text_entities.h" #include "main/main_session.h" #include "history/history.h" #include "history/history_item_components.h" @@ -298,8 +299,7 @@ HistoryItem *ScheduledMessages::append( message.match([&](const MTPDmessage &data) { existing->updateSentContent({ qs(data.vmessage()), - TextUtilities::EntitiesFromMTP( - data.ventities().value_or_empty()) + Api::EntitiesFromMTP(data.ventities().value_or_empty()) }, data.vmedia()); existing->updateReplyMarkup(data.vreply_markup()); existing->updateForwardedInfo(data.vfwd_from()); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index c4a783efa..17ffef451 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "apiwrap.h" #include "mainwidget.h" +#include "api/api_text_entities.h" #include "core/application.h" #include "core/crash_reports.h" // CrashReports::SetAnnotation #include "ui/image/image.h" @@ -1650,7 +1651,7 @@ bool Session::checkEntitiesAndViewsUpdate(const MTPDmessage &data) { if (const auto existing = message(peerToChannel(peer), data.vid().v)) { existing->updateSentContent({ qs(data.vmessage()), - TextUtilities::EntitiesFromMTP(data.ventities().value_or_empty()) + Api::EntitiesFromMTP(data.ventities().value_or_empty()) }, data.vmedia()); existing->updateReplyMarkup(data.vreply_markup()); existing->updateForwardedInfo(data.vfwd_from()); @@ -3676,7 +3677,7 @@ void Session::insertCheckedServiceNotification( MTP_string(sending.text), media, MTPReplyMarkup(), - TextUtilities::EntitiesToMTP(sending.entities), + Api::EntitiesToMTP(sending.entities), MTPint(), MTPint(), MTPstring(), diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index e2e074e69..020dfed4c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -727,7 +727,7 @@ void Widget::onDraggingScrollDelta(int delta) { } void Widget::onDraggingScrollTimer() { - auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); + auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed)); _scroll->scrollToY(_scroll->scrollTop() + delta); } diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index e12eb114e..a4a8a501b 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -169,16 +169,6 @@ void showSettings() { } } -void activateClickHandler(ClickHandlerPtr handler, ClickContext context) { - crl::on_main(App::wnd(), [=] { - handler->onClick(context); - }); -} - -void activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button) { - activateClickHandler(handler, ClickContext{ button }); -} - } // namespace App namespace Ui { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 2817fd8bf..4e40fe6e3 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -74,9 +74,6 @@ void activateBotCommand( void searchByHashtag(const QString &tag, PeerData *inPeer); void showSettings(); -void activateClickHandler(ClickHandlerPtr handler, ClickContext context); -void activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); - } // namespace App namespace Ui { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 6d3b52db6..36f8d7a71 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/image/image.h" #include "ui/text/text_utilities.h" +#include "ui/inactive_press.h" #include "core/file_utilities.h" #include "lang/lang_keys.h" #include "boxes/peers/edit_participant_box.h" @@ -40,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_media_types.h" +#include "data/data_file_origin.h" #include "data/data_channel.h" #include "data/data_user.h" #include "facades.h" @@ -515,6 +517,10 @@ QPoint InnerWidget::tooltipPos() const { return _mousePosition; } +bool InnerWidget::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + HistoryView::Context InnerWidget::elementContext() { return HistoryView::Context::AdminLog; } @@ -941,7 +947,7 @@ void InnerWidget::keyPressEvent(QKeyEvent *e) { copySelectedText(); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - SetClipboardText(getSelectedText(), QClipboard::FindBuffer); + TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else { e->ignore(); @@ -1143,7 +1149,7 @@ void InnerWidget::copyContextImage(PhotoData *photo) { } void InnerWidget::copySelectedText() { - SetClipboardText(getSelectedText()); + TextUtilities::SetClipboardText(getSelectedText()); } void InnerWidget::showStickerPackInfo(not_null document) { @@ -1174,7 +1180,7 @@ void InnerWidget::openContextGif(FullMsgId itemId) { void InnerWidget::copyContextText(FullMsgId itemId) { if (const auto item = session().data().message(itemId)) { - SetClipboardText(HistoryItemText(item)); + TextUtilities::SetClipboardText(HistoryItemText(item)); } } @@ -1324,8 +1330,10 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt _dragStartPosition = mapPointToItem( mapFromGlobal(screenPos), _mouseActionItem); - _pressWasInactive = _controller->widget()->wasInactivePress(); - if (_pressWasInactive) _controller->widget()->setInactivePress(false); + _pressWasInactive = Ui::WasInactivePress(_controller->widget()); + if (_pressWasInactive) { + Ui::MarkInactivePress(_controller->widget(), false); + } if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; @@ -1412,7 +1420,7 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but if (activated) { mouseActionCancel(); - App::activateClickHandler(activated, button); + ActivateClickHandler(window(), activated, button); return; } if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) { @@ -1432,7 +1440,7 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 if (_selectedItem && _selectedText.from != _selectedText.to) { - SetClipboardText( + TextUtilities::SetClipboardText( _selectedItem->selectedText(_selectedText), QClipboard::Selection); } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index beb9b1672..f20c58382 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -81,6 +81,7 @@ public: // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; // HistoryView::ElementDelegate interface. HistoryView::Context elementContext() override; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 958603bd8..41615d44a 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_service.h" #include "history/history_message.h" #include "history/history.h" +#include "api/api_text_entities.h" #include "data/data_channel.h" #include "data/data_user.h" #include "data/data_session.h" @@ -116,7 +117,7 @@ TextWithEntities ExtractEditedText(const MTPMessage &message) { const auto &data = message.c_message(); return { TextUtilities::Clean(qs(data.vmessage())), - TextUtilities::EntitiesFromMTP(data.ventities().value_or_empty()) + Api::EntitiesFromMTP(data.ventities().value_or_empty()) }; } diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 583f7725c..ec5b9c2ea 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -371,7 +371,7 @@ botKbScroll: defaultSolidScroll; historyDateFadeDuration: 200; historyPhotoLeft: 14px; -historyMessageRadius: 6px; +historyMessageRadius: roundRadiusLarge; historyBubbleTailInLeft: icon {{ "bubble_tail", msgInBg }}; historyBubbleTailInLeftSelected: icon {{ "bubble_tail", msgInBgSelected }}; historyBubbleTailOutLeft: icon {{ "bubble_tail", msgOutBg }}; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 202e3eb71..abf6b33a7 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text_options.h" #include "ui/ui_utility.h" +#include "ui/inactive_press.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "boxes/confirm_box.h" @@ -50,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "data/data_photo.h" #include "data/data_user.h" +#include "data/data_file_origin.h" #include "facades.h" #include "app.h" @@ -826,23 +828,23 @@ void HistoryInner::touchUpdateSpeed() { // fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because // of a small horizontal offset when scrolling vertically - const int newSpeedY = (qAbs(pixelsPerSecond.y()) > FingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; - const int newSpeedX = (qAbs(pixelsPerSecond.x()) > FingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; + const int newSpeedY = (qAbs(pixelsPerSecond.y()) > Ui::kFingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; + const int newSpeedX = (qAbs(pixelsPerSecond.x()) > Ui::kFingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; if (_touchScrollState == Ui::TouchScrollState::Auto) { const int oldSpeedY = _touchSpeed.y(); const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) && (oldSpeedX <= 0 && newSpeedX <= 0)) || (oldSpeedX >= 0 && newSpeedX >= 0)) { - _touchSpeed.setY(snap((oldSpeedY + (newSpeedY / 4)), -MaxScrollAccelerated, +MaxScrollAccelerated)); - _touchSpeed.setX(snap((oldSpeedX + (newSpeedX / 4)), -MaxScrollAccelerated, +MaxScrollAccelerated)); + _touchSpeed.setY(snap((oldSpeedY + (newSpeedY / 4)), -Ui::kMaxScrollAccelerated, +Ui::kMaxScrollAccelerated)); + _touchSpeed.setX(snap((oldSpeedX + (newSpeedX / 4)), -Ui::kMaxScrollAccelerated, +Ui::kMaxScrollAccelerated)); } else { _touchSpeed = QPoint(); } } else { // we average the speed to avoid strange effects with the last delta if (!_touchSpeed.isNull()) { - _touchSpeed.setX(snap((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -MaxScrollFlick, +MaxScrollFlick)); - _touchSpeed.setY(snap((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -MaxScrollFlick, +MaxScrollFlick)); + _touchSpeed.setX(snap((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -Ui::kMaxScrollFlick, +Ui::kMaxScrollFlick)); + _touchSpeed.setY(snap((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -Ui::kMaxScrollFlick, +Ui::kMaxScrollFlick)); } else { _touchSpeed = QPoint(newSpeedX, newSpeedY); } @@ -1033,8 +1035,10 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but ? mouseActionView->data().get() : nullptr; _dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), mouseActionView); - _pressWasInactive = _controller->widget()->wasInactivePress(); - if (_pressWasInactive) _controller->widget()->setInactivePress(false); + _pressWasInactive = Ui::WasInactivePress(_controller->widget()); + if (_pressWasInactive) { + Ui::MarkInactivePress(_controller->widget(), false); + } if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; @@ -1187,7 +1191,7 @@ std::unique_ptr HistoryInner::prepareDrag() { } return TextForMimeData(); }(); - if (auto mimeData = MimeDataFromText(selectedText)) { + if (auto mimeData = TextUtilities::MimeDataFromText(selectedText)) { updateDragSelection(nullptr, nullptr, false); _widget->noSelectingScroll(); @@ -1343,7 +1347,7 @@ void HistoryInner::mouseActionFinish( const auto pressedItemId = pressedItemView ? pressedItemView->data()->fullId() : FullMsgId(); - App::activateClickHandler(activated, { + ActivateClickHandler(window(), activated, { button, QVariant::fromValue(pressedItemId) }); @@ -1401,7 +1405,7 @@ void HistoryInner::mouseActionFinish( if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { - SetClipboardText( + TextUtilities::SetClipboardText( view->selectedText(selection), QClipboard::Selection); } @@ -1818,7 +1822,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } void HistoryInner::copySelectedText() { - SetClipboardText(getSelectedText()); + TextUtilities::SetClipboardText(getSelectedText()); } void HistoryInner::savePhotoToFile(not_null photo) { @@ -1893,9 +1897,9 @@ void HistoryInner::saveContextGif(FullMsgId itemId) { void HistoryInner::copyContextText(FullMsgId itemId) { if (const auto item = session().data().message(itemId)) { if (const auto group = session().data().groups().find(item)) { - SetClipboardText(HistoryGroupText(group)); + TextUtilities::SetClipboardText(HistoryGroupText(group)); } else { - SetClipboardText(HistoryItemText(item)); + TextUtilities::SetClipboardText(HistoryItemText(item)); } } } @@ -1986,7 +1990,7 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - SetClipboardText(getSelectedText(), QClipboard::FindBuffer); + TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { auto selectedState = getSelectionState(); @@ -3194,6 +3198,10 @@ QPoint HistoryInner::tooltipPos() const { return _mousePosition; } +bool HistoryInner::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + void HistoryInner::onParentGeometryChanged() { auto mousePos = QCursor::pos(); auto mouseOver = _widget->rect().contains(_widget->mapFromGlobal(mousePos)); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 41a863c42..b26d5e7de 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -110,6 +110,7 @@ public: // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; // HistoryView::ElementDelegate interface. static not_null ElementDelegate(); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 071d7b8d4..e4f3633da 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" +#include "api/api_text_entities.h" #include "history/history.h" #include "history/history_item_components.h" #include "history/history_location_manager.h" @@ -442,7 +443,7 @@ HistoryMessage::HistoryMessage( } setText({ TextUtilities::Clean(qs(data.vmessage())), - TextUtilities::EntitiesFromMTP(data.ventities().value_or_empty()) + Api::EntitiesFromMTP(data.ventities().value_or_empty()) }); if (const auto groupedId = data.vgrouped_id()) { setGroupId( @@ -1051,7 +1052,7 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) { const auto textWithEntities = TextWithEntities{ qs(message.vmessage()), - TextUtilities::EntitiesFromMTP(message.ventities().value_or_empty()) + Api::EntitiesFromMTP(message.ventities().value_or_empty()) }; setReplyMarkup(message.vreply_markup()); if (!isLocalUpdateMedia()) { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 254af33ea..b02b41f62 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_widget.h" #include "api/api_sending.h" +#include "api/api_text_entities.h" #include "boxes/confirm_box.h" #include "boxes/send_files_box.h" #include "boxes/share_box.h" @@ -77,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/text_options.h" #include "ui/unread_badge.h" +#include "ui/delayed_activation.h" #include "main/main_session.h" #include "window/themes/window_theme.h" #include "window/notifications_manager.h" @@ -133,7 +135,7 @@ ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() { void ActivateWindow(not_null controller) { const auto window = controller->widget(); window->activateWindow(); - Core::App().activateWindowDelayed(window); + Ui::ActivateWindowDelayed(window); } bool ShowHistoryEndInsteadOfUnread( @@ -2833,7 +2835,9 @@ void HistoryWidget::saveEditMsg() { _history, session().user()).flags; auto sending = TextWithEntities(); - auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) }; + auto left = TextWithEntities { + textWithTags.text, + TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) }; TextUtilities::PrepareForSending(left, prepareFlags); if (!TextUtilities::CutPart(sending, left, MaxMessageSize)) { @@ -2854,8 +2858,10 @@ void HistoryWidget::saveEditMsg() { if (webPageId == CancelledWebPageId) { sendFlags |= MTPmessages_EditMessage::Flag::f_no_webpage; } - auto localEntities = TextUtilities::EntitiesToMTP(sending.entities); - auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal); + auto localEntities = Api::EntitiesToMTP(sending.entities); + auto sentEntities = Api::EntitiesToMTP( + sending.entities, + Api::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_EditMessage::Flag::f_entities; } @@ -4497,14 +4503,14 @@ void HistoryWidget::sendFileConfirmed( auto caption = TextWithEntities{ file->caption.text, - ConvertTextTagsToEntities(file->caption.tags) + TextUtilities::ConvertTextTagsToEntities(file->caption.tags) }; const auto prepareFlags = Ui::ItemTextOptions( history, session().user()).flags; TextUtilities::PrepareForSending(caption, prepareFlags); TextUtilities::Trim(caption); - auto localEntities = TextUtilities::EntitiesToMTP(caption.entities); + auto localEntities = Api::EntitiesToMTP(caption.entities); if (itemToEdit) { if (const auto id = itemToEdit->groupId()) { @@ -6870,7 +6876,7 @@ QPoint HistoryWidget::clampMousePosition(QPoint point) { } void HistoryWidget::onScrollTimer() { - auto d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); + auto d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed)); _scroll->scrollToY(_scroll->scrollTop() + d); } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 8d857d8bb..76726733f 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_groups.h" #include "data/data_channel.h" +#include "data/data_file_origin.h" #include "core/file_utilities.h" #include "platform/platform_info.h" #include "window/window_peer_menu.h" @@ -576,7 +577,7 @@ base::unique_qptr FillContextMenu( ? tr::lng_context_copy_selected(tr::now) : tr::lng_context_copy_selected_items(tr::now); result->addAction(text, [=] { - SetClipboardText(list->getSelectedText()); + TextUtilities::SetClipboardText(list->getSelectedText()); }); } @@ -609,11 +610,11 @@ base::unique_qptr FillContextMenu( if (const auto item = owner->message(itemId)) { if (asGroup) { if (const auto group = owner->groups().find(item)) { - SetClipboardText(HistoryGroupText(group)); + TextUtilities::SetClipboardText(HistoryGroupText(group)); return; } } - SetClipboardText(HistoryItemText(item)); + TextUtilities::SetClipboardText(HistoryItemText(item)); } }); } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index be5746de3..38745d248 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/widgets/popup_menu.h" #include "ui/toast/toast.h" +#include "ui/inactive_press.h" #include "lang/lang_keys.h" #include "boxes/peers/edit_participant_box.h" #include "data/data_session.h" @@ -1090,6 +1091,10 @@ QPoint ListWidget::tooltipPos() const { return _mousePosition; } +bool ListWidget::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + Context ListWidget::elementContext() { return _delegate->listContext(); } @@ -1545,11 +1550,11 @@ void ListWidget::keyPressEvent(QKeyEvent *e) { } } else if (e == QKeySequence::Copy && (hasSelectedText() || hasSelectedItems())) { - SetClipboardText(getSelectedText()); + TextUtilities::SetClipboardText(getSelectedText()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - SetClipboardText(getSelectedText(), QClipboard::FindBuffer); + TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { _delegate->listDeleteRequest(); @@ -1893,8 +1898,10 @@ void ListWidget::mouseActionStart( const auto pressElement = _overElement; _mouseAction = MouseAction::None; - _pressWasInactive = _controller->widget()->wasInactivePress(); - if (_pressWasInactive) _controller->widget()->setInactivePress(false); + _pressWasInactive = Ui::WasInactivePress(_controller->widget()); + if (_pressWasInactive) { + Ui::MarkInactivePress(_controller->widget(), false); + } if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; @@ -2016,7 +2023,7 @@ void ListWidget::mouseActionFinish( activated = nullptr; } else if (activated) { mouseActionCancel(); - App::activateClickHandler(activated, { + ActivateClickHandler(window(), activated, { button, QVariant::fromValue(pressState.itemId) }); @@ -2058,7 +2065,7 @@ void ListWidget::mouseActionFinish( if (_selectedTextItem && _selectedTextRange.from != _selectedTextRange.to) { if (const auto view = viewForItem(_selectedTextItem)) { - SetClipboardText( + TextUtilities::SetClipboardText( view->selectedText(_selectedTextRange), QClipboard::Selection); } @@ -2303,7 +2310,7 @@ std::unique_ptr ListWidget::prepareDrag() { } return TextForMimeData(); }(); - if (auto mimeData = MimeDataFromText(selectedText)) { + if (auto mimeData = TextUtilities::MimeDataFromText(selectedText)) { clearDragSelection(); // _widget->noSelectingScroll(); #TODO scroll diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index d1687808d..d801a24f9 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -178,6 +178,7 @@ public: // AbstractTooltipShower interface QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; // ElementDelegate interface. Context elementContext() override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index f9913e836..f772eebfe 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "data/data_session.h" #include "data/data_document.h" +#include "data/data_file_origin.h" #include "app.h" #include "styles/style_history.h" diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index e6ce77168..195b13915 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" // isGifPausedAtLeastFor. #include "data/data_session.h" #include "data/data_document.h" +#include "data/data_file_origin.h" #include "lottie/lottie_single_player.h" #include "styles/style_history.h" diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 8a42ab263..fdbd4f5fe 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_download.h" #include "ui/widgets/popup_menu.h" #include "ui/ui_utility.h" +#include "ui/inactive_press.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "mainwidget.h" @@ -1823,8 +1824,13 @@ void ListWidget::mouseActionStart( auto pressLayout = _overLayout; _mouseAction = MouseAction::None; - _pressWasInactive = _controller->parentController()->widget()->wasInactivePress(); - if (_pressWasInactive) _controller->parentController()->widget()->setInactivePress(false); + _pressWasInactive = Ui::WasInactivePress( + _controller->parentController()->widget()); + if (_pressWasInactive) { + Ui::MarkInactivePress( + _controller->parentController()->widget(), + false); + } if (ClickHandler::getPressed() && !hasSelected()) { _mouseAction = MouseAction::PrepareDrag; @@ -2018,7 +2024,7 @@ void ListWidget::mouseActionFinish( _wasSelectedText = false; if (activated) { mouseActionCancel(); - App::activateClickHandler(activated, button); + ActivateClickHandler(window(), activated, button); return; } @@ -2045,7 +2051,7 @@ void ListWidget::mouseActionFinish( #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 //if (hasSelectedText()) { // #TODO linux clipboard - // SetClipboardText(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); + // TextUtilities::SetClipboardText(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); //} #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index c1b49724b..495ee9ed1 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "inline_bots/inline_bot_result.h" +#include "api/api_text_entities.h" #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_session.h" @@ -136,7 +137,7 @@ std::unique_ptr Result::create(uint64 queryId, const MTPBotInlineResult case mtpc_botInlineMessageMediaAuto: { const auto &r = message->c_botInlineMessageMediaAuto(); const auto message = qs(r.vmessage()); - const auto entities = TextUtilities::EntitiesFromMTP( + const auto entities = Api::EntitiesFromMTP( r.ventities().value_or_empty()); if (result->_type == Type::Photo) { if (!result->_photo) { @@ -168,7 +169,7 @@ std::unique_ptr Result::create(uint64 queryId, const MTPBotInlineResult const auto &r = message->c_botInlineMessageText(); result->sendData = std::make_unique( qs(r.vmessage()), - TextUtilities::EntitiesFromMTP(r.ventities().value_or_empty()), + Api::EntitiesFromMTP(r.ventities().value_or_empty()), r.is_no_webpage()); if (result->_type == Type::Photo) { if (!result->_photo) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 591c3a07f..62ef1c29c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "inline_bots/inline_bot_send_data.h" +#include "api/api_text_entities.h" #include "data/data_document.h" #include "inline_bots/inline_bot_result.h" #include "storage/localstorage.h" @@ -78,7 +79,7 @@ QString SendDataCommon::getErrorOnSend( SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const { SentMTPMessageFields result; result.text = MTP_string(_message); - result.entities = TextUtilities::EntitiesToMTP(_entities); + result.entities = Api::EntitiesToMTP(_entities); return result; } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index 4c6d5c540..0c0e0d143 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -146,6 +146,10 @@ QPoint Inner::tooltipPos() const { return _lastMousePos; } +bool Inner::tooltipWindowActive() const { + return Ui::InFocusChain(window()); +} + Inner::~Inner() = default; void Inner::paintEvent(QPaintEvent *e) { @@ -239,7 +243,7 @@ void Inner::mouseReleaseEvent(QMouseEvent *e) { int row = _selected / MatrixRowShift, column = _selected % MatrixRowShift; selectInlineResult(row, column); } else { - App::activateClickHandler(activated, e->button()); + ActivateClickHandler(window(), activated, e->button()); } } @@ -926,8 +930,7 @@ void Widget::startShowAnimation() { _showAnimation = std::make_unique(st::emojiPanAnimation, Ui::PanelAnimation::Origin::BottomLeft); auto inner = rect().marginsRemoved(st::emojiPanMargins); _showAnimation->setFinalImage(std::move(image), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); - auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); + _showAnimation->setCornerMasks(Images::CornersMask(ImageRoundRadius::Small)); _showAnimation->start(); } hideChildren(); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.h b/Telegram/SourceFiles/inline_bots/inline_results_widget.h index cddaeb12b..2c2281e89 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.h +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.h @@ -86,6 +86,7 @@ public: // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; ~Inner(); diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 3962b5b58..d9a3e2d5f 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/wrap/fade_wrap.h" +#include "ui/special_fields.h" #include "main/main_account.h" #include "boxes/confirm_phone_box.h" #include "boxes/confirm_box.h" diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 2ec78ca6c..8853f7b29 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_user.h" #include "data/data_scheduled_messages.h" +#include "api/api_text_entities.h" #include "ui/special_buttons.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -3818,7 +3819,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { } item->updateSentContent({ sent.text, - TextUtilities::EntitiesFromMTP(list.value_or_empty()) + Api::EntitiesFromMTP(list.value_or_empty()) }, d.vmedia()); item->contributeToSlowmode(d.vdate().v); if (!wasAlready) { @@ -4321,7 +4322,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { const auto &d = update.c_updateServiceNotification(); const auto text = TextWithEntities { qs(d.vmessage()), - TextUtilities::EntitiesFromMTP(d.ventities().v) + Api::EntitiesFromMTP(d.ventities().v) }; if (IsForceLogoutNotification(d)) { Core::App().forceLogOut(text); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 190637730..40b80f9fd 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/tooltip.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" #include "lang/lang_cloud_manager.h" @@ -154,6 +155,11 @@ void MainWindow::firstShow() { psFirstShow(); updateTrayMenu(); + + windowDeactivateEvents( + ) | rpl::start_with_next([=] { + Ui::Tooltip::Hide(); + }, lifetime()); } void MainWindow::clearWidgetsHook() { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b94dd770a..99d773f64 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/image/image.h" #include "ui/text/text_utilities.h" +#include "ui/platform/ui_platform_utility.h" #include "ui/toast/toast.h" #include "ui/text_options.h" #include "ui/ui_utility.h" @@ -1115,9 +1116,9 @@ void OverlayWidget::onSaveAs() { filter = mimeType.filterString() + qsl(";;") + FileDialog::AllFilesFilter(); } - psBringToBack(this); + Ui::Platform::BringToBack(this); file = FileNameForSave(tr::lng_save_file(tr::now), filter, qsl("doc"), name, true, alreadyDir); - psShowOverAll(this); + Ui::Platform::ShowOverAll(this); if (!file.isEmpty() && file != location.name()) { if (_doc->data().isEmpty()) { QFile(file).remove(); @@ -1141,7 +1142,7 @@ void OverlayWidget::onSaveAs() { } else { if (!_photo || !_photo->loaded()) return; - psBringToBack(this); + Ui::Platform::BringToBack(this); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( this, @@ -1153,13 +1154,13 @@ void OverlayWidget::onSaveAs() { QString(), false, _photo->date), - crl::guard(this, [this, photo = _photo](const QString &result) { + crl::guard(this, [=, photo = _photo](const QString &result) { if (!result.isEmpty() && _photo == photo && photo->loaded()) { photo->large()->original().save(result, "JPG"); } - psShowOverAll(this); - }), crl::guard(this, [this] { - psShowOverAll(this); + Ui::Platform::ShowOverAll(this); + }), crl::guard(this, [=] { + Ui::Platform::ShowOverAll(this); })); } activateWindow(); @@ -1977,13 +1978,13 @@ void OverlayWidget::updateThemePreviewGeometry() { void OverlayWidget::displayFinished() { updateControls(); if (isHidden()) { - psUpdateOverlayed(this); + Ui::Platform::UpdateOverlayed(this); #ifdef Q_OS_LINUX showFullScreen(); #else // Q_OS_LINUX show(); #endif // Q_OS_LINUX - psShowOverAll(this); + Ui::Platform::ShowOverAll(this); activateWindow(); QApplication::setActiveWindow(this); setFocus(); @@ -3497,7 +3498,7 @@ void OverlayWidget::mouseReleaseEvent(QMouseEvent *e) { showSaveMsgFile(); return; } - App::activateClickHandler(activated, e->button()); + ActivateClickHandler(this, activated, e->button()); return; } diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp index 863a3b42f..d02e76c84 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/wrap/fade_wrap.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper +#include "ui/special_fields.h" #include "boxes/abstract_box.h" #include "boxes/confirm_phone_box.h" #include "data/data_user.h" diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 8e854fd0b..01afcd3fa 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include -#include #include #include @@ -92,10 +91,6 @@ void FallbackFontConfig() { namespace Platform { -bool IsApplicationActive() { - return QApplication::activeWindow() != nullptr; -} - void SetApplicationIcon(const QIcon &icon) { QApplication::setWindowIcon(icon); } @@ -135,12 +130,6 @@ QRect psDesktopRect() { return _monitorRect; } -void psShowOverAll(QWidget *w, bool canFocus) { -} - -void psBringToBack(QWidget *w) { -} - void psWriteDump() { } @@ -243,29 +232,6 @@ void finish() { Notifications::Finish(); } -bool TranslucentWindowsSupported(QPoint globalPosition) { - if (const auto native = QGuiApplication::platformNativeInterface()) { - if (const auto desktop = QApplication::desktop()) { - const auto index = desktop->screenNumber(globalPosition); - const auto screens = QGuiApplication::screens(); - if (const auto screen = (index >= 0 && index < screens.size()) ? screens[index] : QGuiApplication::primaryScreen()) { - if (native->nativeResourceForScreen(QByteArray("compositingEnabled"), screen)) { - return true; - } - - static auto WarnedAbout = base::flat_set(); - if (!WarnedAbout.contains(index)) { - WarnedAbout.insert(index); - LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); - } - } else { - LOG(("WARNING: Could not get screen for index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); - } - } - } - return false; -} - void RegisterCustomScheme() { #ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME auto home = getHomeDir(); @@ -432,9 +398,6 @@ void psAutoStart(bool start, bool silent) { void psSendToMenu(bool send, bool silent) { } -void psUpdateOverlayed(QWidget *widget) { -} - bool linuxMoveFile(const char *from, const char *to) { FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb"); if (!ffrom) { diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 9af2a64c0..2fef15047 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -20,26 +20,15 @@ namespace Platform { inline void SetWatchingMediaKeys(bool watching) { } -bool IsApplicationActive(); - -inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { -} - -inline void InitOnTopPanel(QWidget *panel) { -} - -inline void DeInitOnTopPanel(QWidget *panel) { -} - -inline void ReInitOnTopPanel(QWidget *panel) { -} - QString CurrentExecutablePath(int argc, char *argv[]); inline std::optional LastUserInputTime() { return std::nullopt; } +inline void IgnoreApplicationActivationRightNow() { +} + inline constexpr bool UseMainQueueGeneric() { return true; } @@ -70,15 +59,12 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); -void psShowOverAll(QWidget *w, bool canFocus = true); -void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); void psNewVersion(); -void psUpdateOverlayed(QWidget *widget); inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } diff --git a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm index 0569a15bf..23c818846 100644 --- a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm +++ b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm @@ -22,7 +22,7 @@ #include "data/data_peer_values.h" #include "data/data_session.h" #include "dialogs/dialogs_layout.h" -#include "emoji_config.h" +#include "ui/emoji_config.h" #include "history/history.h" #include "lang/lang_keys.h" #include "mainwidget.h" diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index a5bdf5209..50a8ccd1d 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -16,10 +16,6 @@ class LocationPoint; namespace Platform { -inline bool TranslucentWindowsSupported(QPoint globalPosition) { - return true; -} - QString CurrentExecutablePath(int argc, char *argv[]); void RemoveQuarantine(const QString &path); @@ -67,8 +63,6 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); -void psShowOverAll(QWidget *w, bool canFocus = true); -void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); @@ -77,8 +71,6 @@ bool psShowOpenWithMenu(int x, int y, const QString &file); void psNewVersion(); -void psUpdateOverlayed(QWidget *widget); - void psDownloadPathEnableAccess(); QByteArray psDownloadPathBookmark(const QString &path); QByteArray psPathBookmark(const QString &path); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index a3a9f5fbe..1b5018b4e 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -60,14 +60,6 @@ QRect psDesktopRect() { return _monitorRect; } -void psShowOverAll(QWidget *w, bool canFocus) { - objc_showOverAll(w->winId(), canFocus); -} - -void psBringToBack(QWidget *w) { - objc_bringToBack(w->winId()); -} - void psWriteDump() { #ifndef TDESKTOP_DISABLE_CRASH_REPORTS double v = objc_appkitVersion(); @@ -128,14 +120,6 @@ void finish() { objc_finish(); } -void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { -#ifdef OS_MAC_OLD - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(e->rect(), Qt::transparent); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); -#endif // OS_MAC_OLD -} - QString CurrentExecutablePath(int argc, char *argv[]) { return NS2QString([[NSBundle mainBundle] bundlePath]); } @@ -274,6 +258,10 @@ std::optional LastUserInputTime() { return (crl::now() - static_cast(idleTime)); } +void IgnoreApplicationActivationRightNow() { + objc_ignoreApplicationActivationRightNow(); +} + } // namespace Platform void psNewVersion() { @@ -286,9 +274,6 @@ void psAutoStart(bool start, bool silent) { void psSendToMenu(bool send, bool silent) { } -void psUpdateOverlayed(QWidget *widget) { -} - void psDownloadPathEnableAccess() { objc_downloadPathEnableAccess(Global::DownloadPathBookmark()); } diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.h b/Telegram/SourceFiles/platform/mac/specific_mac_p.h index a149447b7..d02b5b33f 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.h @@ -11,8 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL bool objc_handleMediaKeyEvent(void *e); bool objc_darkMode(); -void objc_showOverAll(WId winId, bool canFocus = true); -void objc_bringToBack(WId winId); void objc_debugShowAlert(const QString &str); void objc_outputDebugString(const QString &str); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 9c13c498b..cbf4f5529 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -35,8 +35,6 @@ namespace { constexpr auto kIgnoreActivationTimeoutMs = 500; -std::optional ApplicationIsActive; - } // namespace NSImage *qt_mac_create_nsimage(const QPixmap &pm); @@ -141,7 +139,6 @@ ApplicationDelegate *_sharedDelegate = nil; } - (void) applicationDidBecomeActive:(NSNotification *)aNotification { - ApplicationIsActive = true; if (Core::IsAppLaunched() && !_ignoreActivation) { Core::App().handleAppActivated(); if (auto window = App::wnd()) { @@ -153,7 +150,6 @@ ApplicationDelegate *_sharedDelegate = nil; } - (void) applicationDidResignActive:(NSNotification *)aNotification { - ApplicationIsActive = false; } - (void) receiveWakeNote:(NSNotification*)aNotification { @@ -207,12 +203,6 @@ void SetWatchingMediaKeys(bool watching) { } } -bool IsApplicationActive() { - return ApplicationIsActive - ? *ApplicationIsActive - : (static_cast(QCoreApplication::instance())->activeWindow() != nullptr); -} - void SetApplicationIcon(const QIcon &icon) { NSImage *image = nil; if (!icon.isNull()) { @@ -224,46 +214,6 @@ void SetApplicationIcon(const QIcon &icon) { [image release]; } -void InitOnTopPanel(QWidget *panel) { - Expects(!panel->windowHandle()); - - // Force creating windowHandle() without creating the platform window yet. - panel->setAttribute(Qt::WA_NativeWindow, true); - panel->windowHandle()->setProperty("_td_macNonactivatingPanelMask", QVariant(true)); - panel->setAttribute(Qt::WA_NativeWindow, false); - - panel->createWinId(); - - auto platformWindow = [reinterpret_cast(panel->winId()) window]; - Assert([platformWindow isKindOfClass:[NSPanel class]]); - - auto platformPanel = static_cast(platformWindow); - [platformPanel setLevel:NSPopUpMenuWindowLevel]; - [platformPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; - [platformPanel setFloatingPanel:YES]; - [platformPanel setHidesOnDeactivate:NO]; - - objc_ignoreApplicationActivationRightNow(); -} - -void DeInitOnTopPanel(QWidget *panel) { - auto platformWindow = [reinterpret_cast(panel->winId()) window]; - Assert([platformWindow isKindOfClass:[NSPanel class]]); - - auto platformPanel = static_cast(platformWindow); - auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorCanJoinAllSpaces)) | NSWindowCollectionBehaviorMoveToActiveSpace; - [platformPanel setCollectionBehavior:newBehavior]; -} - -void ReInitOnTopPanel(QWidget *panel) { - auto platformWindow = [reinterpret_cast(panel->winId()) window]; - Assert([platformWindow isKindOfClass:[NSPanel class]]); - - auto platformPanel = static_cast(platformWindow); - auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorMoveToActiveSpace)) | NSWindowCollectionBehaviorCanJoinAllSpaces; - [platformPanel setCollectionBehavior:newBehavior]; -} - } // namespace Platform bool objc_darkMode() { @@ -279,20 +229,6 @@ bool objc_darkMode() { return result; } -void objc_showOverAll(WId winId, bool canFocus) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd setLevel:NSPopUpMenuWindowLevel]; - if (!canFocus) { - [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; - [wnd setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; - } -} - -void objc_bringToBack(WId winId) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd setLevel:NSModalPanelWindowLevel]; -} - bool objc_handleMediaKeyEvent(void *ev) { auto e = reinterpret_cast(ev); diff --git a/Telegram/SourceFiles/platform/platform_specific.h b/Telegram/SourceFiles/platform/platform_specific.h index 330ba48d3..c6162c95e 100644 --- a/Telegram/SourceFiles/platform/platform_specific.h +++ b/Telegram/SourceFiles/platform/platform_specific.h @@ -27,13 +27,7 @@ enum class SystemSettingsType { }; void SetWatchingMediaKeys(bool watching); -bool IsApplicationActive(); void SetApplicationIcon(const QIcon &icon); -bool TranslucentWindowsSupported(QPoint globalPosition); -void StartTranslucentPaint(QPainter &p, QPaintEvent *e); -void InitOnTopPanel(QWidget *panel); -void DeInitOnTopPanel(QWidget *panel); -void ReInitOnTopPanel(QWidget *panel); void RegisterCustomScheme(); PermissionStatus GetPermissionStatus(PermissionType type); void RequestPermission(PermissionType type, Fn resultCallback); @@ -45,6 +39,8 @@ bool OpenSystemSettings(SystemSettingsType type); return LastUserInputTime().has_value(); } +void IgnoreApplicationActivationRightNow(); + [[nodiscard]] constexpr bool UseMainQueueGeneric(); void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false. diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 0e8215efd..721eb3dd3 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/win/windows_event_filter.h" #include "window/notifications_manager.h" #include "mainwindow.h" +#include "base/crc32hash.h" #include "core/application.h" #include "lang/lang_keys.h" #include "storage/localstorage.h" @@ -661,7 +662,7 @@ int32 MainWindow::screenNameChecksum(const QString &name) const { } else { memcpy(buffer, name.toStdWString().data(), sizeof(buffer)); } - return hashCrc32(buffer, sizeof(buffer)); + return base::crc32(buffer, sizeof(buffer)); } void MainWindow::psRefreshTaskbarIcon() { diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index 83b31d693..973196c65 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -201,12 +201,6 @@ QRect psDesktopRect() { return _monitorRect; } -void psShowOverAll(QWidget *w, bool canFocus) { -} - -void psBringToBack(QWidget *w) { -} - int psCleanup() { __try { @@ -308,10 +302,6 @@ void start() { void finish() { } -bool IsApplicationActive() { - return QApplication::activeWindow() != nullptr; -} - void SetApplicationIcon(const QIcon &icon) { QApplication::setWindowIcon(icon); } @@ -572,17 +562,6 @@ void psSendToMenu(bool send, bool silent) { _manageAppLnk(send, silent, CSIDL_SENDTO, L"-sendpath", L"Telegram send to link.\nYou can disable send to menu item in Telegram settings."); } -void psUpdateOverlayed(QWidget *widget) { - bool wm = widget->testAttribute(Qt::WA_Mapped), wv = widget->testAttribute(Qt::WA_WState_Visible); - if (!wm) widget->setAttribute(Qt::WA_Mapped, true); - if (!wv) widget->setAttribute(Qt::WA_WState_Visible, true); - widget->update(); - QEvent e(QEvent::UpdateRequest); - QGuiApplication::sendEvent(widget, &e); - if (!wm) widget->setAttribute(Qt::WA_Mapped, false); - if (!wv) widget->setAttribute(Qt::WA_WState_Visible, false); -} - void psWriteDump() { #ifndef TDESKTOP_DISABLE_CRASH_REPORTS PROCESS_MEMORY_COUNTERS data = { 0 }; diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index cf2ab893f..c850e3639 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -19,26 +19,11 @@ namespace Platform { inline void SetWatchingMediaKeys(bool watching) { } -bool IsApplicationActive(); - -inline bool TranslucentWindowsSupported(QPoint globalPosition) { - return true; -} - -inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { -} - -inline void InitOnTopPanel(QWidget *panel) { -} - -inline void DeInitOnTopPanel(QWidget *panel) { -} - -inline void ReInitOnTopPanel(QWidget *panel) { -} - QString CurrentExecutablePath(int argc, char *argv[]); +inline void IgnoreApplicationActivationRightNow() { +} + inline constexpr bool UseMainQueueGeneric() { return true; } @@ -74,15 +59,12 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); -void psShowOverAll(QWidget *w, bool canFocus = true); -void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); void psNewVersion(); -void psUpdateOverlayed(QWidget *widget); inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp index 395674a2f..b9302165c 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/win/windows_dlls.h" #include "core/sandbox.h" +#include "ui/inactive_press.h" #include "mainwindow.h" #include "main/main_session.h" #include "facades.h" @@ -113,7 +114,7 @@ bool EventFilter::mainWindowEvent( case WM_ACTIVATE: { if (LOWORD(wParam) == WA_CLICKACTIVE) { - _window->setInactivePress(true); + Ui::MarkInactivePress(_window, true); } if (LOWORD(wParam) != WA_INACTIVE) { _window->shadowsActivate(); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index ab64ae5c1..67754d1fd 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "storage/localimageloader.h" +#include "api/api_text_entities.h" #include "data/data_document.h" #include "data/data_session.h" #include "core/file_utilities.h" @@ -126,9 +127,9 @@ MTPInputSingleMedia PrepareAlbumItemMedia( uint64 randomId) { auto caption = item->originalText(); TextUtilities::Trim(caption); - auto sentEntities = TextUtilities::EntitiesToMTP( + auto sentEntities = Api::EntitiesToMTP( caption.entities, - TextUtilities::ConvertOption::SkipLocal); + Api::ConvertOption::SkipLocal); const auto flags = !sentEntities.v.isEmpty() ? MTPDinputSingleMedia::Flag::f_entities : MTPDinputSingleMedia::Flag(0); diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp index e2ec406f5..4b2afed2d 100644 --- a/Telegram/SourceFiles/support/support_helper.cpp +++ b/Telegram/SourceFiles/support/support_helper.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_drafts.h" #include "data/data_user.h" #include "data/data_session.h" +#include "api/api_text_entities.h" #include "history/history.h" #include "boxes/abstract_box.h" #include "ui/toast/toast.h" @@ -450,7 +451,7 @@ void Helper::applyInfo( info.date = data.vdate().v; info.text = TextWithEntities{ qs(data.vmessage()), - TextUtilities::EntitiesFromMTP(data.ventities().v) }; + Api::EntitiesFromMTP(data.ventities().v) }; if (info.text.empty()) { remove(); } else if (_userInformation[user] != info) { @@ -505,13 +506,13 @@ void Helper::showEditInfoBox(not_null user) { const auto info = infoCurrent(user); const auto editData = TextWithTags{ info.text.text, - ConvertEntitiesToTextTags(info.text.entities) + TextUtilities::ConvertEntitiesToTextTags(info.text.entities) }; const auto save = [=](TextWithTags result, Fn done) { saveInfo(user, TextWithEntities{ result.text, - ConvertTextTagsToEntities(result.tags) + TextUtilities::ConvertTextTagsToEntities(result.tags) }, done); }; Ui::show( @@ -540,9 +541,9 @@ void Helper::saveInfo( Ui::ItemTextDefaultOptions().flags); TextUtilities::Trim(text); - const auto entities = TextUtilities::EntitiesToMTP( + const auto entities = Api::EntitiesToMTP( text.entities, - TextUtilities::ConvertOption::SkipLocal); + Api::ConvertOption::SkipLocal); _userInfoSaving[user].requestId = request(MTPhelp_EditUserInfo( user->inputUser, MTP_string(text.text), diff --git a/Telegram/SourceFiles/ui/abstract_button.cpp b/Telegram/SourceFiles/ui/abstract_button.cpp index a2505c103..799972b8f 100644 --- a/Telegram/SourceFiles/ui/abstract_button.cpp +++ b/Telegram/SourceFiles/ui/abstract_button.cpp @@ -111,12 +111,12 @@ void AbstractButton::setOver(bool over, StateChangeSource source) { if (over && !(_state & StateFlag::Over)) { auto was = _state; _state |= StateFlag::Over; - RegisterLeaveSubscription(this); + Integration::Instance().registerLeaveSubscription(this); onStateChanged(was, source); } else if (!over && (_state & StateFlag::Over)) { auto was = _state; _state &= ~State(StateFlag::Over); - UnregisterLeaveSubscription(this); + Integration::Instance().unregisterLeaveSubscription(this); onStateChanged(was, source); } updateCursor(); diff --git a/Telegram/SourceFiles/ui/basic_click_handlers.cpp b/Telegram/SourceFiles/ui/basic_click_handlers.cpp new file mode 100644 index 000000000..b679ca58a --- /dev/null +++ b/Telegram/SourceFiles/ui/basic_click_handlers.cpp @@ -0,0 +1,68 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/basic_click_handlers.h" + +#include "ui/widgets/tooltip.h" +#include "ui/text/text_entity.h" +#include "ui/ui_integration.h" +#include "base/qthelp_url.h" + +#include +#include +#include +#include + +UrlClickHandler::UrlClickHandler(const QString &url, bool fullDisplayed) +: TextClickHandler(fullDisplayed) +, _originalUrl(url) { + if (isEmail()) { + _readable = _originalUrl; + } else { + const auto original = QUrl(_originalUrl); + const auto good = QUrl(original.isValid() + ? original.toEncoded() + : QString()); + _readable = good.isValid() ? good.toDisplayString() : _originalUrl; + } +} + +QString UrlClickHandler::copyToClipboardContextItemText() const { + return isEmail() + ? Ui::Integration::Instance().phraseContextCopyEmail() + : Ui::Integration::Instance().phraseContextCopyLink(); +} + +QString UrlClickHandler::url() const { + if (isEmail()) { + return _originalUrl; + } + + QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString()); + QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl); + + if (!result.isEmpty() + && !QRegularExpression( + QStringLiteral("^[a-zA-Z]+:")).match(result).hasMatch()) { + // No protocol. + return QStringLiteral("http://") + result; + } + return result; +} + +void UrlClickHandler::Open(QString url, QVariant context) { + Ui::Tooltip::Hide(); + if (!Ui::Integration::Instance().handleUrlClick(url, context) + && !url.isEmpty()) { + QDesktopServices::openUrl(url); + } +} + +auto UrlClickHandler::getTextEntity() const -> TextEntity { + const auto type = isEmail() ? EntityType::Email : EntityType::Url; + return { type, _originalUrl }; +} diff --git a/Telegram/SourceFiles/ui/basic_click_handlers.h b/Telegram/SourceFiles/ui/basic_click_handlers.h new file mode 100644 index 000000000..a25a7ea78 --- /dev/null +++ b/Telegram/SourceFiles/ui/basic_click_handlers.h @@ -0,0 +1,79 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/algorithm.h" +#include "ui/click_handler.h" + +class TextClickHandler : public ClickHandler { +public: + TextClickHandler(bool fullDisplayed = true) + : _fullDisplayed(fullDisplayed) { + } + + QString copyToClipboardText() const override { + return url(); + } + + QString tooltip() const override { + return _fullDisplayed ? QString() : readable(); + } + + void setFullDisplayed(bool full) { + _fullDisplayed = full; + } + +protected: + virtual QString url() const = 0; + virtual QString readable() const { + return url(); + } + + bool _fullDisplayed; + +}; + +class UrlClickHandler : public TextClickHandler { +public: + UrlClickHandler(const QString &url, bool fullDisplayed = true); + + QString copyToClipboardContextItemText() const override; + + QString dragText() const override { + return url(); + } + + TextEntity getTextEntity() const override; + + static void Open(QString url, QVariant context = {}); + void onClick(ClickContext context) const override { + const auto button = context.button; + if (button == Qt::LeftButton || button == Qt::MiddleButton) { + Open(url(), context.other); + } + } + + [[nodiscard]] static bool IsEmail(const QString &url) { + const auto at = url.indexOf('@'), slash = url.indexOf('/'); + return ((at > 0) && (slash < 0 || slash > at)); + } + +protected: + QString url() const override; + QString readable() const override { + return _readable; + } + +private: + [[nodiscard]] bool isEmail() const { + return IsEmail(_originalUrl); + } + + QString _originalUrl, _readable; + +}; diff --git a/Telegram/SourceFiles/ui/click_handler.cpp b/Telegram/SourceFiles/ui/click_handler.cpp new file mode 100644 index 000000000..f201ae06b --- /dev/null +++ b/Telegram/SourceFiles/ui/click_handler.cpp @@ -0,0 +1,172 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/click_handler.h" + +#include "base/algorithm.h" +#include "ui/text/text_entity.h" + +#include + +namespace { + +ClickHandlerPtr &ClickHandlerActive() { + static auto result = ClickHandlerPtr(); + return result; +} + +ClickHandlerPtr &ClickHandlerPressed() { + static auto result = ClickHandlerPtr(); + return result; +} + +} // namespace + +ClickHandlerHost *ClickHandler::_activeHost = nullptr; +ClickHandlerHost *ClickHandler::_pressedHost = nullptr; + +ClickHandlerHost::~ClickHandlerHost() { + ClickHandler::hostDestroyed(this); +} + +bool ClickHandler::setActive( + const ClickHandlerPtr &p, + ClickHandlerHost *host) { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + if (active == p) { + return false; + } + + // emit clickHandlerActiveChanged only when there is no + // other pressed click handler currently, if there is + // this method will be called when it is unpressed + if (active) { + const auto emitClickHandlerActiveChanged = false + || !pressed + || (pressed == active); + const auto wasactive = base::take(active); + if (_activeHost) { + if (emitClickHandlerActiveChanged) { + _activeHost->clickHandlerActiveChanged(wasactive, false); + } + _activeHost = nullptr; + } + } + if (p) { + active = p; + if ((_activeHost = host)) { + bool emitClickHandlerActiveChanged = (!pressed || pressed == active); + if (emitClickHandlerActiveChanged) { + _activeHost->clickHandlerActiveChanged(active, true); + } + } + } + return true; +} + +bool ClickHandler::clearActive(ClickHandlerHost *host) { + if (host && _activeHost != host) { + return false; + } + return setActive(ClickHandlerPtr(), host); +} + +void ClickHandler::pressed() { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + unpressed(); + if (!active) { + return; + } + pressed = active; + if ((_pressedHost = _activeHost)) { + _pressedHost->clickHandlerPressedChanged(pressed, true); + } +} + +ClickHandlerPtr ClickHandler::unpressed() { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + if (pressed) { + const auto activated = (active == pressed); + const auto waspressed = base::take(pressed); + if (_pressedHost) { + _pressedHost->clickHandlerPressedChanged(waspressed, false); + _pressedHost = nullptr; + } + + if (activated) { + return active; + } else if (active && _activeHost) { + // emit clickHandlerActiveChanged for current active + // click handler, which we didn't emit while we has + // a pressed click handler + _activeHost->clickHandlerActiveChanged(active, true); + } + } + return ClickHandlerPtr(); +} + +ClickHandlerPtr ClickHandler::getActive() { + return ClickHandlerActive(); +} + +ClickHandlerPtr ClickHandler::getPressed() { + return ClickHandlerPressed(); +} + +bool ClickHandler::showAsActive(const ClickHandlerPtr &p) { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + return p && (p == active) && (!pressed || (p == pressed)); +} + +bool ClickHandler::showAsPressed(const ClickHandlerPtr &p) { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + return p && (p == active) && (p == pressed); +} + +void ClickHandler::hostDestroyed(ClickHandlerHost *host) { + auto &active = ClickHandlerActive(); + auto &pressed = ClickHandlerPressed(); + + if (_activeHost == host) { + active = nullptr; + _activeHost = nullptr; + } + if (_pressedHost == host) { + pressed = nullptr; + _pressedHost = nullptr; + } +} + +auto ClickHandler::getTextEntity() const -> TextEntity { + return { EntityType::Invalid }; +} + +void ActivateClickHandler( + not_null guard, + ClickHandlerPtr handler, + ClickContext context) { + crl::on_main(guard, [=] { + handler->onClick(context); + }); +} + +void ActivateClickHandler( + not_null guard, + ClickHandlerPtr handler, + Qt::MouseButton button) { + ActivateClickHandler(guard, handler, ClickContext{ button }); +} diff --git a/Telegram/SourceFiles/core/click_handler.h b/Telegram/SourceFiles/ui/click_handler.h similarity index 57% rename from Telegram/SourceFiles/core/click_handler.h rename to Telegram/SourceFiles/ui/click_handler.h index 22fa6f5f7..6c158dfb7 100644 --- a/Telegram/SourceFiles/core/click_handler.h +++ b/Telegram/SourceFiles/ui/click_handler.h @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/basic_types.h" + +#include + class ClickHandler; using ClickHandlerPtr = std::shared_ptr; @@ -65,86 +69,23 @@ public: // This method should be called when mouse leaves the host. // It returns true if the active handler was changed or false otherwise. - static bool clearActive(ClickHandlerHost *host = nullptr) { - if (host && _activeHost != host) { - return false; - } - return setActive(ClickHandlerPtr(), host); - } + static bool clearActive(ClickHandlerHost *host = nullptr); // This method should be called on mouse press event. - static void pressed() { - unpressed(); - if (!_active || !*_active) { - return; - } - _pressed.createIfNull(); - *_pressed = *_active; - if ((_pressedHost = _activeHost)) { - _pressedHost->clickHandlerPressedChanged(*_pressed, true); - } - } + static void pressed(); // This method should be called on mouse release event. // The activated click handler (if any) is returned. - static ClickHandlerPtr unpressed() { - if (_pressed && *_pressed) { - const auto activated = (_active && *_active == *_pressed); - const auto waspressed = base::take(*_pressed); - if (_pressedHost) { - _pressedHost->clickHandlerPressedChanged(waspressed, false); - _pressedHost = nullptr; - } + static ClickHandlerPtr unpressed(); - if (activated) { - return *_active; - } else if (_active && *_active && _activeHost) { - // emit clickHandlerActiveChanged for current active - // click handler, which we didn't emit while we has - // a pressed click handler - _activeHost->clickHandlerActiveChanged(*_active, true); - } - } - return ClickHandlerPtr(); - } + static ClickHandlerPtr getActive(); + static ClickHandlerPtr getPressed(); - static ClickHandlerPtr getActive() { - return _active ? *_active : ClickHandlerPtr(); - } - static ClickHandlerPtr getPressed() { - return _pressed ? *_pressed : ClickHandlerPtr(); - } - - static bool showAsActive(const ClickHandlerPtr &p) { - if (!p || !_active || p != *_active) { - return false; - } - return !_pressed || !*_pressed || (p == *_pressed); - } - static bool showAsPressed(const ClickHandlerPtr &p) { - if (!p || !_active || p != *_active) { - return false; - } - return _pressed && (p == *_pressed); - } - static void hostDestroyed(ClickHandlerHost *host) { - if (_activeHost == host) { - if (_active) { - *_active = nullptr; - } - _activeHost = nullptr; - } - if (_pressedHost == host) { - if (_pressed) { - *_pressed = nullptr; - } - _pressedHost = nullptr; - } - } + static bool showAsActive(const ClickHandlerPtr &p); + static bool showAsPressed(const ClickHandlerPtr &p); + static void hostDestroyed(ClickHandlerHost *host); private: - static NeverFreedPointer _active; - static NeverFreedPointer _pressed; static ClickHandlerHost *_activeHost; static ClickHandlerHost *_pressedHost; @@ -177,3 +118,12 @@ private: Fn _handler; }; + +void ActivateClickHandler( + not_null guard, + ClickHandlerPtr handler, + ClickContext context); +void ActivateClickHandler( + not_null guard, + ClickHandlerPtr handler, + Qt::MouseButton button); diff --git a/Telegram/SourceFiles/ui/delayed_activation.cpp b/Telegram/SourceFiles/ui/delayed_activation.cpp new file mode 100644 index 000000000..d039223ba --- /dev/null +++ b/Telegram/SourceFiles/ui/delayed_activation.cpp @@ -0,0 +1,45 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/delayed_activation.h" + +#include "ui/ui_utility.h" + +#include + +namespace Ui { +namespace { + +auto Paused = false; +auto Window = QPointer(); + +} // namespace + +void ActivateWindowDelayed(not_null widget) { + if (Paused) { + return; + } else if (std::exchange(Window, widget.get())) { + return; + } + crl::on_main(Window, [=] { + if (const auto widget = base::take(Window)) { + if (!widget->isHidden()) { + widget->activateWindow(); + } + } + }); +} + +void PreventDelayedActivation() { + Window = nullptr; + Paused = true; + PostponeCall([] { + Paused = false; + }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/delayed_activation.h b/Telegram/SourceFiles/ui/delayed_activation.h new file mode 100644 index 000000000..60eeaefb2 --- /dev/null +++ b/Telegram/SourceFiles/ui/delayed_activation.h @@ -0,0 +1,15 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { + +void ActivateWindowDelayed(not_null widget); +void PreventDelayedActivation(); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/animations.cpp b/Telegram/SourceFiles/ui/effects/animations.cpp index 262c70ae2..7b575f1b6 100644 --- a/Telegram/SourceFiles/ui/effects/animations.cpp +++ b/Telegram/SourceFiles/ui/effects/animations.cpp @@ -167,7 +167,7 @@ void Manager::schedule() { stopTimer(); _scheduled = true; - Ui::PostponeCall(delayedCallGuard(), [=] { + PostponeCall(delayedCallGuard(), [=] { _scheduled = false; if (_forceImmediateUpdate) { _forceImmediateUpdate = false; diff --git a/Telegram/SourceFiles/ui/effects/fade_animation.cpp b/Telegram/SourceFiles/ui/effects/fade_animation.cpp index 5c7febd09..105ef111c 100644 --- a/Telegram/SourceFiles/ui/effects/fade_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/fade_animation.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/fade_animation.h" #include "ui/ui_utility.h" +#include "ui/painter.h" #include "app.h" namespace Ui { @@ -22,7 +23,7 @@ FadeAnimation::FadeAnimation(TWidget *widget, float64 scale) , _scale(scale) { } -bool FadeAnimation::paint(Painter &p) { +bool FadeAnimation::paint(QPainter &p) { if (_cache.isNull()) return false; const auto cache = _cache; diff --git a/Telegram/SourceFiles/ui/effects/fade_animation.h b/Telegram/SourceFiles/ui/effects/fade_animation.h index bff9eb616..c110f56cf 100644 --- a/Telegram/SourceFiles/ui/effects/fade_animation.h +++ b/Telegram/SourceFiles/ui/effects/fade_animation.h @@ -17,7 +17,7 @@ class FadeAnimation { public: FadeAnimation(TWidget *widget, float64 scale = 1.); - bool paint(Painter &p); + bool paint(QPainter &p); void refreshCache(); using FinishedCallback = Fn; diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp index 4d3109735..b7eba95df 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -55,11 +55,12 @@ void RoundShadowAnimation::setShadow(const style::Shadow &st) { } } -void RoundShadowAnimation::setCornerMasks(const QImage &topLeft, const QImage &topRight, const QImage &bottomLeft, const QImage &bottomRight) { - setCornerMask(_topLeft, topLeft); - setCornerMask(_topRight, topRight); - setCornerMask(_bottomLeft, bottomLeft); - setCornerMask(_bottomRight, bottomRight); +void RoundShadowAnimation::setCornerMasks( + const std::array &corners) { + setCornerMask(_topLeft, corners[0]); + setCornerMask(_topRight, corners[1]); + setCornerMask(_bottomLeft, corners[2]); + setCornerMask(_bottomRight, corners[3]); } void RoundShadowAnimation::setCornerMask(Corner &corner, const QImage &image) { diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.h b/Telegram/SourceFiles/ui/effects/panel_animation.h index d7379f7eb..73c91e096 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.h +++ b/Telegram/SourceFiles/ui/effects/panel_animation.h @@ -13,7 +13,7 @@ namespace Ui { class RoundShadowAnimation { public: - void setCornerMasks(const QImage &topLeft, const QImage &topRight, const QImage &bottomLeft, const QImage &bottomRight); + void setCornerMasks(const std::array &corners); protected: void start(int frameWidth, int frameHeight, float64 devicePixelRatio); diff --git a/Telegram/SourceFiles/ui/emoji_config.cpp b/Telegram/SourceFiles/ui/emoji_config.cpp index c365497c8..62cfad9a4 100644 --- a/Telegram/SourceFiles/ui/emoji_config.cpp +++ b/Telegram/SourceFiles/ui/emoji_config.cpp @@ -14,11 +14,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/bytes.h" #include "base/openssl_help.h" #include "base/parse_helper.h" -#include "main/main_session.h" -#include "app.h" +#include "ui/style/style_core.h" +#include "ui/painter.h" +#include "ui/ui_utility.h" +#include "ui/ui_log.h" +#include "styles/style_basic.h" #include #include +#include +#include + +#include namespace Ui { namespace Emoji { @@ -179,7 +186,7 @@ void SaveToFile(int id, const QImage &image, int size, int index) { if (!f.open(QIODevice::WriteOnly)) { if (!QDir::current().mkpath(internal::CacheFileFolder()) || !f.open(QIODevice::WriteOnly)) { - LOG(("App Error: Could not open emoji cache '%1' for size %2_%3" + UI_LOG(("App Error: Could not open emoji cache '%1' for size %2_%3" ).arg(f.fileName() ).arg(size ).arg(index)); @@ -205,7 +212,7 @@ void SaveToFile(int id, const QImage &image, int size, int index) { || !write(data) || !write(openssl::Sha256(bytes::make_span(header), data)) || false) { - LOG(("App Error: Could not write emoji cache '%1' for size %2" + UI_LOG(("App Error: Could not write emoji cache '%1' for size %2" ).arg(f.fileName() ).arg(size)); } @@ -278,7 +285,7 @@ std::vector LoadSprites(int id) { auto result = std::vector(); const auto folder = (id != 0) ? internal::SetDataPath(id) + '/' - : qsl(":/gui/emoji/"); + : QStringLiteral(":/gui/emoji/"); const auto base = folder + "emoji_"; return ranges::view::ints( 0, @@ -359,7 +366,7 @@ void ClearUniversalChecked() { namespace internal { QString CacheFileFolder() { - return cWorkingDir() + "tdata/emoji"; + return Integration::Instance().emojiCacheFolder(); } QString SetDataPath(int id) { @@ -470,8 +477,8 @@ void Init() { const auto persprite = kImagesPerRow * kImageRowsPerSprite; SpritesCount = (count / persprite) + ((count % persprite) ? 1 : 0); - SizeNormal = style::ConvertScale(18, cScale() * cIntRetinaFactor()); - SizeLarge = int(style::ConvertScale(18 * 4 / 3., cScale() * cIntRetinaFactor())); + SizeNormal = style::ConvertScale(18, style::Scale() * style::DevicePixelRatio()); + SizeLarge = int(style::ConvertScale(18 * 4 / 3., style::Scale() * style::DevicePixelRatio())); Universal = std::make_shared(ReadCurrentSetId()); CanClearUniversal = false; @@ -479,9 +486,9 @@ void Init() { InstanceLarge = std::make_unique(SizeLarge); #if defined Q_OS_MAC && !defined OS_MAC_OLD - if (cScale() != kScaleForTouchBar) { + if (style::Scale() != kScaleForTouchBar) { TouchbarSize = int(style::ConvertScale(18 * 4 / 3., - kScaleForTouchBar * cIntRetinaFactor())); + kScaleForTouchBar * style::DevicePixelRatio())); TouchbarInstance = std::make_unique(TouchbarSize); TouchbarEmoji = TouchbarInstance.get(); } else { @@ -594,7 +601,7 @@ int GetSizeLarge() { #if defined Q_OS_MAC && !defined OS_MAC_OLD int GetSizeTouchbar() { - return (cScale() == kScaleForTouchBar) + return (style::Scale() == kScaleForTouchBar) ? GetSizeLarge() : TouchbarSize; } @@ -697,7 +704,7 @@ QVector GetDefaultRecent() { }; auto result = QVector(); for (const auto oldKey : defaultRecent) { - if (const auto emoji = Ui::Emoji::FromOldKey(oldKey)) { + if (const auto emoji = FromOldKey(oldKey)) { result.push_back(emoji); } } @@ -705,7 +712,7 @@ QVector GetDefaultRecent() { } const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) { - auto &map = (fontHeight == st::msgFont->height * cIntRetinaFactor()) + auto &map = (fontHeight == st::normalFont->height * style::DevicePixelRatio()) ? MainEmojiMap : OtherEmojiMap[fontHeight]; auto i = map.find(emoji->index()); @@ -716,7 +723,7 @@ const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) { SizeNormal + st::emojiPadding * 2, fontHeight, QImage::Format_ARGB32_Premultiplied); - image.setDevicePixelRatio(cRetinaFactor()); + image.setDevicePixelRatio(style::DevicePixelRatio()); image.fill(Qt::transparent); { QPainter p(&image); @@ -725,18 +732,18 @@ const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) { p, emoji, SizeNormal, - st::emojiPadding * cIntRetinaFactor(), + st::emojiPadding * style::DevicePixelRatio(), (fontHeight - SizeNormal) / 2); } return map.emplace( emoji->index(), - App::pixmapFromImageInPlace(std::move(image)) + PixmapFromImage(std::move(image)) ).first->second; } void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) { #if defined Q_OS_MAC && !defined OS_MAC_OLD - const auto s = (cScale() == kScaleForTouchBar) + const auto s = (style::Scale() == kScaleForTouchBar) ? SizeLarge : TouchbarSize; if (size == s) { @@ -835,8 +842,8 @@ void Instance::generateCache() { } void Instance::pushSprite(QImage &&data) { - _sprites.push_back(App::pixmapFromImageInPlace(std::move(data))); - _sprites.back().setDevicePixelRatio(cRetinaFactor()); + _sprites.push_back(PixmapFromImage(std::move(data))); + _sprites.back().setDevicePixelRatio(style::DevicePixelRatio()); } const std::shared_ptr &SourceImages() { diff --git a/Telegram/SourceFiles/ui/emoji_config.h b/Telegram/SourceFiles/ui/emoji_config.h index 393cae0be..f6fe3bcbb 100644 --- a/Telegram/SourceFiles/ui/emoji_config.h +++ b/Telegram/SourceFiles/ui/emoji_config.h @@ -16,8 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include -using EmojiPtr = const Ui::Emoji::One*; - namespace Ui { namespace Emoji { namespace internal { diff --git a/Telegram/SourceFiles/ui/image/image_prepare.cpp b/Telegram/SourceFiles/ui/image/image_prepare.cpp index a0b6f4b5c..ea011b0d0 100644 --- a/Telegram/SourceFiles/ui/image/image_prepare.cpp +++ b/Telegram/SourceFiles/ui/image/image_prepare.cpp @@ -8,8 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image_prepare.h" #include "ui/effects/animation_value.h" -#include "facades.h" -#include "app.h" +#include "ui/style/style_core.h" +#include "ui/painter.h" +#include "base/flat_map.h" +#include "styles/palette.h" +#include "styles/style_basic.h" namespace Images { namespace { @@ -19,8 +22,6 @@ TG_FORCE_INLINE uint64 blurGetColors(const uchar *p) { } const QImage &circleMask(QSize size) { - Assert(Global::started()); - uint64 key = (uint64(uint32(size.width())) << 32) | uint64(uint32(size.height())); @@ -34,7 +35,7 @@ const QImage &circleMask(QSize size) { QImage::Format_ARGB32_Premultiplied); mask.fill(Qt::transparent); { - Painter p(&mask); + QPainter p(&mask); PainterHighQualityEnabler hq(p); p.setBrush(Qt::white); p.setPen(Qt::NoPen); @@ -43,6 +44,31 @@ const QImage &circleMask(QSize size) { return masks.emplace(key, std::move(mask)).first->second; } +std::array PrepareCornersMask(int radius) { + auto result = std::array(); + const auto side = radius * style::DevicePixelRatio(); + auto full = QImage( + QSize(side, side) * 3, + QImage::Format_ARGB32_Premultiplied); + full.fill(Qt::transparent); + { + QPainter p(&full); + PainterHighQualityEnabler hq(p); + + p.setPen(Qt::NoPen); + p.setBrush(Qt::white); + p.drawRoundedRect(0, 0, side * 3, side * 3, side, side); + } + result[0] = full.copy(0, 0, side, side); + result[1] = full.copy(side * 2, 0, side, side); + result[2] = full.copy(0, side * 2, side, side); + result[3] = full.copy(side * 2, side * 2, side, side); + for (auto &image : result) { + image.setDevicePixelRatio(style::DevicePixelRatio()); + } + return result; +} + } // namespace QPixmap PixmapFast(QImage &&image) { @@ -52,6 +78,26 @@ QPixmap PixmapFast(QImage &&image) { return QPixmap::fromImage(std::move(image), Qt::NoFormatConversion); } +const std::array &CornersMask(ImageRoundRadius radius) { + if (radius == ImageRoundRadius::Large) { + static auto Mask = PrepareCornersMask(st::roundRadiusLarge); + return Mask; + } else { + static auto Mask = PrepareCornersMask(st::roundRadiusSmall); + return Mask; + } +} + +std::array PrepareCorners( + ImageRoundRadius radius, + const style::color &color) { + auto result = CornersMask(radius); + for (auto &image : result) { + style::colorizeImage(image, color->c, &image); + } + return result; +} + QImage prepareBlur(QImage img) { if (img.isNull()) { return img; @@ -75,7 +121,7 @@ QImage prepareBlur(QImage img) { if (withalpha) { QImage imgsmall(w, h, img.format()); { - Painter p(&imgsmall); + QPainter p(&imgsmall); PainterHighQualityEnabler hq(p); p.setCompositionMode(QPainter::CompositionMode_Source); @@ -394,7 +440,7 @@ void prepareCircle(QImage &img) { img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); Assert(!img.isNull()); - Painter p(&img); + QPainter p(&img); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.drawImage( QRect(QPoint(), img.size() / img.devicePixelRatio()), @@ -471,12 +517,13 @@ void prepareRound( } Assert(!image.isNull()); - image.setDevicePixelRatio(cRetinaFactor()); - image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(style::DevicePixelRatio()); + image = std::move(image).convertToFormat( + QImage::Format_ARGB32_Premultiplied); Assert(!image.isNull()); - auto masks = App::cornersMask(radius); - prepareRound(image, masks, corners, target); + auto masks = CornersMask(radius); + prepareRound(image, masks.data(), corners, target); } QImage prepareColored(style::color add, QImage image) { @@ -542,12 +589,13 @@ QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, in Assert(!img.isNull()); } if (outerw > 0 && outerh > 0) { - outerw *= cIntRetinaFactor(); - outerh *= cIntRetinaFactor(); + const auto pixelRatio = style::DevicePixelRatio(); + outerw *= pixelRatio; + outerh *= pixelRatio; if (outerw != w || outerh != h) { - img.setDevicePixelRatio(cRetinaFactor()); + img.setDevicePixelRatio(pixelRatio); auto result = QImage(outerw, outerh, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(pixelRatio); if (options & Images::Option::TransparentBackground) { result.fill(Qt::transparent); } @@ -558,7 +606,7 @@ QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, in p.fillRect(0, 0, result.width(), result.height(), st::imageBg); } } - p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); + p.drawImage((result.width() - img.width()) / (2 * pixelRatio), (result.height() - img.height()) / (2 * pixelRatio), img); } img = result; Assert(!img.isNull()); @@ -584,7 +632,7 @@ QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, in Assert(colored != nullptr); img = prepareColored(*colored, std::move(img)); } - img.setDevicePixelRatio(cRetinaFactor()); + img.setDevicePixelRatio(style::DevicePixelRatio()); return img; } diff --git a/Telegram/SourceFiles/ui/image/image_prepare.h b/Telegram/SourceFiles/ui/image/image_prepare.h index b8af5715b..9838549d1 100644 --- a/Telegram/SourceFiles/ui/image/image_prepare.h +++ b/Telegram/SourceFiles/ui/image/image_prepare.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/flags.h" #include "ui/rect_part.h" +#include "ui/style/style_core.h" namespace Storage { namespace Cache { @@ -25,9 +26,13 @@ enum class ImageRoundRadius { namespace Images { -QPixmap PixmapFast(QImage &&image); - -QImage BlurLargeImage(QImage image, int radius); +[[nodiscard]] QPixmap PixmapFast(QImage &&image); +[[nodiscard]] QImage BlurLargeImage(QImage image, int radius); +[[nodiscard]] const std::array &CornersMask( + ImageRoundRadius radius); +[[nodiscard]] std::array PrepareCorners( + ImageRoundRadius radius, + const style::color &color); QImage prepareBlur(QImage image); void prepareRound( diff --git a/Telegram/SourceFiles/ui/inactive_press.cpp b/Telegram/SourceFiles/ui/inactive_press.cpp new file mode 100644 index 000000000..69ed27fe9 --- /dev/null +++ b/Telegram/SourceFiles/ui/inactive_press.cpp @@ -0,0 +1,54 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/inactive_press.h" + +#include "base/timer.h" +#include "base/qt_connection.h" + +#include + +namespace Ui { +namespace { + +constexpr auto kInactivePressTimeout = crl::time(200); + +struct InactivePressedWidget { + QWidget *widget = nullptr; + base::qt_connection connection; + base::Timer timer; +}; + +std::unique_ptr Tracker; + +} // namespace + +void MarkInactivePress(not_null widget, bool was) { + if (!was) { + if (WasInactivePress(widget)) { + Tracker = nullptr; + } + return; + } + + Tracker = std::make_unique(); + Tracker->widget = widget; + Tracker->connection = QObject::connect(widget, &QWidget::destroyed, [=] { + Tracker->connection.release(); + Tracker = nullptr; + }); + Tracker->timer.setCallback([=] { + Tracker = nullptr; + }); + Tracker->timer.callOnce(kInactivePressTimeout); +} + +[[nodiscard]] bool WasInactivePress(not_null widget) { + return Tracker && (Tracker->widget == widget); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/inactive_press.h b/Telegram/SourceFiles/ui/inactive_press.h new file mode 100644 index 000000000..aace0c714 --- /dev/null +++ b/Telegram/SourceFiles/ui/inactive_press.h @@ -0,0 +1,15 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { + +void MarkInactivePress(not_null widget, bool was); +[[nodiscard]] bool WasInactivePress(not_null widget); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.cpp b/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.cpp new file mode 100644 index 000000000..ebdc5a697 --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.cpp @@ -0,0 +1,48 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/platform/linux/ui_platform_utility_linux.h" + +#include "base/flat_set.h" +#include "ui/ui_log.h" + +#include +#include +#include +#include + +namespace Ui { +namespace Platform { + +bool IsApplicationActive() { + return QApplication::activeWindow() != nullptr; +} + +bool TranslucentWindowsSupported(QPoint globalPosition) { + if (const auto native = QGuiApplication::platformNativeInterface()) { + if (const auto desktop = QApplication::desktop()) { + const auto index = desktop->screenNumber(globalPosition); + const auto screens = QGuiApplication::screens(); + if (const auto screen = (index >= 0 && index < screens.size()) ? screens[index] : QGuiApplication::primaryScreen()) { + if (native->nativeResourceForScreen(QByteArray("compositingEnabled"), screen)) { + return true; + } + static auto WarnedAbout = base::flat_set(); + if (!WarnedAbout.contains(index)) { + WarnedAbout.emplace(index); + UI_LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + } + } else { + UI_LOG(("WARNING: Could not get screen for index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + } + } + } + return false; +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.h b/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.h new file mode 100644 index 000000000..5e78e89e1 --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/linux/ui_platform_utility_linux.h @@ -0,0 +1,38 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class QPainter; +class QPaintEvent; + +namespace Ui { +namespace Platform { + +inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { +} + +inline void InitOnTopPanel(not_null panel) { +} + +inline void DeInitOnTopPanel(not_null panel) { +} + +inline void ReInitOnTopPanel(not_null panel) { +} + +inline void UpdateOverlayed(not_null widget) { +} + +inline void ShowOverAll(not_null widget, bool canFocus) { +} + +inline void BringToBack(not_null widget) { +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.h b/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.h new file mode 100644 index 000000000..b7fa2f6b9 --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.h @@ -0,0 +1,23 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +namespace Ui { +namespace Platform { + +inline bool TranslucentWindowsSupported(QPoint globalPosition) { + return true; +} + +inline void UpdateOverlayed(not_null widget) { +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.mm b/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.mm new file mode 100644 index 000000000..993c1f725 --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/mac/ui_platform_utility_mac.mm @@ -0,0 +1,88 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/platform/mac/ui_platform_utility_mac.h" + +#include "ui/ui_integration.h" + +#include +#include +#include + +#include + +namespace Ui { +namespace Platform { + +bool IsApplicationActive() { + return [[NSApplication sharedApplication] isActive]; +} + +void InitOnTopPanel(not_null panel) { + Expects(!panel->windowHandle()); + + // Force creating windowHandle() without creating the platform window yet. + panel->setAttribute(Qt::WA_NativeWindow, true); + panel->windowHandle()->setProperty("_td_macNonactivatingPanelMask", QVariant(true)); + panel->setAttribute(Qt::WA_NativeWindow, false); + + panel->createWinId(); + + auto platformWindow = [reinterpret_cast(panel->winId()) window]; + Assert([platformWindow isKindOfClass:[NSPanel class]]); + + auto platformPanel = static_cast(platformWindow); + [platformPanel setLevel:NSPopUpMenuWindowLevel]; + [platformPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + [platformPanel setFloatingPanel:YES]; + [platformPanel setHidesOnDeactivate:NO]; + + Integration::Instance().activationFromTopPanel(); +} + +void DeInitOnTopPanel(not_null panel) { + auto platformWindow = [reinterpret_cast(panel->winId()) window]; + Assert([platformWindow isKindOfClass:[NSPanel class]]); + + auto platformPanel = static_cast(platformWindow); + auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorCanJoinAllSpaces)) | NSWindowCollectionBehaviorMoveToActiveSpace; + [platformPanel setCollectionBehavior:newBehavior]; +} + +void ReInitOnTopPanel(not_null panel) { + auto platformWindow = [reinterpret_cast(panel->winId()) window]; + Assert([platformWindow isKindOfClass:[NSPanel class]]); + + auto platformPanel = static_cast(platformWindow); + auto newBehavior = ([platformPanel collectionBehavior] & (~NSWindowCollectionBehaviorMoveToActiveSpace)) | NSWindowCollectionBehaviorCanJoinAllSpaces; + [platformPanel setCollectionBehavior:newBehavior]; +} + +void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { +#ifdef OS_MAC_OLD + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(e->rect(), Qt::transparent); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); +#endif // OS_MAC_OLD +} + +void ShowOverAll(not_null widget, bool canFocus) { + NSWindow *wnd = [reinterpret_cast(widget->winId()) window]; + [wnd setLevel:NSPopUpMenuWindowLevel]; + if (!canFocus) { + [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; + [wnd setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + } +} + +void BringToBack(not_null widget) { + NSWindow *wnd = [reinterpret_cast(widget->winId()) window]; + [wnd setLevel:NSModalPanelWindowLevel]; +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/ui_platform_utility.h b/Telegram/SourceFiles/ui/platform/ui_platform_utility.h new file mode 100644 index 000000000..4cd3d97ef --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/ui_platform_utility.h @@ -0,0 +1,41 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class QPoint; +class QPainter; +class QPaintEvent; + +namespace Ui { +namespace Platform { + +[[nodiscard]] bool IsApplicationActive(); + +[[nodiscard]] bool TranslucentWindowsSupported(QPoint globalPosition); +void StartTranslucentPaint(QPainter &p, QPaintEvent *e); + +void InitOnTopPanel(not_null panel); +void DeInitOnTopPanel(not_null panel); +void ReInitOnTopPanel(not_null panel); + +void UpdateOverlayed(not_null widget); +void ShowOverAll(not_null widget, bool canFocus = true); +void BringToBack(not_null widget); + +} // namespace Platform +} // namespace Ui + +// Platform dependent implementations. + +#ifdef Q_OS_MAC +#include "ui/platform/mac/ui_platform_utility_mac.h" +#elif defined Q_OS_LINUX // Q_OS_MAC +#include "ui/platform/linux/ui_platform_utility_linux.h" +#elif defined Q_OS_WINRT || defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX +#include "ui/platform/win/ui_platform_utility_win.h" +#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN diff --git a/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.cpp b/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.cpp new file mode 100644 index 000000000..fe3dc0f1e --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.cpp @@ -0,0 +1,32 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/platform/win/ui_platform_utility_win.h" + +#include + +namespace Ui { +namespace Platform { + +bool IsApplicationActive() { + return QApplication::activeWindow() != nullptr; +} + +void UpdateOverlayed(not_null widget) { + const auto wm = widget->testAttribute(Qt::WA_Mapped); + const auto wv = widget->testAttribute(Qt::WA_WState_Visible); + if (!wm) widget->setAttribute(Qt::WA_Mapped, true); + if (!wv) widget->setAttribute(Qt::WA_WState_Visible, true); + widget->update(); + QEvent e(QEvent::UpdateRequest); + QGuiApplication::sendEvent(widget, &e); + if (!wm) widget->setAttribute(Qt::WA_Mapped, false); + if (!wv) widget->setAttribute(Qt::WA_WState_Visible, false); +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.h b/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.h new file mode 100644 index 000000000..ad83beb45 --- /dev/null +++ b/Telegram/SourceFiles/ui/platform/win/ui_platform_utility_win.h @@ -0,0 +1,41 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +class QPainter; +class QPaintEvent; + +namespace Ui { +namespace Platform { + +inline bool TranslucentWindowsSupported(QPoint globalPosition) { + return true; +} + +inline void InitOnTopPanel(not_null panel) { +} + +inline void DeInitOnTopPanel(not_null panel) { +} + +inline void ReInitOnTopPanel(not_null panel) { +} + +inline void StartTranslucentPaint(QPainter &p, QPaintEvent *e) { +} + +inline void ShowOverAll(not_null widget, bool canFocus) { +} + +inline void BringToBack(not_null widget) { +} + +} // namespace Platform +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/round_rect.cpp b/Telegram/SourceFiles/ui/round_rect.cpp new file mode 100644 index 000000000..5cd6a5c37 --- /dev/null +++ b/Telegram/SourceFiles/ui/round_rect.cpp @@ -0,0 +1,83 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/round_rect.h" + +#include "ui/style/style_core.h" +#include "ui/image/image_prepare.h" +#include "ui/ui_utility.h" + +namespace Ui { + + +void DrawRoundedRect( + QPainter &p, + const QRect &rect, + const QBrush &brush, + const std::array &corners, + RectParts parts) { + const auto pixelRatio = style::DevicePixelRatio(); + const auto x = rect.x(); + const auto y = rect.y(); + const auto w = rect.width(); + const auto h = rect.height(); + auto cornerWidth = corners[0].width() / pixelRatio; + auto cornerHeight = corners[0].height() / pixelRatio; + if (w < 2 * cornerWidth || h < 2 * cornerHeight) return; + if (w > 2 * cornerWidth) { + if (parts & RectPart::Top) { + p.fillRect(x + cornerWidth, y, w - 2 * cornerWidth, cornerHeight, brush); + } + if (parts & RectPart::Bottom) { + p.fillRect(x + cornerWidth, y + h - cornerHeight, w - 2 * cornerWidth, cornerHeight, brush); + } + } + if (h > 2 * cornerHeight) { + if ((parts & RectPart::NoTopBottom) == RectPart::NoTopBottom) { + p.fillRect(x, y + cornerHeight, w, h - 2 * cornerHeight, brush); + } else { + if (parts & RectPart::Left) { + p.fillRect(x, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, brush); + } + if ((parts & RectPart::Center) && w > 2 * cornerWidth) { + p.fillRect(x + cornerWidth, y + cornerHeight, w - 2 * cornerWidth, h - 2 * cornerHeight, brush); + } + if (parts & RectPart::Right) { + p.fillRect(x + w - cornerWidth, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, brush); + } + } + } + if (parts & RectPart::TopLeft) { + p.drawImage(x, y, corners[0]); + } + if (parts & RectPart::TopRight) { + p.drawImage(x + w - cornerWidth, y, corners[1]); + } + if (parts & RectPart::BottomLeft) { + p.drawImage(x, y + h - cornerHeight, corners[2]); + } + if (parts & RectPart::BottomRight) { + p.drawImage(x + w - cornerWidth, y + h - cornerHeight, corners[3]); + } +} + +RoundRect::RoundRect( + ImageRoundRadius radius, + const style::color &color) +: _color(color) +, _corners(Images::PrepareCorners(radius, color)) { + style::PaletteChanged( + ) | rpl::start_with_next([=] { + _corners = Images::PrepareCorners(radius, _color); + }, _lifetime); +} + +void RoundRect::paint(QPainter &p, const QRect &rect) const { + DrawRoundedRect(p, rect, _color, _corners); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/round_rect.h b/Telegram/SourceFiles/ui/round_rect.h new file mode 100644 index 000000000..c7739ecdf --- /dev/null +++ b/Telegram/SourceFiles/ui/round_rect.h @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rect_part.h" +#include "ui/style/style_core.h" + +enum class ImageRoundRadius; +class QPainter; + +namespace Ui { + +void DrawRoundedRect( + QPainter &p, + const QRect &rect, + const QBrush &brush, + const std::array & corners, + RectParts parts = RectPart::Full); + +class RoundRect final { +public: + RoundRect(ImageRoundRadius radius, const style::color &color); + + void paint(QPainter &p, const QRect &rect) const; + +private: + style::color _color; + std::array _corners; + + rpl::lifetime _lifetime; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index 17f0c7fb5..90da8b844 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -1117,4 +1117,8 @@ QPoint SilentToggle::tooltipPos() const { return QCursor::pos(); } +bool SilentToggle::tooltipWindowActive() const { + return InFocusChain(window()); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/special_buttons.h b/Telegram/SourceFiles/ui/special_buttons.h index b297703d3..47ac205fc 100644 --- a/Telegram/SourceFiles/ui/special_buttons.h +++ b/Telegram/SourceFiles/ui/special_buttons.h @@ -278,6 +278,7 @@ public: // AbstractTooltipShower interface QString tooltipText() const override; QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; protected: void mouseMoveEvent(QMouseEvent *e) override; diff --git a/Telegram/SourceFiles/ui/special_fields.cpp b/Telegram/SourceFiles/ui/special_fields.cpp new file mode 100644 index 000000000..c7973ca59 --- /dev/null +++ b/Telegram/SourceFiles/ui/special_fields.cpp @@ -0,0 +1,413 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/special_fields.h" + +#include "core/application.h" +#include "lang/lang_keys.h" +#include "data/data_countries.h" // Data::ValidPhoneCode +#include "numbers.h" + +namespace Ui { +namespace { + +constexpr auto kMaxUsernameLength = 32; + +} // namespace + +CountryCodeInput::CountryCodeInput( + QWidget *parent, + const style::InputField &st) +: MaskedInputField(parent, st) +, _nosignal(false) { +} + +void CountryCodeInput::startErasing(QKeyEvent *e) { + setFocus(); + keyPressEvent(e); +} + +void CountryCodeInput::codeSelected(const QString &code) { + auto wasText = getLastText(); + auto wasCursor = cursorPosition(); + auto newText = '+' + code; + auto newCursor = newText.size(); + setText(newText); + _nosignal = true; + correctValue(wasText, wasCursor, newText, newCursor); + _nosignal = false; + emit changed(); +} + +void CountryCodeInput::correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) { + QString newText, addToNumber; + int oldPos(nowCursor); + int newPos(-1); + int oldLen(now.length()); + int start = 0; + int digits = 5; + newText.reserve(oldLen + 1); + if (oldLen && now[0] == '+') { + if (start == oldPos) { + newPos = newText.length(); + } + ++start; + } + newText += '+'; + for (int i = start; i < oldLen; ++i) { + if (i == oldPos) { + newPos = newText.length(); + } + auto ch = now[i]; + if (ch.isDigit()) { + if (!digits || !--digits) { + addToNumber += ch; + } else { + newText += ch; + } + } + } + if (!addToNumber.isEmpty()) { + auto validCode = Data::ValidPhoneCode(newText.mid(1)); + addToNumber = newText.mid(1 + validCode.length()) + addToNumber; + newText = '+' + validCode; + } + setCorrectedText(now, nowCursor, newText, newPos); + + if (!_nosignal && was != newText) { + emit codeChanged(newText.mid(1)); + } + if (!addToNumber.isEmpty()) { + emit addedToNumber(addToNumber); + } +} + +PhonePartInput::PhonePartInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st/*, tr::lng_phone_ph(tr::now)*/) { +} + +void PhonePartInput::paintAdditionalPlaceholder(Painter &p) { + if (!_pattern.isEmpty()) { + auto t = getDisplayedText(); + auto ph = _additionalPlaceholder.mid(t.size()); + if (!ph.isEmpty()) { + p.setClipRect(rect()); + auto phRect = placeholderRect(); + int tw = phFont()->width(t); + if (tw < phRect.width()) { + phRect.setLeft(phRect.left() + tw); + placeholderAdditionalPrepare(p); + p.drawText(phRect, ph, style::al_topleft); + } + } + } +} + +void PhonePartInput::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Backspace && getLastText().isEmpty()) { + emit voidBackspace(e); + } else { + MaskedInputField::keyPressEvent(e); + } +} + +void PhonePartInput::correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) { + QString newText; + int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = 0; + for (int i = 0; i < oldLen; ++i) { + if (now[i].isDigit()) { + ++digitCount; + } + } + if (digitCount > MaxPhoneTailLength) digitCount = MaxPhoneTailLength; + + bool inPart = !_pattern.isEmpty(); + int curPart = -1, leftInPart = 0; + newText.reserve(oldLen); + for (int i = 0; i < oldLen; ++i) { + if (i == oldPos && newPos < 0) { + newPos = newText.length(); + } + + auto ch = now[i]; + if (ch.isDigit()) { + if (!digitCount--) { + break; + } + if (inPart) { + if (leftInPart) { + --leftInPart; + } else { + newText += ' '; + ++curPart; + inPart = curPart < _pattern.size(); + leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0; + + ++oldPos; + } + } + newText += ch; + } else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') { + if (inPart) { + if (leftInPart) { + } else { + newText += ch; + ++curPart; + inPart = curPart < _pattern.size(); + leftInPart = inPart ? _pattern.at(curPart) : 0; + } + } else { + newText += ch; + } + } + } + auto newlen = newText.size(); + while (newlen > 0 && newText.at(newlen - 1).isSpace()) { + --newlen; + } + if (newlen < newText.size()) { + newText = newText.mid(0, newlen); + } + setCorrectedText(now, nowCursor, newText, newPos); +} + +void PhonePartInput::addedToNumber(const QString &added) { + setFocus(); + auto wasText = getLastText(); + auto wasCursor = cursorPosition(); + auto newText = added + wasText; + auto newCursor = newText.size(); + setText(newText); + setCursorPosition(added.length()); + correctValue(wasText, wasCursor, newText, newCursor); + startPlaceholderAnimation(); +} + +void PhonePartInput::onChooseCode(const QString &code) { + _pattern = phoneNumberParse(code); + if (!_pattern.isEmpty() && _pattern.at(0) == code.size()) { + _pattern.pop_front(); + } else { + _pattern.clear(); + } + _additionalPlaceholder = QString(); + if (!_pattern.isEmpty()) { + _additionalPlaceholder.reserve(20); + for (const auto part : _pattern) { + _additionalPlaceholder.append(' '); + _additionalPlaceholder.append(QString(part, QChar(0x2212))); + } + } + setPlaceholderHidden(!_additionalPlaceholder.isEmpty()); + + auto wasText = getLastText(); + auto wasCursor = cursorPosition(); + auto newText = getLastText(); + auto newCursor = newText.size(); + correctValue(wasText, wasCursor, newText, newCursor); + + startPlaceholderAnimation(); +} + +UsernameInput::UsernameInput( + QWidget *parent, + const style::InputField &st, + rpl::producer placeholder, + const QString &val, + bool isLink) +: MaskedInputField(parent, st, std::move(placeholder), val) { + setLinkPlaceholder( + isLink ? Core::App().createInternalLink(QString()) : QString()); +} + +void UsernameInput::setLinkPlaceholder(const QString &placeholder) { + _linkPlaceholder = placeholder; + if (!_linkPlaceholder.isEmpty()) { + setTextMargins(style::margins(_st.textMargins.left() + _st.font->width(_linkPlaceholder), _st.textMargins.top(), _st.textMargins.right(), _st.textMargins.bottom())); + setPlaceholderHidden(true); + } +} + +void UsernameInput::paintAdditionalPlaceholder(Painter &p) { + if (!_linkPlaceholder.isEmpty()) { + p.setFont(_st.font); + p.setPen(_st.placeholderFg); + p.drawText(QRect(_st.textMargins.left(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), _linkPlaceholder, style::al_topleft); + } +} + +void UsernameInput::correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) { + auto newPos = nowCursor; + auto from = 0, len = now.size(); + for (; from < len; ++from) { + if (!now.at(from).isSpace()) { + break; + } + if (newPos > 0) --newPos; + } + len -= from; + if (len > kMaxUsernameLength) { + len = kMaxUsernameLength + (now.at(from) == '@' ? 1 : 0); + } + for (int32 to = from + len; to > from;) { + --to; + if (!now.at(to).isSpace()) { + break; + } + --len; + } + setCorrectedText(now, nowCursor, now.mid(from, len), newPos); +} + +PhoneInput::PhoneInput( + QWidget *parent, + const style::InputField &st, + rpl::producer placeholder, + const QString &defaultValue, + QString value) +: MaskedInputField(parent, st, std::move(placeholder), value) +, _defaultValue(defaultValue) { + if (value.isEmpty()) { + clearText(); + } else { + auto pos = value.size(); + correctValue(QString(), 0, value, pos); + } +} + +void PhoneInput::focusInEvent(QFocusEvent *e) { + MaskedInputField::focusInEvent(e); + setSelection(cursorPosition(), cursorPosition()); +} + +void PhoneInput::clearText() { + auto value = _defaultValue; + setText(value); + auto pos = value.size(); + correctValue(QString(), 0, value, pos); +} + +void PhoneInput::paintAdditionalPlaceholder(Painter &p) { + if (!_pattern.isEmpty()) { + auto t = getDisplayedText(); + auto ph = _additionalPlaceholder.mid(t.size()); + if (!ph.isEmpty()) { + p.setClipRect(rect()); + auto phRect = placeholderRect(); + int tw = phFont()->width(t); + if (tw < phRect.width()) { + phRect.setLeft(phRect.left() + tw); + placeholderAdditionalPrepare(p); + p.drawText(phRect, ph, style::al_topleft); + } + } + } +} + +void PhoneInput::correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) { + auto digits = now; + digits.replace(QRegularExpression(qsl("[^\\d]")), QString()); + _pattern = phoneNumberParse(digits); + + QString newPlaceholder; + if (_pattern.isEmpty()) { + newPlaceholder = QString(); + } else if (_pattern.size() == 1 && _pattern.at(0) == digits.size()) { + newPlaceholder = QString(_pattern.at(0) + 2, ' ') + tr::lng_contact_phone(tr::now); + } else { + newPlaceholder.reserve(20); + for (int i = 0, l = _pattern.size(); i < l; ++i) { + if (i) { + newPlaceholder.append(' '); + } else { + newPlaceholder.append('+'); + } + newPlaceholder.append(i ? QString(_pattern.at(i), QChar(0x2212)) : digits.mid(0, _pattern.at(i))); + } + } + if (_additionalPlaceholder != newPlaceholder) { + _additionalPlaceholder = newPlaceholder; + setPlaceholderHidden(!_additionalPlaceholder.isEmpty()); + update(); + } + + QString newText; + int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = qMin(digits.size(), MaxPhoneCodeLength + MaxPhoneTailLength); + + bool inPart = !_pattern.isEmpty(), plusFound = false; + int curPart = 0, leftInPart = inPart ? _pattern.at(curPart) : 0; + newText.reserve(oldLen + 1); + newText.append('+'); + for (int i = 0; i < oldLen; ++i) { + if (i == oldPos && newPos < 0) { + newPos = newText.length(); + } + + QChar ch(now[i]); + if (ch.isDigit()) { + if (!digitCount--) { + break; + } + if (inPart) { + if (leftInPart) { + --leftInPart; + } else { + newText += ' '; + ++curPart; + inPart = curPart < _pattern.size(); + leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0; + + ++oldPos; + } + } + newText += ch; + } else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') { + if (inPart) { + if (leftInPart) { + } else { + newText += ch; + ++curPart; + inPart = curPart < _pattern.size(); + leftInPart = inPart ? _pattern.at(curPart) : 0; + } + } else { + newText += ch; + } + } else if (ch == '+') { + plusFound = true; + } + } + if (!plusFound && newText == qstr("+")) { + newText = QString(); + newPos = 0; + } + int32 newlen = newText.size(); + while (newlen > 0 && newText.at(newlen - 1).isSpace()) { + --newlen; + } + if (newlen < newText.size()) { + newText = newText.mid(0, newlen); + } + setCorrectedText(now, nowCursor, newText, newPos); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/special_fields.h b/Telegram/SourceFiles/ui/special_fields.h new file mode 100644 index 000000000..2306c8606 --- /dev/null +++ b/Telegram/SourceFiles/ui/special_fields.h @@ -0,0 +1,121 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/widgets/input_fields.h" + +namespace Ui { + +class CountryCodeInput : public MaskedInputField { + Q_OBJECT + +public: + CountryCodeInput(QWidget *parent, const style::InputField &st); + +public slots: + void startErasing(QKeyEvent *e); + void codeSelected(const QString &code); + +signals: + void codeChanged(const QString &code); + void addedToNumber(const QString &added); + +protected: + void correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) override; + +private: + bool _nosignal; + +}; + +class PhonePartInput : public MaskedInputField { + Q_OBJECT + +public: + PhonePartInput(QWidget *parent, const style::InputField &st); + +public slots: + void addedToNumber(const QString &added); + void onChooseCode(const QString &code); + +signals: + void voidBackspace(QKeyEvent *e); + +protected: + void keyPressEvent(QKeyEvent *e) override; + + void correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) override; + void paintAdditionalPlaceholder(Painter &p) override; + +private: + QVector _pattern; + QString _additionalPlaceholder; + +}; + +class UsernameInput : public MaskedInputField { +public: + UsernameInput( + QWidget *parent, + const style::InputField &st, + rpl::producer placeholder, + const QString &val, + bool isLink); + + void setLinkPlaceholder(const QString &placeholder); + +protected: + void correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) override; + void paintAdditionalPlaceholder(Painter &p) override; + +private: + QString _linkPlaceholder; + +}; + +class PhoneInput : public MaskedInputField { +public: + PhoneInput( + QWidget *parent, + const style::InputField &st, + rpl::producer placeholder, + const QString &defaultValue, + QString value); + + void clearText(); + +protected: + void focusInEvent(QFocusEvent *e) override; + + void correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) override; + void paintAdditionalPlaceholder(Painter &p) override; + +private: + QString _defaultValue; + QVector _pattern; + QString _additionalPlaceholder; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index bb46cffb4..20dfdfdbd 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/palette.h" #include +#include #include #include @@ -28,6 +29,7 @@ constexpr auto kContrastDeltaL = 64; auto PaletteChanges = rpl::event_stream<>(); auto ShortAnimationRunning = rpl::variable(false); auto RunningShortAnimations = 0; +auto ResolvedMonospaceFont = style::font(); std::vector &StyleModules() { static auto result = std::vector(); @@ -40,6 +42,28 @@ void startModules(int scale) { } } +void ResolveMonospaceFont() { + auto family = QString(); + const auto tryFont = [&](const QString &attempt) { + if (family.isEmpty() + && !QFontInfo(QFont(attempt)).family().trimmed().compare( + attempt, + Qt::CaseInsensitive)) { + family = attempt; + } + }; + tryFont("Consolas"); + tryFont("Liberation Mono"); + tryFont("Menlo"); + tryFont("Courier"); + if (family.isEmpty()) { + const auto type = QFontDatabase::FixedFont; + family = QFontDatabase::systemFont(type).family(); + } + const auto size = st::normalFont->f.pixelSize(); + ResolvedMonospaceFont = style::font(size, 0, family); +} + } // namespace void registerModule(ModuleBase *module) { @@ -60,6 +84,17 @@ void StopShortAnimation() { } // namespace internal +void startManager(int scale) { + internal::registerFontFamily("Open Sans"); + internal::startModules(scale); + internal::ResolveMonospaceFont(); +} + +void stopManager() { + internal::destroyFonts(); + internal::destroyIcons(); +} + rpl::producer<> PaletteChanged() { return internal::PaletteChanges.events(); } @@ -72,14 +107,8 @@ rpl::producer ShortAnimationPlaying() { return internal::ShortAnimationRunning.value(); } -void startManager(int scale) { - internal::registerFontFamily("Open Sans"); - internal::startModules(scale); -} - -void stopManager() { - internal::destroyFonts(); - internal::destroyIcons(); +const style::font &MonospaceFont() { + return internal::ResolvedMonospaceFont; } void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) { diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index 6503fa72c..f62dace0a 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -44,6 +44,8 @@ void NotifyPaletteChanged(); [[nodiscard]] rpl::producer ShortAnimationPlaying(); +const style::font &MonospaceFont(); + // *outResult must be r.width() x r.height(), ARGB32_Premultiplied. // QRect(0, 0, src.width(), src.height()) must contain r. void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect = QRect(), QPoint dstPoint = QPoint(0, 0)); diff --git a/Telegram/SourceFiles/ui/style/style_core_direction.h b/Telegram/SourceFiles/ui/style/style_core_direction.h index 2cf6620ff..096087622 100644 --- a/Telegram/SourceFiles/ui/style/style_core_direction.h +++ b/Telegram/SourceFiles/ui/style/style_core_direction.h @@ -17,6 +17,10 @@ namespace style { [[nodiscard]] bool RightToLeft(); void SetRightToLeft(bool rtl); +[[nodiscard]] inline Qt::LayoutDirection LayoutDirection() { + return RightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; +} + inline QRect centerrect(const QRect &inRect, const QRect &rect) { return QRect( inRect.x() + (inRect.width() - rect.width()) / 2, diff --git a/Telegram/SourceFiles/ui/style/style_core_font.cpp b/Telegram/SourceFiles/ui/style/style_core_font.cpp index e3c1c51f2..653dbfff0 100644 --- a/Telegram/SourceFiles/ui/style/style_core_font.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_font.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/style/style_core_font.h" #include "base/algorithm.h" -#include "logs.h" +#include "ui/ui_log.h" #include #include @@ -36,13 +36,13 @@ bool ValidateFont(const QString &familyName, int flags = 0) { checkFont.setStyleStrategy(QFont::PreferQuality); auto realFamily = QFontInfo(checkFont).family(); if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) { - LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily)); + UI_LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily)); return false; } auto metrics = QFontMetrics(checkFont); if (!metrics.height()) { - LOG(("Font Error: got a zero height in '%1'.").arg(familyName)); + UI_LOG(("Font Error: got a zero height in '%1'.").arg(familyName)); return false; } @@ -52,7 +52,7 @@ bool ValidateFont(const QString &familyName, int flags = 0) { bool LoadCustomFont(const QString &filePath, const QString &familyName, int flags = 0) { auto regularId = QFontDatabase::addApplicationFont(filePath); if (regularId < 0) { - LOG(("Font Error: could not add '%1'.").arg(filePath)); + UI_LOG(("Font Error: could not add '%1'.").arg(filePath)); return false; } @@ -65,7 +65,7 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag return false; }; if (!found()) { - LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath)); + UI_LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath)); return false; } @@ -96,13 +96,13 @@ void StartFonts() { if (!regular || !bold) { if (ValidateFont("Segoe UI") && ValidateFont("Segoe UI", style::internal::FontBold)) { OpenSansOverride = "Segoe UI"; - LOG(("Fonts Info: Using Segoe UI instead of Open Sans.")); + UI_LOG(("Fonts Info: Using Segoe UI instead of Open Sans.")); } } if (!semibold) { if (ValidateFont("Segoe UI Semibold")) { OpenSansSemiboldOverride = "Segoe UI Semibold"; - LOG(("Fonts Info: Using Segoe UI Semibold instead of Open Sans Semibold.")); + UI_LOG(("Fonts Info: Using Segoe UI Semibold instead of Open Sans Semibold.")); } } // Disable default fallbacks to Segoe UI, see: diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index cc58fc4b2..7891995be 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -7,16 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/text/text.h" -#include "core/click_handler_types.h" -#include "core/crash_reports.h" +#include "ui/basic_click_handlers.h" #include "ui/text/text_block.h" #include "ui/text/text_isolated_emoji.h" #include "ui/emoji_config.h" -#include "lang/lang_keys.h" +#include "ui/ui_integration.h" #include "platform/platform_info.h" -#include "boxes/confirm_box.h" -#include "mainwindow.h" -#include "app.h" #include #include @@ -111,7 +107,7 @@ QFixed ComputeStopAfter(const TextParseOptions &options, const style::TextStyle // Open Sans tilde fix. bool ComputeCheckTilde(const style::TextStyle &st) { const auto &font = st.font; - return (font->size() * cIntRetinaFactor() == 13) + return (font->size() * style::DevicePixelRatio() == 13) && (font->flags() == 0) && (font->f.family() == qstr("Open Sans")); } @@ -126,10 +122,6 @@ bool chIsBad(QChar ch) { || (ch >= 65024 && ch < 65040 && ch != 65039) || (ch >= 127 && ch < 160 && ch != 156) - || (Platform::IsMac() - && !Platform::IsMac10_7OrGreater() - && (ch == 8207 || ch == 8206 || ch == 8288)) - // qt harfbuzz crash see https://github.com/telegramdesktop/tdesktop/issues/4551 || (Platform::IsMac() && ch == 6158) @@ -811,7 +803,7 @@ void Parser::parseCurrentChar() { void Parser::parseEmojiFromCurrent() { int len = 0; - auto e = Ui::Emoji::Find(_ptr - _emojiLookback, _end, &len); + auto e = Emoji::Find(_ptr - _emojiLookback, _end, &len); if (!e) return; for (int l = len - _emojiLookback - 1; l > 0; --l) { @@ -820,8 +812,8 @@ void Parser::parseEmojiFromCurrent() { if (e->hasPostfix()) { Assert(!_t->_text.isEmpty()); const auto last = _t->_text[_t->_text.size() - 1]; - if (last.unicode() != Ui::Emoji::kPostfix) { - _t->_text.push_back(QChar(Ui::Emoji::kPostfix)); + if (last.unicode() != Emoji::kPostfix) { + _t->_text.push_back(QChar(Emoji::kPostfix)); ++len; } } @@ -940,63 +932,20 @@ void Parser::computeLinkText(const QString &linkData, QString *outLinkText, Link ClickHandlerPtr Parser::CreateHandlerForLink( const TextLinkData &link, const TextParseOptions &options) { + const auto result = Integration::Instance().createLinkHandler( + link.type, + link.text, + link.data, + options); + if (result) { + return result; + } switch (link.type) { - case EntityType::CustomUrl: - return !link.data.isEmpty() - ? std::make_shared(link.data) - : nullptr; - case EntityType::Email: case EntityType::Url: return std::make_shared( link.data, link.displayStatus == LinkDisplayedFull); - - case EntityType::BotCommand: - return std::make_shared(link.data); - - case EntityType::Hashtag: - if (options.flags & TextTwitterMentions) { - return std::make_shared( - (qsl("https://twitter.com/hashtag/") - + link.data.mid(1) - + qsl("?src=hash")), - true); - } else if (options.flags & TextInstagramMentions) { - return std::make_shared( - (qsl("https://instagram.com/explore/tags/") - + link.data.mid(1) - + '/'), - true); - } - return std::make_shared(link.data); - - case EntityType::Cashtag: - return std::make_shared(link.data); - - case EntityType::Mention: - if (options.flags & TextTwitterMentions) { - return std::make_shared( - qsl("https://twitter.com/") + link.data.mid(1), - true); - } else if (options.flags & TextInstagramMentions) { - return std::make_shared( - qsl("https://instagram.com/") + link.data.mid(1) + '/', - true); - } - return std::make_shared(link.data); - - case EntityType::MentionName: { - auto fields = TextUtilities::MentionNameDataToFields(link.data); - if (fields.userId) { - return std::make_shared( - link.text, - fields.userId, - fields.accessHash); - } else { - LOG(("Bad mention name: %1").arg(link.data)); - } - } break; } return nullptr; } @@ -1155,7 +1104,7 @@ public: _align = align; _parDirection = _t->_startDir; - if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir(); + if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = style::LayoutDirection(); if ((*_t->_blocks.cbegin())->type() != TextBlockTNewline) { initNextParagraph(_t->_blocks.cbegin()); } @@ -1195,7 +1144,7 @@ public: } _parDirection = static_cast(b)->nextDirection(); - if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir(); + if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = style::LayoutDirection(); initNextParagraph(i + 1); longWordLine = true; @@ -1598,7 +1547,7 @@ private: } } QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); - if (rtl() && skipIndex == nItems - 1) { + if (style::RightToLeft() && skipIndex == nItems - 1) { for (int32 i = nItems; i > 1;) { --i; visualOrder[i] = visualOrder[i - 1]; @@ -1705,10 +1654,10 @@ private: } } } - Ui::Emoji::Draw( + Emoji::Draw( *_p, static_cast(currentBlock)->emoji, - Ui::Emoji::GetSizeNormal(), + Emoji::GetSizeNormal(), (glyphX + st::emojiPadding).toInt(), _y + _yDelta + emojiY); // } else if (_p && currentBlock->type() == TextBlockSkip) { // debug @@ -1900,7 +1849,7 @@ private: } void prepareElidedLine(QString &lineText, int32 lineStart, int32 &lineLength, AbstractBlock *&_endBlock, int repeat = 0) { - static const QString _Elide = qsl("..."); + static const auto _Elide = QString::fromLatin1("..."); _f = _t->_st->font; QStackTextEngine engine(lineText, _f->f); @@ -2046,7 +1995,7 @@ private: } auto result = f; if ((flags & TextBlockFPre) || (flags & TextBlockFCode)) { - result = App::monofont(); + result = style::MonospaceFont(); if (result->size() != f->size() || result->flags() != f->flags()) { result = style::font(f->size(), f->flags(), result->family()); } @@ -3095,6 +3044,22 @@ void String::drawElided(Painter &painter, int32 left, int32 top, int32 w, int32 p.drawElided(left, top, w, align, lines, yFrom, yTo, removeFromEnd, breakEverywhere, selection); } +void String::drawLeft(Painter &p, int32 left, int32 top, int32 width, int32 outerw, style::align align, int32 yFrom, int32 yTo, TextSelection selection) const { + draw(p, style::RightToLeft() ? (outerw - left - width) : left, top, width, align, yFrom, yTo, selection); +} + +void String::drawLeftElided(Painter &p, int32 left, int32 top, int32 width, int32 outerw, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) const { + drawElided(p, style::RightToLeft() ? (outerw - left - width) : left, top, width, lines, align, yFrom, yTo, removeFromEnd, breakEverywhere, selection); +} + +void String::drawRight(Painter &p, int32 right, int32 top, int32 width, int32 outerw, style::align align, int32 yFrom, int32 yTo, TextSelection selection) const { + draw(p, style::RightToLeft() ? right : (outerw - right - width), top, width, align, yFrom, yTo, selection); +} + +void String::drawRightElided(Painter &p, int32 right, int32 top, int32 width, int32 outerw, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) const { + drawElided(p, style::RightToLeft() ? right : (outerw - right - width), top, width, lines, align, yFrom, yTo, removeFromEnd, breakEverywhere, selection); +} + StateResult String::getState(QPoint point, int width, StateRequest request) const { return Renderer(nullptr, this).getState(point, width, request); } diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 53e9dd0dd..5d7c2cd07 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_entity.h" #include "ui/painter.h" -#include "core/click_handler.h" +#include "ui/click_handler.h" #include "base/flags.h" #include @@ -138,18 +138,10 @@ public: void draw(Painter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }, bool fullWidthSelection = true) const; void drawElided(Painter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const; - void drawLeft(Painter &p, int32 left, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const { - draw(p, rtl() ? (outerw - left - width) : left, top, width, align, yFrom, yTo, selection); - } - void drawLeftElided(Painter &p, int32 left, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const { - drawElided(p, rtl() ? (outerw - left - width) : left, top, width, lines, align, yFrom, yTo, removeFromEnd, breakEverywhere, selection); - } - void drawRight(Painter &p, int32 right, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const { - draw(p, rtl() ? right : (outerw - right - width), top, width, align, yFrom, yTo, selection); - } - void drawRightElided(Painter &p, int32 right, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const { - drawElided(p, rtl() ? right : (outerw - right - width), top, width, lines, align, yFrom, yTo, removeFromEnd, breakEverywhere, selection); - } + void drawLeft(Painter &p, int32 left, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const; + void drawLeftElided(Painter &p, int32 left, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const; + void drawRight(Painter &p, int32 right, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const; + void drawRightElided(Painter &p, int32 right, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const; StateResult getState(QPoint point, int width, StateRequest request = StateRequest()) const; StateResult getStateLeft(QPoint point, int width, int outerw, StateRequest request = StateRequest()) const; @@ -251,7 +243,7 @@ private: } // namespace Ui inline TextSelection snapSelection(int from, int to) { - return { static_cast(snap(from, 0, 0xFFFF)), static_cast(snap(to, 0, 0xFFFF)) }; + return { static_cast(std::clamp(from, 0, 0xFFFF)), static_cast(std::clamp(to, 0, 0xFFFF)) }; } inline TextSelection shiftSelection(TextSelection selection, uint16 byLength) { return snapSelection(int(selection.from) + byLength, int(selection.to) + byLength); diff --git a/Telegram/SourceFiles/ui/text/text_block.cpp b/Telegram/SourceFiles/ui/text/text_block.cpp index b49a126b6..29173c5c2 100644 --- a/Telegram/SourceFiles/ui/text/text_block.cpp +++ b/Telegram/SourceFiles/ui/text/text_block.cpp @@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/text/text_block.h" -#include "core/crash_reports.h" -#include "app.h" +#include "styles/style_basic.h" #include @@ -310,7 +309,7 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi } if ((flags & TextBlockFPre) || (flags & TextBlockFCode)) { - blockFont = App::monofont(); + blockFont = style::MonospaceFont(); if (blockFont->size() != font->size() || blockFont->flags() != font->flags()) { blockFont = style::font(font->size(), font->flags(), blockFont->family()); } @@ -333,13 +332,8 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi const auto part = str.mid(_from, length); - // Attempt to catch a crash in text processing - CrashReports::SetAnnotationRef("CrashString", &part); - QStackTextEngine engine(part, blockFont->f); BlockParser parser(&engine, this, minResizeWidth, _from, part); - - CrashReports::ClearAnnotationRef("CrashString"); } } diff --git a/Telegram/SourceFiles/ui/text/text_block.h b/Telegram/SourceFiles/ui/text/text_block.h index a20b8123c..6fcf1cd51 100644 --- a/Telegram/SourceFiles/ui/text/text_block.h +++ b/Telegram/SourceFiles/ui/text/text_block.h @@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/style/style_core.h" +#include "ui/emoji_config.h" + #include namespace Ui { diff --git a/Telegram/SourceFiles/ui/text/text_entity.cpp b/Telegram/SourceFiles/ui/text/text_entity.cpp index 91b5b1631..44be2c0ee 100644 --- a/Telegram/SourceFiles/ui/text/text_entity.cpp +++ b/Telegram/SourceFiles/ui/text/text_entity.cpp @@ -7,14 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/text/text_entity.h" -#include "main/main_session.h" #include "lang/lang_tag.h" #include "base/qthelp_url.h" +#include "base/qthelp_regex.h" +#include "base/crc32hash.h" +#include "ui/text/text.h" +#include "ui/widgets/input_fields.h" #include "ui/emoji_config.h" -#include "data/data_user.h" -#include "data/data_session.h" #include +#include +#include +#include namespace TextUtilities { namespace { @@ -23,7 +27,7 @@ QString ExpressionMailNameAtEnd() { // Matches email first part (before '@') at the end of the string. // First we find a domain without protocol (like "gmail.com"), then // we find '@' before it and then we look for the name before '@'. - return qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"); + return QString::fromUtf8("[a-zA-Z\\-_\\.0-9]{1,256}$"); } QString Quotes() { @@ -33,12 +37,12 @@ QString Quotes() { QString ExpressionSeparators(const QString &additional) { static const auto quotes = Quotes(); - return qsl("\\s\\.,:;<>|'\"\\[\\]\\{\\}\\~\\!\\?\\%\\^\\(\\)\\-\\+=\\x10") + quotes + additional; + return QString::fromUtf8("\\s\\.,:;<>|'\"\\[\\]\\{\\}\\~\\!\\?\\%\\^\\(\\)\\-\\+=\\x10") + quotes + additional; } QString Separators(const QString &additional) { static const auto quotes = Quotes(); - return qsl(" \x10\n\r\t.,:;<>|'\"[]{}!?%^()-+=") + return QString::fromUtf8(" \x10\n\r\t.,:;<>|'\"[]{}!?%^()-+=") + QChar(0xfdd0) // QTextBeginningOfFrame + QChar(0xfdd1) // QTextEndOfFrame + QChar(QChar::ParagraphSeparator) @@ -48,35 +52,35 @@ QString Separators(const QString &additional) { } QString SeparatorsBold() { - return Separators(qsl("`~/")); + return Separators(QString::fromUtf8("`~/")); } QString SeparatorsItalic() { - return Separators(qsl("`*~/")); + return Separators(QString::fromUtf8("`*~/")); } QString SeparatorsStrikeOut() { - return Separators(qsl("`*~/")); + return Separators(QString::fromUtf8("`*~/")); } QString SeparatorsMono() { - return Separators(qsl("*~/")); + return Separators(QString::fromUtf8("*~/")); } QString ExpressionHashtag() { - return qsl("(^|[") + ExpressionSeparators(qsl("`\\*/")) + qsl("])#[\\w]{2,64}([\\W]|$)"); + return QString::fromUtf8("(^|[") + ExpressionSeparators(QString::fromUtf8("`\\*/")) + QString::fromUtf8("])#[\\w]{2,64}([\\W]|$)"); } QString ExpressionHashtagExclude() { - return qsl("^#?\\d+$"); + return QString::fromUtf8("^#?\\d+$"); } QString ExpressionMention() { - return qsl("(^|[") + ExpressionSeparators(qsl("`\\*/")) + qsl("])@[A-Za-z_0-9]{1,32}([\\W]|$)"); + return QString::fromUtf8("(^|[") + ExpressionSeparators(QString::fromUtf8("`\\*/")) + QString::fromUtf8("])@[A-Za-z_0-9]{1,32}([\\W]|$)"); } QString ExpressionBotCommand() { - return qsl("(^|[") + ExpressionSeparators(qsl("`\\*")) + qsl("])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"); + return QString::fromUtf8("(^|[") + ExpressionSeparators(QString::fromUtf8("`\\*")) + QString::fromUtf8("])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"); } QRegularExpression CreateRegExp(const QString &expression) { @@ -89,340 +93,340 @@ QRegularExpression CreateRegExp(const QString &expression) { return result; } -QSet CreateValidProtocols() { - auto result = QSet(); - auto addOne = [&result](const QString &string) { - result.insert(hashCrc32(string.constData(), string.size() * sizeof(QChar))); +base::flat_set CreateValidProtocols() { + auto result = base::flat_set(); + const auto addOne = [&](const QString &string) { + result.insert(base::crc32(string.constData(), string.size() * sizeof(QChar))); }; - addOne(qsl("itmss")); // itunes - addOne(qsl("http")); - addOne(qsl("https")); - addOne(qsl("ftp")); - addOne(qsl("tg")); // local urls + addOne(QString::fromLatin1("itmss")); // itunes + addOne(QString::fromLatin1("http")); + addOne(QString::fromLatin1("https")); + addOne(QString::fromLatin1("ftp")); + addOne(QString::fromLatin1("tg")); // local urls return result; } -QSet CreateValidTopDomains() { - auto result = QSet(); +base::flat_set CreateValidTopDomains() { + auto result = base::flat_set(); auto addOne = [&result](const QString &string) { - result.insert(hashCrc32(string.constData(), string.size() * sizeof(QChar))); + result.insert(base::crc32(string.constData(), string.size() * sizeof(QChar))); }; - addOne(qsl("ac")); - addOne(qsl("ad")); - addOne(qsl("ae")); - addOne(qsl("af")); - addOne(qsl("ag")); - addOne(qsl("ai")); - addOne(qsl("al")); - addOne(qsl("am")); - addOne(qsl("an")); - addOne(qsl("ao")); - addOne(qsl("aq")); - addOne(qsl("ar")); - addOne(qsl("as")); - addOne(qsl("at")); - addOne(qsl("au")); - addOne(qsl("aw")); - addOne(qsl("ax")); - addOne(qsl("az")); - addOne(qsl("ba")); - addOne(qsl("bb")); - addOne(qsl("bd")); - addOne(qsl("be")); - addOne(qsl("bf")); - addOne(qsl("bg")); - addOne(qsl("bh")); - addOne(qsl("bi")); - addOne(qsl("bj")); - addOne(qsl("bm")); - addOne(qsl("bn")); - addOne(qsl("bo")); - addOne(qsl("br")); - addOne(qsl("bs")); - addOne(qsl("bt")); - addOne(qsl("bv")); - addOne(qsl("bw")); - addOne(qsl("by")); - addOne(qsl("bz")); - addOne(qsl("ca")); - addOne(qsl("cc")); - addOne(qsl("cd")); - addOne(qsl("cf")); - addOne(qsl("cg")); - addOne(qsl("ch")); - addOne(qsl("ci")); - addOne(qsl("ck")); - addOne(qsl("cl")); - addOne(qsl("cm")); - addOne(qsl("cn")); - addOne(qsl("co")); - addOne(qsl("cr")); - addOne(qsl("cu")); - addOne(qsl("cv")); - addOne(qsl("cx")); - addOne(qsl("cy")); - addOne(qsl("cz")); - addOne(qsl("de")); - addOne(qsl("dj")); - addOne(qsl("dk")); - addOne(qsl("dm")); - addOne(qsl("do")); - addOne(qsl("dz")); - addOne(qsl("ec")); - addOne(qsl("ee")); - addOne(qsl("eg")); - addOne(qsl("eh")); - addOne(qsl("er")); - addOne(qsl("es")); - addOne(qsl("et")); - addOne(qsl("eu")); - addOne(qsl("fi")); - addOne(qsl("fj")); - addOne(qsl("fk")); - addOne(qsl("fm")); - addOne(qsl("fo")); - addOne(qsl("fr")); - addOne(qsl("ga")); - addOne(qsl("gd")); - addOne(qsl("ge")); - addOne(qsl("gf")); - addOne(qsl("gg")); - addOne(qsl("gh")); - addOne(qsl("gi")); - addOne(qsl("gl")); - addOne(qsl("gm")); - addOne(qsl("gn")); - addOne(qsl("gp")); - addOne(qsl("gq")); - addOne(qsl("gr")); - addOne(qsl("gs")); - addOne(qsl("gt")); - addOne(qsl("gu")); - addOne(qsl("gw")); - addOne(qsl("gy")); - addOne(qsl("hk")); - addOne(qsl("hm")); - addOne(qsl("hn")); - addOne(qsl("hr")); - addOne(qsl("ht")); - addOne(qsl("hu")); - addOne(qsl("id")); - addOne(qsl("ie")); - addOne(qsl("il")); - addOne(qsl("im")); - addOne(qsl("in")); - addOne(qsl("io")); - addOne(qsl("iq")); - addOne(qsl("ir")); - addOne(qsl("is")); - addOne(qsl("it")); - addOne(qsl("je")); - addOne(qsl("jm")); - addOne(qsl("jo")); - addOne(qsl("jp")); - addOne(qsl("ke")); - addOne(qsl("kg")); - addOne(qsl("kh")); - addOne(qsl("ki")); - addOne(qsl("km")); - addOne(qsl("kn")); - addOne(qsl("kp")); - addOne(qsl("kr")); - addOne(qsl("kw")); - addOne(qsl("ky")); - addOne(qsl("kz")); - addOne(qsl("la")); - addOne(qsl("lb")); - addOne(qsl("lc")); - addOne(qsl("li")); - addOne(qsl("lk")); - addOne(qsl("lr")); - addOne(qsl("ls")); - addOne(qsl("lt")); - addOne(qsl("lu")); - addOne(qsl("lv")); - addOne(qsl("ly")); - addOne(qsl("ma")); - addOne(qsl("mc")); - addOne(qsl("md")); - addOne(qsl("me")); - addOne(qsl("mg")); - addOne(qsl("mh")); - addOne(qsl("mk")); - addOne(qsl("ml")); - addOne(qsl("mm")); - addOne(qsl("mn")); - addOne(qsl("mo")); - addOne(qsl("mp")); - addOne(qsl("mq")); - addOne(qsl("mr")); - addOne(qsl("ms")); - addOne(qsl("mt")); - addOne(qsl("mu")); - addOne(qsl("mv")); - addOne(qsl("mw")); - addOne(qsl("mx")); - addOne(qsl("my")); - addOne(qsl("mz")); - addOne(qsl("na")); - addOne(qsl("nc")); - addOne(qsl("ne")); - addOne(qsl("nf")); - addOne(qsl("ng")); - addOne(qsl("ni")); - addOne(qsl("nl")); - addOne(qsl("no")); - addOne(qsl("np")); - addOne(qsl("nr")); - addOne(qsl("nu")); - addOne(qsl("nz")); - addOne(qsl("om")); - addOne(qsl("pa")); - addOne(qsl("pe")); - addOne(qsl("pf")); - addOne(qsl("pg")); - addOne(qsl("ph")); - addOne(qsl("pk")); - addOne(qsl("pl")); - addOne(qsl("pm")); - addOne(qsl("pn")); - addOne(qsl("pr")); - addOne(qsl("ps")); - addOne(qsl("pt")); - addOne(qsl("pw")); - addOne(qsl("py")); - addOne(qsl("qa")); - addOne(qsl("re")); - addOne(qsl("ro")); - addOne(qsl("ru")); - addOne(qsl("rs")); - addOne(qsl("rw")); - addOne(qsl("sa")); - addOne(qsl("sb")); - addOne(qsl("sc")); - addOne(qsl("sd")); - addOne(qsl("se")); - addOne(qsl("sg")); - addOne(qsl("sh")); - addOne(qsl("si")); - addOne(qsl("sj")); - addOne(qsl("sk")); - addOne(qsl("sl")); - addOne(qsl("sm")); - addOne(qsl("sn")); - addOne(qsl("so")); - addOne(qsl("sr")); - addOne(qsl("ss")); - addOne(qsl("st")); - addOne(qsl("su")); - addOne(qsl("sv")); - addOne(qsl("sx")); - addOne(qsl("sy")); - addOne(qsl("sz")); - addOne(qsl("tc")); - addOne(qsl("td")); - addOne(qsl("tf")); - addOne(qsl("tg")); - addOne(qsl("th")); - addOne(qsl("tj")); - addOne(qsl("tk")); - addOne(qsl("tl")); - addOne(qsl("tm")); - addOne(qsl("tn")); - addOne(qsl("to")); - addOne(qsl("tp")); - addOne(qsl("tr")); - addOne(qsl("tt")); - addOne(qsl("tv")); - addOne(qsl("tw")); - addOne(qsl("tz")); - addOne(qsl("ua")); - addOne(qsl("ug")); - addOne(qsl("uk")); - addOne(qsl("um")); - addOne(qsl("us")); - addOne(qsl("uy")); - addOne(qsl("uz")); - addOne(qsl("va")); - addOne(qsl("vc")); - addOne(qsl("ve")); - addOne(qsl("vg")); - addOne(qsl("vi")); - addOne(qsl("vn")); - addOne(qsl("vu")); - addOne(qsl("wf")); - addOne(qsl("ws")); - addOne(qsl("ye")); - addOne(qsl("yt")); - addOne(qsl("yu")); - addOne(qsl("za")); - addOne(qsl("zm")); - addOne(qsl("zw")); - addOne(qsl("arpa")); - addOne(qsl("aero")); - addOne(qsl("asia")); - addOne(qsl("biz")); - addOne(qsl("cat")); - addOne(qsl("com")); - addOne(qsl("coop")); - addOne(qsl("info")); - addOne(qsl("int")); - addOne(qsl("jobs")); - addOne(qsl("mobi")); - addOne(qsl("museum")); - addOne(qsl("name")); - addOne(qsl("net")); - addOne(qsl("org")); - addOne(qsl("post")); - addOne(qsl("pro")); - addOne(qsl("tel")); - addOne(qsl("travel")); - addOne(qsl("xxx")); - addOne(qsl("edu")); - addOne(qsl("gov")); - addOne(qsl("mil")); - addOne(qsl("local")); - addOne(qsl("xn--lgbbat1ad8j")); - addOne(qsl("xn--54b7fta0cc")); - addOne(qsl("xn--fiqs8s")); - addOne(qsl("xn--fiqz9s")); - addOne(qsl("xn--wgbh1c")); - addOne(qsl("xn--node")); - addOne(qsl("xn--j6w193g")); - addOne(qsl("xn--h2brj9c")); - addOne(qsl("xn--mgbbh1a71e")); - addOne(qsl("xn--fpcrj9c3d")); - addOne(qsl("xn--gecrj9c")); - addOne(qsl("xn--s9brj9c")); - addOne(qsl("xn--xkc2dl3a5ee0h")); - addOne(qsl("xn--45brj9c")); - addOne(qsl("xn--mgba3a4f16a")); - addOne(qsl("xn--mgbayh7gpa")); - addOne(qsl("xn--80ao21a")); - addOne(qsl("xn--mgbx4cd0ab")); - addOne(qsl("xn--l1acc")); - addOne(qsl("xn--mgbc0a9azcg")); - addOne(qsl("xn--mgb9awbf")); - addOne(qsl("xn--mgbai9azgqp6j")); - addOne(qsl("xn--ygbi2ammx")); - addOne(qsl("xn--wgbl6a")); - addOne(qsl("xn--p1ai")); - addOne(qsl("xn--mgberp4a5d4ar")); - addOne(qsl("xn--90a3ac")); - addOne(qsl("xn--yfro4i67o")); - addOne(qsl("xn--clchc0ea0b2g2a9gcd")); - addOne(qsl("xn--3e0b707e")); - addOne(qsl("xn--fzc2c9e2c")); - addOne(qsl("xn--xkc2al3hye2a")); - addOne(qsl("xn--mgbtf8fl")); - addOne(qsl("xn--kprw13d")); - addOne(qsl("xn--kpry57d")); - addOne(qsl("xn--o3cw4h")); - addOne(qsl("xn--pgbs0dh")); - addOne(qsl("xn--j1amh")); - addOne(qsl("xn--mgbaam7a8h")); - addOne(qsl("xn--mgb2ddes")); - addOne(qsl("xn--ogbpf8fl")); + addOne(QString::fromLatin1("ac")); + addOne(QString::fromLatin1("ad")); + addOne(QString::fromLatin1("ae")); + addOne(QString::fromLatin1("af")); + addOne(QString::fromLatin1("ag")); + addOne(QString::fromLatin1("ai")); + addOne(QString::fromLatin1("al")); + addOne(QString::fromLatin1("am")); + addOne(QString::fromLatin1("an")); + addOne(QString::fromLatin1("ao")); + addOne(QString::fromLatin1("aq")); + addOne(QString::fromLatin1("ar")); + addOne(QString::fromLatin1("as")); + addOne(QString::fromLatin1("at")); + addOne(QString::fromLatin1("au")); + addOne(QString::fromLatin1("aw")); + addOne(QString::fromLatin1("ax")); + addOne(QString::fromLatin1("az")); + addOne(QString::fromLatin1("ba")); + addOne(QString::fromLatin1("bb")); + addOne(QString::fromLatin1("bd")); + addOne(QString::fromLatin1("be")); + addOne(QString::fromLatin1("bf")); + addOne(QString::fromLatin1("bg")); + addOne(QString::fromLatin1("bh")); + addOne(QString::fromLatin1("bi")); + addOne(QString::fromLatin1("bj")); + addOne(QString::fromLatin1("bm")); + addOne(QString::fromLatin1("bn")); + addOne(QString::fromLatin1("bo")); + addOne(QString::fromLatin1("br")); + addOne(QString::fromLatin1("bs")); + addOne(QString::fromLatin1("bt")); + addOne(QString::fromLatin1("bv")); + addOne(QString::fromLatin1("bw")); + addOne(QString::fromLatin1("by")); + addOne(QString::fromLatin1("bz")); + addOne(QString::fromLatin1("ca")); + addOne(QString::fromLatin1("cc")); + addOne(QString::fromLatin1("cd")); + addOne(QString::fromLatin1("cf")); + addOne(QString::fromLatin1("cg")); + addOne(QString::fromLatin1("ch")); + addOne(QString::fromLatin1("ci")); + addOne(QString::fromLatin1("ck")); + addOne(QString::fromLatin1("cl")); + addOne(QString::fromLatin1("cm")); + addOne(QString::fromLatin1("cn")); + addOne(QString::fromLatin1("co")); + addOne(QString::fromLatin1("cr")); + addOne(QString::fromLatin1("cu")); + addOne(QString::fromLatin1("cv")); + addOne(QString::fromLatin1("cx")); + addOne(QString::fromLatin1("cy")); + addOne(QString::fromLatin1("cz")); + addOne(QString::fromLatin1("de")); + addOne(QString::fromLatin1("dj")); + addOne(QString::fromLatin1("dk")); + addOne(QString::fromLatin1("dm")); + addOne(QString::fromLatin1("do")); + addOne(QString::fromLatin1("dz")); + addOne(QString::fromLatin1("ec")); + addOne(QString::fromLatin1("ee")); + addOne(QString::fromLatin1("eg")); + addOne(QString::fromLatin1("eh")); + addOne(QString::fromLatin1("er")); + addOne(QString::fromLatin1("es")); + addOne(QString::fromLatin1("et")); + addOne(QString::fromLatin1("eu")); + addOne(QString::fromLatin1("fi")); + addOne(QString::fromLatin1("fj")); + addOne(QString::fromLatin1("fk")); + addOne(QString::fromLatin1("fm")); + addOne(QString::fromLatin1("fo")); + addOne(QString::fromLatin1("fr")); + addOne(QString::fromLatin1("ga")); + addOne(QString::fromLatin1("gd")); + addOne(QString::fromLatin1("ge")); + addOne(QString::fromLatin1("gf")); + addOne(QString::fromLatin1("gg")); + addOne(QString::fromLatin1("gh")); + addOne(QString::fromLatin1("gi")); + addOne(QString::fromLatin1("gl")); + addOne(QString::fromLatin1("gm")); + addOne(QString::fromLatin1("gn")); + addOne(QString::fromLatin1("gp")); + addOne(QString::fromLatin1("gq")); + addOne(QString::fromLatin1("gr")); + addOne(QString::fromLatin1("gs")); + addOne(QString::fromLatin1("gt")); + addOne(QString::fromLatin1("gu")); + addOne(QString::fromLatin1("gw")); + addOne(QString::fromLatin1("gy")); + addOne(QString::fromLatin1("hk")); + addOne(QString::fromLatin1("hm")); + addOne(QString::fromLatin1("hn")); + addOne(QString::fromLatin1("hr")); + addOne(QString::fromLatin1("ht")); + addOne(QString::fromLatin1("hu")); + addOne(QString::fromLatin1("id")); + addOne(QString::fromLatin1("ie")); + addOne(QString::fromLatin1("il")); + addOne(QString::fromLatin1("im")); + addOne(QString::fromLatin1("in")); + addOne(QString::fromLatin1("io")); + addOne(QString::fromLatin1("iq")); + addOne(QString::fromLatin1("ir")); + addOne(QString::fromLatin1("is")); + addOne(QString::fromLatin1("it")); + addOne(QString::fromLatin1("je")); + addOne(QString::fromLatin1("jm")); + addOne(QString::fromLatin1("jo")); + addOne(QString::fromLatin1("jp")); + addOne(QString::fromLatin1("ke")); + addOne(QString::fromLatin1("kg")); + addOne(QString::fromLatin1("kh")); + addOne(QString::fromLatin1("ki")); + addOne(QString::fromLatin1("km")); + addOne(QString::fromLatin1("kn")); + addOne(QString::fromLatin1("kp")); + addOne(QString::fromLatin1("kr")); + addOne(QString::fromLatin1("kw")); + addOne(QString::fromLatin1("ky")); + addOne(QString::fromLatin1("kz")); + addOne(QString::fromLatin1("la")); + addOne(QString::fromLatin1("lb")); + addOne(QString::fromLatin1("lc")); + addOne(QString::fromLatin1("li")); + addOne(QString::fromLatin1("lk")); + addOne(QString::fromLatin1("lr")); + addOne(QString::fromLatin1("ls")); + addOne(QString::fromLatin1("lt")); + addOne(QString::fromLatin1("lu")); + addOne(QString::fromLatin1("lv")); + addOne(QString::fromLatin1("ly")); + addOne(QString::fromLatin1("ma")); + addOne(QString::fromLatin1("mc")); + addOne(QString::fromLatin1("md")); + addOne(QString::fromLatin1("me")); + addOne(QString::fromLatin1("mg")); + addOne(QString::fromLatin1("mh")); + addOne(QString::fromLatin1("mk")); + addOne(QString::fromLatin1("ml")); + addOne(QString::fromLatin1("mm")); + addOne(QString::fromLatin1("mn")); + addOne(QString::fromLatin1("mo")); + addOne(QString::fromLatin1("mp")); + addOne(QString::fromLatin1("mq")); + addOne(QString::fromLatin1("mr")); + addOne(QString::fromLatin1("ms")); + addOne(QString::fromLatin1("mt")); + addOne(QString::fromLatin1("mu")); + addOne(QString::fromLatin1("mv")); + addOne(QString::fromLatin1("mw")); + addOne(QString::fromLatin1("mx")); + addOne(QString::fromLatin1("my")); + addOne(QString::fromLatin1("mz")); + addOne(QString::fromLatin1("na")); + addOne(QString::fromLatin1("nc")); + addOne(QString::fromLatin1("ne")); + addOne(QString::fromLatin1("nf")); + addOne(QString::fromLatin1("ng")); + addOne(QString::fromLatin1("ni")); + addOne(QString::fromLatin1("nl")); + addOne(QString::fromLatin1("no")); + addOne(QString::fromLatin1("np")); + addOne(QString::fromLatin1("nr")); + addOne(QString::fromLatin1("nu")); + addOne(QString::fromLatin1("nz")); + addOne(QString::fromLatin1("om")); + addOne(QString::fromLatin1("pa")); + addOne(QString::fromLatin1("pe")); + addOne(QString::fromLatin1("pf")); + addOne(QString::fromLatin1("pg")); + addOne(QString::fromLatin1("ph")); + addOne(QString::fromLatin1("pk")); + addOne(QString::fromLatin1("pl")); + addOne(QString::fromLatin1("pm")); + addOne(QString::fromLatin1("pn")); + addOne(QString::fromLatin1("pr")); + addOne(QString::fromLatin1("ps")); + addOne(QString::fromLatin1("pt")); + addOne(QString::fromLatin1("pw")); + addOne(QString::fromLatin1("py")); + addOne(QString::fromLatin1("qa")); + addOne(QString::fromLatin1("re")); + addOne(QString::fromLatin1("ro")); + addOne(QString::fromLatin1("ru")); + addOne(QString::fromLatin1("rs")); + addOne(QString::fromLatin1("rw")); + addOne(QString::fromLatin1("sa")); + addOne(QString::fromLatin1("sb")); + addOne(QString::fromLatin1("sc")); + addOne(QString::fromLatin1("sd")); + addOne(QString::fromLatin1("se")); + addOne(QString::fromLatin1("sg")); + addOne(QString::fromLatin1("sh")); + addOne(QString::fromLatin1("si")); + addOne(QString::fromLatin1("sj")); + addOne(QString::fromLatin1("sk")); + addOne(QString::fromLatin1("sl")); + addOne(QString::fromLatin1("sm")); + addOne(QString::fromLatin1("sn")); + addOne(QString::fromLatin1("so")); + addOne(QString::fromLatin1("sr")); + addOne(QString::fromLatin1("ss")); + addOne(QString::fromLatin1("st")); + addOne(QString::fromLatin1("su")); + addOne(QString::fromLatin1("sv")); + addOne(QString::fromLatin1("sx")); + addOne(QString::fromLatin1("sy")); + addOne(QString::fromLatin1("sz")); + addOne(QString::fromLatin1("tc")); + addOne(QString::fromLatin1("td")); + addOne(QString::fromLatin1("tf")); + addOne(QString::fromLatin1("tg")); + addOne(QString::fromLatin1("th")); + addOne(QString::fromLatin1("tj")); + addOne(QString::fromLatin1("tk")); + addOne(QString::fromLatin1("tl")); + addOne(QString::fromLatin1("tm")); + addOne(QString::fromLatin1("tn")); + addOne(QString::fromLatin1("to")); + addOne(QString::fromLatin1("tp")); + addOne(QString::fromLatin1("tr")); + addOne(QString::fromLatin1("tt")); + addOne(QString::fromLatin1("tv")); + addOne(QString::fromLatin1("tw")); + addOne(QString::fromLatin1("tz")); + addOne(QString::fromLatin1("ua")); + addOne(QString::fromLatin1("ug")); + addOne(QString::fromLatin1("uk")); + addOne(QString::fromLatin1("um")); + addOne(QString::fromLatin1("us")); + addOne(QString::fromLatin1("uy")); + addOne(QString::fromLatin1("uz")); + addOne(QString::fromLatin1("va")); + addOne(QString::fromLatin1("vc")); + addOne(QString::fromLatin1("ve")); + addOne(QString::fromLatin1("vg")); + addOne(QString::fromLatin1("vi")); + addOne(QString::fromLatin1("vn")); + addOne(QString::fromLatin1("vu")); + addOne(QString::fromLatin1("wf")); + addOne(QString::fromLatin1("ws")); + addOne(QString::fromLatin1("ye")); + addOne(QString::fromLatin1("yt")); + addOne(QString::fromLatin1("yu")); + addOne(QString::fromLatin1("za")); + addOne(QString::fromLatin1("zm")); + addOne(QString::fromLatin1("zw")); + addOne(QString::fromLatin1("arpa")); + addOne(QString::fromLatin1("aero")); + addOne(QString::fromLatin1("asia")); + addOne(QString::fromLatin1("biz")); + addOne(QString::fromLatin1("cat")); + addOne(QString::fromLatin1("com")); + addOne(QString::fromLatin1("coop")); + addOne(QString::fromLatin1("info")); + addOne(QString::fromLatin1("int")); + addOne(QString::fromLatin1("jobs")); + addOne(QString::fromLatin1("mobi")); + addOne(QString::fromLatin1("museum")); + addOne(QString::fromLatin1("name")); + addOne(QString::fromLatin1("net")); + addOne(QString::fromLatin1("org")); + addOne(QString::fromLatin1("post")); + addOne(QString::fromLatin1("pro")); + addOne(QString::fromLatin1("tel")); + addOne(QString::fromLatin1("travel")); + addOne(QString::fromLatin1("xxx")); + addOne(QString::fromLatin1("edu")); + addOne(QString::fromLatin1("gov")); + addOne(QString::fromLatin1("mil")); + addOne(QString::fromLatin1("local")); + addOne(QString::fromLatin1("xn--lgbbat1ad8j")); + addOne(QString::fromLatin1("xn--54b7fta0cc")); + addOne(QString::fromLatin1("xn--fiqs8s")); + addOne(QString::fromLatin1("xn--fiqz9s")); + addOne(QString::fromLatin1("xn--wgbh1c")); + addOne(QString::fromLatin1("xn--node")); + addOne(QString::fromLatin1("xn--j6w193g")); + addOne(QString::fromLatin1("xn--h2brj9c")); + addOne(QString::fromLatin1("xn--mgbbh1a71e")); + addOne(QString::fromLatin1("xn--fpcrj9c3d")); + addOne(QString::fromLatin1("xn--gecrj9c")); + addOne(QString::fromLatin1("xn--s9brj9c")); + addOne(QString::fromLatin1("xn--xkc2dl3a5ee0h")); + addOne(QString::fromLatin1("xn--45brj9c")); + addOne(QString::fromLatin1("xn--mgba3a4f16a")); + addOne(QString::fromLatin1("xn--mgbayh7gpa")); + addOne(QString::fromLatin1("xn--80ao21a")); + addOne(QString::fromLatin1("xn--mgbx4cd0ab")); + addOne(QString::fromLatin1("xn--l1acc")); + addOne(QString::fromLatin1("xn--mgbc0a9azcg")); + addOne(QString::fromLatin1("xn--mgb9awbf")); + addOne(QString::fromLatin1("xn--mgbai9azgqp6j")); + addOne(QString::fromLatin1("xn--ygbi2ammx")); + addOne(QString::fromLatin1("xn--wgbl6a")); + addOne(QString::fromLatin1("xn--p1ai")); + addOne(QString::fromLatin1("xn--mgberp4a5d4ar")); + addOne(QString::fromLatin1("xn--90a3ac")); + addOne(QString::fromLatin1("xn--yfro4i67o")); + addOne(QString::fromLatin1("xn--clchc0ea0b2g2a9gcd")); + addOne(QString::fromLatin1("xn--3e0b707e")); + addOne(QString::fromLatin1("xn--fzc2c9e2c")); + addOne(QString::fromLatin1("xn--xkc2al3hye2a")); + addOne(QString::fromLatin1("xn--mgbtf8fl")); + addOne(QString::fromLatin1("xn--kprw13d")); + addOne(QString::fromLatin1("xn--kpry57d")); + addOne(QString::fromLatin1("xn--o3cw4h")); + addOne(QString::fromLatin1("xn--pgbs0dh")); + addOne(QString::fromLatin1("xn--j1amh")); + addOne(QString::fromLatin1("xn--mgbaam7a8h")); + addOne(QString::fromLatin1("xn--mgb2ddes")); + addOne(QString::fromLatin1("xn--ogbpf8fl")); addOne(QString::fromUtf8("\xd1\x80\xd1\x84")); return result; } @@ -1132,7 +1136,58 @@ inline QChar RemoveOneAccent(uint32 code) { } const QRegularExpression &RegExpWordSplit() { - static const auto result = QRegularExpression (qsl("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]")); + static const auto result = QRegularExpression(QString::fromLatin1("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]")); + return result; +} + +[[nodiscard]] QString ExpandCustomLinks(const TextWithTags &text) { + const auto entities = ConvertTextTagsToEntities(text.tags); + auto &&urls = ranges::make_iterator_range( + entities.begin(), + entities.end() + ) | ranges::view::filter([](const EntityInText &entity) { + return entity.type() == EntityType::CustomUrl; + }); + const auto &original = text.text; + if (urls.begin() == urls.end()) { + return original; + } + auto result = QString(); + auto offset = 0; + for (const auto &entity : urls) { + const auto till = entity.offset() + entity.length(); + if (till > offset) { + result.append(original.midRef(offset, till - offset)); + } + result.append(qstr(" (")).append(entity.data()).append(')'); + offset = till; + } + if (original.size() > offset) { + result.append(original.midRef(offset)); + } + return result; +} + +std::unique_ptr MimeDataFromText( + TextWithTags &&text, + const QString &expanded) { + if (expanded.isEmpty()) { + return nullptr; + } + + auto result = std::make_unique(); + result->setText(expanded); + if (!text.tags.isEmpty()) { + for (auto &tag : text.tags) { + tag.id = Ui::Integration::Instance().convertTagToMimeTag(tag.id); + } + result->setData( + TextUtilities::TagsTextMimeType(), + text.text.toUtf8()); + result->setData( + TextUtilities::TagsMimeType(), + TextUtilities::SerializeTags(text.tags)); + } return result; } @@ -1168,7 +1223,7 @@ QString MarkdownBoldGoodBefore() { } QString MarkdownBoldBadAfter() { - return qsl("*"); + return QString::fromLatin1("*"); } QString MarkdownItalicGoodBefore() { @@ -1176,7 +1231,7 @@ QString MarkdownItalicGoodBefore() { } QString MarkdownItalicBadAfter() { - return qsl("_"); + return QString::fromLatin1("_"); } QString MarkdownStrikeOutGoodBefore() { @@ -1184,7 +1239,7 @@ QString MarkdownStrikeOutGoodBefore() { } QString MarkdownStrikeOutBadAfter() { - return qsl("~"); + return QString::fromLatin1("~"); } QString MarkdownCodeGoodBefore() { @@ -1192,7 +1247,7 @@ QString MarkdownCodeGoodBefore() { } QString MarkdownCodeBadAfter() { - return qsl("`\n\r"); + return QString::fromLatin1("`\n\r"); } QString MarkdownPreGoodBefore() { @@ -1200,17 +1255,17 @@ QString MarkdownPreGoodBefore() { } QString MarkdownPreBadAfter() { - return qsl("`"); + return QString::fromLatin1("`"); } bool IsValidProtocol(const QString &protocol) { static const auto list = CreateValidProtocols(); - return list.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); + return list.contains(base::crc32(protocol.constData(), protocol.size() * sizeof(QChar))); } bool IsValidTopDomain(const QString &protocol) { static const auto list = CreateValidTopDomains(); - return list.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); + return list.contains(base::crc32(protocol.constData(), protocol.size() * sizeof(QChar))); } QString Clean(const QString &text) { @@ -1321,14 +1376,19 @@ QString RemoveEmoji(const QString &text) { return result; } -QStringList PrepareSearchWords(const QString &query, const QRegularExpression *SplitterOverride) { +QStringList PrepareSearchWords( + const QString &query, + const QRegularExpression *SplitterOverride) { auto clean = RemoveAccents(query.trimmed().toLower()); auto result = QStringList(); if (!clean.isEmpty()) { - auto list = clean.split(SplitterOverride ? *SplitterOverride : RegExpWordSplit(), QString::SkipEmptyParts); + auto list = clean.split(SplitterOverride + ? *SplitterOverride + : RegExpWordSplit(), + QString::SkipEmptyParts); auto size = list.size(); result.reserve(list.size()); - for_const (auto &word, list) { + for (const auto &word : std::as_const(list)) { auto trimmed = word.trimmed(); if (!trimmed.isEmpty()) { result.push_back(trimmed); @@ -1488,112 +1548,6 @@ bool checkTagStartInCommand(const QChar *start, int32 len, int32 tagStart, int32 return inCommand; } -EntitiesInText EntitiesFromMTP(const QVector &entities) { - auto result = EntitiesInText(); - if (!entities.isEmpty()) { - result.reserve(entities.size()); - for_const (auto &entity, entities) { - switch (entity.type()) { - case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break; - case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPhone: break; // Skipping phones. - case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityMentionName: { - auto &d = entity.c_messageEntityMentionName(); - auto data = [&d] { - if (auto user = Auth().data().userLoaded(d.vuser_id().v)) { - return MentionNameDataFromFields({ - d.vuser_id().v, - user->accessHash() }); - } - return MentionNameDataFromFields(d.vuser_id().v); - }; - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data() }); - } break; - case mtpc_inputMessageEntityMentionName: { - auto &d = entity.c_inputMessageEntityMentionName(); - auto data = ([&d]() -> QString { - if (d.vuser_id().type() == mtpc_inputUserSelf) { - return MentionNameDataFromFields(Auth().userId()); - } else if (d.vuser_id().type() == mtpc_inputUser) { - auto &user = d.vuser_id().c_inputUser(); - return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v }); - } - return QString(); - })(); - if (!data.isEmpty()) { - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); - } - } break; - case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break; - // #TODO entities - } - } - } - return result; -} - -MTPVector EntitiesToMTP(const EntitiesInText &entities, ConvertOption option) { - auto v = QVector(); - v.reserve(entities.size()); - for_const (auto &entity, entities) { - if (entity.length() <= 0) continue; - if (option == ConvertOption::SkipLocal - && entity.type() != EntityType::Bold - && entity.type() != EntityType::Italic - && entity.type() != EntityType::Underline - && entity.type() != EntityType::StrikeOut - && entity.type() != EntityType::Code // #TODO entities - && entity.type() != EntityType::Pre - && entity.type() != EntityType::MentionName - && entity.type() != EntityType::CustomUrl) { - continue; - } - - auto offset = MTP_int(entity.offset()); - auto length = MTP_int(entity.length()); - switch (entity.type()) { - case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break; - case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break; - case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break; - case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break; - case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break; - case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break; - case EntityType::MentionName: { - auto inputUser = ([](const QString &data) -> MTPInputUser { - auto fields = MentionNameDataToFields(data); - if (fields.userId == Auth().userId()) { - return MTP_inputUserSelf(); - } else if (fields.userId) { - return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash)); - } - return MTP_inputUserEmpty(); - })(entity.data()); - if (inputUser.type() != mtpc_inputUserEmpty) { - v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser)); - } - } break; - case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break; - case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break; - case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break; - case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break; - case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break; - case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities - case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break; - } - } - return MTP_vector(std::move(v)); -} - TextWithEntities ParseEntities(const QString &text, int32 flags) { const auto rich = ((flags & TextParseRichText) != 0); auto result = TextWithEntities{ text, EntitiesInText() }; @@ -1897,7 +1851,7 @@ void ApplyServerCleaning(TextWithEntities &result) { // Replace tabs with two spaces. if (auto tabs = std::count(result.text.cbegin(), result.text.cend(), '\t')) { - auto replacement = qsl(" "); + auto replacement = QString::fromLatin1(" "); auto replacementLength = replacement.size(); auto shift = (replacementLength - 1); result.text.resize(len + shift * tabs); @@ -2022,11 +1976,111 @@ TextWithTags::Tags DeserializeTags(QByteArray data, int textLength) { } QString TagsMimeType() { - return qsl("application/x-td-field-tags"); + return QString::fromLatin1("application/x-td-field-tags"); } QString TagsTextMimeType() { - return qsl("application/x-td-field-text"); + return QString::fromLatin1("application/x-td-field-text"); +} + +bool IsMentionLink(const QString &link) { + return link.startsWith(kMentionTagStart); +} + +EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) { + EntitiesInText result; + if (tags.isEmpty()) { + return result; + } + + result.reserve(tags.size()); + for (const auto &tag : tags) { + const auto push = [&]( + EntityType type, + const QString &data = QString()) { + result.push_back( + EntityInText(type, tag.offset, tag.length, data)); + }; + if (IsMentionLink(tag.id)) { + if (auto match = qthelp::regex_match("^(\\d+\\.\\d+)(/|$)", tag.id.midRef(kMentionTagStart.size()))) { + push(EntityType::MentionName, match->captured(1)); + } + } else if (tag.id == Ui::InputField::kTagBold) { + push(EntityType::Bold); + } else if (tag.id == Ui::InputField::kTagItalic) { + push(EntityType::Italic); + } else if (tag.id == Ui::InputField::kTagUnderline) { + push(EntityType::Underline); + } else if (tag.id == Ui::InputField::kTagStrikeOut) { + push(EntityType::StrikeOut); + } else if (tag.id == Ui::InputField::kTagCode) { + push(EntityType::Code); + } else if (tag.id == Ui::InputField::kTagPre) { // #TODO entities + push(EntityType::Pre); + } else /*if (ValidateUrl(tag.id)) */{ // We validate when we insert. + push(EntityType::CustomUrl, tag.id); + } + } + return result; +} + +TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) { + TextWithTags::Tags result; + if (entities.isEmpty()) { + return result; + } + + result.reserve(entities.size()); + for (const auto &entity : entities) { + const auto push = [&](const QString &tag) { + result.push_back({ entity.offset(), entity.length(), tag }); + }; + switch (entity.type()) { + case EntityType::MentionName: { + auto match = QRegularExpression(R"(^(\d+\.\d+)$)").match(entity.data()); + if (match.hasMatch()) { + push(kMentionTagStart + entity.data()); + } + } break; + case EntityType::CustomUrl: { + const auto url = entity.data(); + if (Ui::InputField::IsValidMarkdownLink(url) + && !IsMentionLink(url)) { + push(url); + } + } break; + case EntityType::Bold: push(Ui::InputField::kTagBold); break; + case EntityType::Italic: push(Ui::InputField::kTagItalic); break; + case EntityType::Underline: + push(Ui::InputField::kTagUnderline); + break; + case EntityType::StrikeOut: + push(Ui::InputField::kTagStrikeOut); + break; + case EntityType::Code: push(Ui::InputField::kTagCode); break; // #TODO entities + case EntityType::Pre: push(Ui::InputField::kTagPre); break; + } + } + return result; +} + +std::unique_ptr MimeDataFromText(const TextForMimeData &text) { + return MimeDataFromText( + { text.rich.text, ConvertEntitiesToTextTags(text.rich.entities) }, + text.expanded); +} + +std::unique_ptr MimeDataFromText(TextWithTags &&text) { + const auto expanded = ExpandCustomLinks(text); + return MimeDataFromText(std::move(text), expanded); +} + +void SetClipboardText( + const TextForMimeData &text, + QClipboard::Mode mode) { + if (auto data = MimeDataFromText(text)) { + QGuiApplication::clipboard()->setMimeData(data.release(), mode); + } } } // namespace TextUtilities @@ -2083,8 +2137,8 @@ TextWithEntities ReplaceTag::Call(TextWithEntities &&original, return; } auto newEnd = newOffset + replacementEntity->length(); - newOffset = snap(newOffset, replacementPosition, replacementEnd); - newEnd = snap(newEnd, replacementPosition, replacementEnd); + newOffset = std::clamp(newOffset, replacementPosition, replacementEnd); + newEnd = std::clamp(newEnd, replacementPosition, replacementEnd); if (auto newLength = newEnd - newOffset) { result.entities.push_back({ replacementEntity->type(), newOffset, newLength, replacementEntity->data() }); } @@ -2092,7 +2146,7 @@ TextWithEntities ReplaceTag::Call(TextWithEntities &&original, } }; - for_const (auto &entity, original.entities) { + for (const auto &entity : std::as_const(original.entities)) { // Transform the entity by the replacement. auto offset = entity.offset(); auto end = offset + entity.length(); @@ -2102,8 +2156,8 @@ TextWithEntities ReplaceTag::Call(TextWithEntities &&original, if (end > replacementPosition) { end = end + replacement.text.size() - kTagReplacementSize; } - offset = snap(offset, 0, result.text.size()); - end = snap(end, 0, result.text.size()); + offset = std::clamp(offset, 0, result.text.size()); + end = std::clamp(end, 0, result.text.size()); // Add all replacement entities that start before the current original entity. addReplacementEntitiesUntil(offset); diff --git a/Telegram/SourceFiles/ui/text/text_entity.h b/Telegram/SourceFiles/ui/text/text_entity.h index 921111edf..34ad388e2 100644 --- a/Telegram/SourceFiles/ui/text/text_entity.h +++ b/Telegram/SourceFiles/ui/text/text_entity.h @@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/basic_types.h" #include +#include +#include enum class EntityType { Invalid = 0, @@ -311,13 +313,6 @@ inline QString MentionNameDataFromFields(const MentionNameFields &fields) { return result; } -EntitiesInText EntitiesFromMTP(const QVector &entities); -enum class ConvertOption { - WithLocal, - SkipLocal, -}; -MTPVector EntitiesToMTP(const EntitiesInText &entities, ConvertOption option = ConvertOption::WithLocal); - // New entities are added to the ones that are already in result. // Changes text if (flags & TextParseMarkdown). TextWithEntities ParseEntities(const QString &text, int32 flags); @@ -345,6 +340,18 @@ TextWithTags::Tags DeserializeTags(QByteArray data, int textLength); QString TagsMimeType(); QString TagsTextMimeType(); +inline const auto kMentionTagStart = qstr("mention://user."); + +[[nodiscard]] bool IsMentionLink(const QString &link); +EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags); +TextWithTags::Tags ConvertEntitiesToTextTags( + const EntitiesInText &entities); +std::unique_ptr MimeDataFromText(const TextForMimeData &text); +std::unique_ptr MimeDataFromText(TextWithTags &&text); +void SetClipboardText( + const TextForMimeData &text, + QClipboard::Mode mode = QClipboard::Clipboard); + } // namespace TextUtilities namespace Lang { diff --git a/Telegram/SourceFiles/ui/text/text_utilities.cpp b/Telegram/SourceFiles/ui/text/text_utilities.cpp index da700a297..99638d377 100644 --- a/Telegram/SourceFiles/ui/text/text_utilities.cpp +++ b/Telegram/SourceFiles/ui/text/text_utilities.cpp @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/text/text_utilities.h" +#include "base/algorithm.h" + +#include + namespace Ui { namespace Text { namespace { diff --git a/Telegram/SourceFiles/ui/text/text_utilities.h b/Telegram/SourceFiles/ui/text/text_utilities.h index 8df726b21..516d36ae8 100644 --- a/Telegram/SourceFiles/ui/text/text_utilities.h +++ b/Telegram/SourceFiles/ui/text/text_utilities.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/text/text_entity.h" + namespace Ui { namespace Text { namespace details { diff --git a/Telegram/SourceFiles/ui/text_options.h b/Telegram/SourceFiles/ui/text_options.h index fe84377b6..d40d292a1 100644 --- a/Telegram/SourceFiles/ui/text_options.h +++ b/Telegram/SourceFiles/ui/text_options.h @@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; class PeerData; +struct TextParseOptions; + namespace Ui { void InitTextOptions(); diff --git a/Telegram/SourceFiles/ui/ui_integration.cpp b/Telegram/SourceFiles/ui/ui_integration.cpp new file mode 100644 index 000000000..c3609e23e --- /dev/null +++ b/Telegram/SourceFiles/ui/ui_integration.cpp @@ -0,0 +1,120 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/ui_integration.h" + +#include "ui/text/text_entity.h" +#include "ui/basic_click_handlers.h" + +namespace Ui { +namespace { + +Integration *IntegrationInstance = nullptr; + +} // namespace + +void Integration::Set(not_null instance) { + IntegrationInstance = instance; +} + +Integration &Integration::Instance() { + Expects(IntegrationInstance != nullptr); + + return *IntegrationInstance; +} + +void Integration::textActionsUpdated() { +} + +void Integration::activationFromTopPanel() { +} + +std::shared_ptr Integration::createLinkHandler( + EntityType type, + const QString &text, + const QString &data, + const TextParseOptions &options) { + switch (type) { + case EntityType::CustomUrl: + return !data.isEmpty() + ? std::make_shared(data, false) + : nullptr; + } + return nullptr; +} + +bool Integration::handleUrlClick( + const QString &url, + const QVariant &context) { + return false; +} + +QString Integration::convertTagToMimeTag(const QString &tagId) { + return tagId; +} + +const Emoji::One *Integration::defaultEmojiVariant(const Emoji::One *emoji) { + return emoji; +} + +rpl::producer<> Integration::forcePopupMenuHideRequests() { + return rpl::never(); +} + +QString Integration::phraseContextCopyText() { + return "Copy text"; +} + +QString Integration::phraseContextCopyEmail() { + return "Copy email"; +} + +QString Integration::phraseContextCopyLink() { + return "Copy link"; +} + +QString Integration::phraseContextCopySelected() { + return "Copy to clipboard"; +} + +QString Integration::phraseFormattingTitle() { + return "Formatting"; +} + +QString Integration::phraseFormattingLinkCreate() { + return "Create link"; +} + +QString Integration::phraseFormattingLinkEdit() { + return "Edit link"; +} + +QString Integration::phraseFormattingClear() { + return "Plain text"; +} + +QString Integration::phraseFormattingBold() { + return "Bold"; +} + +QString Integration::phraseFormattingItalic() { + return "Italic"; +} + +QString Integration::phraseFormattingUnderline() { + return "Underline"; +} + +QString Integration::phraseFormattingStrikeOut() { + return "Strike-through"; +} + +QString Integration::phraseFormattingMonospace() { + return "Monospace"; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/ui_integration.h b/Telegram/SourceFiles/ui/ui_integration.h index 8a3c33b74..01c1f8814 100644 --- a/Telegram/SourceFiles/ui/ui_integration.h +++ b/Telegram/SourceFiles/ui/ui_integration.h @@ -11,10 +11,62 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL // Methods that must be implemented outside lib_ui. -namespace Ui { +class QString; +class QWidget; +class QVariant; -void PostponeCall(FnMut &&callable); -void RegisterLeaveSubscription(not_null widget); -void UnregisterLeaveSubscription(not_null widget); +struct TextParseOptions; +class ClickHandler; +enum class EntityType; + +namespace Ui { +namespace Emoji { +class One; +} // namespace Emoji + +class Integration { +public: + static void Set(not_null instance); + static Integration &Instance(); + + virtual void postponeCall(FnMut &&callable) = 0; + virtual void registerLeaveSubscription(not_null widget) = 0; + virtual void unregisterLeaveSubscription(not_null widget) = 0; + + virtual void writeLogEntry(const QString &entry) = 0; + [[nodiscard]] virtual QString emojiCacheFolder() = 0; + + virtual void textActionsUpdated(); + virtual void activationFromTopPanel(); + + [[nodiscard]] virtual std::shared_ptr createLinkHandler( + EntityType type, + const QString &text, + const QString &data, + const TextParseOptions &options); + [[nodiscard]] virtual bool handleUrlClick( + const QString &url, + const QVariant &context); + [[nodiscard]] virtual QString convertTagToMimeTag(const QString &tagId); + [[nodiscard]] virtual const Emoji::One *defaultEmojiVariant( + const Emoji::One *emoji); + + [[nodiscard]] virtual rpl::producer<> forcePopupMenuHideRequests(); + + [[nodiscard]] virtual QString phraseContextCopyText(); + [[nodiscard]] virtual QString phraseContextCopyEmail(); + [[nodiscard]] virtual QString phraseContextCopyLink(); + [[nodiscard]] virtual QString phraseContextCopySelected(); + [[nodiscard]] virtual QString phraseFormattingTitle(); + [[nodiscard]] virtual QString phraseFormattingLinkCreate(); + [[nodiscard]] virtual QString phraseFormattingLinkEdit(); + [[nodiscard]] virtual QString phraseFormattingClear(); + [[nodiscard]] virtual QString phraseFormattingBold(); + [[nodiscard]] virtual QString phraseFormattingItalic(); + [[nodiscard]] virtual QString phraseFormattingUnderline(); + [[nodiscard]] virtual QString phraseFormattingStrikeOut(); + [[nodiscard]] virtual QString phraseFormattingMonospace(); + +}; } // namespace Ui diff --git a/Telegram/SourceFiles/ui/ui_log.cpp b/Telegram/SourceFiles/ui/ui_log.cpp new file mode 100644 index 000000000..578ce51a7 --- /dev/null +++ b/Telegram/SourceFiles/ui/ui_log.cpp @@ -0,0 +1,18 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/ui_log.h" + +#include "ui/ui_integration.h" + +namespace Ui { + +void WriteLogEntry(const QString &message) { + Integration::Instance().writeLogEntry(message); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/ui_log.h b/Telegram/SourceFiles/ui/ui_log.h new file mode 100644 index 000000000..f67e77e97 --- /dev/null +++ b/Telegram/SourceFiles/ui/ui_log.h @@ -0,0 +1,16 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { + +void WriteLogEntry(const QString &message); + +} // namespace Ui + +#define UI_LOG(message) (::Ui::WriteLogEntry(QString message)) diff --git a/Telegram/SourceFiles/ui/ui_pch.h b/Telegram/SourceFiles/ui/ui_pch.h index 5210fd835..4daffd028 100644 --- a/Telegram/SourceFiles/ui/ui_pch.h +++ b/Telegram/SourceFiles/ui/ui_pch.h @@ -17,8 +17,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include +#include +#include #include #include #include +#include +#include + +#include "base/algorithm.h" +#include "base/basic_types.h" +#include "base/flat_map.h" +#include "base/flat_set.h" diff --git a/Telegram/SourceFiles/ui/ui_utility.cpp b/Telegram/SourceFiles/ui/ui_utility.cpp index 5947332da..878d59197 100644 --- a/Telegram/SourceFiles/ui/ui_utility.cpp +++ b/Telegram/SourceFiles/ui/ui_utility.cpp @@ -7,8 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/ui_utility.h" +#include "ui/style/style_core.h" + #include #include +#include + +#include namespace Ui { namespace { @@ -97,8 +102,8 @@ QPixmap GrabWidget(not_null target, QRect rect, QColor bg) { rect = target->rect(); } - auto result = QPixmap(rect.size() * cIntRetinaFactor()); - result.setDevicePixelRatio(cRetinaFactor()); + auto result = QPixmap(rect.size() * style::DevicePixelRatio()); + result.setDevicePixelRatio(style::DevicePixelRatio()); if (!target->testAttribute(Qt::WA_OpaquePaintEvent)) { result.fill(bg); } @@ -116,9 +121,9 @@ QImage GrabWidgetToImage(not_null target, QRect rect, QColor bg) { } auto result = QImage( - rect.size() * cIntRetinaFactor(), + rect.size() * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(style::DevicePixelRatio()); if (!target->testAttribute(Qt::WA_OpaquePaintEvent)) { result.fill(bg); } @@ -148,6 +153,10 @@ void ForceFullRepaint(not_null widget) { refresher->show(); } +void PostponeCall(FnMut &&callable) { + Integration::Instance().postponeCall(std::move(callable)); +} + void SendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button, const QPoint &globalPoint) { if (const auto windowHandle = widget->window()->windowHandle()) { const auto localPoint = windowHandle->mapFromGlobal(globalPoint); @@ -167,4 +176,8 @@ void SendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton } } +QPixmap PixmapFromImage(QImage &&image) { + return QPixmap::fromImage(std::move(image), Qt::ColorOnly); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/ui_utility.h b/Telegram/SourceFiles/ui/ui_utility.h index 18f47e23b..fbd4d7309 100644 --- a/Telegram/SourceFiles/ui/ui_utility.h +++ b/Telegram/SourceFiles/ui/ui_utility.h @@ -8,10 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/unique_qptr.h" +#include "ui/rect_part.h" #include "ui/ui_integration.h" #include +class QPixmap; +class QImage; + +enum class RectPart; +using RectParts = base::flags; + template class object_ptr; @@ -137,6 +144,8 @@ void RenderWidget( void ForceFullRepaint(not_null widget); +void PostponeCall(FnMut &&callable); + template < typename Guard, typename Callable, @@ -182,4 +191,6 @@ QPointer MakeWeak(not_null object) { return QPointer(object.get()); } +[[nodiscard]] QPixmap PixmapFromImage(QImage &&image); + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index d1b0416f1..a646e6ba9 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -9,8 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/effects/animations.h" +#include "ui/text/text.h" #include "styles/style_widgets.h" +class Painter; + namespace Ui { class AbstractCheckView { diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp index 28404ed0f..699331f46 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/widgets/dropdown_menu.h" -#include "lang/lang_keys.h" +#include namespace Ui { @@ -20,7 +20,7 @@ DropdownMenu::DropdownMenu(QWidget *parent, const style::DropdownMenu &st) : Inn // Not ready with submenus yet. //DropdownMenu::DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap) //, _st(st) { -// _menu = setOwnedWidget(object_ptr(this, menu, _st.menu)); +// _menu = setOwnedWidget(object_ptr(this, menu, _st.menu)); // init(); // // for (auto action : actions()) { @@ -140,7 +140,7 @@ bool DropdownMenu::handleKeyPress(int key) { } else if (key == Qt::Key_Escape) { hideMenu(_parent ? true : false); return true; - } else if (key == (rtl() ? Qt::Key_Right : Qt::Key_Left)) { + } else if (key == (style::RightToLeft() ? Qt::Key_Right : Qt::Key_Left)) { if (_parent) { hideMenu(true); return true; @@ -185,6 +185,18 @@ void DropdownMenu::hideEvent(QHideEvent *e) { } } +void DropdownMenu::keyPressEvent(QKeyEvent *e) { + forwardKeyPress(e->key()); +} + +void DropdownMenu::mouseMoveEvent(QMouseEvent *e) { + forwardMouseMove(e->globalPos()); +} + +void DropdownMenu::mousePressEvent(QMouseEvent *e) { + forwardMousePress(e->globalPos()); +} + void DropdownMenu::hideMenu(bool fast) { if (isHidden()) return; if (_parent && !isHiding()) { diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h index 759f95e53..8caf9ef4d 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h @@ -35,16 +35,9 @@ public: protected: void focusOutEvent(QFocusEvent *e) override; void hideEvent(QHideEvent *e) override; - - void keyPressEvent(QKeyEvent *e) override { - forwardKeyPress(e->key()); - } - void mouseMoveEvent(QMouseEvent *e) override { - forwardMouseMove(e->globalPos()); - } - void mousePressEvent(QMouseEvent *e) override { - forwardMousePress(e->globalPos()); - } + void keyPressEvent(QKeyEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; private slots: void onHidden() { @@ -63,7 +56,7 @@ private: void init(); void hideFinish(); - using TriggeredSource = Ui::Menu::TriggeredSource; + using TriggeredSource = Menu::TriggeredSource; void handleActivated(QAction *action, int actionTop, TriggeredSource source); void handleTriggered(QAction *action, int actionTop, TriggeredSource source); void forwardKeyPress(int key); @@ -89,7 +82,7 @@ private: const style::DropdownMenu &_st; Fn _hiddenCallback; - QPointer _menu; + QPointer _menu; // Not ready with submenus yet. //using Submenus = QMap; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 0a49428a7..66b7a62fa 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -7,13 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/widgets/inner_dropdown.h" -#include "mainwindow.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" #include "ui/effects/panel_animation.h" #include "ui/image/image_prepare.h" #include "ui/ui_utility.h" -#include "app.h" namespace { @@ -29,6 +27,7 @@ InnerDropdown::InnerDropdown( const style::InnerDropdown &st) : RpWidget(parent) , _st(st) +, _roundRect(ImageRoundRadius::Small, _st.bg) , _scroll(this, _st.scroll) { _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated())); @@ -111,7 +110,7 @@ void InnerDropdown::onScroll() { } void InnerDropdown::paintEvent(QPaintEvent *e) { - Painter p(this); + QPainter p(this); if (_a_show.animating()) { if (auto opacity = _a_opacity.value(_hiding ? 0. : 1.)) { @@ -133,7 +132,7 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { if (!_cache.isNull()) _cache = QPixmap(); const auto inner = rect().marginsRemoved(_st.padding); Shadow::paint(p, inner, width(), _st.shadow); - App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small); + _roundRect.paint(p, inner); } } @@ -295,11 +294,12 @@ void InnerDropdown::startShowAnimation() { auto cache = grabForPanelAnimation(); _a_opacity = base::take(opacityAnimation); + const auto pixelRatio = style::DevicePixelRatio(); _showAnimation = std::make_unique(_st.animation, _origin); auto inner = rect().marginsRemoved(_st.padding); - _showAnimation->setFinalImage(std::move(cache), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); - auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); + _showAnimation->setFinalImage(std::move(cache), QRect(inner.topLeft() * pixelRatio, inner.size() * pixelRatio)); + _showAnimation->setCornerMasks( + Images::CornersMask(ImageRoundRadius::Small)); _showAnimation->start(); } hideChildren(); @@ -308,12 +308,13 @@ void InnerDropdown::startShowAnimation() { QImage InnerDropdown::grabForPanelAnimation() { SendPendingMoveResizeEvents(this); - auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); + const auto pixelRatio = style::DevicePixelRatio(); + auto result = QImage(size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(pixelRatio); result.fill(Qt::transparent); { - Painter p(&result); - App::roundRect(p, rect().marginsRemoved(_st.padding), _st.bg, ImageRoundRadius::Small); + QPainter p(&result); + _roundRect.paint(p, rect().marginsRemoved(_st.padding)); for (const auto child : children()) { if (const auto widget = qobject_cast(child)) { RenderWidget(p, widget, widget->pos()); diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h index 9c6fffccc..5449dc1a1 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_widgets.h" #include "ui/rp_widget.h" +#include "ui/round_rect.h" #include "ui/effects/animations.h" #include "ui/effects/panel_animation.h" #include "base/object_ptr.h" @@ -19,7 +20,7 @@ namespace Ui { class ScrollArea; -class InnerDropdown : public Ui::RpWidget { +class InnerDropdown : public RpWidget { Q_OBJECT public: @@ -105,14 +106,15 @@ private: const style::InnerDropdown &_st; + RoundRect _roundRect; PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; std::unique_ptr _showAnimation; - Ui::Animations::Simple _a_show; + Animations::Simple _a_show; bool _autoHiding = true; bool _hiding = false; QPixmap _cache; - Ui::Animations::Simple _a_opacity; + Animations::Simple _a_opacity; QTimer _hideTimer; bool _ignoreShowEvents = false; @@ -120,7 +122,7 @@ private: Fn _hideStartCallback; Fn _hiddenCallback; - object_ptr _scroll; + object_ptr _scroll; int _maxHeight = 0; diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp index f0d168535..1c0b13c0b 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -8,21 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" -#include "ui/countryinput.h" +#include "ui/text/text.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" +#include "base/openssl_help.h" #include "chat_helpers/emoji_suggestions_helper.h" -#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities #include "platform/platform_info.h" -#include "window/themes/window_theme.h" -#include "lang/lang_keys.h" -#include "data/data_user.h" -#include "data/data_countries.h" // Data::ValidPhoneCode -#include "mainwindow.h" -#include "numbers.h" -#include "main/main_session.h" -#include "core/application.h" -#include "app.h" #include #include @@ -31,11 +22,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include namespace Ui { namespace { -constexpr auto kMaxUsernameLength = 32; constexpr auto kInstantReplaceRandomId = QTextFormat::UserProperty; constexpr auto kInstantReplaceWhatId = QTextFormat::UserProperty + 1; constexpr auto kInstantReplaceWithId = QTextFormat::UserProperty + 2; @@ -74,7 +65,7 @@ private: InputDocument::InputDocument(QObject *parent, const style::InputField &st) : QTextDocument(parent) , _st(st) { - Ui::Emoji::Updated( + Emoji::Updated( ) | rpl::start_with_next([=] { _emojiCache.clear(); }, _lifetime); @@ -90,11 +81,11 @@ QVariant InputDocument::loadResource(int type, const QUrl &name) { return i->second; } auto result = [&] { - if (const auto emoji = Ui::Emoji::FromUrl(name.toDisplayString())) { + if (const auto emoji = Emoji::FromUrl(name.toDisplayString())) { const auto height = std::max( - _st.font->height * cIntRetinaFactor(), - Ui::Emoji::GetSizeNormal()); - return QVariant(Ui::Emoji::SinglePixmap(emoji, height)); + _st.font->height * style::DevicePixelRatio(), + Emoji::GetSizeNormal()); + return QVariant(Emoji::SinglePixmap(emoji, height)); } return QVariant(); }(); @@ -624,8 +615,8 @@ QString AccumulateText(Iterator begin, Iterator end) { } QTextImageFormat PrepareEmojiFormat(EmojiPtr emoji, const QFont &font) { - const auto factor = cIntRetinaFactor(); - const auto size = Ui::Emoji::GetSizeNormal(); + const auto factor = style::DevicePixelRatio(); + const auto size = Emoji::GetSizeNormal(); const auto width = size + st::emojiPadding * factor * 2; const auto height = std::max(QFontMetrics(font).height() * factor, size); auto result = QTextImageFormat(); @@ -702,7 +693,7 @@ QTextCharFormat PrepareTagFormat( result.setFont(st.font->strikeout()); } else if (tag == kTagCode || tag == kTagPre) { result.setForeground(st::defaultTextPalette.monoFg); - result.setFont(AdjustFont(App::monofont(), st.font)); + result.setFont(AdjustFont(style::MonospaceFont(), st.font)); } else { result.setForeground(st.textFg); result.setFont(st.font); @@ -816,42 +807,16 @@ struct FormattingAction { }; -QString ExpandCustomLinks(const TextWithTags &text) { - const auto entities = ConvertTextTagsToEntities(text.tags); - auto &&urls = ranges::make_iterator_range( - entities.begin(), - entities.end() - ) | ranges::view::filter([](const EntityInText &entity) { - return entity.type() == EntityType::CustomUrl; - }); - const auto &original = text.text; - if (urls.begin() == urls.end()) { - return original; - } - auto result = QString(); - auto offset = 0; - for (const auto &entity : urls) { - const auto till = entity.offset() + entity.length(); - if (till > offset) { - result.append(original.midRef(offset, till - offset)); - } - result.append(qstr(" (")).append(entity.data()).append(')'); - offset = till; - } - if (original.size() > offset) { - result.append(original.midRef(offset)); - } - return result; -} - } // namespace -const QString InputField::kTagBold = qsl("**"); -const QString InputField::kTagItalic = qsl("__"); -const QString InputField::kTagUnderline = qsl("^^"); // Not for Markdown. -const QString InputField::kTagStrikeOut = qsl("~~"); -const QString InputField::kTagCode = qsl("`"); -const QString InputField::kTagPre = qsl("```"); +// kTagUnderline is not used for Markdown. + +const QString InputField::kTagBold = QStringLiteral("**"); +const QString InputField::kTagItalic = QStringLiteral("__"); +const QString InputField::kTagUnderline = QStringLiteral("^^"); +const QString InputField::kTagStrikeOut = QStringLiteral("~~"); +const QString InputField::kTagCode = QStringLiteral("`"); +const QString InputField::kTagPre = QStringLiteral("```"); class InputField::Inner final : public QTextEdit { public: @@ -979,16 +944,17 @@ FlatInput::FlatInput( refreshPlaceholder(text); }, lifetime()); - subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { - if (update.paletteChanged()) { - updatePalette(); - } - }); + style::PaletteChanged( + ) | rpl::start_with_next([=] { + updatePalette(); + }, lifetime()); updatePalette(); connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChange(const QString &))); connect(this, SIGNAL(textEdited(const QString &)), this, SLOT(onTextEdited())); - if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); + connect(this, &FlatInput::selectionChanged, [] { + Integration::Instance().textActionsUpdated(); + }); setStyle(InputStyle::instance()); QLineEdit::setTextMargins(0, 0, 0, 0); @@ -1168,7 +1134,7 @@ void FlatInput::refreshPlaceholder(const QString &text) { void FlatInput::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new Ui::PopupMenu(this, menu))->popup(e->globalPos()); + (new PopupMenu(this, menu))->popup(e->globalPos()); } } @@ -1214,7 +1180,7 @@ QRect FlatInput::placeholderRect() const { void FlatInput::correctValue(const QString &was, QString &now) { } -void FlatInput::phPrepare(Painter &p, float64 placeholderFocused) { +void FlatInput::phPrepare(QPainter &p, float64 placeholderFocused) { p.setFont(_st.font); p.setPen(anim::pen(_st.phColor, _st.phFocusColor, placeholderFocused)); } @@ -1257,12 +1223,12 @@ void FlatInput::onTextEdited() { if (wasText != _oldtext) emit changed(); updatePlaceholder(); - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void FlatInput::onTextChange(const QString &text) { _oldtext = text; - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } InputField::InputField( @@ -1306,7 +1272,7 @@ InputField::InputField( , _inner(std::make_unique(this)) , _lastTextWithTags(value) , _placeholderFull(std::move(placeholder)) { - _inner->setDocument(Ui::CreateChild(_inner.get(), _st)); + _inner->setDocument(CreateChild(_inner.get(), _st)); _inner->setAcceptRichText(false); resize(_st.width, _minHeight); @@ -1326,12 +1292,10 @@ InputField::InputField( refreshPlaceholder(text); }, lifetime()); - subscribe(Window::Theme::Background(), [=]( - const Window::Theme::BackgroundUpdate &update) { - if (update.paletteChanged()) { - updatePalette(); - } - }); + style::PaletteChanged( + ) | rpl::start_with_next([=] { + updatePalette(); + }, lifetime()); _defaultCharFormat = _inner->textCursor().charFormat(); updatePalette(); @@ -1355,9 +1319,9 @@ InputField::InputField( connect(_inner.get(), SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); connect(_inner.get(), SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); connect(_inner.get(), SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged())); - if (App::wnd()) { - connect(_inner.get(), SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); - } + connect(_inner.get(), &Inner::selectionChanged, [] { + Integration::Instance().textActionsUpdated(); + }); const auto bar = _inner->verticalScrollBar(); _scrollTop = bar->value(); @@ -1483,7 +1447,8 @@ void InputField::setTagMimeProcessor( } void InputField::setAdditionalMargin(int margin) { - _inner->setStyleSheet(qsl("QTextEdit { margin: %1px; }").arg(margin)); + _inner->setStyleSheet( + QString::fromLatin1("QTextEdit { margin: %1px; }").arg(margin)); _additionalMargin = margin; checkContentHeight(); } @@ -1613,7 +1578,7 @@ bool InputField::heightAutoupdated() { + _st.textMargins.top() + _st.textMargins.bottom() + 2 * _additionalMargin; - const auto newHeight = snap(contentHeight, _minHeight, _maxHeight); + const auto newHeight = std::clamp(contentHeight, _minHeight, _maxHeight); if (height() != newHeight) { resize(width(), newHeight); return true; @@ -1684,7 +1649,7 @@ void InputField::paintEvent(QPaintEvent *e) { auto borderShownDegree = _a_borderShown.value(1.); auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = snap(_borderAnimationStart, 0, width()); + auto borderStart = std::clamp(_borderAnimationStart, 0, width()); auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); if (borderTo > borderFrom) { @@ -1704,7 +1669,7 @@ void InputField::paintEvent(QPaintEvent *e) { QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins)); r.moveTop(r.top() + placeholderTop); - if (rtl()) r.moveLeft(width() - r.left() - r.width()); + if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree; auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree); @@ -1739,7 +1704,7 @@ void InputField::paintEvent(QPaintEvent *e) { } else { auto r = rect().marginsRemoved(_st.textMargins + _st.placeholderMargins); r.moveLeft(r.left() + placeholderLeft); - if (rtl()) r.moveLeft(width() - r.left() - r.width()); + if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); p.drawText(r, _placeholder, _st.placeholderAlign); } @@ -1924,7 +1889,7 @@ QString InputField::getTextPart( const auto emojiText = [&] { if (format.isImageFormat()) { const auto imageName = format.toImageFormat().name(); - if (const auto emoji = Ui::Emoji::FromUrl(imageName)) { + if (const auto emoji = Emoji::FromUrl(imageName)) { return emoji->text(); } } @@ -2003,7 +1968,7 @@ bool InputField::isRedoAvailable() const { void InputField::processFormatting(int insertPosition, int insertEnd) { // Tilde formatting. - const auto tildeFormatting = (_st.font->f.pixelSize() * cIntRetinaFactor() == 13) + const auto tildeFormatting = (_st.font->f.pixelSize() * style::DevicePixelRatio() == 13) && (_st.font->f.family() == qstr("Open Sans")); auto isTildeFragment = false; const auto tildeFixedFont = AdjustFont(st::semiboldFont, _st.font); @@ -2103,7 +2068,7 @@ void InputField::processFormatting(int insertPosition, int insertEnd) { } auto emojiLength = 0; - if (const auto emoji = Ui::Emoji::Find(ch, textEnd, &emojiLength)) { + if (const auto emoji = Emoji::Find(ch, textEnd, &emojiLength)) { // Replace emoji if no current action is prepared. if (action.type == ActionType::Invalid) { action.type = ActionType::InsertEmoji; @@ -2324,7 +2289,7 @@ void InputField::handleContentsChanged() { checkContentHeight(); } startPlaceholderAnimation(); - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void InputField::highlightMarkdown() { @@ -2362,12 +2327,12 @@ void InputField::highlightMarkdown() { void InputField::onUndoAvailable(bool avail) { _undoAvailable = avail; - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void InputField::onRedoAvailable(bool avail) { _redoAvailable = avail; - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void InputField::setDisplayFocused(bool focused) { @@ -2414,28 +2379,13 @@ void InputField::startPlaceholderAnimation() { } QMimeData *InputField::createMimeDataFromSelectionInner() const { - auto result = std::make_unique(); const auto cursor = _inner->textCursor(); const auto start = cursor.selectionStart(); const auto end = cursor.selectionEnd(); - if (end > start) { - auto textWithTags = getTextWithTagsPart(start, end); - result->setText(ExpandCustomLinks(textWithTags)); - if (!textWithTags.tags.isEmpty()) { - if (_tagMimeProcessor) { - for (auto &tag : textWithTags.tags) { - tag.id = _tagMimeProcessor->mimeTagFromTag(tag.id); - } - } - result->setData( - TextUtilities::TagsTextMimeType(), - textWithTags.text.toUtf8()); - result->setData( - TextUtilities::TagsMimeType(), - TextUtilities::SerializeTags(textWithTags.tags)); - } - } - return result.release(); + return TextUtilities::MimeDataFromText((end > start) + ? getTextWithTagsPart(start, end) + : TextWithTags() + ).release(); } void InputField::customUpDown(bool isCustom) { @@ -3058,21 +3008,12 @@ void InputField::commitInstantReplacement( auto format = [&]() -> QTextCharFormat { auto emojiLength = 0; - const auto emoji = Ui::Emoji::Find(with, &emojiLength); + const auto emoji = Emoji::Find(with, &emojiLength); if (!emoji || with.size() != emojiLength) { return _defaultCharFormat; } - const auto use = [&] { - if (!emoji->hasVariants()) { - return emoji; - } - const auto nonColored = emoji->nonColoredId(); - const auto it = cEmojiVariants().constFind(nonColored); - return (it != cEmojiVariants().cend()) - ? emoji->variant(it.value()) - : emoji; - }(); - AddRecentEmoji(use); + const auto use = Integration::Instance().defaultEmojiVariant( + emoji); return PrepareEmojiFormat(use, _st.font); }(); const auto replacement = format.isImageFormat() @@ -3080,7 +3021,9 @@ void InputField::commitInstantReplacement( : with; format.setProperty(kInstantReplaceWhatId, original); format.setProperty(kInstantReplaceWithId, replacement); - format.setProperty(kInstantReplaceRandomId, rand_value()); + format.setProperty( + kInstantReplaceRandomId, + openssl::RandomValue()); ApplyTagFormat(format, cursor.charFormat()); cursor.insertText(replacement, format); } @@ -3362,7 +3305,7 @@ bool InputField::revertFormatReplace() { void InputField::contextMenuEventInner(QContextMenuEvent *e) { if (const auto menu = _inner->createStandardContextMenu()) { addMarkdownActions(menu, e); - _contextMenu = base::make_unique_q(this, menu); + _contextMenu = base::make_unique_q(this, menu); _contextMenu->popup(e->globalPos()); } } @@ -3373,7 +3316,11 @@ void InputField::addMarkdownActions( if (!_markdownEnabled) { return; } - const auto formatting = new QAction(tr::lng_menu_formatting(tr::now), menu); + auto &integration = Integration::Instance(); + + const auto formatting = new QAction( + integration.phraseFormattingTitle(), + menu); addMarkdownMenuAction(menu, formatting); const auto submenu = new QMenu(menu); @@ -3417,24 +3364,24 @@ void InputField::addMarkdownActions( const auto selection = editLinkSelection(e); const auto data = selectionEditLinkData(selection); const auto base = data.link.isEmpty() - ? tr::lng_menu_formatting_link_create(tr::now) - : tr::lng_menu_formatting_link_edit(tr::now); + ? integration.phraseFormattingLinkCreate() + : integration.phraseFormattingLinkEdit(); add(base, kEditLinkSequence, false, [=] { editMarkdownLink(selection); }); }; const auto addclear = [&] { const auto disabled = !hasText || !hasTags; - add(tr::lng_menu_formatting_clear(tr::now), kClearFormatSequence, disabled, [=] { + add(integration.phraseFormattingClear(), kClearFormatSequence, disabled, [=] { clearSelectionMarkdown(); }); }; - addtag(tr::lng_menu_formatting_bold(tr::now), QKeySequence::Bold, kTagBold); - addtag(tr::lng_menu_formatting_italic(tr::now), QKeySequence::Italic, kTagItalic); - addtag(tr::lng_menu_formatting_underline(tr::now), QKeySequence::Underline, kTagUnderline); - addtag(tr::lng_menu_formatting_strike_out(tr::now), kStrikeOutSequence, kTagStrikeOut); - addtag(tr::lng_menu_formatting_monospace(tr::now), kMonospaceSequence, kTagCode); + addtag(integration.phraseFormattingBold(), QKeySequence::Bold, kTagBold); + addtag(integration.phraseFormattingItalic(), QKeySequence::Italic, kTagItalic); + addtag(integration.phraseFormattingUnderline(), QKeySequence::Underline, kTagUnderline); + addtag(integration.phraseFormattingStrikeOut(), kStrikeOutSequence, kTagStrikeOut); + addtag(integration.phraseFormattingMonospace(), kMonospaceSequence, kTagCode); if (_editLinkCallback) { submenu->addSeparator(); @@ -3595,11 +3542,10 @@ MaskedInputField::MaskedInputField( refreshPlaceholder(text); }, lifetime()); - subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { - if (update.paletteChanged()) { - updatePalette(); - } - }); + style::PaletteChanged( + ) | rpl::start_with_next([=] { + updatePalette(); + }, lifetime()); updatePalette(); setAttribute(Qt::WA_OpaquePaintEvent); @@ -3608,7 +3554,9 @@ MaskedInputField::MaskedInputField( connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int))); connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(onTextEdited())); - if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); + connect(this, &MaskedInputField::selectionChanged, [] { + Integration::Instance().textActionsUpdated(); + }); setStyle(InputStyle::instance()); QLineEdit::setTextMargins(0, 0, 0, 0); @@ -3738,7 +3686,7 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { auto borderShownDegree = _a_borderShown.value(1.); auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = snap(_borderAnimationStart, 0, width()); + auto borderStart = std::clamp(_borderAnimationStart, 0, width()); auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); if (borderTo > borderFrom) { @@ -3759,7 +3707,7 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins)); r.moveTop(r.top() + placeholderTop); - if (rtl()) r.moveLeft(width() - r.left() - r.width()); + if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree; auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree); @@ -3784,7 +3732,7 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins)); r.moveLeft(r.left() + placeholderLeft); - if (rtl()) r.moveLeft(width() - r.left() - r.width()); + if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width()); p.setFont(_st.placeholderFont); p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, focusedDegree)); @@ -3869,7 +3817,7 @@ void MaskedInputField::setPlaceholder(rpl::producer placeholder) { void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) { if (const auto menu = createStandardContextMenu()) { - (new Ui::PopupMenu(this, menu))->popup(e->globalPos()); + (new PopupMenu(this, menu))->popup(e->globalPos()); } } @@ -3982,213 +3930,19 @@ void MaskedInputField::onTextEdited() { if (wasText != _oldtext) emit changed(); startPlaceholderAnimation(); - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void MaskedInputField::onTextChange(const QString &text) { _oldtext = QLineEdit::text(); setErrorShown(false); - if (App::wnd()) App::wnd()->updateGlobalMenu(); + Integration::Instance().textActionsUpdated(); } void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) { _oldcursor = position; } -CountryCodeInput::CountryCodeInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st) -, _nosignal(false) { -} - -void CountryCodeInput::startErasing(QKeyEvent *e) { - setFocus(); - keyPressEvent(e); -} - -void CountryCodeInput::codeSelected(const QString &code) { - auto wasText = getLastText(); - auto wasCursor = cursorPosition(); - auto newText = '+' + code; - auto newCursor = newText.size(); - setText(newText); - _nosignal = true; - correctValue(wasText, wasCursor, newText, newCursor); - _nosignal = false; - emit changed(); -} - -void CountryCodeInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - QString newText, addToNumber; - int oldPos(nowCursor), newPos(-1), oldLen(now.length()), start = 0, digits = 5; - newText.reserve(oldLen + 1); - if (oldLen && now[0] == '+') { - if (start == oldPos) { - newPos = newText.length(); - } - ++start; - } - newText += '+'; - for (int i = start; i < oldLen; ++i) { - if (i == oldPos) { - newPos = newText.length(); - } - auto ch = now[i]; - if (ch.isDigit()) { - if (!digits || !--digits) { - addToNumber += ch; - } else { - newText += ch; - } - } - } - if (!addToNumber.isEmpty()) { - auto validCode = Data::ValidPhoneCode(newText.mid(1)); - addToNumber = newText.mid(1 + validCode.length()) + addToNumber; - newText = '+' + validCode; - } - setCorrectedText(now, nowCursor, newText, newPos); - - if (!_nosignal && was != newText) { - emit codeChanged(newText.mid(1)); - } - if (!addToNumber.isEmpty()) { - emit addedToNumber(addToNumber); - } -} - -PhonePartInput::PhonePartInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st/*, tr::lng_phone_ph(tr::now)*/) { -} - -void PhonePartInput::paintAdditionalPlaceholder(Painter &p) { - if (!_pattern.isEmpty()) { - auto t = getDisplayedText(); - auto ph = _additionalPlaceholder.mid(t.size()); - if (!ph.isEmpty()) { - p.setClipRect(rect()); - auto phRect = placeholderRect(); - int tw = phFont()->width(t); - if (tw < phRect.width()) { - phRect.setLeft(phRect.left() + tw); - placeholderAdditionalPrepare(p); - p.drawText(phRect, ph, style::al_topleft); - } - } - } -} - -void PhonePartInput::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Backspace && getLastText().isEmpty()) { - emit voidBackspace(e); - } else { - MaskedInputField::keyPressEvent(e); - } -} - -void PhonePartInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - QString newText; - int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = 0; - for (int i = 0; i < oldLen; ++i) { - if (now[i].isDigit()) { - ++digitCount; - } - } - if (digitCount > MaxPhoneTailLength) digitCount = MaxPhoneTailLength; - - bool inPart = !_pattern.isEmpty(); - int curPart = -1, leftInPart = 0; - newText.reserve(oldLen); - for (int i = 0; i < oldLen; ++i) { - if (i == oldPos && newPos < 0) { - newPos = newText.length(); - } - - auto ch = now[i]; - if (ch.isDigit()) { - if (!digitCount--) { - break; - } - if (inPart) { - if (leftInPart) { - --leftInPart; - } else { - newText += ' '; - ++curPart; - inPart = curPart < _pattern.size(); - leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0; - - ++oldPos; - } - } - newText += ch; - } else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') { - if (inPart) { - if (leftInPart) { - } else { - newText += ch; - ++curPart; - inPart = curPart < _pattern.size(); - leftInPart = inPart ? _pattern.at(curPart) : 0; - } - } else { - newText += ch; - } - } - } - auto newlen = newText.size(); - while (newlen > 0 && newText.at(newlen - 1).isSpace()) { - --newlen; - } - if (newlen < newText.size()) { - newText = newText.mid(0, newlen); - } - setCorrectedText(now, nowCursor, newText, newPos); -} - -void PhonePartInput::addedToNumber(const QString &added) { - setFocus(); - auto wasText = getLastText(); - auto wasCursor = cursorPosition(); - auto newText = added + wasText; - auto newCursor = newText.size(); - setText(newText); - setCursorPosition(added.length()); - correctValue(wasText, wasCursor, newText, newCursor); - startPlaceholderAnimation(); -} - -void PhonePartInput::onChooseCode(const QString &code) { - _pattern = phoneNumberParse(code); - if (!_pattern.isEmpty() && _pattern.at(0) == code.size()) { - _pattern.pop_front(); - } else { - _pattern.clear(); - } - _additionalPlaceholder = QString(); - if (!_pattern.isEmpty()) { - _additionalPlaceholder.reserve(20); - for (const auto part : _pattern) { - _additionalPlaceholder.append(' '); - _additionalPlaceholder.append(QString(part, QChar(0x2212))); - } - } - setPlaceholderHidden(!_additionalPlaceholder.isEmpty()); - - auto wasText = getLastText(); - auto wasCursor = cursorPosition(); - auto newText = getLastText(); - auto newCursor = newText.size(); - correctValue(wasText, wasCursor, newText, newCursor); - - startPlaceholderAnimation(); -} - PasswordInput::PasswordInput( QWidget *parent, const style::InputField &st, @@ -4266,194 +4020,4 @@ void HexInput::correctValue( setCorrectedText(now, nowCursor, newText, newPos); } -UsernameInput::UsernameInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val, - bool isLink) -: MaskedInputField(parent, st, std::move(placeholder), val) { - setLinkPlaceholder( - isLink ? Core::App().createInternalLink(QString()) : QString()); -} - -void UsernameInput::setLinkPlaceholder(const QString &placeholder) { - _linkPlaceholder = placeholder; - if (!_linkPlaceholder.isEmpty()) { - setTextMargins(style::margins(_st.textMargins.left() + _st.font->width(_linkPlaceholder), _st.textMargins.top(), _st.textMargins.right(), _st.textMargins.bottom())); - setPlaceholderHidden(true); - } -} - -void UsernameInput::paintAdditionalPlaceholder(Painter &p) { - if (!_linkPlaceholder.isEmpty()) { - p.setFont(_st.font); - p.setPen(_st.placeholderFg); - p.drawText(QRect(_st.textMargins.left(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), _linkPlaceholder, style::al_topleft); - } -} - -void UsernameInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - auto newPos = nowCursor; - auto from = 0, len = now.size(); - for (; from < len; ++from) { - if (!now.at(from).isSpace()) { - break; - } - if (newPos > 0) --newPos; - } - len -= from; - if (len > kMaxUsernameLength) { - len = kMaxUsernameLength + (now.at(from) == '@' ? 1 : 0); - } - for (int32 to = from + len; to > from;) { - --to; - if (!now.at(to).isSpace()) { - break; - } - --len; - } - setCorrectedText(now, nowCursor, now.mid(from, len), newPos); -} - -PhoneInput::PhoneInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &defaultValue, - QString value) -: MaskedInputField(parent, st, std::move(placeholder), value) -, _defaultValue(defaultValue) { - if (value.isEmpty()) { - clearText(); - } else { - auto pos = value.size(); - correctValue(QString(), 0, value, pos); - } -} - -void PhoneInput::focusInEvent(QFocusEvent *e) { - MaskedInputField::focusInEvent(e); - setSelection(cursorPosition(), cursorPosition()); -} - -void PhoneInput::clearText() { - auto value = _defaultValue; - setText(value); - auto pos = value.size(); - correctValue(QString(), 0, value, pos); -} - -void PhoneInput::paintAdditionalPlaceholder(Painter &p) { - if (!_pattern.isEmpty()) { - auto t = getDisplayedText(); - auto ph = _additionalPlaceholder.mid(t.size()); - if (!ph.isEmpty()) { - p.setClipRect(rect()); - auto phRect = placeholderRect(); - int tw = phFont()->width(t); - if (tw < phRect.width()) { - phRect.setLeft(phRect.left() + tw); - placeholderAdditionalPrepare(p); - p.drawText(phRect, ph, style::al_topleft); - } - } - } -} - -void PhoneInput::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - auto digits = now; - digits.replace(QRegularExpression(qsl("[^\\d]")), QString()); - _pattern = phoneNumberParse(digits); - - QString newPlaceholder; - if (_pattern.isEmpty()) { - newPlaceholder = QString(); - } else if (_pattern.size() == 1 && _pattern.at(0) == digits.size()) { - newPlaceholder = QString(_pattern.at(0) + 2, ' ') + tr::lng_contact_phone(tr::now); - } else { - newPlaceholder.reserve(20); - for (int i = 0, l = _pattern.size(); i < l; ++i) { - if (i) { - newPlaceholder.append(' '); - } else { - newPlaceholder.append('+'); - } - newPlaceholder.append(i ? QString(_pattern.at(i), QChar(0x2212)) : digits.mid(0, _pattern.at(i))); - } - } - if (_additionalPlaceholder != newPlaceholder) { - _additionalPlaceholder = newPlaceholder; - setPlaceholderHidden(!_additionalPlaceholder.isEmpty()); - update(); - } - - QString newText; - int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = qMin(digits.size(), MaxPhoneCodeLength + MaxPhoneTailLength); - - bool inPart = !_pattern.isEmpty(), plusFound = false; - int curPart = 0, leftInPart = inPart ? _pattern.at(curPart) : 0; - newText.reserve(oldLen + 1); - newText.append('+'); - for (int i = 0; i < oldLen; ++i) { - if (i == oldPos && newPos < 0) { - newPos = newText.length(); - } - - QChar ch(now[i]); - if (ch.isDigit()) { - if (!digitCount--) { - break; - } - if (inPart) { - if (leftInPart) { - --leftInPart; - } else { - newText += ' '; - ++curPart; - inPart = curPart < _pattern.size(); - leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0; - - ++oldPos; - } - } - newText += ch; - } else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') { - if (inPart) { - if (leftInPart) { - } else { - newText += ch; - ++curPart; - inPart = curPart < _pattern.size(); - leftInPart = inPart ? _pattern.at(curPart) : 0; - } - } else { - newText += ch; - } - } else if (ch == '+') { - plusFound = true; - } - } - if (!plusFound && newText == qstr("+")) { - newText = QString(); - newPos = 0; - } - int32 newlen = newText.size(); - while (newlen > 0 && newText.at(newlen - 1).isSpace()) { - --newlen; - } - if (newlen < newText.size()) { - newText = newText.mid(0, newlen); - } - setCorrectedText(now, nowCursor, newText, newPos); -} - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.h b/Telegram/SourceFiles/ui/widgets/input_fields.h index d15762d2d..ae480a05c 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.h +++ b/Telegram/SourceFiles/ui/widgets/input_fields.h @@ -7,15 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/emoji_config.h" #include "ui/rp_widget.h" #include "ui/effects/animations.h" +#include "ui/text/text_entity.h" #include "styles/style_widgets.h" #include #include #include -class UserData; +class QTouchEvent; +class Painter; namespace Ui { @@ -51,7 +54,7 @@ enum class InputSubmitSettings { None, }; -class FlatInput : public Ui::RpWidgetWrap, private base::Subscriber { +class FlatInput : public RpWidgetWrap { // The Q_OBJECT meta info is used for qobject_cast! Q_OBJECT @@ -110,7 +113,7 @@ protected: return _st.font; } - void phPrepare(Painter &p, float64 placeholderFocused); + void phPrepare(QPainter &p, float64 placeholderFocused); private: void updatePalette(); @@ -136,7 +139,7 @@ private: QPoint _touchStart; }; -class InputField : public RpWidget, private base::Subscriber { +class InputField : public RpWidget { Q_OBJECT public: @@ -215,10 +218,8 @@ public: // (and then to clipboard or to drag-n-drop object), here is a strategy for that. class TagMimeProcessor { public: - virtual QString mimeTagFromTag(const QString &tagId) = 0; virtual QString tagFromMimeTag(const QString &mimeTag) = 0; - virtual ~TagMimeProcessor() { - } + virtual ~TagMimeProcessor() = default; }; void setTagMimeProcessor(std::unique_ptr &&processor); @@ -481,17 +482,17 @@ private: rpl::variable _placeholderFull; QString _placeholder; int _placeholderAfterSymbols = 0; - Ui::Animations::Simple _a_placeholderShifted; + Animations::Simple _a_placeholderShifted; bool _placeholderShifted = false; QPainterPath _placeholderPath; - Ui::Animations::Simple _a_borderShown; + Animations::Simple _a_borderShown; int _borderAnimationStart = 0; - Ui::Animations::Simple _a_borderOpacity; + Animations::Simple _a_borderOpacity; bool _borderVisible = false; - Ui::Animations::Simple _a_focused; - Ui::Animations::Simple _a_error; + Animations::Simple _a_focused; + Animations::Simple _a_error; bool _focused = false; bool _error = false; @@ -504,7 +505,7 @@ private: bool _correcting = false; MimeDataHook _mimeDataHook; - base::unique_qptr _contextMenu; + base::unique_qptr _contextMenu; QTextCharFormat _defaultCharFormat; @@ -515,9 +516,7 @@ private: }; -class MaskedInputField - : public RpWidgetWrap - , private base::Subscriber { +class MaskedInputField : public RpWidgetWrap { // The Q_OBJECT meta info is used for qobject_cast! Q_OBJECT @@ -638,17 +637,17 @@ private: rpl::variable _placeholderFull; QString _placeholder; - Ui::Animations::Simple _a_placeholderShifted; + Animations::Simple _a_placeholderShifted; bool _placeholderShifted = false; QPainterPath _placeholderPath; - Ui::Animations::Simple _a_borderShown; + Animations::Simple _a_borderShown; int _borderAnimationStart = 0; - Ui::Animations::Simple _a_borderOpacity; + Animations::Simple _a_borderOpacity; bool _borderVisible = false; - Ui::Animations::Simple _a_focused; - Ui::Animations::Simple _a_error; + Animations::Simple _a_focused; + Animations::Simple _a_error; bool _focused = false; bool _error = false; @@ -662,61 +661,6 @@ private: QPoint _touchStart; }; -class CountryCodeInput : public MaskedInputField { - Q_OBJECT - -public: - CountryCodeInput(QWidget *parent, const style::InputField &st); - -public slots: - void startErasing(QKeyEvent *e); - void codeSelected(const QString &code); - -signals: - void codeChanged(const QString &code); - void addedToNumber(const QString &added); - -protected: - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - -private: - bool _nosignal; - -}; - -class PhonePartInput : public MaskedInputField { - Q_OBJECT - -public: - PhonePartInput(QWidget *parent, const style::InputField &st); - -public slots: - void addedToNumber(const QString &added); - void onChooseCode(const QString &code); - -signals: - void voidBackspace(QKeyEvent *e); - -protected: - void keyPressEvent(QKeyEvent *e) override; - - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - void paintAdditionalPlaceholder(Painter &p) override; - -private: - QVector _pattern; - QString _additionalPlaceholder; - -}; - class PasswordInput : public MaskedInputField { public: PasswordInput(QWidget *parent, const style::InputField &st, rpl::producer placeholder = nullptr, const QString &val = QString()); @@ -749,56 +693,4 @@ protected: }; -class UsernameInput : public MaskedInputField { -public: - UsernameInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &val, - bool isLink); - - void setLinkPlaceholder(const QString &placeholder); - -protected: - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - void paintAdditionalPlaceholder(Painter &p) override; - -private: - QString _linkPlaceholder; - -}; - -class PhoneInput : public MaskedInputField { -public: - PhoneInput( - QWidget *parent, - const style::InputField &st, - rpl::producer placeholder, - const QString &defaultValue, - QString value); - - void clearText(); - -protected: - void focusInEvent(QFocusEvent *e) override; - - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - void paintAdditionalPlaceholder(Painter &p) override; - -private: - QString _defaultValue; - QVector _pattern; - QString _additionalPlaceholder; - -}; - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp index 5b0dd8de8..75d6e0e7b 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -7,17 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/widgets/labels.h" +#include "ui/text/text_entity.h" #include "ui/widgets/popup_menu.h" -#include "core/click_handler_types.h" // UrlClickHandler -#include "chat_helpers/message_field.h" // SetClipboardText/MimeDataFromText -#include "mainwindow.h" -#include "lang/lang_keys.h" -#include "facades.h" -#include "app.h" +#include "ui/basic_click_handlers.h" // UrlClickHandler +#include "ui/inactive_press.h" #include #include #include +#include #include namespace Ui { @@ -49,7 +47,7 @@ void CrossFadeAnimation::addLine(Part was, Part now) { void CrossFadeAnimation::paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow) { if (_lines.isEmpty()) return; - for_const (auto &line, _lines) { + for (const auto &line : std::as_const(_lines)) { paintLine(p, line, positionReady, alphaWas, alphaNow); } } @@ -64,11 +62,12 @@ void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positio return; } + const auto pixelRatio = style::DevicePixelRatio(); auto positionWas = line.was.position; auto positionNow = line.now.position; auto left = anim::interpolate(positionWas.x(), positionNow.x(), positionReady); - auto topDelta = (snapshotNow.height() / cIntRetinaFactor()) - (snapshotWas.height() / cIntRetinaFactor()); - auto widthDelta = (snapshotNow.width() / cIntRetinaFactor()) - (snapshotWas.width() / cIntRetinaFactor()); + auto topDelta = (snapshotNow.height() / pixelRatio) - (snapshotWas.height() / pixelRatio); + auto widthDelta = (snapshotNow.width() / pixelRatio) - (snapshotWas.width() / pixelRatio); auto topWas = anim::interpolate(positionWas.y(), positionNow.y() + topDelta, positionReady); auto topNow = topWas - topDelta; @@ -76,22 +75,22 @@ void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positio if (!snapshotWas.isNull()) { p.drawPixmap(left, topWas, snapshotWas); if (topDelta > 0) { - p.fillRect(left, topWas - topDelta, snapshotWas.width() / cIntRetinaFactor(), topDelta, _bg); + p.fillRect(left, topWas - topDelta, snapshotWas.width() / pixelRatio, topDelta, _bg); } } if (widthDelta > 0) { - p.fillRect(left + (snapshotWas.width() / cIntRetinaFactor()), topNow, widthDelta, snapshotNow.height() / cIntRetinaFactor(), _bg); + p.fillRect(left + (snapshotWas.width() / pixelRatio), topNow, widthDelta, snapshotNow.height() / pixelRatio, _bg); } p.setOpacity(alphaNow); if (!snapshotNow.isNull()) { p.drawPixmap(left, topNow, snapshotNow); if (topDelta < 0) { - p.fillRect(left, topNow + topDelta, snapshotNow.width() / cIntRetinaFactor(), -topDelta, _bg); + p.fillRect(left, topNow + topDelta, snapshotNow.width() / pixelRatio, -topDelta, _bg); } } if (widthDelta < 0) { - p.fillRect(left + (snapshotNow.width() / cIntRetinaFactor()), topWas, -widthDelta, snapshotWas.height() / cIntRetinaFactor(), _bg); + p.fillRect(left + (snapshotNow.width() / pixelRatio), topWas, -widthDelta, snapshotWas.height() / pixelRatio, _bg); } } @@ -140,8 +139,7 @@ void LabelSimple::paintEvent(QPaintEvent *e) { FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st) : RpWidget(parent) , _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) -, _contextCopyText(tr::lng_context_copy_text(tr::now)) { +, _st(st) { init(); } @@ -151,8 +149,7 @@ FlatLabel::FlatLabel( const style::FlatLabel &st) : RpWidget(parent) , _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) -, _contextCopyText(tr::lng_context_copy_text(tr::now)) { +, _st(st) { setText(text); init(); } @@ -163,14 +160,14 @@ FlatLabel::FlatLabel( const style::FlatLabel &st) : RpWidget(parent) , _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) -, _contextCopyText(tr::lng_context_copy_text(tr::now)) { +, _st(st) { textUpdated(); std::move( text ) | rpl::start_with_next([this](const QString &value) { setText(value); }, lifetime()); + init(); } FlatLabel::FlatLabel( @@ -179,17 +176,19 @@ FlatLabel::FlatLabel( const style::FlatLabel &st) : RpWidget(parent) , _text(st.minWidth ? st.minWidth : QFIXED_MAX) -, _st(st) -, _contextCopyText(tr::lng_context_copy_text(tr::now)) { +, _st(st) { textUpdated(); std::move( text ) | rpl::start_with_next([this](const TextWithEntities &value) { setMarkedText(value); }, lifetime()); + init(); } void FlatLabel::init() { + _contextCopyText = Integration::Instance().phraseContextCopyText(); + _trippleClickTimer.setSingleShot(true); _touchSelectTimer.setSingleShot(true); @@ -323,7 +322,7 @@ void FlatLabel::mousePressEvent(QMouseEvent *e) { dragActionStart(e->globalPos(), e->button()); } -Ui::Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButton button) { +Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButton button) { _lastMousePos = p; auto state = dragActionUpdate(); @@ -331,8 +330,10 @@ Ui::Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButto ClickHandler::pressed(); _dragAction = NoDrag; - _dragWasInactive = App::wnd()->wasInactivePress(); - if (_dragWasInactive) App::wnd()->setInactivePress(false); + _dragWasInactive = WasInactivePress(window()); + if (_dragWasInactive) { + MarkInactivePress(window(), false); + } if (ClickHandler::getPressed()) { _dragStartPosition = mapFromGlobal(_lastMousePos); @@ -376,7 +377,7 @@ Ui::Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButto return state; } -Ui::Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton button) { +Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton button) { _lastMousePos = p; auto state = dragActionUpdate(); @@ -392,15 +393,18 @@ Ui::Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButt _selectionType = TextSelectType::Letters; if (activated) { + const auto guard = window(); if (!_clickHandlerFilter || _clickHandlerFilter(activated, button)) { - App::activateClickHandler(activated, button); + ActivateClickHandler(guard, activated, button); } } #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 if (!_selection.empty()) { - SetClipboardText(_text.toTextForMimeData(_selection), QClipboard::Selection); + TextUtilities::SetClipboardText( + _text.toTextForMimeData(_selection), + QClipboard::Selection); } #endif // Q_OS_LINUX32 || Q_OS_LINUX64 @@ -471,7 +475,7 @@ void FlatLabel::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; if (!selection.empty()) { - SetClipboardText(_text.toTextForMimeData(selection), QClipboard::FindBuffer); + TextUtilities::SetClipboardText(_text.toTextForMimeData(selection), QClipboard::FindBuffer); } #endif // Q_OS_MAC } @@ -573,12 +577,13 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) uponSelection = hasSelection; } - _contextMenu = new Ui::PopupMenu(this); + _contextMenu = new PopupMenu(this); if (fullSelection && !_contextCopyText.isEmpty()) { _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); } else if (uponSelection && !fullSelection) { - _contextMenu->addAction(tr::lng_context_copy_selected(tr::now), this, SLOT(onCopySelectedText())); + const auto text = Integration::Instance().phraseContextCopySelected(); + _contextMenu->addAction(text, this, SLOT(onCopySelectedText())); } else if (!hasSelection && !_contextCopyText.isEmpty()) { _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); } @@ -607,12 +612,12 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) void FlatLabel::onCopySelectedText() { const auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; if (!selection.empty()) { - SetClipboardText(_text.toTextForMimeData(selection)); + TextUtilities::SetClipboardText(_text.toTextForMimeData(selection)); } } void FlatLabel::onCopyContextText() { - SetClipboardText(_text.toTextForMimeData()); + TextUtilities::SetClipboardText(_text.toTextForMimeData()); } void FlatLabel::onTouchSelect() { @@ -646,8 +651,8 @@ void FlatLabel::onExecuteDrag() { } return TextForMimeData(); }(); - if (auto mimeData = MimeDataFromText(selectedText)) { - auto drag = new QDrag(App::wnd()); + if (auto mimeData = TextUtilities::MimeDataFromText(selectedText)) { + auto drag = new QDrag(window()); drag->setMimeData(mimeData.release()); drag->exec(Qt::CopyAction); @@ -708,7 +713,8 @@ std::unique_ptr FlatLabel::CrossFade( if (lineWidth < 0) { lineWidth = other.lineWidths[index]; } - auto fullWidth = data.full.width() / cIntRetinaFactor(); + const auto pixelRatio = style::DevicePixelRatio(); + auto fullWidth = data.full.width() / pixelRatio; auto top = index * data.lineHeight + data.lineAddTop; auto left = 0; if (label->_st.align & Qt::AlignHCenter) { @@ -716,10 +722,10 @@ std::unique_ptr FlatLabel::CrossFade( } else if (label->_st.align & Qt::AlignRight) { left += (fullWidth - lineWidth); } - auto snapshotRect = data.full.rect().intersected(QRect(left * cIntRetinaFactor(), top * cIntRetinaFactor(), lineWidth * cIntRetinaFactor(), label->_st.style.font->height * cIntRetinaFactor())); + auto snapshotRect = data.full.rect().intersected(QRect(left * pixelRatio, top * pixelRatio, lineWidth * pixelRatio, label->_st.style.font->height * pixelRatio)); if (!snapshotRect.isEmpty()) { - result.snapshot = App::pixmapFromImageInPlace(data.full.copy(snapshotRect)); - result.snapshot.setDevicePixelRatio(cRetinaFactor()); + result.snapshot = PixmapFromImage(data.full.copy(snapshotRect)); + result.snapshot.setDevicePixelRatio(pixelRatio); } auto positionBase = position + label->pos(); result.position = positionBase + QPoint(label->_st.margin.left() + left, label->_st.margin.top() + top); @@ -732,7 +738,7 @@ std::unique_ptr FlatLabel::CrossFade( return result; } -Ui::Text::StateResult FlatLabel::dragActionUpdate() { +Text::StateResult FlatLabel::dragActionUpdate() { auto m = mapFromGlobal(_lastMousePos); auto state = getTextState(m); updateHover(state); @@ -745,7 +751,7 @@ Ui::Text::StateResult FlatLabel::dragActionUpdate() { return state; } -void FlatLabel::updateHover(const Ui::Text::StateResult &state) { +void FlatLabel::updateHover(const Text::StateResult &state) { bool lnkChanged = ClickHandler::setActive(state.link, this); if (!_selectable) { @@ -808,15 +814,15 @@ void FlatLabel::refreshCursor(bool uponSymbol) { } } -Ui::Text::StateResult FlatLabel::getTextState(const QPoint &m) const { - Ui::Text::StateRequestElided request; +Text::StateResult FlatLabel::getTextState(const QPoint &m) const { + Text::StateRequestElided request; request.align = _st.align; if (_selectable) { - request.flags |= Ui::Text::StateRequest::Flag::LookupSymbol; + request.flags |= Text::StateRequest::Flag::LookupSymbol; } int textWidth = width() - _st.margin.left() - _st.margin.right(); - Ui::Text::StateResult state; + Text::StateResult state; bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth()); bool renderElided = _breakEverywhere || heightExceeded; if (renderElided) { @@ -824,7 +830,7 @@ Ui::Text::StateResult FlatLabel::getTextState(const QPoint &m) const { auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2); request.lines = lines; if (_breakEverywhere) { - request.flags |= Ui::Text::StateRequest::Flag::BreakEverywhere; + request.flags |= Text::StateRequest::Flag::BreakEverywhere; } state = _text.getStateElided(m - QPoint(_st.margin.left(), _st.margin.top()), textWidth, request); } else { diff --git a/Telegram/SourceFiles/ui/widgets/labels.h b/Telegram/SourceFiles/ui/widgets/labels.h index bb6474d98..5ce3d89c2 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.h +++ b/Telegram/SourceFiles/ui/widgets/labels.h @@ -9,11 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/wrap/padding_wrap.h" +#include "ui/text/text.h" +#include "ui/click_handler.h" #include "boxes/abstract_box.h" #include "styles/style_widgets.h" #include +class QTouchEvent; + namespace Ui { class PopupMenu; @@ -155,11 +159,11 @@ private: void init(); void textUpdated(); - Ui::Text::StateResult dragActionUpdate(); - Ui::Text::StateResult dragActionStart(const QPoint &p, Qt::MouseButton button); - Ui::Text::StateResult dragActionFinish(const QPoint &p, Qt::MouseButton button); - void updateHover(const Ui::Text::StateResult &state); - Ui::Text::StateResult getTextState(const QPoint &m) const; + Text::StateResult dragActionUpdate(); + Text::StateResult dragActionStart(const QPoint &p, Qt::MouseButton button); + Text::StateResult dragActionFinish(const QPoint &p, Qt::MouseButton button); + void updateHover(const Text::StateResult &state); + Text::StateResult getTextState(const QPoint &m) const; void refreshCursor(bool uponSymbol); int countTextWidth() const; @@ -205,7 +209,7 @@ private: QPoint _trippleClickPoint; QTimer _trippleClickTimer; - Ui::PopupMenu *_contextMenu = nullptr; + PopupMenu *_contextMenu = nullptr; QString _contextCopyText; ClickHandlerFilter _clickHandlerFilter; @@ -218,7 +222,7 @@ private: }; -class DividerLabel : public PaddingWrap { +class DividerLabel : public PaddingWrap { public: using PaddingWrap::PaddingWrap; diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index f658f039f..ba103cba8 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -11,10 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/text/text.h" +#include + namespace Ui { struct Menu::ActionData { - Ui::Text::String text; + Text::String text; QString shortcut; const style::icon *icon = nullptr; const style::icon *iconOver = nullptr; @@ -313,7 +315,7 @@ void Menu::handleKeyPress(int key) { itemPressed(TriggeredSource::Keyboard); return; } - if (key == (rtl() ? Qt::Key_Left : Qt::Key_Right)) { + if (key == (style::RightToLeft() ? Qt::Key_Left : Qt::Key_Right)) { if (_selected >= 0 && _actionsData[_selected].hasSubmenu) { itemPressed(TriggeredSource::Keyboard); return; diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index ea7d15175..3ff6f189a 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -9,14 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/image/image_prepare.h" +#include "ui/platform/ui_platform_utility.h" #include "ui/ui_utility.h" +#include "ui/delayed_activation.h" #include "platform/platform_info.h" -#include "platform/platform_specific.h" -#include "mainwindow.h" -#include "core/application.h" -#include "lang/lang_keys.h" -#include "app.h" +#include +#include #include #include @@ -25,6 +24,7 @@ namespace Ui { PopupMenu::PopupMenu(QWidget *parent, const style::PopupMenu &st) : RpWidget(parent) , _st(st) +, _roundRect(ImageRoundRadius::Small, _st.menu.itemBg) , _menu(this, _st.menu) { init(); } @@ -32,6 +32,7 @@ PopupMenu::PopupMenu(QWidget *parent, const style::PopupMenu &st) PopupMenu::PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st) : RpWidget(parent) , _st(st) +, _roundRect(ImageRoundRadius::Small, _st.menu.itemBg) , _menu(this, menu, _st.menu) { init(); @@ -46,9 +47,7 @@ PopupMenu::PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st) void PopupMenu::init() { using namespace rpl::mappers; - rpl::merge( - Core::App().passcodeLockChanges(), - Core::App().termsLockChanges() + Integration::Instance().forcePopupMenuHideRequests( ) | rpl::start_with_next([=] { hideMenu(true); }, lifetime()); @@ -113,7 +112,7 @@ const std::vector> &PopupMenu::actions() const { } void PopupMenu::paintEvent(QPaintEvent *e) { - Painter p(this); + QPainter p(this); if (_useTransparency) { Platform::StartTranslucentPaint(p, e); @@ -137,10 +136,10 @@ void PopupMenu::paintEvent(QPaintEvent *e) { } } -void PopupMenu::paintBg(Painter &p) { +void PopupMenu::paintBg(QPainter &p) { if (_useTransparency) { Shadow::paint(p, _inner, width(), _st.shadow); - App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); + _roundRect.paint(p, _inner); } else { p.fillRect(0, 0, width() - _padding.right(), _padding.top(), _st.shadow.fallback); p.fillRect(width() - _padding.right(), 0, _padding.right(), height() - _padding.bottom(), _st.shadow.fallback); @@ -190,7 +189,7 @@ void PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSou currentSubmenu->hideMenu(true); } if (submenu) { - QPoint p(_inner.x() + (rtl() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop); + QPoint p(_inner.x() + (style::RightToLeft() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop); _activeSubmenu = submenu; _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); @@ -213,7 +212,7 @@ bool PopupMenu::handleKeyPress(int key) { } else if (key == Qt::Key_Escape) { hideMenu(_parent ? true : false); return true; - } else if (key == (rtl() ? Qt::Key_Right : Qt::Key_Left)) { + } else if (key == (style::RightToLeft() ? Qt::Key_Right : Qt::Key_Left)) { if (_parent) { hideMenu(true); return true; @@ -258,6 +257,18 @@ void PopupMenu::hideEvent(QHideEvent *e) { } } +void PopupMenu::keyPressEvent(QKeyEvent *e) { + forwardKeyPress(e->key()); +} + +void PopupMenu::mouseMoveEvent(QMouseEvent *e) { + forwardMouseMove(e->globalPos()); +} + +void PopupMenu::mousePressEvent(QMouseEvent *e) { + forwardMousePress(e->globalPos()); +} + void PopupMenu::hideMenu(bool fast) { if (isHidden()) return; if (_parent && !_a_opacity.animating()) { @@ -368,11 +379,12 @@ void PopupMenu::startShowAnimation() { auto cache = grabForPanelAnimation(); _a_opacity = base::take(opacityAnimation); + const auto pixelRatio = style::DevicePixelRatio(); _showAnimation = std::make_unique(_st.animation, _origin); - _showAnimation->setFinalImage(std::move(cache), QRect(_inner.topLeft() * cIntRetinaFactor(), _inner.size() * cIntRetinaFactor())); + _showAnimation->setFinalImage(std::move(cache), QRect(_inner.topLeft() * pixelRatio, _inner.size() * pixelRatio)); if (_useTransparency) { - auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); + _showAnimation->setCornerMasks( + Images::CornersMask(ImageRoundRadius::Small)); } else { _showAnimation->setSkipShadow(true); } @@ -400,13 +412,14 @@ void PopupMenu::showAnimationCallback() { QImage PopupMenu::grabForPanelAnimation() { SendPendingMoveResizeEvents(this); - auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); + const auto pixelRatio = style::DevicePixelRatio(); + auto result = QImage(size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(pixelRatio); result.fill(Qt::transparent); { - Painter p(&result); + QPainter p(&result); if (_useTransparency) { - App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); + _roundRect.paint(p, _inner); } else { p.fillRect(_inner, _st.menu.itemBg); } @@ -428,7 +441,7 @@ void PopupMenu::popup(const QPoint &p) { } void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) { - if (!parent && Platform::IsMac() && !Platform::IsApplicationActive()) { + if (!parent && ::Platform::IsMac() && !Platform::IsApplicationActive()) { _hiding = false; _a_opacity.stop(); _a_show.stop(); @@ -447,7 +460,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou _useTransparency = Platform::TranslucentWindowsSupported(p); setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); handleCompositingUpdate(); - if (rtl()) { + if (style::RightToLeft()) { if (w.x() - width() < r.x() - _padding.left()) { if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); @@ -488,9 +501,9 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou startShowAnimation(); - psUpdateOverlayed(this); + Platform::UpdateOverlayed(this); show(); - psShowOverAll(this); + Platform::ShowOverAll(this); activateWindow(); } @@ -500,7 +513,7 @@ PopupMenu::~PopupMenu() { } if (const auto parent = parentWidget()) { if (QApplication::focusWidget() != nullptr) { - Core::App().activateWindowDelayed(parent); + ActivateWindowDelayed(parent); } } if (_destroyedCallback) { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index a9f727a7d..05ca5ceaa 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -8,15 +8,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "styles/style_widgets.h" -#include "ui/rp_widget.h" #include "ui/widgets/menu.h" #include "ui/effects/animations.h" #include "ui/effects/panel_animation.h" +#include "ui/round_rect.h" +#include "ui/rp_widget.h" #include "base/object_ptr.h" namespace Ui { -class PopupMenu : public Ui::RpWidget, private base::Subscriber { +class PopupMenu : public RpWidget { public: PopupMenu(QWidget *parent, const style::PopupMenu &st = st::defaultPopupMenu); PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); @@ -42,19 +43,12 @@ protected: void paintEvent(QPaintEvent *e) override; void focusOutEvent(QFocusEvent *e) override; void hideEvent(QHideEvent *e) override; - - void keyPressEvent(QKeyEvent *e) override { - forwardKeyPress(e->key()); - } - void mouseMoveEvent(QMouseEvent *e) override { - forwardMouseMove(e->globalPos()); - } - void mousePressEvent(QMouseEvent *e) override { - forwardMousePress(e->globalPos()); - } + void keyPressEvent(QKeyEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; private: - void paintBg(Painter &p); + void paintBg(QPainter &p); void hideFast(); void setOrigin(PanelAnimation::Origin origin); void showAnimated(PanelAnimation::Origin origin); @@ -74,7 +68,7 @@ private: void hideFinished(); void showStarted(); - using TriggeredSource = Ui::Menu::TriggeredSource; + using TriggeredSource = Menu::TriggeredSource; void handleCompositingUpdate(); void handleMenuResize(); void handleActivated(QAction *action, int actionTop, TriggeredSource source); @@ -101,7 +95,8 @@ private: const style::PopupMenu &_st; - object_ptr _menu; + RoundRect _roundRect; + object_ptr _menu; using Submenus = QMap; Submenus _submenus; @@ -115,12 +110,12 @@ private: PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; std::unique_ptr _showAnimation; - Ui::Animations::Simple _a_show; + Animations::Simple _a_show; bool _useTransparency = true; bool _hiding = false; QPixmap _cache; - Ui::Animations::Simple _a_opacity; + Animations::Simple _a_opacity; bool _deleteOnHide = true; bool _triggering = false; diff --git a/Telegram/SourceFiles/ui/widgets/scroll_area.cpp b/Telegram/SourceFiles/ui/widgets/scroll_area.cpp index 3dcbe270e..37187f476 100644 --- a/Telegram/SourceFiles/ui/widgets/scroll_area.cpp +++ b/Telegram/SourceFiles/ui/widgets/scroll_area.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/widgets/scroll_area.h" +#include "ui/painter.h" #include "ui/ui_utility.h" #include @@ -25,7 +26,7 @@ ScrollShadow::ScrollShadow(ScrollArea *parent, const style::ScrollArea *st) : QW } void ScrollShadow::paintEvent(QPaintEvent *e) { - Painter p(this); + QPainter p(this); p.fillRect(rect(), _st->shColor); } @@ -51,7 +52,7 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::ScrollArea *st) } void ScrollBar::recountSize() { - setGeometry(_vertical ? QRect(rtl() ? 0 : (area()->width() - _st->width), _st->deltat, _st->width, area()->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, area()->height() - _st->width, area()->width() - _st->deltat - _st->deltab, _st->width)); + setGeometry(_vertical ? QRect(style::RightToLeft() ? 0 : (area()->width() - _st->width), _st->deltat, _st->width, area()->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, area()->height() - _st->width, area()->width() - _st->deltat - _st->deltab, _st->width)); } void ScrollBar::onValueChanged() { @@ -175,7 +176,7 @@ void ScrollBar::paintEvent(QPaintEvent *e) { auto opacity = _a_opacity.value(_hiding ? 0. : 1.); if (opacity == 0.) return; - Painter p(this); + QPainter p(this); auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0; auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax; p.setPen(Qt::NoPen); @@ -279,7 +280,7 @@ ScrollArea::ScrollArea(QWidget *parent, const style::ScrollArea &st, bool handle , _topShadow(this, &_st) , _bottomShadow(this, &_st) , _touchEnabled(handleTouch) { - setLayoutDirection(cLangDir()); + setLayoutDirection(style::LayoutDirection()); setFocusPolicy(Qt::NoFocus); connect(_verticalBar, SIGNAL(topShadowVisibility(bool)), _topShadow, SLOT(changeVisibility(bool))); @@ -414,23 +415,23 @@ void ScrollArea::touchUpdateSpeed() { // fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because // of a small horizontal offset when scrolling vertically - const int newSpeedY = (qAbs(pixelsPerSecond.y()) > FingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; - const int newSpeedX = (qAbs(pixelsPerSecond.x()) > FingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; + const int newSpeedY = (qAbs(pixelsPerSecond.y()) > kFingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; + const int newSpeedX = (qAbs(pixelsPerSecond.x()) > kFingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; if (_touchScrollState == TouchScrollState::Auto) { const int oldSpeedY = _touchSpeed.y(); const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) && (oldSpeedX <= 0 && newSpeedX <= 0)) || (oldSpeedX >= 0 && newSpeedX >= 0)) { - _touchSpeed.setY(snap((oldSpeedY + (newSpeedY / 4)), -MaxScrollAccelerated, +MaxScrollAccelerated)); - _touchSpeed.setX(snap((oldSpeedX + (newSpeedX / 4)), -MaxScrollAccelerated, +MaxScrollAccelerated)); + _touchSpeed.setY(std::clamp((oldSpeedY + (newSpeedY / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated)); + _touchSpeed.setX(std::clamp((oldSpeedX + (newSpeedX / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated)); } else { _touchSpeed = QPoint(); } } else { // we average the speed to avoid strange effects with the last delta if (!_touchSpeed.isNull()) { - _touchSpeed.setX(snap((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -MaxScrollFlick, +MaxScrollFlick)); - _touchSpeed.setY(snap((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -MaxScrollFlick, +MaxScrollFlick)); + _touchSpeed.setX(std::clamp((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick)); + _touchSpeed.setY(std::clamp((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick)); } else { _touchSpeed = QPoint(newSpeedX, newSpeedY); } @@ -588,7 +589,7 @@ void ScrollArea::scrollContentsBy(int dx, int dy) { } bool ScrollArea::touchScroll(const QPoint &delta) { - int32 scTop = scrollTop(), scMax = scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); + int32 scTop = scrollTop(), scMax = scrollTopMax(), scNew = std::clamp(scTop - delta.y(), 0, scMax); if (scNew == scTop) return false; scrollToY(scNew); diff --git a/Telegram/SourceFiles/ui/widgets/scroll_area.h b/Telegram/SourceFiles/ui/widgets/scroll_area.h index e82875d40..ee9b3221d 100644 --- a/Telegram/SourceFiles/ui/widgets/scroll_area.h +++ b/Telegram/SourceFiles/ui/widgets/scroll_area.h @@ -14,9 +14,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +#include namespace Ui { +// 37px per 15ms while select-by-drag. +inline constexpr auto kMaxScrollSpeed = 37; + +// Touch flick ignore 3px. +inline constexpr auto kFingerAccuracyThreshold = 3; + +// 4000px per second. +inline constexpr auto kMaxScrollAccelerated = 4000; + +// 2500px per second. +inline constexpr auto kMaxScrollFlick = 2500; + enum class TouchScrollState { Manual, // Scrolling manually with the finger on the screen Auto, // Scrolling automatically @@ -106,14 +119,14 @@ private: crl::time _hideIn = 0; QTimer _hideTimer; - Ui::Animations::Simple _a_over; - Ui::Animations::Simple _a_barOver; - Ui::Animations::Simple _a_opacity; + Animations::Simple _a_over; + Animations::Simple _a_barOver; + Animations::Simple _a_opacity; QRect _bar; }; -class ScrollArea : public Ui::RpWidgetWrap { +class ScrollArea : public RpWidgetWrap { Q_OBJECT public: diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp index 3c8d30789..fab73dc97 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/toast/toast.h" #include "ui/widgets/tooltip.h" +#include "ui/platform/ui_platform_utility.h" #include "window/layer_widget.h" #include "window/themes/window_theme.h" #include "core/application.h" @@ -164,7 +165,7 @@ void SeparatePanel::initLayout() { } }); - Platform::InitOnTopPanel(this); + Ui::Platform::InitOnTopPanel(this); } void SeparatePanel::createBorderImage() { @@ -350,7 +351,7 @@ void SeparatePanel::setInnerSize(QSize size) { void SeparatePanel::initGeometry(QSize size) { const auto center = Core::App().getPointForCallPanelCenter(); - _useTransparency = Platform::TranslucentWindowsSupported(center); + _useTransparency = Ui::Platform::TranslucentWindowsSupported(center); _padding = _useTransparency ? st::callShadow.extend : style::margins( @@ -397,7 +398,7 @@ void SeparatePanel::paintEvent(QPaintEvent *e) { finishAnimating(); if (isHidden()) return; } else { - Platform::StartTranslucentPaint(p, e); + Ui::Platform::StartTranslucentPaint(p, e); p.setOpacity(opacity); PainterHighQualityEnabler hq(p); @@ -418,7 +419,7 @@ void SeparatePanel::paintEvent(QPaintEvent *e) { } if (_useTransparency) { - Platform::StartTranslucentPaint(p, e); + Ui::Platform::StartTranslucentPaint(p, e); paintShadowBorder(p); } else { paintOpaqueBorder(p); diff --git a/Telegram/SourceFiles/ui/widgets/shadow.cpp b/Telegram/SourceFiles/ui/widgets/shadow.cpp index 2f1ed2682..a4bd8c929 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.cpp +++ b/Telegram/SourceFiles/ui/widgets/shadow.cpp @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "styles/style_widgets.h" +#include "styles/palette.h" + +#include +#include namespace Ui { @@ -22,7 +26,11 @@ PlainShadow::PlainShadow(QWidget *parent, style::color color) resize(st::lineWidth, st::lineWidth); } -void Shadow::paint(Painter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) { +void PlainShadow::paintEvent(QPaintEvent *e) { + QPainter(this).fillRect(e->rect(), _color); +} + +void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) { auto left = (sides & RectPart::Left); auto top = (sides & RectPart::Top); auto right = (sides & RectPart::Right); @@ -90,19 +98,19 @@ QPixmap Shadow::grab( (sides & RectPart::Bottom) ? shadow.extend.bottom() : 0 ); auto full = QRect(0, 0, extend.left() + rect.width() + extend.right(), extend.top() + rect.height() + extend.bottom()); - auto result = QPixmap(full.size() * cIntRetinaFactor()); - result.setDevicePixelRatio(cRetinaFactor()); + auto result = QPixmap(full.size() * style::DevicePixelRatio()); + result.setDevicePixelRatio(style::DevicePixelRatio()); result.fill(Qt::transparent); { - Painter p(&result); - Ui::Shadow::paint(p, full.marginsRemoved(extend), full.width(), shadow); + QPainter p(&result); + Shadow::paint(p, full.marginsRemoved(extend), full.width(), shadow); RenderWidget(p, target, QPoint(extend.left(), extend.top())); } return result; } void Shadow::paintEvent(QPaintEvent *e) { - Painter p(this); + QPainter p(this); paint(p, rect().marginsRemoved(_st.extend), width(), _st, _sides); } diff --git a/Telegram/SourceFiles/ui/widgets/shadow.h b/Telegram/SourceFiles/ui/widgets/shadow.h index a33c97075..9bbd11e67 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.h +++ b/Telegram/SourceFiles/ui/widgets/shadow.h @@ -22,9 +22,7 @@ public: PlainShadow(QWidget *parent, style::color color); protected: - void paintEvent(QPaintEvent *e) override { - Painter(this).fillRect(e->rect(), _color); - } + void paintEvent(QPaintEvent *e) override; private: style::color _color; @@ -43,7 +41,7 @@ public: } static void paint( - Painter &p, + QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.cpp b/Telegram/SourceFiles/ui/widgets/tooltip.cpp index df65d32b6..982d4b24a 100644 --- a/Telegram/SourceFiles/ui/widgets/tooltip.cpp +++ b/Telegram/SourceFiles/ui/widgets/tooltip.cpp @@ -7,10 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/widgets/tooltip.h" -#include "mainwindow.h" -#include "platform/platform_specific.h" #include "ui/ui_utility.h" -#include "app.h" +#include "ui/platform/ui_platform_utility.h" +#include "base/invoke_queued.h" #include "styles/style_widgets.h" #include @@ -20,14 +19,6 @@ namespace Ui { Tooltip *TooltipInstance = nullptr; -bool AbstractTooltipShower::tooltipWindowActive() const { - if (auto window = App::wnd()) { - window->updateIsActive(0); - return window->isActive(); - } - return false; -} - const style::Tooltip *AbstractTooltipShower::tooltipSt() const { return &st::defaultTooltip; } @@ -47,16 +38,13 @@ Tooltip::Tooltip() : RpWidget(nullptr) { _showTimer.setCallback([=] { performShow(); }); _hideByLeaveTimer.setCallback([=] { Hide(); }); - - App::wnd()->windowDeactivateEvents( - ) | rpl::start_with_next([=] { - Hide(); - }, lifetime()); } void Tooltip::performShow() { if (_shower) { - auto text = _shower->tooltipWindowActive() ? _shower->tooltipText() : QString(); + auto text = _shower->tooltipWindowActive() + ? _shower->tooltipText() + : QString(); if (text.isEmpty()) { Hide(); } else { @@ -113,7 +101,7 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip * // count tooltip position QPoint p(m + _st->shift); - if (rtl()) { + if (style::RightToLeft()) { p.setX(m.x() - s.width() - _st->shift.x()); } if (s.width() < 2 * _st->shift.x()) { @@ -189,7 +177,7 @@ void Tooltip::Hide() { instance->_showTimer.cancel(); instance->_hideByLeaveTimer.cancel(); instance->hide(); - InvokeQueued(instance, [instance] { instance->deleteLater(); }); + InvokeQueued(instance, [=] { instance->deleteLater(); }); } } @@ -246,12 +234,15 @@ void ImportantTooltip::countApproachSide(RectParts preferSide) { if ((allowedAbove && allowedBelow) || (!allowedAbove && !allowedBelow)) { _side = preferSide; } else { - _side = (allowedAbove ? RectPart::Top : RectPart::Bottom) | (preferSide & (RectPart::Left | RectPart::Center | RectPart::Right)); + _side = (allowedAbove ? RectPart::Top : RectPart::Bottom) + | (preferSide & (RectPart::Left | RectPart::Center | RectPart::Right)); } if (_useTransparency) { - auto arrow = QImage(QSize(_st.arrow * 2, _st.arrow) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + auto arrow = QImage( + QSize(_st.arrow * 2, _st.arrow) * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); arrow.fill(Qt::transparent); - arrow.setDevicePixelRatio(cRetinaFactor()); + arrow.setDevicePixelRatio(style::DevicePixelRatio()); { Painter p(&arrow); PainterHighQualityEnabler hq(p); @@ -266,7 +257,7 @@ void ImportantTooltip::countApproachSide(RectParts preferSide) { if (_side & RectPart::Bottom) { arrow = std::move(arrow).transformed(QTransform(1, 0, 0, -1, 0, 0)); } - _arrow = App::pixmapFromImageInPlace(std::move(arrow)); + _arrow = PixmapFromImage(std::move(arrow)); } } diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.h b/Telegram/SourceFiles/ui/widgets/tooltip.h index 6bc0e02e2..d2bc51f4f 100644 --- a/Telegram/SourceFiles/ui/widgets/tooltip.h +++ b/Telegram/SourceFiles/ui/widgets/tooltip.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "base/object_ptr.h" #include "ui/effects/animations.h" +#include "ui/text/text.h" #include "ui/rp_widget.h" #include "ui/rect_part.h" @@ -24,13 +25,13 @@ class AbstractTooltipShower { public: virtual QString tooltipText() const = 0; virtual QPoint tooltipPos() const = 0; - virtual bool tooltipWindowActive() const; + virtual bool tooltipWindowActive() const = 0; virtual const style::Tooltip *tooltipSt() const; virtual ~AbstractTooltipShower(); }; -class Tooltip : public Ui::RpWidget { +class Tooltip : public RpWidget { public: static void Show(int32 delay, const AbstractTooltipShower *shower); static void Hide(); diff --git a/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp b/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp index 803f411e0..e9e6f094a 100644 --- a/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp +++ b/Telegram/SourceFiles/ui/wrap/fade_wrap.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/widgets/shadow.h" +#include "ui/painter.h" +#include "styles/palette.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp b/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp index bfc979e55..fe99f0f75 100644 --- a/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp +++ b/Telegram/SourceFiles/ui/wrap/slide_wrap.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/wrap/slide_wrap.h" +#include "styles/style_basic.h" + #include #include diff --git a/Telegram/SourceFiles/ui/wrap/slide_wrap.h b/Telegram/SourceFiles/ui/wrap/slide_wrap.h index d53aefb77..f95bb0f51 100644 --- a/Telegram/SourceFiles/ui/wrap/slide_wrap.h +++ b/Telegram/SourceFiles/ui/wrap/slide_wrap.h @@ -63,7 +63,7 @@ private: bool _toggled = true; rpl::event_stream _toggledChanged; - Ui::Animations::Simple _animation; + Animations::Simple _animation; int _duration = 0; }; @@ -127,14 +127,14 @@ inline object_ptr> CreateSlideSkipWidget( class MultiSlideTracker { public: template - void track(const Ui::SlideWrap *wrap) { + void track(const SlideWrap *wrap) { _widgets.push_back(wrap); } rpl::producer atLeastOneShownValue() const; private: - std::vector*> _widgets; + std::vector*> _widgets; }; diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp index 4e68a2634..ff0d601d5 100644 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ b/Telegram/SourceFiles/window/layer_widget.cpp @@ -344,6 +344,16 @@ bool LayerWidget::overlaps(const QRect &globalRect) { return false; } +void LayerWidget::mousePressEvent(QMouseEvent *e) { + e->accept(); +} + +void LayerWidget::resizeEvent(QResizeEvent *e) { + if (_resizedCallback) { + _resizedCallback(); + } +} + void LayerStackWidget::setHideByBackgroundClick(bool hide) { _hideByBackgroundClick = hide; } diff --git a/Telegram/SourceFiles/window/layer_widget.h b/Telegram/SourceFiles/window/layer_widget.h index 5ffa1fcc2..3d41806f2 100644 --- a/Telegram/SourceFiles/window/layer_widget.h +++ b/Telegram/SourceFiles/window/layer_widget.h @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" #include "base/object_ptr.h" -#include "data/data_file_origin.h" +#include "base/flags.h" namespace Lottie { class SinglePlayer; @@ -76,14 +76,8 @@ protected: callback(); } } - void mousePressEvent(QMouseEvent *e) override { - e->accept(); - } - void resizeEvent(QResizeEvent *e) override { - if (_resizedCallback) { - _resizedCallback(); - } - } + void mousePressEvent(QMouseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; virtual void doSetInnerFocus() { setFocus(); } diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 0a4ef73d3..0a2562d6b 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "data/data_session.h" #include "main/main_session.h" +#include "base/crc32hash.h" #include "ui/ui_utility.h" #include "apiwrap.h" #include "mainwindow.h" @@ -42,7 +43,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Window { namespace { -constexpr auto kInactivePressTimeout = crl::time(200); constexpr auto kSaveWindowPositionTimeout = crl::time(1000); } // namespace @@ -158,7 +158,6 @@ MainWindow::MainWindow(not_null controller) } _isActiveTimer.setCallback([this] { updateIsActive(0); }); - _inactivePressTimer.setCallback([this] { setInactivePress(false); }); } Main::Account &MainWindow::account() const { @@ -446,8 +445,8 @@ void MainWindow::setTitleVisible(bool visible) { } int32 MainWindow::screenNameChecksum(const QString &name) const { - auto bytes = name.toUtf8(); - return hashCrc32(bytes.constData(), bytes.size()); + const auto bytes = name.toUtf8(); + return base::crc32(bytes.constData(), bytes.size()); } void MainWindow::setPositionInited() { @@ -665,15 +664,6 @@ void MainWindow::launchDrag(std::unique_ptr data) { } } -void MainWindow::setInactivePress(bool inactive) { - _wasInactivePress = inactive; - if (_wasInactivePress) { - _inactivePressTimer.callOnce(kInactivePressTimeout); - } else { - _inactivePressTimer.cancel(); - } -} - MainWindow::~MainWindow() = default; } // namespace Window diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index e908d0c10..739520329 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -43,10 +43,6 @@ public: } Main::Account &account() const; Window::SessionController *sessionController() const; - void setInactivePress(bool inactive); - bool wasInactivePress() const { - return _wasInactivePress; - } bool hideNoQuit(); @@ -187,8 +183,6 @@ private: bool _isActive = false; base::Timer _isActiveTimer; - bool _wasInactivePress = false; - base::Timer _inactivePressTimer; base::Observable _dragFinished; rpl::event_stream<> _leaveEvents; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index f5f63c00b..54ddca477 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/platform/ui_platform_utility.h" #include "ui/text_options.h" #include "ui/emoji_config.h" #include "ui/empty_userpic.h" @@ -390,7 +391,7 @@ Widget::Widget( setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_OpaquePaintEvent); - Platform::InitOnTopPanel(this); + Ui::Platform::InitOnTopPanel(this); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim); } @@ -481,7 +482,7 @@ void Widget::addToHeight(int add) { auto newHeight = height() + add; auto newPosition = computePosition(newHeight); updateGeometry(newPosition.x(), newPosition.y(), width(), newHeight); - psUpdateOverlayed(this); + Ui::Platform::UpdateOverlayed(this); } void Widget::updateGeometry(int x, int y, int width, int height) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index dd7942825..11409f74e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/parse_helper.h" #include "base/zlib_help.h" #include "base/unixtime.h" +#include "base/crc32hash.h" #include "data/data_session.h" #include "main/main_account.h" // Account::sessionValue. #include "ui/image/image.h" @@ -353,7 +354,7 @@ bool LoadTheme( cache->colors = style::main_palette::save(); } cache->paletteChecksum = style::palette::Checksum(); - cache->contentChecksum = hashCrc32(content.constData(), content.size()); + cache->contentChecksum = base::crc32(content.constData(), content.size()); } return true; } @@ -364,7 +365,7 @@ bool InitializeFromCache( if (cache.paletteChecksum != style::palette::Checksum()) { return false; } - if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) { + if (cache.contentChecksum != base::crc32(content.constData(), content.size())) { return false; } @@ -1220,7 +1221,7 @@ void KeepFromEditor( auto &object = saved.object; cache.colors = style::main_palette::save(); cache.paletteChecksum = style::palette::Checksum(); - cache.contentChecksum = hashCrc32(content.constData(), content.size()); + cache.contentChecksum = base::crc32(content.constData(), content.size()); cache.background = themeParsed.background; cache.tiled = themeParsed.tiled; object.cloud = cloud; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 18c169f84..2b7c2b55c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/image/image_prepare.h" #include "ui/toast/toast.h" +#include "ui/special_fields.h" #include "info/profile/info_profile_button.h" #include "main/main_account.h" #include "main/main_session.h" diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index f063542a9..96109def0 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "mainwindow.h" #include "core/application.h" +#include "api/api_text_entities.h" #include "ui/text/text.h" #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" @@ -187,7 +188,7 @@ TermsLock TermsLock::FromMTP(const MTPDhelp_termsOfService &data) { bytes::make_vector(data.vid().c_dataJSON().vdata().v), TextWithEntities { TextUtilities::Clean(qs(data.vtext())), - TextUtilities::EntitiesFromMTP(data.ventities().v) }, + Api::EntitiesFromMTP(data.ventities().v) }, (minAge ? std::make_optional(minAge->v) : std::nullopt), data.is_popup() }; diff --git a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp index d20d8ae7e..4c05b8b77 100644 --- a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp +++ b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp @@ -226,7 +226,7 @@ std::vector Completer::resolve() { if (!_querySize) { return std::vector(); } - _initialList = Ui::Emoji::internal::GetReplacements(*_queryBegin); + _initialList = internal::GetReplacements(*_queryBegin); if (!_initialList) { return std::vector(); } diff --git a/Telegram/gyp/codegen.gyp b/Telegram/gyp/codegen.gyp index b2702a681..fa19de33e 100644 --- a/Telegram/gyp/codegen.gyp +++ b/Telegram/gyp/codegen.gyp @@ -55,7 +55,9 @@ 'common_executable.gypi', 'qt.gypi', ], - + 'dependencies': [ + 'lib_base.gyp:lib_base', + ], 'include_dirs': [ '<(src_loc)', ], diff --git a/Telegram/gyp/lib_base.gyp b/Telegram/gyp/lib_base.gyp index 94c09b93a..04da8e59d 100644 --- a/Telegram/gyp/lib_base.gyp +++ b/Telegram/gyp/lib_base.gyp @@ -50,6 +50,8 @@ '<(src_loc)/base/binary_guard.h', '<(src_loc)/base/build_config.h', '<(src_loc)/base/bytes.h', + '<(src_loc)/base/crc32hash.cpp', + '<(src_loc)/base/crc32hash.h', '<(src_loc)/base/concurrent_timer.cpp', '<(src_loc)/base/concurrent_timer.h', '<(src_loc)/base/flags.h', diff --git a/Telegram/gyp/lib_ui.gyp b/Telegram/gyp/lib_ui.gyp index baea31c78..9cd552281 100644 --- a/Telegram/gyp/lib_ui.gyp +++ b/Telegram/gyp/lib_ui.gyp @@ -15,9 +15,11 @@ 'includes': [ 'common.gypi', 'qt.gypi', + 'qt_moc.gypi', 'codegen_styles_rule.gypi', 'codegen_rules_ui.gypi', 'pch.gypi', + 'openssl.gypi', ], 'dependencies': [ 'codegen.gyp:codegen_emoji', @@ -42,6 +44,7 @@ 'dependent_style_files': [ ], 'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles_ui.timestamp', + 'list_sources_command': 'python <(DEPTH)/list_sources.py --input <(DEPTH)/lib_ui_sources.txt --replace src_loc=<(src_loc)', 'pch_source': '<(src_loc)/ui/ui_pch.cpp', 'pch_header': '<(src_loc)/ui/ui_pch.h', }, @@ -58,33 +61,11 @@ ], 'sources': [ '<@(style_files)', - '<(src_loc)/ui/effects/animation_value.cpp', - '<(src_loc)/ui/effects/animation_value.h', - '<(src_loc)/ui/effects/animations.cpp', - '<(src_loc)/ui/effects/animations.h', - '<(src_loc)/ui/style/style_core.cpp', - '<(src_loc)/ui/style/style_core.h', - '<(src_loc)/ui/style/style_core_color.cpp', - '<(src_loc)/ui/style/style_core_color.h', - '<(src_loc)/ui/style/style_core_direction.cpp', - '<(src_loc)/ui/style/style_core_direction.h', - '<(src_loc)/ui/style/style_core_font.cpp', - '<(src_loc)/ui/style/style_core_font.h', - '<(src_loc)/ui/style/style_core_icon.cpp', - '<(src_loc)/ui/style/style_core_icon.h', - '<(src_loc)/ui/style/style_core_scale.cpp', - '<(src_loc)/ui/style/style_core_scale.h', - '<(src_loc)/ui/style/style_core_types.cpp', - '<(src_loc)/ui/style/style_core_types.h', - '<(src_loc)/ui/widgets/buttons.cpp', - '<(src_loc)/ui/widgets/buttons.h', - '<(src_loc)/ui/abstract_button.cpp', - '<(src_loc)/ui/abstract_button.h', - '<(src_loc)/ui/painter.h', - '<(src_loc)/ui/ui_integration.h', - '<(src_loc)/ui/ui_utility.h', - '<(emoji_suggestions_loc)/emoji_suggestions.cpp', - '<(emoji_suggestions_loc)/emoji_suggestions.h', + '