diff --git a/Telegram/Resources/art/mac_setup.tiff b/Telegram/Resources/art/mac_setup.tiff new file mode 100644 index 000000000..71d835469 Binary files /dev/null and b/Telegram/Resources/art/mac_setup.tiff differ diff --git a/Telegram/Resources/art/osxsetup.tif b/Telegram/Resources/art/osxsetup.tif deleted file mode 100644 index 8603fd35f..000000000 Binary files a/Telegram/Resources/art/osxsetup.tif and /dev/null differ diff --git a/Telegram/Resources/art/osxsetup.tiff b/Telegram/Resources/art/osxsetup.tiff deleted file mode 100644 index bbd73e042..000000000 Binary files a/Telegram/Resources/art/osxsetup.tiff and /dev/null differ diff --git a/Telegram/Resources/art/osxsetup@2x.tif b/Telegram/Resources/art/osxsetup@2x.tif deleted file mode 100644 index 89a9aa09f..000000000 Binary files a/Telegram/Resources/art/osxsetup@2x.tif and /dev/null differ diff --git a/Telegram/Resources/art/osxtray.png b/Telegram/Resources/art/osxtray.png deleted file mode 100644 index fe7b2b7a4..000000000 Binary files a/Telegram/Resources/art/osxtray.png and /dev/null differ diff --git a/Telegram/Resources/art/sunrise.jpg b/Telegram/Resources/art/sunrise.jpg new file mode 100644 index 000000000..49b188bb0 Binary files /dev/null and b/Telegram/Resources/art/sunrise.jpg differ diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 48417db17..d6487ec1c 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -20,7 +20,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "colors.palette"; -using "basic_types.style"; +TextPalette { + linkFg: color; + monoFg: color; + selectBg: color; + selectOverlay: color; +} + +TextStyle { + font: font; + linkFont: font; + linkFontOver: font; + lineHeight: pixels; +} semibold: "Open Sans Semibold"; @@ -36,18 +48,22 @@ emojiPadding: 1px; lineWidth: 1px; -defaultTooltip: Tooltip { - textBg: tooltipBg; - textFg: tooltipFg; - textFont: normalFont; - textBorder: tooltipBorderFg; - textPadding: margins(5px, 2px, 5px, 2px); - - shift: point(-20px, 20px); - skip: 10px; - - widthMax: 800px; - linesMax: 12; +defaultTextPalette: TextPalette { + linkFg: windowActiveTextFg; + monoFg: windowSubTextFg; + selectBg: msgInBgSelected; + selectOverlay: msgSelectOverlay; +} +defaultTextStyle: TextStyle { + font: normalFont; + linkFont: normalFont; + linkFontOver: font(fsize underline); + lineHeight: 0px; +} +semiboldTextStyle: TextStyle(defaultTextStyle) { + font: semiboldFont; + linkFont: semiboldFont; + linkFontOver: font(fsize semibold underline); } shadowToggleDuration: 200; @@ -75,6 +91,7 @@ activeFadeOutDuration: 3000; msgMaxWidth: 430px; msgFont: font(fsize); msgNameFont: semiboldFont; +msgNameStyle: semiboldTextStyle; msgServiceFont: semiboldFont; msgServiceNameFont: semiboldFont; msgServicePhotoWidth: 100px; @@ -102,56 +119,43 @@ msgDateImgDelta: 4px; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; -defaultTextStyle: TextStyle { - linkFlags: font(fsize); - linkFlagsOver: font(fsize underline); - linkFg: windowActiveTextFg; - linkFgDown: windowActiveTextFg; - monoFg: windowSubTextFg; - selectBg: msgInBgSelected; - selectOverlay: msgSelectOverlay; - lineHeight: 0px; -} -serviceTextStyle: TextStyle(defaultTextStyle) { - linkFlags: msgServiceFont; - linkFlagsOver: font(fsize semibold underline); +messageTextStyle: defaultTextStyle; +msgDateTextStyle: defaultTextStyle; +serviceTextPalette: TextPalette(defaultTextPalette) { linkFg: msgServiceFg; - linkFgDown: msgServiceFg; monoFg: msgServiceFg; selectBg: msgServiceBgSelected; selectOverlay: msgServiceBgSelected; } -inTextStyle: TextStyle(defaultTextStyle) { +serviceTextStyle: TextStyle(defaultTextStyle) { + font: msgServiceFont; + linkFont: msgServiceFont; + linkFontOver: font(fsize semibold underline); +} +inTextPalette: TextPalette(defaultTextPalette) { monoFg: msgInMonoFg; selectBg: msgInBgSelected; selectOverlay: msgSelectOverlay; } -outTextStyle: TextStyle(defaultTextStyle) { +outTextPalette: TextPalette(defaultTextPalette) { monoFg: msgOutMonoFg; selectBg: msgOutBgSelected; selectOverlay: msgSelectOverlay; } -inFwdTextStyle: TextStyle(defaultTextStyle) { - linkFlags: semiboldFont; - linkFlagsOver: semiboldFont; +fwdTextStyle: TextStyle(semiboldTextStyle) { + linkFontOver: semiboldFont; +} +inFwdTextPalette: TextPalette(defaultTextPalette) { linkFg: msgInServiceFg; - linkFgDown: msgInServiceFg; } -outFwdTextStyle: TextStyle(inFwdTextStyle) { +outFwdTextPalette: TextPalette(defaultTextPalette) { linkFg: msgOutServiceFg; - linkFgDown: msgOutServiceFg; } -inFwdTextStyleSelected: TextStyle(inFwdTextStyle) { +inFwdTextPaletteSelected: TextPalette(defaultTextPalette) { linkFg: msgInServiceFgSelected; - linkFgDown: msgInServiceFgSelected; } -outFwdTextStyleSelected: TextStyle(inFwdTextStyle) { +outFwdTextPaletteSelected: TextPalette(defaultTextPalette) { linkFg: msgOutServiceFgSelected; - linkFgDown: msgOutServiceFgSelected; -} -mediaviewTextStyle: TextStyle(defaultTextStyle) { - linkFg: mediaviewTextLinkFg; - linkFgDown: mediaviewTextLinkFg; } mediaPadding: margins(0px, 0px, 0px, 0px); @@ -164,37 +168,13 @@ mediaUnreadSize: 7px; mediaUnreadSkip: 5px; mediaUnreadTop: 6px; -mediaInStyle: TextStyle(defaultTextStyle) { +mediaInPalette: TextPalette(defaultTextPalette) { linkFg: mediaInFg; - linkFgDown: mediaInFg; } -mediaInStyleSelected: TextStyle(defaultTextStyle) { +mediaInPaletteSelected: TextPalette(defaultTextPalette) { linkFg: mediaInFgSelected; - linkFgDown: mediaInFgSelected; } -msgFileMenuSize: size(36px, 36px); -msgFileSize: 44px; -msgFilePadding: margins(14px, 12px, 11px, 12px); -msgFileThumbSize: 72px; -msgFileThumbPadding: margins(10px, 10px, 14px, 10px); -msgFileThumbNameTop: 12px; -msgFileThumbStatusTop: 32px; -msgFileThumbLinkTop: 60px; -msgFileNameTop: 16px; -msgFileStatusTop: 37px; -msgFileMinWidth: 294px; - -msgFileOverDuration: 200; -msgFileRadialLine: 3px; - -msgVideoSize: size(320px, 240px); - -msgWaveformBar: 2px; -msgWaveformSkip: 1px; -msgWaveformMin: 2px; -msgWaveformMax: 20px; - textRectMargins: margins(-2px, -1px, -2px, -1px); searchedBarHeight: 32px; @@ -229,44 +209,6 @@ maxStickerSize: 256px; maxGifSize: 320px; maxSignatureSize: 144px; -mvThickFont: semiboldFont; -mvFont: normalFont; - -mvTextLeft: 16px; -mvTextSkip: 10px; -mvHeaderTop: 48px; -mvTextTop: 24px; -mvTextOpacity: 0.5; -mvTextOverOpacity: 1; - -mvIconOpacity: 0.45; -mvIconOverOpacity: 1; -mvControlBgOpacity: 0.3; -mvControlMargin: 0px; -mvControlSize: 90px; -mvIconSize: size(60px, 56px); - -mvWaitHide: 2000; -mvHideDuration: 1000; -mvShowDuration: 200; -mvFadeDuration: 150; - -mvDeltaFromLastAction: 5px; -mvSwipeDistance: 80px; - -mvCaptionPadding: margins(18px, 10px, 18px, 10px); -mvCaptionMargin: size(11px, 11px); -mvCaptionRadius: 2px; -mvCaptionFont: font(fsize); - -medviewSaveMsgCheck: icon {{ "mediaview_save_check", mediaviewSaveMsgFg }}; -medviewSaveMsgFont: font(16px); -medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); -medviewSaveMsgCheckPos: point(23px, 21px); -medviewSaveMsgShowing: 200; -medviewSaveMsgShown: 2000; -medviewSaveMsgHiding: 2500; - radialSize: size(50px, 50px); radialLine: 3px; radialDuration: 350; @@ -285,11 +227,13 @@ locationSize: size(320px, 240px); webPageLeft: 10px; webPageBar: 2px; webPageTitleFont: semiboldFont; +webPageTitleStyle: semiboldTextStyle; webPageTitleOutFg: historyTextOutFg; webPageTitleInFg: historyTextInFg; webPageDescriptionOutFg: historyTextOutFg; webPageDescriptionInFg: historyTextInFg; webPageDescriptionFont: normalFont; +webPageDescriptionStyle: defaultTextStyle; webPagePhotoSize: 100px; webPagePhotoDelta: 8px; @@ -315,7 +259,7 @@ inlineRowFileDescriptionTop: 23px; inlineResultsMinWidth: 64px; inlineDurationMargin: 3px; -toastFont: normalFont; +toastTextStyle: defaultTextStyle; toastMaxWidth: 480px; toastMinMargin: 13px; toastPadding: margins(19px, 13px, 19px, 12px); diff --git a/Telegram/Resources/icons/mac_tray_icon.png b/Telegram/Resources/icons/mac_tray_icon.png new file mode 100644 index 000000000..a57fb5ad7 Binary files /dev/null and b/Telegram/Resources/icons/mac_tray_icon.png differ diff --git a/Telegram/Resources/icons/mac_tray_icon@2x.png b/Telegram/Resources/icons/mac_tray_icon@2x.png new file mode 100644 index 000000000..8a954a383 Binary files /dev/null and b/Telegram/Resources/icons/mac_tray_icon@2x.png differ diff --git a/Telegram/Resources/icons/mac_window_shadow_top_left.png b/Telegram/Resources/icons/mac_window_shadow_top_left.png new file mode 100644 index 000000000..bcf9c0451 Binary files /dev/null and b/Telegram/Resources/icons/mac_window_shadow_top_left.png differ diff --git a/Telegram/Resources/icons/mac_window_shadow_top_left@2x.png b/Telegram/Resources/icons/mac_window_shadow_top_left@2x.png new file mode 100644 index 000000000..71bb8ddfa Binary files /dev/null and b/Telegram/Resources/icons/mac_window_shadow_top_left@2x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index c79ae8b07..cba51885d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -988,6 +988,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_mediaview_saved" = "Image was saved to your [c]Downloads[/c] folder"; +"lng_theme_preview_title" = "Theme Preview"; +"lng_theme_preview_generating" = "Generating color theme preview..."; +"lng_theme_preview_invalid" = "Invalid data in this theme file."; +"lng_theme_preview_apply" = "Apply this theme"; + "lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team"; "lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}"; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index de0283463..05c99a2d6 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -25,7 +25,7 @@ windowBg: #ffffff; windowFg: #000000; windowBgOver: #f1f1f1; windowBgRipple: #e5e5e5; -windowFgOver: windowFg; +windowFgOver: #000000; // windowBgOver; windowSubTextFg: #999999; windowSubTextFgOver: #919191; windowBoldFg: #222222; @@ -37,34 +37,34 @@ windowShadowFg: #000000; windowShadowFgFallback: #f1f1f1; shadowFg: #00000018; slideFadeOutBg: #0000003c; -slideFadeOutShadowFg: windowShadowFg; +slideFadeOutShadowFg: #000000; // windowShadowFgFallback; imageBg: #000000; imageBgTransparent: #ffffff; -activeButtonBg: windowBgActive; +activeButtonBg: #40a7e3; // windowFgActive; activeButtonBgOver: #39a5db; activeButtonBgRipple: #2095d0; -activeButtonFg: windowFgActive; -activeButtonFgOver: activeButtonFg; +activeButtonFg: #ffffff; // windowActiveTextFg; +activeButtonFgOver: activeButtonFgOver; activeButtonSecondaryFg: #cceeff; -activeButtonSecondaryFgOver: activeButtonSecondaryFg; +activeButtonSecondaryFgOver: activeButtonSecondaryFgOver; activeLineFg: #37a1de; activeLineFgError: #e48383; -lightButtonBg: windowBg; +lightButtonBg: #ffffff; // windowFg; lightButtonBgOver: #e3f1fa; lightButtonBgRipple: #c9e4f6; -lightButtonFg: windowActiveTextFg; -lightButtonFgOver: lightButtonFg; +lightButtonFg: #168acd; // windowShadowFg; +lightButtonFgOver: lightButtonFgOver; attentionButtonFg: #d14e4e; attentionButtonFgOver: #d14e4e; attentionButtonBgOver: #fcdfde; attentionButtonBgRipple: #f4c3c2; -outlineButtonBg: windowBg; -outlineButtonBgOver: lightButtonBgOver; -outlineButtonOutlineFg: windowBgActive; -outlineButtonBgRipple: lightButtonBgRipple; -menuBg: windowBg; -menuBgOver: windowBgOver; -menuBgRipple: windowBgRipple; +outlineButtonBg: #ffffff; // windowFg; +outlineButtonBgOver: #e3f1fa; // lightButtonBgRipple; +outlineButtonOutlineFg: #40a7e3; // windowFgActive; +outlineButtonBgRipple: #c9e4f6; // lightButtonFg; +menuBg: #ffffff; // windowFg; +menuBgOver: #f1f1f1; // windowBgRipple; +menuBgRipple: #e5e5e5; // windowFgOver; menuIconFg: #a8a8a8; menuIconFgOver: #999999; menuSubmenuArrowFg: #373737; @@ -76,25 +76,25 @@ scrollBg: #0000001a; scrollBgOver: #0000002c; smallCloseIconFg: #c7c7c7; smallCloseIconFgOver: #a3a3a3; -radialFg: windowFgActive; +radialFg: #ffffff; // windowActiveTextFg; radialBg: #00000056; -placeholderFg: windowSubTextFg; +placeholderFg: #999999; // windowSubTextFgOver; placeholderFgActive: #aaaaaa; inputBorderFg: #e0e0e0; filterInputBorderFg: #54c3f3; checkboxFg: #b3b3b3; sliderBgInactive: #e1eaef; -sliderBgActive: windowBgActive; +sliderBgActive: #40a7e3; // windowFgActive; tooltipBg: #eef2f5; tooltipFg: #5d6c80; tooltipBorderFg: #c9d1db; -titleBg: windowBgOver; +titleBg: #f1f1f1; // windowBgRipple; titleShadow: #00000003; titleButtonFg: #ababab; titleButtonBgOver: #e5e5e5; titleButtonFgOver: #9a9a9a; titleButtonCloseBgOver: #e81123; -titleButtonCloseFgOver: windowFgActive; +titleButtonCloseFgOver: #ffffff; // windowActiveTextFg; titleFgActive: #3e3c3e; titleFg: #acacac; trayCounterBg: #f23c34; @@ -103,32 +103,32 @@ trayCounterFg: #ffffff; trayCounterBgMacInvert: #ffffff; trayCounterFgMacInvert: #ffffff01; layerBg: #0000007f; -cancelIconFg: menuIconFg; -cancelIconFgOver: menuIconFgOver; -boxBg: windowBg; -boxTextFg: windowFg; +cancelIconFg: #a8a8a8; // menuIconFgOver; +cancelIconFgOver: #999999; // menuSubmenuArrowFg; +boxBg: #ffffff; // windowFg; +boxTextFg: #000000; // windowBgOver; boxTextFgGood: #4ab44a; boxTextFgError: #d84d4d; boxTitleFg: #404040; -boxSearchBg: boxBg; -boxSearchCancelIconFg: cancelIconFg; -boxSearchCancelIconFgOver: cancelIconFgOver; +boxSearchBg: #ffffff; // boxTextFg; +boxSearchCancelIconFg: #a8a8a8; // cancelIconFgOver; +boxSearchCancelIconFgOver: #999999; // boxBg; boxTitleAdditionalFg: #808080; -boxTitleCloseFg: cancelIconFg; -boxTitleCloseFgOver: cancelIconFgOver; -membersAboutLimitFg: windowSubTextFgOver; -contactsBg: windowBg; -contactsBgOver: windowBgOver; -contactsNameFg: boxTextFg; -contactsStatusFg: windowSubTextFg; -contactsStatusFgOver: windowSubTextFgOver; -contactsStatusFgOnline: windowActiveTextFg; -photoCropFadeBg: layerBg; +boxTitleCloseFg: #a8a8a8; // cancelIconFgOver; +boxTitleCloseFgOver: #999999; // boxBg; +membersAboutLimitFg: #919191; // windowBoldFg; +contactsBg: #ffffff; // windowFg; +contactsBgOver: #f1f1f1; // windowBgRipple; +contactsNameFg: #000000; // boxTextFgGood; +contactsStatusFg: #999999; // windowSubTextFgOver; +contactsStatusFgOver: #919191; // windowBoldFg; +contactsStatusFgOnline: #168acd; // windowShadowFg; +photoCropFadeBg: #0000007f; // cancelIconFg; photoCropPointFg: #ffffff7f; -introBg: windowBg; -introTitleFg: windowBoldFg; -introDescriptionFg: windowSubTextFg; -introErrorFg: windowSubTextFg; +introBg: #ffffff; // windowFg; +introTitleFg: windowBoldFgOver; +introDescriptionFg: #999999; // windowSubTextFgOver; +introErrorFg: #999999; // windowSubTextFgOver; introCoverTopBg: #0f89d0; introCoverBottomBg: #39b0f0; introCoverIconsFg: #5ec6ff; @@ -136,83 +136,83 @@ introCoverPlaneTrace: #5ec6ff69; introCoverPlaneInner: #c6d8e8; introCoverPlaneOuter: #a1bed4; introCoverPlaneTop: #ffffff; -dialogsMenuIconFg: menuIconFg; -dialogsMenuIconFgOver: menuIconFgOver; -dialogsBg: windowBg; -dialogsNameFg: windowBoldFg; -dialogsChatIconFg: dialogsNameFg; -dialogsDateFg: windowSubTextFg; -dialogsTextFg: windowSubTextFg; -dialogsTextFgService: windowActiveTextFg; +dialogsMenuIconFg: #a8a8a8; // menuIconFgOver; +dialogsMenuIconFgOver: #999999; // menuSubmenuArrowFg; +dialogsBg: #ffffff; // windowFg; +dialogsNameFg: windowBoldFgOver; +dialogsChatIconFg: dialogsChatIconFg; +dialogsDateFg: #999999; // windowSubTextFgOver; +dialogsTextFg: #999999; // windowSubTextFgOver; +dialogsTextFgService: #168acd; // windowShadowFg; dialogsDraftFg: #dd4b39; -dialogsVerifiedIconBg: windowBgActive; -dialogsVerifiedIconFg: windowFgActive; +dialogsVerifiedIconBg: #40a7e3; // windowFgActive; +dialogsVerifiedIconFg: #ffffff; // windowActiveTextFg; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; -dialogsUnreadBg: windowBgActive; +dialogsUnreadBg: #40a7e3; // windowFgActive; dialogsUnreadBgMuted: #bbbbbb; -dialogsUnreadFg: windowFgActive; -dialogsBgOver: windowBgOver; -dialogsNameFgOver: windowBoldFgOver; -dialogsChatIconFgOver: dialogsNameFgOver; -dialogsDateFgOver: windowSubTextFgOver; -dialogsTextFgOver: windowSubTextFgOver; -dialogsTextFgServiceOver: dialogsTextFgService; -dialogsDraftFgOver: dialogsDraftFg; -dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; -dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; -dialogsSendingIconFgOver: dialogsSendingIconFg; -dialogsSentIconFgOver: dialogsSentIconFg; -dialogsUnreadBgOver: dialogsUnreadBg; -dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; -dialogsUnreadFgOver: dialogsUnreadFg; +dialogsUnreadFg: #ffffff; // windowActiveTextFg; +dialogsBgOver: #f1f1f1; // windowBgRipple; +dialogsNameFgOver: #222222; // windowBgActive; +dialogsChatIconFgOver: dialogsChatIconFgOver; +dialogsDateFgOver: #919191; // windowBoldFg; +dialogsTextFgOver: #919191; // windowBoldFg; +dialogsTextFgServiceOver: #168acd; // dialogsDraftFg; +dialogsDraftFgOver: #dd4b39; // dialogsVerifiedIconBg; +dialogsVerifiedIconBgOver: #40a7e3; // dialogsVerifiedIconFg; +dialogsVerifiedIconFgOver: #ffffff; // dialogsSendingIconFg; +dialogsSendingIconFgOver: #c1c1c1; // dialogsSentIconFg; +dialogsSentIconFgOver: #5dc452; // dialogsUnreadBg; +dialogsUnreadBgOver: #40a7e3; // dialogsUnreadBgMuted; +dialogsUnreadBgMutedOver: #bbbbbb; // dialogsUnreadFg; +dialogsUnreadFgOver: #ffffff; // dialogsBgOver; dialogsBgActive: #419fd9; -dialogsNameFgActive: windowFgActive; -dialogsChatIconFgActive: dialogsNameFgActive; -dialogsDateFgActive: windowFgActive; -dialogsTextFgActive: windowFgActive; -dialogsTextFgServiceActive: dialogsTextFgActive; +dialogsNameFgActive: #ffffff; // windowActiveTextFg; +dialogsChatIconFgActive: dialogsChatIconFgActive; +dialogsDateFgActive: #ffffff; // windowActiveTextFg; +dialogsTextFgActive: #ffffff; // windowActiveTextFg; +dialogsTextFgServiceActive: dialogsTextFgServiceActive; dialogsDraftFgActive: #c6e1f7; -dialogsVerifiedIconBgActive: dialogsTextFgActive; -dialogsVerifiedIconFgActive: dialogsBgActive; +dialogsVerifiedIconBgActive: dialogsTextFgServiceActive; +dialogsVerifiedIconFgActive: #419fd9; // dialogsNameFgActive; dialogsSendingIconFgActive: #ffffff99; -dialogsSentIconFgActive: dialogsTextFgActive; -dialogsUnreadBgActive: dialogsTextFgActive; -dialogsUnreadBgMutedActive: dialogsDraftFgActive; -dialogsUnreadFgActive: dialogsBgActive; -dialogsForwardBg: dialogsBgActive; -dialogsForwardFg: dialogsNameFgActive; -searchedBarBg: windowBgOver; -searchedBarBorder: shadowFg; -searchedBarFg: windowSubTextFgOver; -topBarBg: windowBg; -emojiPanBg: windowBg; -emojiPanCategories: #f7f7f7; // windowBg; -emojiPanHeaderFg: windowSubTextFg; -emojiPanHeaderBg: #fffffff2; // emojiPanBg; +dialogsSentIconFgActive: dialogsTextFgServiceActive; +dialogsUnreadBgActive: dialogsTextFgServiceActive; +dialogsUnreadBgMutedActive: #c6e1f7; // dialogsVerifiedIconBgActive; +dialogsUnreadFgActive: #419fd9; // dialogsNameFgActive; +dialogsForwardBg: #419fd9; // dialogsNameFgActive; +dialogsForwardFg: dialogsChatIconFgActive; +searchedBarBg: #f1f1f1; // windowBgRipple; +searchedBarBorder: #00000018; // slideFadeOutBg; +searchedBarFg: #919191; // windowBoldFg; +topBarBg: #ffffff; // windowFg; +emojiPanBg: #ffffff; // windowFg; +emojiPanCategories: #f7f7f7; // windowFg; +emojiPanHeaderFg: #999999; // windowSubTextFgOver; +emojiPanHeaderBg: #fffffff2; // emojiPanCategories; stickerPanDeleteBg: #000000cc; -stickerPanDeleteFg: windowFgActive; +stickerPanDeleteFg: #ffffff; // windowActiveTextFg; stickerPreviewBg: #ffffffb0; -historyTextInFg: windowFg; -historyTextOutFg: windowFg; -historyCaptionInFg: historyTextInFg; -historyCaptionOutFg: historyTextOutFg; -historyFileNameInFg: historyTextInFg; -historyFileNameOutFg: historyTextOutFg; -historyOutIconFg: dialogsSentIconFg; +historyTextInFg: #000000; // windowBgOver; +historyTextOutFg: #000000; // windowBgOver; +historyCaptionInFg: historyTextOutFg; +historyCaptionOutFg: historyCaptionInFg; +historyFileNameInFg: historyTextOutFg; +historyFileNameOutFg: historyCaptionInFg; +historyOutIconFg: #5dc452; // dialogsUnreadBg; historyOutIconFgSelected: #4da79f; -historyIconFgInverted: windowFgActive; +historyIconFgInverted: #ffffff; // windowActiveTextFg; historySendingOutIconFg: #98d292; historySendingInIconFg: #a0adb5; historySendingInvertedIconFg: #ffffffc8; historySystemBg: #89a0b47f; historySystemBgSelected: #bbc8d4a2; -historySystemFg: windowFgActive; +historySystemFg: #ffffff; // windowActiveTextFg; historyUnreadBarBg: #fcfbfa; -historyUnreadBarBorder: shadowFg; +historyUnreadBarBorder: #00000018; // slideFadeOutBg; historyUnreadBarFg: #538bb4; historyForwardChooseBg: #0000004c; -historyForwardChooseFg: windowFgActive; +historyForwardChooseFg: #ffffff; // windowActiveTextFg; historyPeer1NameFg: #c03d33; historyPeer1UserpicBg: #ed9482; historyPeer1UserpicFg: #d3644b; @@ -222,7 +222,7 @@ historyPeer2UserpicFg: #75c057; historyPeer3NameFg: #d09306; historyPeer3UserpicBg: #efd289; historyPeer3UserpicFg: #e4a861; -historyPeer4NameFg: windowActiveTextFg; +historyPeer4NameFg: #168acd; // windowShadowFg; historyPeer4UserpicBg: #8fbfe9; historyPeer4UserpicFg: #649fd3; historyPeer5NameFg: #8544d6; @@ -241,14 +241,14 @@ historyScrollBarBg: #556e837a; historyScrollBarBgOver: #556e83bc; historyScrollBg: #556e834c; historyScrollBgOver: #556e836b; -msgInBg: windowBg; +msgInBg: #ffffff; // windowFg; msgInBgSelected: #c2dcf2; msgOutBg: #effdde; msgOutBgSelected: #b7dbdb; msgSelectOverlay: #358cd44c; msgStickerOverlay: #358cd47f; -msgInServiceFg: windowActiveTextFg; -msgInServiceFgSelected: windowActiveTextFg; +msgInServiceFg: #168acd; // windowShadowFg; +msgInServiceFgSelected: #168acd; // windowShadowFg; msgOutServiceFg: #3a8e26; msgOutServiceFgSelected: #367570; msgInShadow: #748ea229; @@ -259,25 +259,25 @@ msgInDateFg: #a0acb6; msgInDateFgSelected: #6a9cc5; msgOutDateFg: #6cc264; msgOutDateFgSelected: #50a79c; -msgServiceFg: windowFgActive; +msgServiceFg: #ffffff; // windowActiveTextFg; msgServiceBg: #556e837f; msgServiceBgSelected: #8ca0b3a2; -msgInReplyBarColor: activeLineFg; -msgInReplyBarSelColor: activeLineFg; -msgOutReplyBarColor: historyOutIconFg; -msgOutReplyBarSelColor: historyOutIconFgSelected; -msgImgReplyBarColor: msgServiceFg; +msgInReplyBarColor: #37a1de; // activeLineFgError; +msgInReplyBarSelColor: #37a1de; // activeLineFgError; +msgOutReplyBarColor: #5dc452; // historyOutIconFgSelected; +msgOutReplyBarSelColor: #4da79f; // historyIconFgInverted; +msgImgReplyBarColor: #ffffff; // msgServiceBg; msgInMonoFg: #4e7391; msgOutMonoFg: #469165; -msgDateImgFg: msgServiceFg; +msgDateImgFg: #ffffff; // msgServiceBg; msgDateImgBg: #00000054; msgDateImgBgOver: #00000074; msgDateImgBgSelected: #1c4a7187; -msgFileThumbLinkInFg: lightButtonFg; -msgFileThumbLinkInFgSelected: lightButtonFgOver; +msgFileThumbLinkInFg: lightButtonFgOver; +msgFileThumbLinkInFgSelected: #168acd; // attentionButtonFg; msgFileThumbLinkOutFg: #5eba5b; msgFileThumbLinkOutFgSelected: #31a298; -msgFileInBg: windowBgActive; +msgFileInBg: #40a7e3; // windowFgActive; msgFileInBgOver: #4eade3; msgFileInBgSelected: #51a3d3; msgFileOutBg: #78c67f; @@ -299,7 +299,7 @@ msgFile4Bg: #efc274; msgFile4BgDark: #e6a561; msgFile4BgOver: #dc9c5a; msgFile4BgSelected: #b19d84; -msgWaveformInActive: windowBgActive; +msgWaveformInActive: #40a7e3; // windowFgActive; msgWaveformInActiveSelected: #51a3d3; msgWaveformInInactive: #d4dee6; msgWaveformInInactiveSelected: #9cc1e1; @@ -308,81 +308,81 @@ msgWaveformOutActiveSelected: #6badad; msgWaveformOutInactive: #b3e2b4; msgWaveformOutInactiveSelected: #91c3c3; msgBotKbOverBgAdd: #ffffff20; -msgBotKbIconFg: msgServiceFg; +msgBotKbIconFg: #ffffff; // msgServiceBg; msgBotKbRippleBg: #00000020; -mediaInFg: msgInDateFg; -mediaInFgSelected: msgInDateFgSelected; -mediaOutFg: msgOutDateFg; -mediaOutFgSelected: msgOutDateFgSelected; +mediaInFg: #a0acb6; // msgInDateFgSelected; +mediaInFgSelected: #6a9cc5; // msgOutDateFg; +mediaOutFg: #6cc264; // msgOutDateFgSelected; +mediaOutFgSelected: #50a79c; // msgServiceFg; youtubePlayIconBg: #e83131c8; -youtubePlayIconFg: windowFgActive; +youtubePlayIconFg: #ffffff; // windowActiveTextFg; videoPlayIconBg: #0000007f; videoPlayIconFg: #ffffff; toastBg: #000000b2; -toastFg: windowFgActive; -reportSpamBg: emojiPanHeaderBg; -reportSpamFg: windowFg; +toastFg: #ffffff; // windowActiveTextFg; +reportSpamBg: #fffffff2; // stickerPanDeleteBg; +reportSpamFg: #000000; // windowBgOver; historyToDownShadow: #00000040; -historyComposeAreaBg: msgInBg; -historyComposeAreaFg: historyTextInFg; -historyComposeAreaFgService: msgInDateFg; -historyComposeIconFg: menuIconFg; -historyComposeIconFgOver: menuIconFgOver; -historySendIconFg: windowBgActive; -historySendIconFgOver: windowBgActive; -historyPinnedBg: historyComposeAreaBg; -historyReplyBg: historyComposeAreaBg; -historyReplyCancelFg: cancelIconFg; -historyReplyCancelFgOver: cancelIconFgOver; -historyComposeButtonBg: historyComposeAreaBg; -historyComposeButtonBgOver: windowBgOver; -historyComposeButtonBgRipple: windowBgRipple; +historyComposeAreaBg: #ffffff; // msgInBgSelected; +historyComposeAreaFg: historyTextOutFg; +historyComposeAreaFgService: #a0acb6; // msgInDateFgSelected; +historyComposeIconFg: #a8a8a8; // menuIconFgOver; +historyComposeIconFgOver: #999999; // menuSubmenuArrowFg; +historySendIconFg: #40a7e3; // windowFgActive; +historySendIconFgOver: #40a7e3; // windowFgActive; +historyPinnedBg: #ffffff; // historyComposeAreaFg; +historyReplyBg: #ffffff; // historyComposeAreaFg; +historyReplyCancelFg: #a8a8a8; // cancelIconFgOver; +historyReplyCancelFgOver: #999999; // boxBg; +historyComposeButtonBg: #ffffff; // historyComposeAreaFg; +historyComposeButtonBgOver: #f1f1f1; // windowBgRipple; +historyComposeButtonBgRipple: #e5e5e5; // windowFgOver; overviewCheckBg: #00000040; -overviewCheckFg: windowBg; -overviewCheckFgActive: windowBg; +overviewCheckFg: #ffffff; // windowFg; +overviewCheckFgActive: #ffffff; // windowFg; overviewPhotoSelectOverlay: #40ace333; profileStatusFgOver: #7c99b2; -notificationsBoxMonitorFg: windowFg; -notificationsBoxScreenBg: dialogsBgActive; -notificationSampleUserpicFg: windowBgActive; -notificationSampleCloseFg: #d7d7d7; // windowSubTextFg; -notificationSampleTextFg: #d7d7d7; // windowSubTextFg; -notificationSampleNameFg: #939393; // windowSubTextFg; -mainMenuBg: windowBg; -mainMenuCoverBg: dialogsBgActive; -mainMenuCoverFg: windowFgActive; -mediaPlayerBg: windowBg; -mediaPlayerActiveFg: windowBgActive; -mediaPlayerInactiveFg: sliderBgInactive; +notificationsBoxMonitorFg: #000000; // windowBgOver; +notificationsBoxScreenBg: #419fd9; // dialogsNameFgActive; +notificationSampleUserpicFg: #40a7e3; // windowFgActive; +notificationSampleCloseFg: #d7d7d7; // windowSubTextFgOver; +notificationSampleTextFg: #d7d7d7; // windowSubTextFgOver; +notificationSampleNameFg: #939393; // windowSubTextFgOver; +mainMenuBg: #ffffff; // windowFg; +mainMenuCoverBg: #419fd9; // dialogsNameFgActive; +mainMenuCoverFg: #ffffff; // windowActiveTextFg; +mediaPlayerBg: #ffffff; // windowFg; +mediaPlayerActiveFg: #40a7e3; // windowFgActive; +mediaPlayerInactiveFg: #e1eaef; // sliderBgActive; mediaPlayerDisabledFg: #9dd1ef; -mediaviewFileBg: windowBg; -mediaviewFileNameFg: windowFg; -mediaviewFileSizeFg: windowSubTextFg; +mediaviewFileBg: #ffffff; // windowFg; +mediaviewFileNameFg: #000000; // windowBgOver; +mediaviewFileSizeFg: #999999; // windowSubTextFgOver; mediaviewFileRedCornerFg: #d55959; mediaviewFileYellowCornerFg: #e8a659; mediaviewFileGreenCornerFg: #49a957; mediaviewFileBlueCornerFg: #599dcf; -mediaviewFileExtFg: activeButtonFg; +mediaviewFileExtFg: activeButtonFgOver; mediaviewMenuBg: #383838; mediaviewMenuBgOver: #505050; mediaviewMenuBgRipple: #676767; -mediaviewMenuFg: windowFgActive; +mediaviewMenuFg: #ffffff; // windowActiveTextFg; mediaviewBg: #222222eb; -mediaviewVideoBg: imageBg; +mediaviewVideoBg: #000000; // imageBgTransparent; mediaviewControlBg: #0000003c; -mediaviewControlFg: windowFgActive; +mediaviewControlFg: #ffffff; // windowActiveTextFg; mediaviewCaptionBg: #11111180; -mediaviewCaptionFg: mediaviewControlFg; +mediaviewCaptionFg: #ffffff; // mediaviewCaptionBg; mediaviewTextLinkFg: #91d9ff; -mediaviewSaveMsgBg: toastBg; -mediaviewSaveMsgFg: toastFg; +mediaviewSaveMsgBg: #000000b2; // toastFg; +mediaviewSaveMsgFg: #ffffff; // reportSpamBg; mediaviewPlaybackActive: #c7c7c7; mediaviewPlaybackInactive: #252525; mediaviewPlaybackActiveOver: #ffffff; mediaviewPlaybackInactiveOver: #474747; mediaviewPlaybackProgressFg: #ffffffc7; -mediaviewPlaybackIconFg: mediaviewPlaybackActive; -mediaviewPlaybackIconFgOver: mediaviewPlaybackActiveOver; +mediaviewPlaybackIconFg: #c7c7c7; // mediaviewPlaybackInactive; +mediaviewPlaybackIconFgOver: #ffffff; // mediaviewPlaybackInactiveOver; mediaviewTransparentBg: #ffffff; mediaviewTransparentFg: #cccccc; -notificationBg: windowBg; +notificationBg: #ffffff; // windowFg; diff --git a/Telegram/Resources/telegram.qrc b/Telegram/Resources/telegram.qrc index 1ab21fca9..6fece6a45 100644 --- a/Telegram/Resources/telegram.qrc +++ b/Telegram/Resources/telegram.qrc @@ -8,6 +8,7 @@ art/bg_initial.png art/icon256.png art/iconbig256.png + art/sunrise.jpg qmime/freedesktop.org.xml diff --git a/Telegram/Resources/telegram_mac.qrc b/Telegram/Resources/telegram_mac.qrc index 8feea0191..03585ec03 100644 --- a/Telegram/Resources/telegram_mac.qrc +++ b/Telegram/Resources/telegram_mac.qrc @@ -1,5 +1,2 @@ - - art/osxtray.png - diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 5c1db03dc..282bef664 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2163,7 +2163,7 @@ namespace { text = d.second; } - void prepareCorners(RoundCorners index, int32 radius, const style::color &color, const style::color *shadow = 0, QImage *cors = 0) { + void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) { int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor(); QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; { @@ -2178,7 +2178,7 @@ namespace { p.setBrush((*shadow)->b); p.drawRoundedRect(0, s, r * 3, r * 3, r, r); } - p.setBrush(color->b); + p.setBrush(brush); p.drawRoundedRect(0, 0, r * 3, r * 3, r, r); } if (!cors) cors = localCors; @@ -2212,14 +2212,13 @@ namespace { } void createCorners() { - style::color white = { 255, 255, 255, 255 }; QImage mask[4]; - prepareCorners(LargeMaskCorners, msgRadius(), white, nullptr, mask); + prepareCorners(LargeMaskCorners, msgRadius(), QColor(255, 255, 255), nullptr, mask); for (int i = 0; i < 4; ++i) { ::cornersMaskLarge[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); ::cornersMaskLarge[i]->setDevicePixelRatio(cRetinaFactor()); } - prepareCorners(SmallMaskCorners, st::buttonRadius, white, nullptr, mask); + prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask); for (int i = 0; i < 4; ++i) { ::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); ::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor()); @@ -2295,7 +2294,7 @@ namespace { using Update = Window::Theme::BackgroundUpdate; static auto subscription = Window::Theme::Background()->add_subscription([](const Update &update) { - if (update.type == Update::Type::TestingTheme) { + if (update.paletteChanged()) { clearCorners(); createCorners(); @@ -2770,7 +2769,7 @@ namespace { if (radius == ImageRoundRadius::Large) { complexAdjustRect(corners, rect, overlayParts); } - roundRect(p, rect, textstyleCurrent()->selectOverlay, overlayCorners, nullptr, overlayParts); + roundRect(p, rect, p.textPalette().selectOverlay, overlayCorners, nullptr, overlayParts); } void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners) { @@ -2787,7 +2786,8 @@ namespace { } return ::cornersMaskSmall; } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) { + + 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(); if (w < 2 * cornerWidth || h < 2 * cornerHeight) return; @@ -2831,11 +2831,11 @@ namespace { } } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow, RectParts parts) { + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow, RectParts parts) { roundRect(p, x, y, w, h, bg, ::corners[index], shadow, parts); } - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts) { + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, RoundCorners index, RectParts parts) { auto &corner = ::corners[index]; auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor(); auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor(); @@ -2852,7 +2852,7 @@ namespace { } } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts) { + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts) { auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24); auto i = cornersMap.find(colorKey); if (i == cornersMap.cend()) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index bb8bb0fac..fdd9d7b85 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -307,16 +307,16 @@ namespace App { void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners); QImage **cornersMask(ImageRoundRadius radius); - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); - inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) { + 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); } - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full); - inline void roundShadow(Painter &p, const QRect &rect, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full) { + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, RoundCorners index, RectParts parts = RectPart::Full); + inline void roundShadow(Painter &p, const QRect &rect, style::color shadow, RoundCorners index, RectParts parts = RectPart::Full) { return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, index, parts); } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full); - inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) { + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full); + inline void roundRect(Painter &p, const QRect &rect, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) { return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts); } diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 698842594..d345ad6c6 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_instance.h" #include "window/notifications_manager.h" #include "history/history_location_manager.h" +#include "core/task_queue.h" namespace { @@ -364,6 +365,10 @@ void Application::closeApplication() { #endif // !TDESKTOP_DISABLE_AUTOUPDATE } +void Application::onMainThreadTask() { + base::TaskQueue::ProcessMainTasks(); +} + #ifndef TDESKTOP_DISABLE_AUTOUPDATE void Application::updateCheck() { startUpdateCheck(false); diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index 6dd3b6e5c..c37dfe0f1 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -48,6 +48,8 @@ public slots: void startApplication(); // will be done in exec() void closeApplication(); // will be done in aboutToQuit() + void onMainThreadTask(); + private: typedef QPair LocalClient; typedef QList LocalClients; diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index 1c59b885f..f8c000ecf 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -34,9 +34,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org AboutBox::AboutBox(QWidget *parent) : _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink) -, _text1(this, lang(lng_about_text_1), Ui::FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) -, _text2(this, lang(lng_about_text_2), Ui::FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) -, _text3(this, st::aboutLabel, st::aboutTextStyle) { +, _text1(this, lang(lng_about_text_1), Ui::FlatLabel::InitType::Rich, st::aboutLabel) +, _text2(this, lang(lng_about_text_2), Ui::FlatLabel::InitType::Rich, st::aboutLabel) +, _text3(this, st::aboutLabel) { } void AboutBox::prepare() { diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 5e4b7e3d5..037a5e19d 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -427,8 +427,8 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing) , _public(this, qsl("channel_privacy"), 0, lang(channel->isMegagroup() ? lng_create_public_group_title : lng_create_public_channel_title), true, st::defaultBoxCheckbox) , _private(this, qsl("channel_privacy"), 1, lang(channel->isMegagroup() ? lng_create_private_group_title : lng_create_private_channel_title), false, st::defaultBoxCheckbox) , _aboutPublicWidth(width() - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultBoxCheckbox.textPosition.x()) -, _aboutPublic(st::normalFont, lang(channel->isMegagroup() ? lng_create_public_group_about : lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth) -, _aboutPrivate(st::normalFont, lang(channel->isMegagroup() ? lng_create_private_group_about : lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth) +, _aboutPublic(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_public_group_about : lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth) +, _aboutPrivate(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_private_group_about : lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth) , _link(this, st::setupChannelLink, QString(), channel->username, true) { } @@ -1219,9 +1219,9 @@ void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selecte p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), lang(lng_channels_too_much_public_revoke), _revokeWidth); p.setPen(st::contactsStatusFg); - textstyleSet(&st::revokePublicLinkStatusStyle); + p.setTextPalette(st::revokePublicLinkStatusPalette); row.status.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsStatusTop, namew, width()); - textstyleRestore(); + p.restoreTextPalette(); } void RevokePublicLinkBox::getPublicDone(const MTPmessages_Chats &result) { @@ -1232,10 +1232,8 @@ void RevokePublicLinkBox::getPublicDone(const MTPmessages_Chats &result) { ChatRow row; row.peer = peer; - row.name.setText(st::contactsNameFont, peer->name, _textNameOptions); - textstyleSet(&st::revokePublicLinkStatusStyle); - row.status.setText(st::normalFont, qsl("telegram.me/") + textcmdLink(1, peer->userName()), _textDlgOptions); - textstyleRestore(); + row.name.setText(st::contactsNameStyle, peer->name, _textNameOptions); + row.status.setText(st::defaultTextStyle, qsl("telegram.me/") + textcmdLink(1, peer->userName()), _textDlgOptions); _rows.push_back(std_::move(row)); } } diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 00811a506..24c3a5d40 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -67,8 +67,7 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) subscribe(FileDownload::ImageLoaded(), [this] { update(); }); using Update = Window::Theme::BackgroundUpdate; subscribe(Window::Theme::Background(), [this](const Update &update) { - if (update.type == Update::Type::TestingTheme - || update.type == Update::Type::RevertingTheme) { + if (update.paletteChanged()) { _check->invalidateCache(); } }); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 18ccce2e8..80f83f51e 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -30,19 +30,9 @@ boxButtonFont: font(boxFontSize semibold); defaultBoxButton: RoundButton(defaultLightButton) { width: -24px; height: 36px; - padding: margins(0px, 0px, 0px, 0px); - - textTop: 8px; - font: boxButtonFont; - - ripple: RippleAnimation(defaultRippleAnimation) { - color: lightButtonBgRipple; - } } -cancelBoxButton: defaultBoxButton; - attentionBoxButton: RoundButton(defaultBoxButton) { textFg: attentionButtonFg; textFgOver: attentionButtonFgOver; @@ -118,8 +108,12 @@ boxMediumSkip: 20px; boxButtonPadding: margins(8px, 12px, 13px, 12px); boxLayerButtonPadding: margins(8px, 8px, 8px, 8px); boxLabel: FlatLabel(defaultFlatLabel) { - font: font(boxFontSize); align: align(topleft); + style: TextStyle(defaultTextStyle) { + font: font(boxFontSize); + linkFont: font(boxFontSize); + linkFontOver: font(boxFontSize underline); + } } countryRowHeight: 36px; @@ -153,14 +147,17 @@ cropSkip: 13px; cropMinSize: 20px; confirmInviteTitle: FlatLabel(defaultFlatLabel) { - font: font(16px semibold); align: align(center); width: 320px; maxHeight: 24px; textFg: windowBoldFg; + style: TextStyle(defaultTextStyle) { + font: font(16px semibold); + linkFont: font(16px semibold); + linkFontOver: font(16px semibold underline); + } } -confirmInviteStatus: FlatLabel(defaultFlatLabel) { - font: font(boxFontSize); +confirmInviteStatus: FlatLabel(boxLabel) { align: align(center); width: 320px; maxHeight: 20px; @@ -174,7 +171,6 @@ confirmInviteUserHeight: 84px; confirmInviteUserPhotoSize: 56px; confirmInviteUserPhotoTop: 166px; confirmInviteUserName: FlatLabel(defaultFlatLabel) { - font: normalFont; align: align(center); width: 66px; maxHeight: 20px; @@ -187,16 +183,12 @@ confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) { confirmPhoneCodeField: InputField(defaultInputField) { } -revokePublicLinkStatusStyle: TextStyle(defaultTextStyle) { +revokePublicLinkStatusPalette: TextPalette(defaultTextPalette) { linkFg: contactsStatusFgOnline; - linkFgDown: contactsStatusFgOnline; - linkFlagsOver: font(fsize); } aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) { - font: normalFont; align: align(topleft); width: 320px; - textFg: windowFg; } contactUserIcon: icon {{ "add_contact_user", menuIconFg }}; @@ -228,7 +220,11 @@ contactPhoneSkip: 30px; contactsPhotoSize: 42px; contactsPadding: margins(16px, 7px, 16px, 7px); contactsNameTop: 2px; -contactsNameFont: semiboldFont; +contactsNameStyle: TextStyle(defaultTextStyle) { + font: semiboldFont; + linkFont: semiboldFont; + linkFontOver: semiboldFont; +} contactsStatusTop: 23px; contactsStatusFont: font(fsize); contactsCheckPosition: point(8px, 16px); @@ -255,7 +251,7 @@ contactsMultiSelect: MultiSelect { padding: margins(6px, 7px, 12px, 0px); maxWidth: 128px; height: 32px; - font: normalFont; + style: defaultTextStyle; textBg: contactsBgOver; textFg: windowFg; textActiveBg: activeButtonBg; @@ -345,7 +341,11 @@ sharePhotoCheckbox: RoundImageCheckbox(contactsPhotoCheckbox) { imageRadius: 28px; imageSmallRadius: 24px; } -shareNameFont: font(11px); +shareNameStyle: TextStyle(defaultTextStyle) { + font: font(11px); + linkFont: font(11px); + linkFontOver: font(11px); +} shareNameFg: windowFg; shareNameActiveFg: windowActiveTextFg; shareNameTop: 6px; @@ -460,12 +460,11 @@ aboutVersionLink: LinkButton(defaultLinkButton) { aboutTextTop: 34px; aboutSkip: 14px; aboutLabel: FlatLabel(defaultFlatLabel) { - font: normalFont; width: 330px; align: align(topleft); -} -aboutTextStyle: TextStyle(defaultTextStyle) { - lineHeight: 22px; + style: TextStyle(defaultTextStyle) { + lineHeight: 22px; + } } autoDownloadTopDelta: 10px; @@ -513,10 +512,16 @@ backgroundScroll: ScrollArea(boxLayerScroll) { deltab: 10px; } +passcodeTextStyle: TextStyle(defaultTextStyle) { + lineHeight: 20px; +} + usernamePadding: margins(23px, 6px, 21px, 12px); usernameSkip: 49px; -usernameTextStyle: TextStyle(defaultTextStyle) { - lineHeight: 20px; +usernameTextStyle: TextStyle(passcodeTextStyle) { + font: boxTextFont; + linkFont: boxTextFont; + linkFontOver: font(boxFontSize underline); } usernameDefaultFg: windowSubTextFg; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 646591bc8..f11226cf3 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -102,18 +102,17 @@ ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString } base::lambda ConfirmBox::generateInformCallback(const base::lambda_copy &closedCallback) { - return base::lambda_guarded(this, [this, closedCallback] { + auto callback = closedCallback; + return base::lambda_guarded(this, [this, callback] { closeBox(); - if (closedCallback) { - closedCallback(); + if (callback) { + callback(); } }); } void ConfirmBox::init(const QString &text) { - textstyleSet(&st::boxTextStyle); - _text.setText(st::boxTextFont, text, _informative ? _confirmBoxTextOptions : _textPlainOptions); - textstyleRestore(); + _text.setText(st::boxTextStyle, text, _informative ? _confirmBoxTextOptions : _textPlainOptions); } void ConfirmBox::prepare() { @@ -125,11 +124,9 @@ void ConfirmBox::prepare() { } void ConfirmBox::textUpdated() { - textstyleSet(&st::boxTextStyle); _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = qMin(_text.countHeight(_textWidth), 16 * int(st::boxTextStyle.lineHeight)); setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxPadding.bottom()); - textstyleRestore(); setMouseTracking(_text.hasLinks()); } @@ -192,9 +189,7 @@ void ConfirmBox::updateLink() { void ConfirmBox::updateHover() { QPoint m(mapFromGlobal(_lastMousePos)); - textstyleSet(&st::boxTextStyle); auto state = _text.getStateLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width()); - textstyleRestore(); ClickHandler::setActive(state.link, this); } @@ -214,9 +209,7 @@ void ConfirmBox::paintEvent(QPaintEvent *e) { // draw box title / text p.setPen(st::boxTextFg); - textstyleSet(&st::boxTextStyle); _text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), 16, style::al_left); - textstyleRestore(); } InformBox::InformBox(QWidget*, const QString &text, base::lambda_copy &&closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, lang(lng_box_ok), std_::move(closedCallback)) { @@ -226,7 +219,7 @@ InformBox::InformBox(QWidget*, const QString &text, const QString &doneText, bas } MaxInviteBox::MaxInviteBox(QWidget*, const QString &link) -: _text(st::boxTextFont, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +: _text(st::boxTextStyle, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) , _link(link) { } @@ -309,13 +302,11 @@ void ConvertToSupergroupBox::prepare() { addButton(lang(lng_profile_convert_confirm), [this] { convertToSupergroup(); }); addButton(lang(lng_cancel), [this] { closeBox(); }); - textstyleSet(&st::boxTextStyle); - _text.setText(st::boxTextFont, text.join('\n'), _confirmBoxTextOptions); - _note.setText(st::boxTextFont, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); + _text.setText(st::boxTextStyle, text.join('\n'), _confirmBoxTextOptions); + _note.setText(st::boxTextStyle, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); _textWidth = st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = _text.countHeight(_textWidth); setDimensions(st::boxWideWidth, _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth)); - textstyleRestore(); } void ConvertToSupergroupBox::convertToSupergroup() { @@ -368,10 +359,8 @@ void ConvertToSupergroupBox::paintEvent(QPaintEvent *e) { // draw box title / text p.setPen(st::boxTextFg); - textstyleSet(&st::boxTextStyle); _text.drawLeft(p, st::boxPadding.left(), 0, _textWidth, width()); _note.drawLeft(p, st::boxPadding.left(), _textHeight + st::boxPadding.bottom(), _textWidth, width()); - textstyleRestore(); } PinMessageBox::PinMessageBox(QWidget*, ChannelData *channel, MsgId msgId) diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index ce20acadb..12b908698 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -562,8 +562,8 @@ ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter members , _membersFilter(membersFilter) , _allAdmins(this, lang(lng_chat_all_members_admins), !_chat->adminsEnabled(), st::defaultBoxCheckbox) , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) -, _aboutAllAdmins(st::normalFont, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth) -, _aboutAdmins(st::normalFont, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth) +, _aboutAllAdmins(st::defaultTextStyle, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth) +, _aboutAdmins(st::defaultTextStyle, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth) , _customList((membersFilter == MembersFilter::Recent) ? std_::unique_ptr() : std_::make_unique(Dialogs::SortMode::Add)) , _contacts((membersFilter == MembersFilter::Recent) ? App::main()->contactsList() : _customList.get()) , _addContactLnk(this, lang(lng_add_contact_button)) { @@ -640,8 +640,7 @@ void ContactsBox::Inner::init() { using Update = Window::Theme::BackgroundUpdate; subscribe(Window::Theme::Background(), [this](const Update &update) { - if (update.type == Update::Type::TestingTheme - || update.type == Update::Type::RevertingTheme) { + if (update.paletteChanged()) { invalidateCache(); } }); @@ -897,7 +896,7 @@ ContactsBox::Inner::ContactData *ContactsBox::Inner::contactData(Dialogs::Row *r if (usingMultiSelect() && _checkedContacts.contains(peer)) { data->checkbox->setChecked(true, Ui::RoundImageCheckbox::SetStyle::Fast); } - data->name.setText(st::contactsNameFont, peer->name, _textNameOptions); + data->name.setText(st::contactsNameStyle, peer->name, _textNameOptions); if (peer->isUser()) { data->statusText = App::onlineText(peer->asUser(), _time); data->statusHasOnlineColor = App::onlineColorUse(peer->asUser(), _time); @@ -1746,7 +1745,7 @@ void ContactsBox::Inner::peopleReceived(const QString &query, const QVectorcheckbox->setChecked(true, Ui::RoundImageCheckbox::SetStyle::Fast); } - data->name.setText(st::contactsNameFont, peer->name, _textNameOptions); + data->name.setText(st::contactsNameStyle, peer->name, _textNameOptions); data->statusText = '@' + peer->userName(); _byUsernameFiltered.push_back(peer); diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 1911053ee..e3a6054a6 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -374,7 +374,7 @@ void MembersBox::Inner::refresh() { resize(width(), st::membersMarginTop + st::noContactsHeight + st::membersMarginBottom); _aboutHeight = 0; } else { - _about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size())); + _about.setText(st::boxTextStyle, lng_channel_only_last_shown(lt_count, _rows.size())); _aboutHeight = st::membersAboutLimitPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutLimitPadding.bottom(); if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { _aboutHeight = 0; @@ -420,7 +420,7 @@ MembersBox::Inner::MemberData *MembersBox::Inner::data(int32 index) { return result; } MemberData *result = _datas[index] = new MemberData(); - result->name.setText(st::contactsNameFont, _rows[index]->name, _textNameOptions); + result->name.setText(st::contactsNameStyle, _rows[index]->name, _textNameOptions); int32 t = unixtime(); result->online = App::onlineText(_rows[index], t);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); result->onlineColor = App::onlineColorUse(_rows[index], t); @@ -496,7 +496,7 @@ void MembersBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names for (int32 i = 0, l = _rows.size(); i < l; ++i) { if (_rows.at(i) == peer) { if (_datas.at(i)) { - _datas.at(i)->name.setText(st::contactsNameFont, peer->name, _textNameOptions); + _datas.at(i)->name.setText(st::contactsNameStyle, peer->name, _textNameOptions); update(0, st::membersMarginTop + i * _rowHeight, width(), _rowHeight); } else { break; diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index 123107e22..0773a8979 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -53,19 +53,15 @@ PasscodeBox::PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray & , _passwordHint(this, st::defaultInputField, lang(curSalt.isEmpty() ? lng_cloud_password_hint : lng_cloud_password_change_hint)) , _recoverEmail(this, st::defaultInputField, lang(lng_cloud_password_email)) , _recover(this, lang(lng_signin_recover)) { - textstyleSet(&st::usernameTextStyle); - if (!hint.isEmpty()) _hintText.setText(st::normalFont, lng_signin_hint(lt_password_hint, hint)); - textstyleRestore(); + if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint)); } void PasscodeBox::prepare() { addButton(lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), [this] { onSave(); }); addButton(lang(lng_cancel), [this] { closeBox(); }); - textstyleSet(&st::usernameTextStyle); - _about.setRichText(st::normalFont, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about)); + _about.setRichText(st::passcodeTextStyle, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about)); _aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5); - textstyleRestore(); if (_turningOff) { _oldPasscode->show(); setTitle(lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove)); @@ -146,8 +142,6 @@ void PasscodeBox::paintEvent(QPaintEvent *e) { Painter p(this); - textstyleSet(&st::usernameTextStyle); - int32 w = st::boxWidth - st::boxPadding.left() * 1.5; int32 abouty = (_passwordHint->isHidden() ? (_reenterPasscode->isHidden() ? (_oldPasscode->y() + (_hasRecovery && !_hintText.isEmpty() ? st::passcodeTextLine : 0)) : _reenterPasscode->y()) + st::passcodeSkip : _passwordHint->y() + st::passcodeLittleSkip) + _oldPasscode->height() + st::passcodePadding.bottom(); p.setPen(st::boxTextFg); @@ -171,8 +165,6 @@ void PasscodeBox::paintEvent(QPaintEvent *e) { p.setPen(st::boxTextFgError); p.drawText(QRect(st::boxPadding.left(), _recoverEmail->y() + _recoverEmail->height(), w, st::passcodeTextLine), _emailError, style::al_left); } - - textstyleRestore(); } void PasscodeBox::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 81753b930..a12e23b28 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -102,14 +102,14 @@ SendFilesBox::SendFilesBox(QWidget*, const QString &filepath, QImage image, Comp if (_preview.isNull()) { if (filepath.isEmpty()) { auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); - _nameText.setText(st::semiboldFont, filename, _textNameOptions); + _nameText.setText(st::semiboldTextStyle, filename, _textNameOptions); _statusText = qsl("%1x%2").arg(_image.width()).arg(_image.height()); _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); _fileIsImage = true; } else { auto fileinfo = QFileInfo(filepath); auto filename = fileinfo.fileName(); - _nameText.setText(st::semiboldFont, filename, _textNameOptions); + _nameText.setText(st::semiboldTextStyle, filename, _textNameOptions); _statusText = formatSizeText(fileinfo.size()); _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); _fileIsImage = fileIsImage(filename, mimeTypeForFile(fileinfo).name()); @@ -127,7 +127,7 @@ SendFilesBox::SendFilesBox(QWidget*, const QString &phone, const QString &firstn : _contactPhone(phone) , _contactFirstName(firstname) , _contactLastName(lastname) { - _nameText.setText(st::semiboldFont, lng_full_name(lt_first_name, _contactFirstName, lt_last_name, _contactLastName), _textNameOptions); + _nameText.setText(st::semiboldTextStyle, lng_full_name(lt_first_name, _contactFirstName, lt_last_name, _contactLastName), _textNameOptions); _statusText = _contactPhone; _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); } @@ -400,9 +400,9 @@ EditCaptionBox::EditCaptionBox(QWidget*, HistoryItem *msg) if (doc) { if (doc->voice()) { - _name.setText(st::semiboldFont, lang(lng_media_audio), _textNameOptions); + _name.setText(st::semiboldTextStyle, lang(lng_media_audio), _textNameOptions); } else { - _name.setText(st::semiboldFont, documentName(doc), _textNameOptions); + _name.setText(st::semiboldTextStyle, documentName(doc), _textNameOptions); } _status = formatSizeText(doc->size); _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status)); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 4a7f9514a..a44b5e970 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -298,8 +298,7 @@ ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac using Update = Window::Theme::BackgroundUpdate; subscribe(Window::Theme::Background(), [this](const Update &update) { - if (update.type == Update::Type::TestingTheme - || update.type == Update::Type::RevertingTheme) { + if (update.paletteChanged()) { invalidateCache(); } }); @@ -362,7 +361,7 @@ void ShareBox::Inner::updateChat(PeerData *peer) { } void ShareBox::Inner::updateChatName(Chat *chat, PeerData *peer) { - chat->name.setText(st::shareNameFont, peer->name, _textNameOptions); + chat->name.setText(st::shareNameStyle, peer->name, _textNameOptions); } void ShareBox::Inner::repaintChatAtIndex(int index) { @@ -600,7 +599,7 @@ void ShareBox::Inner::updateUpon(const QPoint &pos) { auto left = _rowsLeft + qFloor(column * _rowWidthReal) + st::shareColumnSkip / 2; auto top = _rowsTop + row * _rowHeight + st::sharePhotoTop; auto xupon = (x >= left) && (x < left + (_rowWidth - st::shareColumnSkip)); - auto yupon = (y >= top) && (y < top + st::sharePhotoCheckbox.imageRadius * 2 + st::shareNameTop + st::shareNameFont->height * 2); + auto yupon = (y >= top) && (y < top + st::sharePhotoCheckbox.imageRadius * 2 + st::shareNameTop + st::shareNameStyle.font->height * 2); auto upon = (xupon && yupon) ? (row * _columnCount + column) : -1; if (upon >= displayedChatsCount()) { upon = -1; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 5ff8efa88..913bd7064 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -148,7 +148,7 @@ StickersBox::StickersBox(QWidget*, const Stickers::Order &archivedIds) : _section(Section::ArchivedPart) , _archived(0, this, archivedIds) , _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) -, _about(st::boxTextFont, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { +, _about(st::boxTextStyle, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { } void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result) { @@ -691,7 +691,7 @@ void StickersBox::Inner::paintRow(Painter &p, int index, TimeMs ms) { int statusx = namex; int statusy = st::contactsPadding.top() + st::contactsStatusTop; - p.setFont(st::contactsNameFont); + p.setFont(st::contactsNameStyle.font); p.setPen(st::contactsNameFg); p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth); @@ -1225,10 +1225,10 @@ int StickersBox::Inner::fillSetCount(const Stickers::Set &set) const { QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const { auto result = set.title; - int titleWidth = st::contactsNameFont->width(result); + int titleWidth = st::contactsNameStyle.font->width(result); if (titleWidth > maxNameWidth) { - result = st::contactsNameFont->elided(result, maxNameWidth); - titleWidth = st::contactsNameFont->width(result); + result = st::contactsNameStyle.font->elided(result, maxNameWidth); + titleWidth = st::contactsNameStyle.font->width(result); } if (outTitleWidth) { *outTitleWidth = titleWidth; diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index b91250367..757a8c167 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -49,10 +49,8 @@ void UsernameBox::prepare() { connect(_username, SIGNAL(submitted(bool)), this, SLOT(onSave())); connect(_link, SIGNAL(clicked()), this, SLOT(onLinkClick())); - textstyleSet(&st::usernameTextStyle); - _about.setRichText(st::boxTextFont, lang(lng_username_about)); + _about.setRichText(st::usernameTextStyle, lang(lng_username_about)); setDimensions(st::boxWidth, st::usernamePadding.top() + _username->height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom()); - textstyleRestore(); _checkTimer->setSingleShot(true); connect(_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck())); @@ -81,10 +79,8 @@ void UsernameBox::paintEvent(QPaintEvent *e) { p.drawTextLeft(st::usernamePadding.left(), _username->y() + _username->height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), lang(lng_username_choose)); } p.setPen(st::boxTextFg); - textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); _about.drawLeft(p, st::usernamePadding.left(), _username->y() + _username->height() + st::usernameSkip, availw, width()); - textstyleRestore(); int32 linky = _username->y() + _username->height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); if (_link->isHidden()) { @@ -102,9 +98,7 @@ void UsernameBox::resizeEvent(QResizeEvent *e) { _username->resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _username->height()); _username->moveToLeft(st::usernamePadding.left(), st::usernamePadding.top()); - textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); - textstyleRestore(); int32 linky = _username->y() + _username->height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); _link->moveToLeft(st::usernamePadding.left(), linky + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2)); } diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 4568f6f70..40983ec08 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -326,13 +326,7 @@ QString Generator::typeToDefaultValue(structure::Type type) const { QString Generator::valueAssignmentCode(structure::Value value) const { auto copy = value.copyOf(); if (!copy.isEmpty()) { - auto result = "st::" + copy.back(); - - // Copy is disabled for colors. - if (value.type().tag == Tag::Color || value.type().tag == Tag::Struct) { - result += ".clone()"; - } - return result; + return "st::" + copy.back(); } switch (value.type().tag) { @@ -343,7 +337,14 @@ QString Generator::valueAssignmentCode(structure::Value value) const { case Tag::String: return QString("qsl(%1)").arg(stringToEncodedString(value.String())); case Tag::Color: { auto v(value.Color()); - return QString("{ %1, %2, %3, %4 }").arg(v.red).arg(v.green).arg(v.blue).arg(v.alpha); + if (v.red == v.green && v.red == v.blue && v.red == 0 && v.alpha == 255) { + return QString("st::windowFg"); + } else if (v.red == v.green && v.red == v.blue && v.red == 255 && v.alpha == 0) { + return QString("st::transparent"); + } else { + common::logError(common::kErrorInternal, "") << "bad color value"; + return QString(); + } } break; case Tag::Point: { auto v(value.Point()); @@ -449,9 +450,13 @@ public:\n\ \n\ // Created not inited, should be finalized before usage.\n\ void finalize();\n\ -\n"; +\n\ + int indexOfColor(color c) const;\n\ + color colorAtIndex(int index) const;\n\ +\n\ + inline const color &get_transparent() const { return _colors[0]; }; // special color\n"; - int indexInPalette = 0; + int indexInPalette = 1; if (!module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool { auto name = variable.name.back(); if (variable.value.type().tag != structure::TypeTag::Color) { @@ -459,7 +464,7 @@ public:\n\ } auto index = (indexInPalette++); - header_->stream() << "\tinline const color &" << name << "() const { return _colors[" << index << "]; };\n"; + header_->stream() << "\tinline const color &get_" << name << "() const { return _colors[" << index << "]; };\n"; return true; })) return false; @@ -468,7 +473,7 @@ public:\n\ \n\ palette &operator=(const palette &other) {\n\ auto wasReady = _ready;\n\ - for (int i = 0; i != " << count << "; ++i) {\n\ + for (int i = 0; i != kCount; ++i) {\n\ if (other._status[i] == Status::Loaded) {\n\ if (_status[i] == Status::Initial) {\n\ new (data(i)) internal::ColorData(*other.data(i));\n\ @@ -494,8 +499,10 @@ public:\n\ }\n\ \n\ private:\n\ + static constexpr auto kCount = " << count << ";\n\ +\n\ void clear() {\n\ - for (int i = 0; i != " << count << "; ++i) {\n\ + for (int i = 0; i != kCount; ++i) {\n\ if (_status[i] != Status::Initial) {\n\ data(i)->~ColorData();\n\ _status[i] = Status::Initial;\n\ @@ -540,15 +547,15 @@ private:\n\ Loaded,\n\ };\n\ \n\ - alignas(alignof(internal::ColorData)) char _data[sizeof(internal::ColorData) * " << count << "];\n\ + alignas(alignof(internal::ColorData)) char _data[sizeof(internal::ColorData) * kCount];\n\ \n\ - color _colors[" << count << "] = {\n"; + color _colors[kCount] = {\n"; for (int i = 0; i != count; ++i) { header_->stream() << "\t\tdata(" << i << "),\n"; } header_->stream() << "\ };\n\ - Status _status[" << count << "] = { Status::Initial };\n\ + Status _status[kCount] = { Status::Initial };\n\ bool _ready = false;\n\ \n\ };\n\ @@ -561,11 +568,10 @@ bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ bool setColor(QLatin1String name, QLatin1String from);\n\ void apply(const palette &other);\n\ void reset();\n\ +int indexOfColor(color c);\n\ \n\ } // namespace main_palette\n"; - header_->newline(); - return true; } @@ -601,21 +607,8 @@ bool Generator::writeStructsDefinitions() { } bool result = module_.enumStructs([this](const Struct &value) -> bool { - QStringList fields; - for (auto field : value.fields) { - auto clone = field.name.back(); - if (field.type.tag == Tag::Color || field.type.tag == Tag::Struct) { - clone += ".clone()"; - } - fields.push_back(clone); - } header_->stream() << "\ -struct " << value.name.back() << " {\n\ - " << value.name.back() << " clone() const {\n\ - return { " << fields.join(", ") << " };\n\ - }\n"; - if (!fields.empty()) header_->newline(); - +struct " << value.name.back() << " {\n"; for (auto &field : value.fields) { auto type = typeToString(field.type); if (type.isEmpty()) { @@ -638,6 +631,9 @@ bool Generator::writeRefsDeclarations() { header_->pushNamespace("st"); + if (isPalette_) { + header_->stream() << "extern const style::color &transparent; // special color\n"; + } bool result = module_.enumVariables([this](const Variable &value) -> bool { auto name = value.name.back(); auto type = typeToString(value.value.type()); @@ -699,6 +695,9 @@ bool Generator::writeRefsDefinition() { return true; } + if (isPalette_) { + source_->stream() << "const style::color &transparent(_palette.get_transparent()); // special color\n"; + } bool result = module_.enumVariables([this](const Variable &variable) -> bool { auto name = variable.name.back(); auto type = typeToString(variable.value.type()); @@ -707,7 +706,7 @@ bool Generator::writeRefsDefinition() { } source_->stream() << "const " << type << " &" << name << "("; if (isPalette_) { - source_->stream() << "_palette." << name << "()"; + source_->stream() << "_palette.get_" << name << "()"; } else { source_->stream() << "_" << name; } @@ -719,13 +718,30 @@ bool Generator::writeRefsDefinition() { bool Generator::writeSetPaletteColor() { source_->newline(); - source_->stream() << "\ + source_->stream() << "\n\ +int palette::indexOfColor(style::color c) const {\n\ + auto start = data(0);\n\ + if (c._data >= start && c._data < start + kCount) {\n\ + return static_cast(c._data - start);\n\ + }\n\ + return -1;\n\ +}\n\ +\n\ +color palette::colorAtIndex(int index) const {\n\ + t_assert(_ready);\n\ + t_assert(index >= 0 && index < kCount);\n\ + return _colors[index];\n\ +}\n\ +\n\ void palette::finalize() {\n\ if (_ready) return;\n\ - _ready = true;\n\n"; + _ready = true;\n\ +\n\ + compute(0, -1, { 255, 255, 255, 0}); // special color\n"; - int indexInPalette = 0; + int indexInPalette = 1; QByteArray checksumString; + checksumString.append("&transparent:{ 255, 255, 255, 0 }"); bool result = module_.enumVariables([this, &indexInPalette, &checksumString](const Variable &variable) -> bool { auto name = variable.name.back(); auto index = indexInPalette++; @@ -735,8 +751,9 @@ void palette::finalize() {\n\ } auto color = variable.value.Color(); auto fallbackIndex = paletteIndices_.value(colorFallbackName(variable.value), -1); - source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", {" << color.red << ", " << color.green << ", " << color.blue << ", " << color.alpha << "});\n"; - checksumString.append('&' + name + ':' + valueAssignmentCode(variable.value)); + auto assignment = QString("{ %1, %2, %3, %4 }").arg(color.red).arg(color.green).arg(color.blue).arg(color.alpha); + source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", " << assignment << ");\n"; + checksumString.append('&' + name + ':' + assignment); return true; }); auto count = indexInPalette; @@ -888,6 +905,10 @@ void reset() {\n\ style::internal::resetIcons();\n\ }\n\ \n\ +int indexOfColor(color c) {\n\ + return _palette.indexOfColor(c);\n\ +}\n\ +\n\ } // namespace main_palette\n\ \n"; @@ -1226,8 +1247,12 @@ bool Generator::writeSampleTheme(const QString &filepath) { return false; } auto color = variable.value.Color(); - auto colorString = paletteColorValue(color); + //color.red = uchar(rand() % 256); + //color.green = uchar(rand() % 256); + //color.blue = uchar(rand() % 256); + //auto fallbackIndex = -1; auto fallbackIndex = paletteIndices_.value(colorFallbackName(variable.value), -1); + auto colorString = paletteColorValue(color); if (fallbackIndex >= 0) { auto fallbackVariable = module_.findVariableInModule(names[fallbackIndex], module_); if (!fallbackVariable || fallbackVariable->value.type().tag != structure::TypeTag::Color) { diff --git a/Telegram/SourceFiles/core/lambda.h b/Telegram/SourceFiles/core/lambda.h index e7cafbb3f..1933a3da3 100644 --- a/Telegram/SourceFiles/core/lambda.h +++ b/Telegram/SourceFiles/core/lambda.h @@ -94,47 +94,43 @@ namespace internal { template struct lambda_wrap_helper_move_impl; - // - // Disable large lambda support. - // If you really need it, just store data in some std_::unique_ptr. - // - //template - //struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { - // using JustLambda = std_::decay_simple_t; - // using LambdaPtr = std_::unique_ptr; - // using Parent = lambda_wrap_helper_base; - // static void construct_move_other_method(void *lambda, void *source) { - // auto source_lambda = static_cast(source); - // new (lambda) LambdaPtr(std_::move(*source_lambda)); - // } - // static void construct_move_lambda_method(void *lambda, void *source) { - // auto source_lambda = static_cast(source); - // new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); - // } - // static Return call_method(const void *lambda, Args... args) { - // return (**static_cast(lambda))(std_::forward(args)...); - // } - // static void destruct_method(const void *lambda) { - // static_cast(lambda)->~LambdaPtr(); - // } - // lambda_wrap_helper_move_impl() : Parent( - // &Parent::bad_construct_copy, - // &lambda_wrap_helper_move_impl::construct_move_other_method, - // &lambda_wrap_helper_move_impl::call_method, - // &lambda_wrap_helper_move_impl::destruct_method) { - // } - // - //protected: - // lambda_wrap_helper_move_impl( - // typename Parent::construct_copy_other_type construct_copy_other - // ) : Parent( - // construct_copy_other, - // &lambda_wrap_helper_move_impl::construct_move_other_method, - // &lambda_wrap_helper_move_impl::call_method, - // &lambda_wrap_helper_move_impl::destruct_method) { - // } - // - //}; + template + struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { + using JustLambda = std_::decay_simple_t; + using LambdaPtr = std_::unique_ptr; + using Parent = lambda_wrap_helper_base; + static void construct_move_other_method(void *lambda, void *source) { + auto source_lambda = static_cast(source); + new (lambda) LambdaPtr(std_::move(*source_lambda)); + } + static void construct_move_lambda_method(void *lambda, void *source) { + auto source_lambda = static_cast(source); + new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); + } + static Return call_method(const void *lambda, Args... args) { + return (**static_cast(lambda))(std_::forward(args)...); + } + static void destruct_method(const void *lambda) { + static_cast(lambda)->~LambdaPtr(); + } + lambda_wrap_helper_move_impl() : Parent( + &Parent::bad_construct_copy, + &lambda_wrap_helper_move_impl::construct_move_other_method, + &lambda_wrap_helper_move_impl::call_method, + &lambda_wrap_helper_move_impl::destruct_method) { + } + + protected: + lambda_wrap_helper_move_impl( + typename Parent::construct_copy_other_type construct_copy_other + ) : Parent( + construct_copy_other, + &lambda_wrap_helper_move_impl::construct_move_other_method, + &lambda_wrap_helper_move_impl::call_method, + &lambda_wrap_helper_move_impl::destruct_method) { + } + + }; template struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { @@ -189,27 +185,23 @@ namespace internal { template struct lambda_wrap_helper_copy_impl; - // - // Disable large lambda support. - // If you really need it, just store data in some QSharedPointer. - // - //template - //struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { - // using JustLambda = std_::decay_simple_t; - // using LambdaPtr = std_::unique_ptr; - // using Parent = lambda_wrap_helper_move_impl; - // static void construct_copy_other_method(void *lambda, const void *source) { - // auto source_lambda = static_cast(source); - // new (lambda) LambdaPtr(std_::make_unique(*source_lambda->get())); - // } - // static void construct_copy_lambda_method(void *lambda, const void *source) { - // auto source_lambda = static_cast(source); - // new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); - // } - // lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { - // } - // - //}; + template + struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { + using JustLambda = std_::decay_simple_t; + using LambdaPtr = std_::unique_ptr; + using Parent = lambda_wrap_helper_move_impl; + static void construct_copy_other_method(void *lambda, const void *source) { + auto source_lambda = static_cast(source); + new (lambda) LambdaPtr(std_::make_unique(*source_lambda->get())); + } + static void construct_copy_lambda_method(void *lambda, const void *source) { + auto source_lambda = static_cast(source); + new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); + } + lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { + } + + }; template struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { @@ -399,6 +391,13 @@ struct lambda_type_resolver; template struct lambda_type_resolver { using type = lambda; + static constexpr auto is_mutable = false; +}; + +template +struct lambda_type_resolver { + using type = lambda; + static constexpr auto is_mutable = true; }; template diff --git a/Telegram/SourceFiles/core/task_queue.cpp b/Telegram/SourceFiles/core/task_queue.cpp new file mode 100644 index 000000000..297fd09ed --- /dev/null +++ b/Telegram/SourceFiles/core/task_queue.cpp @@ -0,0 +1,415 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "core/task_queue.h" + +namespace base { +namespace { + +auto MainThreadId = QThread::currentThreadId(); +const auto MaxThreadsCount = qMax(QThread::idealThreadCount(), 2); + +template +class Thread : public QThread { +public: + Thread(Lambda code) : _code(std_::move(code)) { + } + void run() override { + _code(); + } + +private: + Lambda _code; + +}; + +template +object_ptr> MakeThread(Lambda code) { + return object_ptr>(std_::move(code)); +} + +} // namespace + +class TaskQueue::TaskQueueList { +public: + TaskQueueList(); + + void Register(TaskQueue *queue); + void Unregister(TaskQueue *queue); + bool IsInList(TaskQueue *queue) const; + void Clear(); + bool Empty(int list_index_) const; + TaskQueue *TakeFirst(int list_index_); + +private: + void Insert(TaskQueue *queue, int list_index_); + void Remove(TaskQueue *queue, int list_index_); + + TaskQueue *Tail() { return &tail_; } + const TaskQueue *Tail() const { return &tail_; } + + TaskQueue tail_ = { Type::Special, Priority::Normal }; + TaskQueue *(lists_[kQueuesListsCount]); + +}; + +class TaskQueue::TaskThreadPool { + struct Private { + }; + +public: + TaskThreadPool(const Private &) { } + static const QSharedPointer &Instance(); + + void AddQueueTask(TaskQueue *queue, Task &&task); + void RemoveQueue(TaskQueue *queue); + + ~TaskThreadPool(); + +private: + + void ThreadFunction(); + + std_::vector_of_moveable> threads_; + QMutex queues_mutex_; + + // queues_mutex_ must be locked when working with the list. + TaskQueueList queue_list_; + + QWaitCondition thread_condition_; + bool stopped_ = false; + int tasks_in_process_ = 0; + int background_tasks_in_process_ = 0; + +}; + +TaskQueue::TaskQueueList::TaskQueueList() { + for (auto &list : lists_) { + list = &tail_; + } +} + +void TaskQueue::TaskQueueList::Register(TaskQueue *queue) { + t_assert(!queue->SerialTaskInProcess()); + + Insert(queue, kAllQueuesList); + if (queue->priority_ == Priority::Normal) { + Insert(queue, kOnlyNormalQueuesList); + } +} + +void TaskQueue::TaskQueueList::Unregister(TaskQueue *queue) { + Remove(queue, kAllQueuesList); + if (queue->priority_ == Priority::Normal) { + Remove(queue, kOnlyNormalQueuesList); + } +} + +void TaskQueue::TaskQueueList::Insert(TaskQueue *queue, int list_index_) { + t_assert(list_index_ < kQueuesListsCount); + + auto tail = Tail(); + if (lists_[list_index_] == tail) { + lists_[list_index_] = queue; + } + + auto &list_entry = queue->list_entries_[list_index_]; + t_assert(list_entry.after == nullptr); + if ((list_entry.before = tail->list_entries_[list_index_].before)) { + list_entry.before->list_entries_[list_index_].after = queue; + } + list_entry.after = tail; + tail->list_entries_[list_index_].before = queue; +} + +void TaskQueue::TaskQueueList::Remove(TaskQueue *queue, int list_index_) { + t_assert(list_index_ < kQueuesListsCount); + + auto &list_entry = queue->list_entries_[list_index_]; + t_assert(list_entry.after != nullptr); + if (lists_[list_index_] == queue) { + lists_[list_index_] = list_entry.after; + } else { + t_assert(list_entry.before != nullptr); + list_entry.before->list_entries_[list_index_].after = list_entry.after; + } + list_entry.after->list_entries_[list_index_].before = list_entry.before; + list_entry.before = list_entry.after = nullptr; +} + +bool TaskQueue::TaskQueueList::IsInList(TaskQueue *queue) const { + if (queue->list_entries_[kAllQueuesList].after) { + return true; + } + t_assert(queue->list_entries_[kOnlyNormalQueuesList].after == nullptr); + return false; +} + +void TaskQueue::TaskQueueList::Clear() { + auto tail = Tail(); + for (int i = 0; i < kQueuesListsCount; ++i) { + for (auto j = lists_[i], next = j; j != tail; j = next) { + auto &list_entry = j->list_entries_[i]; + next = list_entry.after; + list_entry.before = list_entry.after = nullptr; + } + lists_[i] = tail; + } +} + +bool TaskQueue::TaskQueueList::Empty(int list_index_) const { + t_assert(list_index_ < kQueuesListsCount); + + auto list = lists_[list_index_]; + t_assert(list != nullptr); + return (list->list_entries_[list_index_].after == nullptr); +} + +TaskQueue *TaskQueue::TaskQueueList::TakeFirst(int list_index_) { + t_assert(!Empty(list_index_)); + + auto queue = lists_[list_index_]; + Unregister(queue); +// log_msgs.push_back("Unregistered from list in TakeFirst"); + return queue; +} + +void TaskQueue::TaskThreadPool::AddQueueTask(TaskQueue *queue, Task &&task) { + QMutexLocker lock(&queues_mutex_); + + queue->tasks_.push_back(new Task(std::move(task))); + auto list_was_empty = queue_list_.Empty(kAllQueuesList); + auto threads_count = threads_.size(); + auto all_threads_processing = (threads_count == tasks_in_process_); + auto some_threads_are_vacant = !all_threads_processing && list_was_empty; + auto will_create_thread = !some_threads_are_vacant && (threads_count < MaxThreadsCount); + + if (!queue->SerialTaskInProcess()) { + if (!queue_list_.IsInList(queue)) { + queue_list_.Register(queue); + } + } + if (will_create_thread) { + threads_.push_back(MakeThread([this]() { + ThreadFunction(); + })); + threads_.back()->start(); + } else if (some_threads_are_vacant) { + t_assert(threads_count > tasks_in_process_); + thread_condition_.wakeOne(); + } +} + +void TaskQueue::TaskThreadPool::RemoveQueue(TaskQueue *queue) { + QMutexLocker lock(&queues_mutex_); + if (queue_list_.IsInList(queue)) { + queue_list_.Unregister(queue); + } + if (queue->destroyed_flag_) { + *queue->destroyed_flag_ = true; + } +} + +TaskQueue::TaskThreadPool::~TaskThreadPool() { + { + QMutexLocker lock(&queues_mutex_); + queue_list_.Clear(); + stopped_ = true; + } + thread_condition_.wakeAll(); + for (auto &thread : threads_) { + thread->wait(); + } +} + +const QSharedPointer &TaskQueue::TaskThreadPool::Instance() { // static + static auto Pool = MakeShared(Private()); + return Pool; +} + +void TaskQueue::TaskThreadPool::ThreadFunction() { + // Flag marking that the previous processed task was + // with a Background priority. We count all the background + // tasks being processed. + bool background_task = false; + + // Saved serial queue pointer. When we process a serial + // queue task we don't return the queue to the list until + // the task is processed and we return it on the next cycle. + TaskQueue *serial_queue = nullptr; + bool serial_queue_destroyed = false; + bool task_was_processed = false; + while (true) { + std_::unique_ptr task; + { + QMutexLocker lock(&queues_mutex_); + + // Finish the previous task processing. + if (task_was_processed) { + --tasks_in_process_; + } + if (background_task) { + --background_tasks_in_process_; + background_task = false; + } + if (serial_queue) { + if (!serial_queue_destroyed) { + serial_queue->destroyed_flag_ = nullptr; + if (!serial_queue->tasks_.empty()) { + queue_list_.Register(serial_queue); + } + } + serial_queue = nullptr; + serial_queue_destroyed = false; + } + + // Wait for a task to appear in the queues list. + while (queue_list_.Empty(kAllQueuesList)) { + if (stopped_) { + return; + } + thread_condition_.wait(&queues_mutex_); + } + + // Select a task we will be processing. + auto processing_background = (background_tasks_in_process_ > 0); + auto take_only_normal = processing_background && !queue_list_.Empty(kOnlyNormalQueuesList); + auto take_from_list_ = take_only_normal ? kOnlyNormalQueuesList : kAllQueuesList; + auto queue = queue_list_.TakeFirst(take_from_list_); + + t_assert(!queue->tasks_.empty()); + + task.reset(queue->tasks_.front()); + queue->tasks_.pop_front(); + + if (queue->type_ == Type::Serial) { + // Serial queues are returned in the list for processing + // only after the task is finished. + serial_queue = queue; + t_assert(serial_queue->destroyed_flag_ == nullptr); + serial_queue->destroyed_flag_ = &serial_queue_destroyed; + } else if (!queue->tasks_.empty()) { + queue_list_.Register(queue); + } + + ++tasks_in_process_; + task_was_processed = true; + if (queue->priority_ == Priority::Background) { + ++background_tasks_in_process_; + background_task = true; + } + } + + (*task)(); + } +} + +TaskQueue::TaskQueue(Type type, Priority priority) +: type_(type) +, priority_(priority) { + if (type_ != Type::Main && type_ != Type::Special) { + weak_thread_pool_ = TaskThreadPool::Instance(); + } +} + +TaskQueue::~TaskQueue() { + if (type_ != Type::Main && type_ != Type::Special) { + if (auto thread_pool = weak_thread_pool_.lock()) { + thread_pool->RemoveQueue(this); + } + } + for (auto task : take(tasks_)) { + delete task; + } +} + +void TaskQueue::Put(Task &&task) { + if (type_ == Type::Main) { + QMutexLocker lock(&tasks_mutex_); + tasks_.push_back(new Task(std::move(task))); + + Sandbox::MainThreadTaskAdded(); + } else { + t_assert(type_ != Type::Special); + TaskThreadPool::Instance()->AddQueueTask(this, std::move(task)); + } +} + +void TaskQueue::ProcessMainTasks() { // static + t_assert(QThread::currentThreadId() == MainThreadId); + + while (ProcessOneMainTask()) { + } +} + +void TaskQueue::ProcessMainTasks(TimeMs max_time_spent) { // static + t_assert(QThread::currentThreadId() == MainThreadId); + + auto start_time = getms(); + while (ProcessOneMainTask()) { + if (getms() >= start_time + max_time_spent) { + break; + } + } +} + +bool TaskQueue::ProcessOneMainTask() { // static + std_::unique_ptr task; + { + QMutexLocker lock(&Main().tasks_mutex_); + auto &tasks = Main().tasks_; + if (tasks.empty()) { + return false; + } + + task.reset(tasks.front()); + tasks.pop_front(); + } + + (*task)(); + return true; +} + +bool TaskQueue::IsMyThread() const { + if (type_ == Type::Main) { + return (QThread::currentThreadId() == MainThreadId); + } + t_assert(type_ != Type::Special); + return false; +} + +// Default queues. +TaskQueue &TaskQueue::Main() { // static + static TaskQueue MainQueue { Type::Main, Priority::Normal }; + return MainQueue; +} + +TaskQueue &TaskQueue::Normal() { // static + static TaskQueue NormalQueue { Type::Concurrent, Priority::Normal }; + return NormalQueue; +} + +TaskQueue &TaskQueue::Background() { // static + static TaskQueue BackgroundQueue { Type::Concurrent, Priority::Background }; + return BackgroundQueue; +} + +} // namespace base diff --git a/Telegram/SourceFiles/core/task_queue.h b/Telegram/SourceFiles/core/task_queue.h new file mode 100644 index 000000000..58aa71cc5 --- /dev/null +++ b/Telegram/SourceFiles/core/task_queue.h @@ -0,0 +1,100 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace base { + +using Task = lambda; + +// An attempt to create/use a TaskQueue or one of the default queues +// after the main() has returned leads to an undefined behaviour. +class TaskQueue { + enum class Type { + Main, // Unique queue for main thread tasks. + Serial, + Concurrent, + Special, // Unique special queue for thread pool lists terminal item. + }; + +public: + enum class Priority { + Normal, + Background, + }; + + // Creating custom serial queues. + TaskQueue(Priority priority) : TaskQueue(Type::Serial, priority) { + } + + // Default main and two concurrent queues. + static TaskQueue &Main(); + static TaskQueue &Normal(); + static TaskQueue &Background(); + + void Put(Task &&task); + + static void ProcessMainTasks(); + static void ProcessMainTasks(TimeMs max_time_spent); + + ~TaskQueue(); + +private: + static bool ProcessOneMainTask(); + + TaskQueue(Type type, Priority priority); + + bool IsMyThread() const; + bool SerialTaskInProcess() const { + return (destroyed_flag_ != nullptr); + } + + const Type type_; + const Priority priority_; + + QList tasks_; // TODO: std_::deque_of_moveable + QMutex tasks_mutex_; // Only for the main queue. + + // Only for the other queues, not main. + class TaskThreadPool; + QWeakPointer weak_thread_pool_; + + class TaskQueueList; + + struct TaskQueueListEntry { + TaskQueue *before = nullptr; + TaskQueue *after = nullptr; + }; + + // Thread pool queues linked list. + static constexpr int kAllQueuesList = 0; + + // Thread pool queues linked list with excluded Background queues. + static constexpr int kOnlyNormalQueuesList = 1; + + static constexpr int kQueuesListsCount = 2; + TaskQueueListEntry list_entries_[kQueuesListsCount]; + + // Only for Serial queues: non-null value means a task is currently processed. + bool *destroyed_flag_ = nullptr; + +}; + +} // namespace base diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index e4582101c..024233007 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -19,7 +19,6 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; -using "basic_types.style"; using "ui/widgets/widgets.style"; @@ -32,6 +31,11 @@ dialogsRippleBg: windowBgRipple; dialogsRippleBgActive: activeButtonBgRipple; dialogsTextFont: font(fsize); +dialogsTextStyle: TextStyle(defaultTextStyle) { + font: dialogsTextFont; + linkFont: dialogsTextFont; + linkFontOver: dialogsTextFont; +} dialogsDateFont: font(13px); dialogsDateSkip: 5px; dialogsNameTop: 2px; @@ -55,30 +59,23 @@ dialogsScroll: ScrollArea(defaultScrollArea) { bottomsh: 0px; } -dialogsTextStyle: TextStyle(defaultTextStyle) { +dialogsTextPalette: TextPalette(defaultTextPalette) { linkFg: dialogsTextFgService; - linkFgDown: dialogsTextFgService; - linkFlagsOver: font(fsize); } -dialogsTextStyleOver: TextStyle(dialogsTextStyle) { +dialogsTextPaletteOver: TextPalette(defaultTextPalette) { linkFg: dialogsTextFgServiceOver; - linkFgDown: dialogsTextFgServiceOver; } -dialogsTextStyleActive: TextStyle(dialogsTextStyle) { +dialogsTextPaletteActive: TextPalette(defaultTextPalette) { linkFg: dialogsTextFgServiceActive; - linkFgDown: dialogsTextFgServiceActive; } -dialogsTextStyleDraft: TextStyle(dialogsTextStyle) { +dialogsTextPaletteDraft: TextPalette(defaultTextPalette) { linkFg: dialogsDraftFg; - linkFgDown: dialogsDraftFg; } -dialogsTextStyleDraftOver: TextStyle(dialogsTextStyle) { +dialogsTextPaletteDraftOver: TextPalette(defaultTextPalette) { linkFg: dialogsDraftFgOver; - linkFgDown: dialogsDraftFgOver; } -dialogsTextStyleDraftActive: TextStyle(dialogsTextStyle) { +dialogsTextPaletteDraftActive: TextPalette(defaultTextPalette) { linkFg: dialogsDraftFgActive; - linkFgDown: dialogsDraftFgActive; } dialogsMenuToggle: IconButton { diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 067e281d6..ba405597f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -87,12 +87,12 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i if (history->cloudDraftTextCache.isEmpty()) { auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, textClean(draft->textWithTags.text)); - history->cloudDraftTextCache.setText(st::dialogsTextFont, draftText, _textDlgOptions); + history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, _textDlgOptions); } p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); - textstyleSet(&(active ? st::dialogsTextStyleDraftActive : (selected ? st::dialogsTextStyleDraftOver : st::dialogsTextStyleDraft))); + p.setTextPalette(active ? st::dialogsTextPaletteDraftActive : (selected ? st::dialogsTextPaletteDraftOver : st::dialogsTextPaletteDraft)); history->cloudDraftTextCache.drawElided(p, nameleft, texttop, namewidth, 1); - textstyleRestore(); + p.restoreTextPalette(); } } else if (!item) { auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); @@ -127,7 +127,7 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth); } - if (history->peer->isUser() && history->peer->isVerified()) { + if (history->peer->isVerified()) { auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon)); rectForName.setWidth(rectForName.width() - icon->width()); icon->paint(p, rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0), fullWidth); @@ -144,13 +144,13 @@ struct UnreadBadgeSizeData { class UnreadBadgeStyleData : public Data::AbstractStructure { public: UnreadBadgeSizeData sizes[UnreadBadgeSizesCount]; - const style::color *bg[6] = { - &st::dialogsUnreadBg, - &st::dialogsUnreadBgOver, - &st::dialogsUnreadBgActive, - &st::dialogsUnreadBgMuted, - &st::dialogsUnreadBgMutedOver, - &st::dialogsUnreadBgMutedActive + style::color bg[6] = { + st::dialogsUnreadBg, + st::dialogsUnreadBgOver, + st::dialogsUnreadBgActive, + st::dialogsUnreadBgMuted, + st::dialogsUnreadBgMutedOver, + st::dialogsUnreadBgMutedActive }; }; Data::GlobalStructurePointer unreadBadgeStyle; @@ -161,7 +161,7 @@ void createCircleMask(UnreadBadgeSizeData *data, int size) { data->circle = style::createCircleMask(size); } -QImage colorizeCircleHalf(UnreadBadgeSizeData *data, int size, int half, int xoffset, const style::color &color) { +QImage colorizeCircleHalf(UnreadBadgeSizeData *data, int size, int half, int xoffset, style::color color) { auto result = style::colorizeImage(data->circle, color, QRect(xoffset, 0, half, size)); result.setDevicePixelRatio(cRetinaFactor()); return result; @@ -194,14 +194,14 @@ void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st) if (badgeData->left[index].isNull()) { int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor(); createCircleMask(badgeData, size); - badgeData->left[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, *bg)); - badgeData->right[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, imgsize - imgsizehalf, *bg)); + badgeData->left[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, bg)); + badgeData->right[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, imgsize - imgsizehalf, bg)); } int bar = rect.width() - 2 * sizehalf; p.drawPixmap(rect.x(), rect.y(), badgeData->left[index]); if (bar) { - p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), *bg); + p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), bg); } p.drawPixmap(rect.x() + sizehalf + bar, rect.y(), badgeData->right[index]); } diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 217c4ca5c..0aa9f987e 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -535,7 +535,18 @@ void WorkingDirReady() { } } +object_ptr MainThreadTaskHandler = { nullptr }; + +void MainThreadTaskAdded() { + if (!started()) { + return; + } + + MainThreadTaskHandler->call(); +} + void start() { + MainThreadTaskHandler.create(QCoreApplication::instance(), "onMainThreadTask"); SandboxData = new internal::Data(); SandboxData->LangSystemISO = psCurrentLanguage(); @@ -556,6 +567,7 @@ bool started() { void finish() { delete SandboxData; SandboxData = nullptr; + MainThreadTaskHandler.destroy(); } uint64 UserTag() { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 2b6e44ba6..a1888c89e 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -225,6 +225,8 @@ namespace Sandbox { bool CheckBetaVersionDir(); void WorkingDirReady(); +void MainThreadTaskAdded(); + void start(); bool started(); void finish(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 97a93a2dc..567cb926f 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -239,7 +239,7 @@ bool History::mySendActionUpdated(SendAction::Type type, bool doing) { return true; } -bool History::paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, const style::color &color, TimeMs ms) { +bool History::paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, style::color color, TimeMs ms) { if (_sendActionAnimation) { _sendActionAnimation.paint(p, color, x, y + st::normalFont->ascent, outerWidth, ms); auto animationWidth = _sendActionAnimation.width(); @@ -324,7 +324,7 @@ bool History::updateSendActionNeedsAnimating(TimeMs ms, bool force) { } if (_sendActionString != newTypingString) { _sendActionString = newTypingString; - _sendActionText.setText(st::dialogsTextFont, _sendActionString, _textNameOptions); + _sendActionText.setText(st::dialogsTextStyle, _sendActionString, _textNameOptions); } } auto result = (!_typing.isEmpty() || !_sendActions.isEmpty()); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 0da6a9285..c1207ca25 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -339,7 +339,7 @@ public: void unregSendAction(UserData *from); bool updateSendActionNeedsAnimating(UserData *user, const MTPSendMessageAction &action); bool mySendActionUpdated(SendAction::Type type, bool doing); - bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, const style::color &color, TimeMs ms); + bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, style::color color, TimeMs ms); void clearLastKeyboard(); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 0a701e40e..b20845d8f 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -346,7 +346,11 @@ botKbBg: menuBgOver; botKbOverBg: menuBgOver; botKbDownBg: menuBgRipple; botKbColor: windowBoldFgOver; -botKbFont: font(15px semibold); +botKbStyle: TextStyle(defaultTextStyle) { + font: font(15px semibold); + linkFont: font(15px semibold); + linkFontOver: font(15px semibold); +} botKbButton: BotKeyboardButton { margin: 10px; padding: 10px; @@ -404,3 +408,26 @@ historyUnreadBarFont: semiboldFont; historyForwardChooseMargins: margins(30px, 10px, 30px, 10px); historyForwardChooseFont: font(16px); + +msgFileMenuSize: size(36px, 36px); +msgFileSize: 44px; +msgFilePadding: margins(14px, 12px, 11px, 12px); +msgFileThumbSize: 72px; +msgFileThumbPadding: margins(10px, 10px, 14px, 10px); +msgFileThumbNameTop: 12px; +msgFileThumbStatusTop: 32px; +msgFileThumbLinkTop: 60px; +msgFileNameTop: 16px; +msgFileStatusTop: 37px; +msgFileMinWidth: 294px; +msgFileTopMinus: 6px; + +msgFileOverDuration: 200; +msgFileRadialLine: 3px; + +msgVideoSize: size(320px, 240px); + +msgWaveformBar: 2px; +msgWaveformSkip: 1px; +msgWaveformMin: 2px; +msgWaveformMax: 20px; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 54c53e996..755db6adc 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -111,7 +111,7 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s) auto str = row.at(j).text; button.type = row.at(j).type; button.link = MakeShared(item, i, j); - button.text.setText(_st->textFont(), textOneLine(str), _textPlainOptions); + button.text.setText(_st->textStyle(), textOneLine(str), _textPlainOptions); button.characters = str.isEmpty() ? 1 : str.size(); } _rows.push_back(newRow); @@ -381,12 +381,12 @@ void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKe } int tx = rect.x(), tw = rect.width(); - if (tw >= st::botKbFont->elidew + _st->padding * 2) { + if (tw >= st::botKbStyle.font->elidew + _st->padding * 2) { tx += _st->padding; tw -= _st->padding * 2; - } else if (tw > st::botKbFont->elidew) { - tx += (tw - st::botKbFont->elidew) / 2; - tw = st::botKbFont->elidew; + } else if (tw > st::botKbStyle.font->elidew) { + tx += (tw - st::botKbStyle.font->elidew) / 2; + tw = st::botKbStyle.font->elidew; } button.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } @@ -928,14 +928,14 @@ QString HistoryItem::inDialogsText() const { void HistoryItem::drawInDialog(Painter &p, const QRect &r, bool active, bool selected, const HistoryItem *&cacheFor, Text &cache) const { if (cacheFor != this) { cacheFor = this; - cache.setText(st::dialogsTextFont, inDialogsText(), _textDlgOptions); + cache.setText(st::dialogsTextStyle, inDialogsText(), _textDlgOptions); } if (r.width()) { - textstyleSet(&(active ? st::dialogsTextStyleActive : (selected ? st::dialogsTextStyleOver : st::dialogsTextStyle))); + p.setTextPalette(active ? st::dialogsTextPaletteActive : (selected ? st::dialogsTextPaletteOver : st::dialogsTextPalette)); p.setFont(st::dialogsTextFont); p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); cache.drawElided(p, r.left(), r.top(), r.width(), r.height() / st::dialogsTextFont->height); - textstyleRestore(); + p.restoreTextPalette(); } } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 644343d59..15ca09084 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -294,7 +294,7 @@ public: } virtual void startPaint(Painter &p) const = 0; - virtual style::font textFont() const = 0; + virtual const style::TextStyle &textStyle() const = 0; int buttonSkip() const; int buttonPadding() const; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index db8445fa3..8c0f92b93 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -223,7 +223,7 @@ HistoryPhoto::HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { setLinks(MakeShared(_data), MakeShared(_data), MakeShared(_data)); if (!caption.isEmpty()) { - _caption.setText(st::msgFont, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); + _caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); } init(); } @@ -597,7 +597,7 @@ HistoryVideo::HistoryVideo(HistoryItem *parent, DocumentData *document, const QS , _thumbw(1) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { if (!caption.isEmpty()) { - _caption.setText(st::msgFont, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); + _caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); } setDocumentLinks(_data); @@ -900,10 +900,14 @@ ImagePtr HistoryVideo::replyPreview() { return _data->replyPreview; } +HistoryDocumentCaptioned::HistoryDocumentCaptioned() +: _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) { +} + + HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback(const HistoryDocument *that) - : _position(0) - , a_progress(0., 0.) - , _a_progress(animation(const_cast(that), &HistoryDocument::step_voiceProgress)) { +: a_progress(0., 0.) +, _a_progress(animation(const_cast(that), &HistoryDocument::step_voiceProgress)) { } void HistoryDocumentVoice::ensurePlayback(const HistoryDocument *that) const { @@ -932,7 +936,7 @@ HistoryDocument::HistoryDocument(HistoryItem *parent, DocumentData *document, co setStatusSize(FileStatusSizeReady); if (auto captioned = Get()) { - captioned->_caption.setText(st::msgFont, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); + captioned->_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); } } @@ -1024,6 +1028,9 @@ void HistoryDocument::initDimensions() { } else { _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } + if (!isBubbleTop()) { + _minh -= st::msgFileTopMinus; + } if (captioned) { auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right(); @@ -1048,6 +1055,9 @@ int HistoryDocument::resizeGetHeight(int width) { } else { _height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } + if (!isBubbleTop()) { + _height -= st::msgFileTopMinus; + } auto captionw = _width - st::msgPadding.left() - st::msgPadding.right(); _height += captioned->_caption.countHeight(captionw); if (isBubbleBottom()) { @@ -1077,18 +1087,19 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, bool showPause = updateStatusText(); bool radial = isRadialAnimation(ms); + auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; int nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0; if (auto thumbed = Get()) { nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - nametop = st::msgFileThumbNameTop; + nametop = st::msgFileThumbNameTop - topMinus; nameright = st::msgFileThumbPadding.left(); - statustop = st::msgFileThumbStatusTop; - linktop = st::msgFileThumbLinkTop; - bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + statustop = st::msgFileThumbStatusTop - topMinus; + linktop = st::msgFileThumbLinkTop - topMinus; + bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() - topMinus; auto inWebPage = (_parent->getMedia() != this); auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, _width)); QPixmap thumb; if (loaded) { thumb = _data->thumb->pixSingle(thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); @@ -1098,7 +1109,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, p.drawPixmap(rthumb.topLeft(), thumb); if (selected) { auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners; - App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners); + App::roundRect(p, rthumb, p.textPalette().selectOverlay, overlayCorners); } if (radial || (!loaded && !_data->loading())) { @@ -1147,12 +1158,12 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, } } else { nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); - nametop = st::msgFileNameTop; + nametop = st::msgFileNameTop - topMinus; nameright = st::msgFilePadding.left(); - statustop = st::msgFileStatusTop; - bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + statustop = st::msgFileStatusTop - topMinus; + bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() - topMinus; - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, st::msgFileSize, st::msgFileSize, _width)); p.setPen(Qt::NoPen); if (selected) { p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); @@ -1192,7 +1203,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, })(); icon->paintInCenter(p, inner); } - int32 namewidth = _width - nameleft - nameright; + auto namewidth = _width - nameleft - nameright; if (auto voice = Get()) { const VoiceWaveform *wf = 0; @@ -1298,12 +1309,13 @@ HistoryTextState HistoryDocument::getState(int x, int y, HistoryStateRequest req bool showPause = updateStatusText(); int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0; + auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; if (auto thumbed = Get()) { nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - linktop = st::msgFileThumbLinkTop; - bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + linktop = st::msgFileThumbLinkTop - topMinus; + bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() - topMinus; - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, _width)); if ((_data->loading() || _data->uploading() || !loaded) && rthumb.contains(x, y)) { result.link = (_data->loading() || _data->uploading()) ? _cancell : _savel; @@ -1317,9 +1329,9 @@ HistoryTextState HistoryDocument::getState(int x, int y, HistoryStateRequest req } } } else { - bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() - topMinus; - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, st::msgFileSize, st::msgFileSize, _width)); if ((_data->loading() || _data->uploading() || !loaded) && inner.contains(x, y)) { result.link = (_data->loading() || _data->uploading()) ? _cancell : _savel; return result; @@ -1477,6 +1489,10 @@ bool HistoryDocument::updateStatusText() const { return showPause; } +QMargins HistoryDocument::bubbleMargins() const { + return Get() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding; +} + void HistoryDocument::step_voiceProgress(float64 ms, bool timer) { if (auto voice = Get()) { if (voice->_playback) { @@ -1529,7 +1545,7 @@ HistoryGif::HistoryGif(HistoryItem *parent, DocumentData *document, const QStrin setStatusSize(FileStatusSizeReady); if (!caption.isEmpty()) { - _caption.setText(st::msgFont, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); + _caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent)); } _data->thumb->load(); @@ -2215,13 +2231,10 @@ ClickHandlerPtr addContactClickHandler(HistoryItem *item) { HistoryContact::HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone) : HistoryMedia(parent) , _userId(userId) -, _contact(0) -, _phonew(0) , _fname(first) , _lname(last) -, _phone(App::formatPhone(phone)) -, _linkw(0) { - _name.setText(st::semiboldFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); +, _phone(App::formatPhone(phone)) { + _name.setText(st::semiboldTextStyle, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); _phonew = st::normalFont->width(_phone); } @@ -2264,6 +2277,9 @@ void HistoryContact::initDimensions() { } else { _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } + if (!isBubbleTop()) { + _minh -= st::msgFileTopMinus; + } _height = _minh; } @@ -2279,21 +2295,22 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T } int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; if (_userId) { nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - nametop = st::msgFileThumbNameTop; + nametop = st::msgFileThumbNameTop - topMinus; nameright = st::msgFileThumbPadding.left(); - statustop = st::msgFileThumbStatusTop; - linktop = st::msgFileThumbLinkTop; + statustop = st::msgFileThumbStatusTop - topMinus; + linktop = st::msgFileThumbLinkTop - topMinus; - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width)); if (_contact) { _contact->paintUserpic(p, st::msgFileThumbSize, rthumb.x(), rthumb.y()); } else { p.drawPixmap(rthumb.topLeft(), userDefPhoto(qAbs(_userId) % kUserColorsCount)->pixCircled(st::msgFileThumbSize, st::msgFileThumbSize)); } if (selected) { - App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners); + App::roundRect(p, rthumb, p.textPalette().selectOverlay, SelectedOverlaySmallCorners); } bool over = ClickHandler::showAsActive(_linkl); @@ -2302,11 +2319,11 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T p.drawTextLeft(nameleft, linktop, width, _link, _linkw); } else { nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); - nametop = st::msgFileNameTop; + nametop = st::msgFileNameTop - topMinus; nameright = st::msgFilePadding.left(); - statustop = st::msgFileStatusTop; + statustop = st::msgFileStatusTop - topMinus; - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, st::msgFileSize, st::msgFileSize, width)); p.drawPixmap(inner.topLeft(), userDefPhoto(qAbs(_parent->id) % kUserColorsCount)->pixCircled(st::msgFileSize, st::msgFileSize)); } int32 namewidth = width - nameleft - nameright; @@ -2326,9 +2343,10 @@ HistoryTextState HistoryContact::getState(int x, int y, HistoryStateRequest requ bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; if (_userId) { nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - linktop = st::msgFileThumbLinkTop; + linktop = st::msgFileThumbLinkTop - topMinus; if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, _width).contains(x, y)) { result.link = _linkl; return result; @@ -2375,6 +2393,7 @@ void HistoryContact::updateSentMedia(const MTPMessageMedia &media) { } namespace { + QString siteNameFromUrl(const QString &url) { QUrl u(url); QString pretty = u.isValid() ? u.toDisplayString() : url; @@ -2400,6 +2419,7 @@ int32 articleThumbHeight(PhotoData *thumb, int32 width) { } int32 _lineHeight = 0; + } // namespace HistoryWebPage::HistoryWebPage(HistoryItem *parent, WebPageData *data) : HistoryMedia(parent) @@ -2479,13 +2499,13 @@ void HistoryWebPage::initDimensions() { } else if (_data->siteName == qstr("Instagram")) { opts = &_instagramDescriptionOptions; } - _description.setText(st::webPageDescriptionFont, text, *opts); + _description.setText(st::webPageDescriptionStyle, text, *opts); } if (_title.isEmpty() && !title.isEmpty()) { if (!_asArticle && !_attach && _description.isEmpty()) { title += _parent->skipBlock(); } - _title.setText(st::webPageTitleFont, title, _webpageTitleOptions); + _title.setText(st::webPageTitleStyle, title, _webpageTitleOptions); } if (!_siteNameWidth && !_data->siteName.isEmpty()) { _siteNameWidth = st::webPageTitleFont->width(_data->siteName); @@ -2682,7 +2702,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T } p.drawPixmapLeft(padding.left() + width - pw, tshift, _width, pix); if (selected) { - App::roundRect(p, rtlrect(padding.left() + width - pw, tshift, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners); + App::roundRect(p, rtlrect(padding.left() + width - pw, tshift, pw, _pixh, _width), p.textPalette().selectOverlay, SelectedOverlaySmallCorners); } width -= pw + st::webPagePhotoDelta; } @@ -2943,11 +2963,11 @@ void HistoryGame::initDimensions() { if (_description.isEmpty() && !_data->description.isEmpty()) { auto text = _data->description; if (!text.isEmpty()) { - _description.setText(st::webPageDescriptionFont, text, _webpageDescriptionOptions); + _description.setText(st::webPageDescriptionStyle, text, _webpageDescriptionOptions); } } if (_title.isEmpty() && !title.isEmpty()) { - _title.setText(st::webPageTitleFont, title, _webpageTitleOptions); + _title.setText(st::webPageTitleStyle, title, _webpageTitleOptions); } // init dimensions @@ -3183,7 +3203,7 @@ TextSelection HistoryGame::adjustSelection(TextSelection selection, TextSelectTy } bool HistoryGame::consumeMessageText(const TextWithEntities &textWithEntities) { - _description.setMarkedText(st::webPageDescriptionFont, textWithEntities, itemTextOptions(_parent)); + _description.setMarkedText(st::webPageDescriptionStyle, textWithEntities, itemTextOptions(_parent)); return true; } @@ -3279,10 +3299,10 @@ HistoryLocation::HistoryLocation(HistoryItem *parent, const LocationCoords &coor , _description(st::msgMinWidth) , _link(new LocationClickHandler(coords)) { if (!title.isEmpty()) { - _title.setText(st::webPageTitleFont, textClean(title), _webpageTitleOptions); + _title.setText(st::webPageTitleStyle, textClean(title), _webpageTitleOptions); } if (!description.isEmpty()) { - _description.setText(st::webPageDescriptionFont, textClean(description), _webpageDescriptionOptions); + _description.setText(st::webPageDescriptionStyle, textClean(description), _webpageDescriptionOptions); } } diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index c58391476..0e9dc6f62 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -295,7 +295,9 @@ struct HistoryDocumentThumbed : public RuntimeComponent mutable QString _link; }; struct HistoryDocumentCaptioned : public RuntimeComponent { - Text _caption = { int(st::msgFileMinWidth) - st::msgPadding.left() - st::msgPadding.right() }; + HistoryDocumentCaptioned(); + + Text _caption; }; struct HistoryDocumentNamed : public RuntimeComponent { QString _name; @@ -305,7 +307,7 @@ class HistoryDocument; struct HistoryDocumentVoicePlayback { HistoryDocumentVoicePlayback(const HistoryDocument *that); - int32 _position; + int32 _position = 0; anim::value a_progress; BasicAnimation _a_progress; }; @@ -384,9 +386,7 @@ public: bool customInfoLayout() const override { return false; } - QMargins bubbleMargins() const override { - return Get() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding; - } + QMargins bubbleMargins() const override; bool hideForwardedFrom() const override { return _data->song(); } @@ -631,14 +631,14 @@ public: private: int32 _userId; - UserData *_contact; + UserData *_contact = nullptr; - int32 _phonew; + int _phonew = 0; QString _fname, _lname, _phone; Text _name; ClickHandlerPtr _linkl; - int32 _linkw; + int _linkw = 0; QString _link; }; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index cd13a87a9..83a0de382 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -112,7 +112,7 @@ void HistoryMessageSigned::create(UserData *from, const QDateTime &date) { if (timew + namew > st::maxSignatureSize) { name = st::msgDateFont->elided(from->firstName, st::maxSignatureSize - timew); } - _signature.setText(st::msgDateFont, name + time, _textNameOptions); + _signature.setText(st::msgDateTextStyle, name + time, _textNameOptions); } int HistoryMessageSigned::maxWidth() const { @@ -123,7 +123,7 @@ void HistoryMessageEdited::create(const QDateTime &editDate, const QDateTime &da _editDate = editDate; QString time = date.toString(cTimeFormat()); - _edited.setText(st::msgDateFont, lang(lng_edited) + ' ' + time, _textNameOptions); + _edited.setText(st::msgDateTextStyle, lang(lng_edited) + ' ' + time, _textNameOptions); } int HistoryMessageEdited::maxWidth() const { @@ -151,9 +151,7 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { } } TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; - textstyleSet(&st::inFwdTextStyle); - _text.setText(st::msgServiceNameFont, text, opts); - textstyleRestore(); + _text.setText(st::fwdTextStyle, text, opts); _text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? goToMessageClickHandler(_authorOriginal, _originalId) : _authorOriginal->openLink()); if (via) { _text.setLink(2, via->_lnk); @@ -174,7 +172,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) { } if (replyToMsg) { - replyToText.setText(st::msgFont, textClean(replyToMsg->inReplyText()), _textDlgOptions); + replyToText.setText(st::messageTextStyle, textClean(replyToMsg->inReplyText()), _textDlgOptions); updateName(); @@ -214,7 +212,7 @@ bool HistoryMessageReply::isNameUpdated() const { void HistoryMessageReply::updateName() const { if (replyToMsg) { QString name = (_replyToVia && replyToMsg->author()->isUser()) ? replyToMsg->author()->asUser()->firstName : App::peerName(replyToMsg->author()); - replyToName.setText(st::msgServiceNameFont, name, _textNameOptions); + replyToName.setText(st::fwdTextStyle, name, _textNameOptions); replyToVersion = replyToMsg->author()->nameVersion; bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; @@ -248,12 +246,12 @@ void HistoryMessageReply::itemRemoved(HistoryMessage *holder, HistoryItem *remov void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const { bool selected = (flags & PaintSelected), outbg = holder->hasOutLayout(); - const style::color *bar = &st::msgImgReplyBarColor; + style::color bar = st::msgImgReplyBarColor; if (flags & PaintInBubble) { - bar = &((flags & PaintSelected) ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor)); + bar = (flags & PaintSelected) ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor); } QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x)); - p.fillRect(rbar, *bar); + p.fillRect(rbar, bar); if (w > st::msgReplyBarSkip) { if (replyToMsg) { @@ -266,7 +264,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in QRect to(rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x)); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small)); if (selected) { - App::roundRect(p, to, textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners); + App::roundRect(p, to, p.textPalette().selectOverlay, SelectedOverlaySmallCorners); } } } @@ -305,8 +303,8 @@ void HistoryMessage::KeyboardStyle::startPaint(Painter &p) const { p.setPen(st::msgServiceFg); } -style::font HistoryMessage::KeyboardStyle::textFont() const { - return st::msgServiceFont; +const style::TextStyle &HistoryMessage::KeyboardStyle::textStyle() const { + return st::serviceTextStyle; } void HistoryMessage::KeyboardStyle::repaint(const HistoryItem *item) const { @@ -1026,22 +1024,18 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) { if (mediaDisplayed && _media->consumeMessageText(textWithEntities)) { setEmptyText(); } else { - textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle)); if (_media && _media->isDisplayed() && !_media->isAboveMessage()) { - _text.setMarkedText(st::msgFont, textWithEntities, itemTextOptions(this)); + _text.setMarkedText(st::messageTextStyle, textWithEntities, itemTextOptions(this)); } else { - _text.setMarkedText(st::msgFont, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this)); + _text.setMarkedText(st::messageTextStyle, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this)); } - textstyleRestore(); _textWidth = -1; _textHeight = 0; } } void HistoryMessage::setEmptyText() { - textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle)); - _text.setMarkedText(st::msgFont, { QString(), EntitiesInText() }, itemTextOptions(this)); - textstyleRestore(); + _text.setMarkedText(st::messageTextStyle, { QString(), EntitiesInText() }, itemTextOptions(this)); _textWidth = -1; _textHeight = 0; @@ -1271,12 +1265,12 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, T float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration)); float64 o = p.opacity(); p.setOpacity(o * dt); - p.fillRect(0, skiph, _history->width, height - skiph, textstyleCurrent()->selectOverlay->b); + p.fillRect(0, skiph, _history->width, height - skiph, st::defaultTextPalette.selectOverlay); p.setOpacity(o); } } - textstyleSet(&(outbg ? st::outTextStyle : st::inTextStyle)); + p.setTextPalette(outbg ? st::outTextPalette : st::inTextPalette); if (auto keyboard = inlineReplyKeyboard()) { int h = st::msgBotKbButton.margin + keyboard->naturalHeight(); @@ -1344,7 +1338,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, T p.translate(-left, -top); } - textstyleRestore(); + p.restoreTextPalette(); auto reply = Get(); if (reply && reply->isNameUpdated()) { @@ -1382,9 +1376,9 @@ void HistoryMessage::paintForwardedInfo(Painter &p, QRect &trect, bool selected) auto fwd = Get(); bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * serviceFont->height); - textstyleSet(&(selected ? (hasOutLayout() ? st::outFwdTextStyleSelected : st::inFwdTextStyleSelected) : (hasOutLayout() ? st::outFwdTextStyle : st::inFwdTextStyle))); + p.setTextPalette(selected ? (hasOutLayout() ? st::outFwdTextPaletteSelected : st::inFwdTextPaletteSelected) : (hasOutLayout() ? st::outFwdTextPalette : st::inFwdTextPalette)); fwd->_text.drawElided(p, trect.x(), trect.y(), trect.width(), 2, style::al_left, 0, -1, 0, breakEverywhere); - textstyleSet(&(hasOutLayout() ? st::outTextStyle : st::inTextStyle)); + p.setTextPalette(hasOutLayout() ? st::outTextPalette : st::inTextPalette); trect.setY(trect.y() + (((fwd->_text.maxWidth() > trect.width()) ? 2 : 1) * serviceFont->height)); } @@ -1478,10 +1472,8 @@ int HistoryMessage::performResizeGetHeight(int width) { } else { auto textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1); if (textWidth != _textWidth) { - textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle)); _textWidth = textWidth; _textHeight = _text.countHeight(textWidth); - textstyleRestore(); } _height = _textHeight; } @@ -1676,9 +1668,7 @@ bool HistoryMessage::getStateForwardedInfo(int x, int y, QRect &trect, HistoryTe if (breakEverywhere) { textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere; } - textstyleSet(&st::inFwdTextStyle); *outResult = fwd->_text.getState(x - trect.left(), y - trect.top(), trect.width(), textRequest); - textstyleRestore(); outResult->symbol = 0; outResult->afterSymbol = false; if (breakEverywhere) { @@ -1722,9 +1712,7 @@ bool HistoryMessage::getStateViaBotIdInfo(int x, int y, QRect &trect, HistoryTex bool HistoryMessage::getStateText(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const { if (trect.contains(x, y)) { - textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle)); *outResult = _text.getState(x - trect.x(), y - trect.y(), trect.width(), request.forText()); - textstyleRestore(); return true; } return false; @@ -2105,9 +2093,7 @@ QString HistoryService::inReplyText() const { } void HistoryService::setServiceText(const QString &text, const Links &links) { - textstyleSet(&st::serviceTextStyle); - _text.setText(st::msgServiceFont, text, _historySrvOptions); - textstyleRestore(); + _text.setText(st::serviceTextStyle, text, _historySrvOptions); for (int i = 0, count = links.size(); i != count; ++i) { _text.setLink(1 + i, links.at(i)); } @@ -2169,9 +2155,7 @@ int32 HistoryService::resizeGetHeight_(int32 width) { int32 nwidth = qMax(width - st::msgServicePadding.left() - st::msgServicePadding.right(), 0); if (nwidth != _textWidth) { _textWidth = nwidth; - textstyleSet(&st::serviceTextStyle); _textHeight = _text.countHeight(nwidth); - textstyleRestore(); } if (width >= _maxw) { _height += _minh; @@ -2231,11 +2215,9 @@ HistoryTextState HistoryService::getState(int x, int y, HistoryStateRequest requ auto outer = QRect(left, st::msgServiceMargin.top(), width, height); auto trect = outer.marginsAdded(-st::msgServicePadding); if (trect.contains(x, y)) { - textstyleSet(&st::serviceTextStyle); auto textRequest = request.forText(); textRequest.align = style::al_center; result = _text.getState(x - trect.x(), y - trect.y(), trect.width(), textRequest); - textstyleRestore(); if (auto gamescore = Get()) { if (!result.link && result.cursor == HistoryInTextCursorState && outer.contains(x, y)) { result.link = gamescore->lnk; diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index dfaf81cfc..66188beaa 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -211,7 +211,7 @@ private: int buttonRadius() const override; void startPaint(Painter &p) const override; - style::font textFont() const override; + const style::TextStyle &textStyle() const override; void repaint(const HistoryItem *item) const override; protected: diff --git a/Telegram/SourceFiles/history/history_service_layout.cpp b/Telegram/SourceFiles/history/history_service_layout.cpp index 828df5c06..88c11785e 100644 --- a/Telegram/SourceFiles/history/history_service_layout.cpp +++ b/Telegram/SourceFiles/history/history_service_layout.cpp @@ -196,16 +196,15 @@ void ServiceMessagePainter::paint(Painter &p, const HistoryService *message, con } else { int skiph = st::msgServiceMargin.top() - st::msgServiceMargin.bottom(); - textstyleSet(&st::inTextStyle); float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration)); float64 o = p.opacity(); p.setOpacity(o * dt); - p.fillRect(0, skiph, message->history()->width, message->height() - skiph, textstyleCurrent()->selectOverlay->b); + p.fillRect(0, skiph, message->history()->width, message->height() - skiph, st::defaultTextPalette.selectOverlay); p.setOpacity(o); } } - textstyleSet(&st::serviceTextStyle); + p.setTextPalette(st::serviceTextPalette); if (auto media = message->getMedia()) { height -= st::msgServiceMargin.top() + media->height(); @@ -229,7 +228,7 @@ void ServiceMessagePainter::paint(Painter &p, const HistoryService *message, con p.setFont(st::msgServiceFont); message->_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, context.selection, false); - textstyleRestore(); + p.restoreTextPalette(); } void ServiceMessagePainter::paintDate(Painter &p, const QDateTime &date, int y, int w) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ba6fd5788..95ca7cbde 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -422,7 +422,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { bool noHistoryDisplayed = _firstLoading || historyDisplayedEmpty; if (!_firstLoading && _botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { if (r.y() < _botAbout->rect.y() + _botAbout->rect.height() && r.y() + r.height() > _botAbout->rect.y()) { - textstyleSet(&st::inTextStyle); + p.setTextPalette(st::inTextPalette); App::roundRect(p, _botAbout->rect, st::msgInBg, MessageInCorners, &st::msgInShadow); p.setFont(st::msgNameFont); @@ -432,7 +432,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { p.setPen(st::historyTextInFg); _botAbout->info->text.draw(p, _botAbout->rect.left() + st::msgPadding.left(), _botAbout->rect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, _botAbout->width); - textstyleRestore(); + p.restoreTextPalette(); } } else if (noHistoryDisplayed) { HistoryLayout::paintEmpty(p, width(), height()); @@ -1637,7 +1637,7 @@ void HistoryInner::updateBotInfo(bool recount) { int newh = 0; if (_botAbout && !_botAbout->info->description.isEmpty()) { if (_botAbout->info->text.isEmpty()) { - _botAbout->info->text.setText(st::msgFont, _botAbout->info->description, _historyBotNoMonoOptions); + _botAbout->info->text.setText(st::messageTextStyle, _botAbout->info->description, _historyBotNoMonoOptions); if (recount) { int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right(); if (tw > st::msgMaxWidth) tw = st::msgMaxWidth; @@ -2359,9 +2359,9 @@ MessageField::MessageField(HistoryWidget *history, const style::FlatTextarea &st } bool MessageField::hasSendText() const { - auto &text(getTextWithTags().text); + auto &text = getTextWithTags().text; for (auto *ch = text.constData(), *e = ch + text.size(); ch != e; ++ch) { - ushort code = ch->unicode(); + auto code = ch->unicode(); if (code != ' ' && code != '\n' && code != '\r' && !chReplacedBySpace(code)) { return true; } @@ -2472,11 +2472,11 @@ void BotKeyboard::paintEvent(QPaintEvent *e) { void BotKeyboard::Style::startPaint(Painter &p) const { p.setPen(st::botKbColor); - p.setFont(st::botKbFont); + p.setFont(st::botKbStyle.font); } -style::font BotKeyboard::Style::textFont() const { - return st::botKbFont; +const style::TextStyle &BotKeyboard::Style::textStyle() const { + return st::botKbStyle; } void BotKeyboard::Style::repaint(const HistoryItem *item) const { @@ -2677,28 +2677,28 @@ void BotKeyboard::updateSelected() { HistoryHider::HistoryHider(MainWidget *parent, bool forwardSelected) : TWidget(parent) , _forwardSelected(forwardSelected) , _send(this, lang(lng_forward_send), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +, _cancel(this, lang(lng_cancel), st::defaultBoxButton) { init(); } HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : TWidget(parent) , _sharedContact(sharedContact) , _send(this, lang(lng_forward_send), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +, _cancel(this, lang(lng_cancel), st::defaultBoxButton) { init(); } HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent) , _sendPath(true) , _send(this, lang(lng_forward_send), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +, _cancel(this, lang(lng_cancel), st::defaultBoxButton) { init(); } HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : TWidget(parent) , _botAndQuery(botAndQuery) , _send(this, lang(lng_forward_send), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +, _cancel(this, lang(lng_cancel), st::defaultBoxButton) { init(); } @@ -2706,7 +2706,7 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString , _shareUrl(url) , _shareText(text) , _send(this, lang(lng_forward_send), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +, _cancel(this, lang(lng_cancel), st::defaultBoxButton) { init(); } @@ -2746,9 +2746,7 @@ void HistoryHider::paintEvent(QPaintEvent *e) { App::roundRect(p, _box, st::boxBg, BoxCorners); p.setPen(st::boxTextFg); - textstyleSet(&st::boxTextStyle); _toText.drawLeftElided(p, _box.left() + st::boxPadding.left(), _box.y() + st::boxTopMargin + st::boxPadding.top(), _toTextWidth + 2, width(), 1, style::al_left); - textstyleRestore(); } else { auto w = st::historyForwardChooseMargins.left() + _chooseWidth + st::historyForwardChooseMargins.right(); auto h = st::historyForwardChooseMargins.top() + st::historyForwardChooseFont->height + st::historyForwardChooseMargins.bottom(); @@ -2856,7 +2854,7 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { bool HistoryHider::offerPeer(PeerId peer) { if (!peer) { _offered = nullptr; - _toText.setText(st::boxTextFont, QString()); + _toText.setText(st::boxTextStyle, QString()); _toTextWidth = 0; resizeEvent(nullptr); return false; @@ -2896,9 +2894,7 @@ bool HistoryHider::offerPeer(PeerId peer) { return false; } - textstyleSet(&st::boxTextStyle); - _toText.setText(st::boxTextFont, phrase, _textNameOptions); - textstyleRestore(); + _toText.setText(st::boxTextStyle, phrase, _textNameOptions); _toTextWidth = _toText.maxWidth(); if (_toTextWidth > _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right()) { _toTextWidth = _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right(); @@ -6089,7 +6085,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { updateReplyToName(); - _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -6108,7 +6104,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); - _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -6180,15 +6176,14 @@ bool HistoryWidget::paintTopBar(Painter &p, int decreaseWidth, TimeMs ms) { if (!_history) return false; auto increaseLeft = (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) ? (st::topBarArrowPadding.left() - st::topBarArrowPadding.right()) : 0; - decreaseWidth += increaseLeft; auto nameleft = st::topBarArrowPadding.right() + increaseLeft; auto nametop = st::topBarArrowPadding.top(); auto statustop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height; - auto namewidth = width() - decreaseWidth - st::topBarArrowPadding.left() - st::topBarArrowPadding.right(); + auto namewidth = width() - decreaseWidth - nameleft - st::topBarArrowPadding.right(); p.setFont(st::dialogsTextFont); if (!_history->paintSendAction(p, nameleft, statustop, namewidth, width(), st::historyStatusFgTyping, ms)) { p.setPen(_titlePeerTextOnline ? st::historyStatusFgActive : st::historyStatusFg); - p.drawText(nameleft, st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height + st::dialogsTextFont->ascent, _titlePeerText); + p.drawText(nameleft, statustop + st::dialogsTextFont->ascent, _titlePeerText); } p.setPen(st::dialogsNameFg); @@ -6374,8 +6369,8 @@ void HistoryWidget::moveFieldControls() { } void HistoryWidget::updateFieldSize() { - bool kbShowShown = _history && !_kbShown && _keyboard->hasMarkup(); - int fieldWidth = width() - _attachToggle->width(); + auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup(); + auto fieldWidth = width() - _attachToggle->width() - st::historySendRight; fieldWidth -= _send->width(); fieldWidth -= _attachEmoji->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); @@ -7382,7 +7377,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); - _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -7638,7 +7633,7 @@ void HistoryWidget::updatePinnedBar(bool force) { _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); } if (_pinnedBar->msg) { - _pinnedBar->text.setText(st::msgFont, textClean(_pinnedBar->msg->notificationText()), _textDlgOptions); + _pinnedBar->text.setText(st::messageTextStyle, textClean(_pinnedBar->msg->notificationText()), _textDlgOptions); update(); } else if (force) { if (_peer && _peer->isMegagroup()) { @@ -7870,7 +7865,7 @@ void HistoryWidget::onReplyToMessage() { } else { _replyEditMsg = to; _replyToId = to->id; - _replyEditMsgText.setText(st::msgFont, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); updateBotKeyboard(); @@ -8207,13 +8202,13 @@ void HistoryWidget::updatePreview() { _fieldBarCancel->show(); updateMouseTracking(); if (_previewData->pendingTill) { - _previewTitle.setText(st::msgServiceNameFont, lang(lng_preview_loading), _textNameOptions); + _previewTitle.setText(st::msgNameStyle, lang(lng_preview_loading), _textNameOptions); #ifndef OS_MAC_OLD auto linkText = _previewLinks.splitRef(' ').at(0).toString(); #else // OS_MAC_OLD auto linkText = _previewLinks.split(' ').at(0); #endif // OS_MAC_OLD - _previewDescription.setText(st::msgFont, textClean(linkText), _textDlgOptions); + _previewDescription.setText(st::messageTextStyle, textClean(linkText), _textDlgOptions); int32 t = (_previewData->pendingTill - unixtime()) * 1000; if (t <= 0) t = 1; @@ -8244,8 +8239,8 @@ void HistoryWidget::updatePreview() { title = lang(lng_attach_photo); } } - _previewTitle.setText(st::msgServiceNameFont, title, _textNameOptions); - _previewDescription.setText(st::msgFont, textClean(desc), _textDlgOptions); + _previewTitle.setText(st::msgNameStyle, title, _textNameOptions); + _previewDescription.setText(st::messageTextStyle, textClean(desc), _textDlgOptions); } } else if (!readyToForward() && !replyToId() && !_editMsgId) { _fieldBarCancel->hide(); @@ -8509,7 +8504,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) { _replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId); } if (_replyEditMsg) { - _replyEditMsgText.setText(st::msgFont, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); updateBotKeyboard(); @@ -8539,7 +8534,7 @@ void HistoryWidget::updateForwarding(bool force) { void HistoryWidget::updateReplyToName() { if (_editMsgId) return; if (!_replyEditMsg && (_replyToId || !_kbReplyTo)) return; - _replyToName.setText(st::msgServiceNameFont, App::peerName((_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()), _textNameOptions); + _replyToName.setText(st::msgNameStyle, App::peerName((_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()), _textNameOptions); _replyToNameVersion = (_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()->nameVersion; } @@ -8826,7 +8821,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { PainterHighQualityEnabler hq(p); QRect to, from; - App::main()->backgroundParams(fill, to, from); + Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from); to.moveTop(to.top() + fromy); p.drawPixmap(to, pix, from); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index c7854da4a..cabbcabba 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -439,7 +439,7 @@ private: int buttonRadius() const override; void startPaint(Painter &p) const override; - style::font textFont() const override; + const style::TextStyle &textStyle() const override; void repaint(const HistoryItem *item) const override; protected: diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 1139bc23f..d0a47931e 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -572,7 +572,7 @@ void Video::initDimensions() { if (title.isEmpty()) { title = lang(lng_media_video); } - _title.setText(st::semiboldFont, title, titleOpts); + _title.setText(st::semiboldTextStyle, title, titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 descriptionLines = withThumb ? (titleHeight > st::semiboldFont->height ? 1 : 2) : 3; @@ -582,7 +582,7 @@ void Video::initDimensions() { if (description.isEmpty()) { description = _duration; } - _description.setText(st::normalFont, description, descriptionOpts); + _description.setText(st::defaultTextStyle, description, descriptionOpts); int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); _minh = st::inlineThumbSize; @@ -683,10 +683,10 @@ void File::initDimensions() { int textWidth = _maxw - (st::msgFileSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; - _description.setText(st::normalFont, _result->getLayoutDescription(), descriptionOpts); + _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); _minh = st::msgFileSize; _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; @@ -892,11 +892,11 @@ void Contact::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; - _description.setText(st::normalFont, _result->getLayoutDescription(), descriptionOpts); + _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); int32 descriptionHeight = qMin(_description.countHeight(_maxw), st::normalFont->height); _minh = st::msgFileSize; @@ -989,13 +989,13 @@ void Article::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 descriptionLines = (_withThumb || _url) ? 2 : 3; QString description = _result->getLayoutDescription(); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; - _description.setText(st::normalFont, description, descriptionOpts); + _description.setText(st::defaultTextStyle, description, descriptionOpts); int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); _minh = titleHeight + descriptionHeight; @@ -1028,14 +1028,14 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) ImagePtr thumb = getResultThumb(); if (thumb->isNull() && !_thumbLetter.isEmpty()) { int32 index = (_thumbLetter.at(0).unicode() % 4); - const style::color *colors[] = { - &st::msgFile3Bg, - &st::msgFile4Bg, - &st::msgFile2Bg, - &st::msgFile1Bg + style::color colors[] = { + st::msgFile3Bg, + st::msgFile4Bg, + st::msgFile2Bg, + st::msgFile1Bg }; - p.fillRect(rthumb, *colors[index]); + p.fillRect(rthumb, colors[index]); if (!_thumbLetter.isEmpty()) { p.setFont(st::linksLetterFont); p.setPen(st::linksLetterFg); @@ -1157,13 +1157,13 @@ void Game::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 descriptionLines = 2; QString description = _result->getLayoutDescription(); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; - _description.setText(st::normalFont, description, descriptionOpts); + _description.setText(st::defaultTextStyle, description, descriptionOpts); int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); _minh = titleHeight + descriptionHeight; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index e0b8bfdee..364149d4c 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -44,31 +44,40 @@ introPhotoIconPosition: point(23px, 25px); introPhotoTop: 10px; introCoverTitle: FlatLabel(defaultFlatLabel) { - font: font(22px semibold); textFg: introTitleFg; align: align(center); + style: TextStyle(defaultTextStyle) { + font: font(22px semibold); + linkFont: font(22px semibold); + linkFontOver: font(22px semibold underline); + } } introCoverTitleTop: 136px; introCoverDescription: FlatLabel(defaultFlatLabel) { - font: font(15px); textFg: introDescriptionFg; align: align(center); -} -introCoverDescriptionTextStyle: TextStyle(defaultTextStyle) { - lineHeight: 24px; + style: TextStyle(defaultTextStyle) { + font: font(15px); + linkFont: font(15px); + linkFontOver: font(15px underline); + lineHeight: 24px; + } } introCoverDescriptionTop: 174px; introTitle: FlatLabel(defaultFlatLabel) { - font: font(17px semibold); textFg: introTitleFg; + style: TextStyle(defaultTextStyle) { + font: font(17px semibold); + linkFont: font(17px semibold); + linkFontOver: font(17px semibold underline); + } } introTitleTop: 1px; introDescription: FlatLabel(defaultFlatLabel) { - font: normalFont; textFg: introDescriptionFg; -} -introDescriptionTextStyle: TextStyle(defaultTextStyle) { - lineHeight: 20px; + style: TextStyle(defaultTextStyle) { + lineHeight: 20px; + } } introDescriptionTop: 34px; @@ -119,7 +128,6 @@ introPasswordHintTop: 151px; introPasswordHint: FlatLabel(introDescription) { textFg: windowFg; } -introPasswordHintTextStyle: introDescriptionTextStyle; introResetButton: RoundButton(defaultLightButton) { textFg: attentionButtonFg; @@ -141,11 +149,11 @@ introErrorTop: 235px; introErrorBelowLinkTop: 220px; introErrorDuration: 200; -introError: introDescription; +introError: FlatLabel(introDescription) { +} introErrorCentered: FlatLabel(introError) { align: align(center); } -introErrorTextStyle: introDescriptionTextStyle; introBackButton: IconButton(defaultIconButton) { width: 56px; diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 021a1a5c3..13431656f 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -87,7 +87,7 @@ CodeWidget::CodeWidget(QWidget *parent, Widget::Data *data) : Step(parent, data) , _callTimer(this) , _callStatus(getData()->callStatus) , _callTimeout(getData()->callTimeout) -, _callLabel(this, st::introDescription, st::introDescriptionTextStyle) +, _callLabel(this, st::introDescription) , _checkRequest(this) { connect(_code, SIGNAL(changed()), this, SLOT(onInputChange())); connect(_callTimer, SIGNAL(timeout()), this, SLOT(onSendCall())); @@ -153,7 +153,7 @@ void CodeWidget::showCodeError(const QString &text) { } void CodeWidget::setInnerFocus() { - _code->setFocus(); + _code->setFocusFast(); } void CodeWidget::activate() { diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 69e5d51f8..681163c5a 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -90,7 +90,7 @@ void PhoneWidget::showSignup() { showPhoneError(lang(lng_bad_phone_noreg)); if (!_signup) { auto signupText = lng_phone_notreg(lt_link_start, textcmdStartLink(1), lt_link_end, textcmdStopLink(), lt_signup_start, textcmdStartLink(2), lt_signup_end, textcmdStopLink()); - auto inner = object_ptr(this, signupText, Ui::FlatLabel::InitType::Rich, st::introDescription, st::introDescriptionTextStyle); + auto inner = object_ptr(this, signupText, Ui::FlatLabel::InitType::Rich, st::introDescription); _signup.create(this, std_::move(inner), st::introErrorDuration); _signup->entity()->setLink(1, MakeShared(qsl("https://telegram.org"), false)); _signup->entity()->setLink(2, MakeShared([this] { @@ -231,7 +231,7 @@ void PhoneWidget::selectCountry(const QString &c) { } void PhoneWidget::setInnerFocus() { - _phone->setFocus(); + _phone->setFocusFast(); } void PhoneWidget::activate() { diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 74039575f..7edd9d250 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -39,7 +39,7 @@ PwdCheckWidget::PwdCheckWidget(QWidget *parent, Widget::Data *data) : Step(paren , _hasRecovery(getData()->hasRecovery) , _hint(getData()->pwdHint) , _pwdField(this, st::introPassword, lang(lng_signin_password)) -, _pwdHint(this, st::introPasswordHint, st::introPasswordHintTextStyle) +, _pwdHint(this, st::introPasswordHint) , _codeField(this, st::introPassword, lang(lng_signin_code)) , _toRecover(this, lang(lng_signin_recover)) , _toPassword(this, lang(lng_signin_try_password)) @@ -77,9 +77,9 @@ void PwdCheckWidget::resizeEvent(QResizeEvent *e) { void PwdCheckWidget::setInnerFocus() { if (_pwdField->isHidden()) { - _codeField->setFocus(); + _codeField->setFocusFast(); } else { - _pwdField->setFocus(); + _pwdField->setFocusFast(); } } diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 3849f9921..c79beb7f3 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -105,9 +105,9 @@ void SignupWidget::resizeEvent(QResizeEvent *e) { void SignupWidget::setInnerFocus() { if (_invertOrder || _last->hasFocus()) { - _last->setFocus(); + _last->setFocusFast(); } else { - _first->setFocus(); + _first->setFocusFast(); } } diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index f9629fbfa..94f67d3b7 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -647,8 +647,7 @@ void Widget::Step::showError(const QString &text) { if (_error) _error->hideAnimated(); } else { if (!_error) { - auto &st = _errorCentered ? st::introErrorCentered : st::introError; - _error.create(this, object_ptr(this, st, st::introErrorTextStyle), st::introErrorDuration); + _error.create(this, object_ptr(this, _errorCentered ? st::introErrorCentered : st::introError), st::introErrorDuration); _error->hideFast(); } _error->entity()->setText(text); @@ -660,12 +659,13 @@ void Widget::Step::showError(const QString &text) { Widget::Step::Step(QWidget *parent, Data *data, bool hasCover) : TWidget(parent) , _data(data) , _hasCover(hasCover) -, _title(this, _hasCover ? st::introCoverTitle : st::introTitle, st::defaultTextStyle) -, _description(this, object_ptr(this, _hasCover ? st::introCoverDescription : st::introDescription, _hasCover ? st::introCoverDescriptionTextStyle : st::introDescriptionTextStyle), st::introErrorDuration) { +, _title(this, _hasCover ? st::introCoverTitle : st::introTitle) +, _description(this, object_ptr(this, _hasCover ? st::introCoverDescription : st::introDescription), st::introErrorDuration) { hide(); } void Widget::Step::prepareShowAnimated(Step *after) { + setInnerFocus(); if (hasCover() || after->hasCover()) { _coverAnimation = prepareCoverAnimation(after); prepareCoverMask(); diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 7ff203715..a73374703 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -383,16 +383,6 @@ bool LayerStackWidget::layerShown() const { void LayerStackWidget::setCacheImages() { auto bodyCache = QPixmap(), mainMenuCache = QPixmap(); - if (isAncestorOf(App::wnd()->focusWidget())) { - setFocus(); - } - if (_mainMenu) { - setAttribute(Qt::WA_OpaquePaintEvent, false); - hideChildren(); - bodyCache = myGrab(App::wnd()->bodyWidget()); - showChildren(); - mainMenuCache = Ui::Shadow::grab(_mainMenu, st::boxRoundShadow, Ui::Shadow::Side::Right); - } auto specialLayerCache = QPixmap(); if (_specialLayer) { auto sides = Ui::Shadow::Side::Left | Ui::Shadow::Side::Right; @@ -408,6 +398,16 @@ void LayerStackWidget::setCacheImages() { if (auto layer = currentLayer()) { layerCache = Ui::Shadow::grab(layer, st::boxRoundShadow); } + if (isAncestorOf(App::wnd()->focusWidget())) { + setFocus(); + } + if (_mainMenu) { + setAttribute(Qt::WA_OpaquePaintEvent, false); + hideChildren(); + bodyCache = myGrab(App::wnd()->bodyWidget()); + showChildren(); + mainMenuCache = Ui::Shadow::grab(_mainMenu, st::boxRoundShadow, Ui::Shadow::Side::Right); + } setAttribute(Qt::WA_OpaquePaintEvent, !bodyCache.isNull()); updateLayerBoxes(); _background->setCacheImages(std_::move(bodyCache), std_::move(mainMenuCache), std_::move(specialLayerCache), std_::move(layerCache)); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index c30aa186a..6508cd1a4 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -210,44 +210,44 @@ int32 documentColorIndex(DocumentData *document, QString &ext) { return colorIndex; } -const style::color &documentColor(int32 colorIndex) { - static const style::color *colors[] = { - &st::msgFile1Bg, - &st::msgFile2Bg, - &st::msgFile3Bg, - &st::msgFile4Bg +style::color documentColor(int32 colorIndex) { + const style::color colors[] = { + st::msgFile1Bg, + st::msgFile2Bg, + st::msgFile3Bg, + st::msgFile4Bg }; - return *colors[colorIndex & 3]; + return colors[colorIndex & 3]; } -const style::color &documentDarkColor(int32 colorIndex) { - static const style::color *colors[] = { - &st::msgFile1BgDark, - &st::msgFile2BgDark, - &st::msgFile3BgDark, - &st::msgFile4BgDark +style::color documentDarkColor(int32 colorIndex) { + static style::color colors[] = { + st::msgFile1BgDark, + st::msgFile2BgDark, + st::msgFile3BgDark, + st::msgFile4BgDark }; - return *colors[colorIndex & 3]; + return colors[colorIndex & 3]; } -const style::color &documentOverColor(int32 colorIndex) { - static const style::color *colors[] = { - &st::msgFile1BgOver, - &st::msgFile2BgOver, - &st::msgFile3BgOver, - &st::msgFile4BgOver +style::color documentOverColor(int32 colorIndex) { + static style::color colors[] = { + st::msgFile1BgOver, + st::msgFile2BgOver, + st::msgFile3BgOver, + st::msgFile4BgOver }; - return *colors[colorIndex & 3]; + return colors[colorIndex & 3]; } -const style::color &documentSelectedColor(int32 colorIndex) { - static const style::color *colors[] = { - &st::msgFile1BgSelected, - &st::msgFile2BgSelected, - &st::msgFile3BgSelected, - &st::msgFile4BgSelected +style::color documentSelectedColor(int32 colorIndex) { + static style::color colors[] = { + st::msgFile1BgSelected, + st::msgFile2BgSelected, + st::msgFile3BgSelected, + st::msgFile4BgSelected }; - return *colors[colorIndex & 3]; + return colors[colorIndex & 3]; } RoundCorners documentCorners(int32 colorIndex) { diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 458cfe54c..fb6a754b6 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -84,10 +84,10 @@ QString formatPlayedText(qint64 played, qint64 duration); QString documentName(DocumentData *document); TextWithEntities documentNameWithEntities(DocumentData *document); int32 documentColorIndex(DocumentData *document, QString &ext); -const style::color &documentColor(int colorIndex); -const style::color &documentDarkColor(int colorIndex); -const style::color &documentOverColor(int colorIndex); -const style::color &documentSelectedColor(int colorIndex); +style::color documentColor(int colorIndex); +style::color documentDarkColor(int colorIndex); +style::color documentOverColor(int colorIndex); +style::color documentSelectedColor(int colorIndex); RoundCorners documentCorners(int colorIndex); bool documentIsValidMediaFile(const QString &filepath); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 6b27f0a37..a957d553e 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -299,8 +299,8 @@ void MainWidget::updateForwardingTexts() { text = lng_forward_messages(lt_count, _toForward.size()); } } - _toForwardFrom.setText(st::msgServiceNameFont, from, _textNameOptions); - _toForwardText.setText(st::msgFont, textClean(text), _textDlgOptions); + _toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions); + _toForwardText.setText(st::messageTextStyle, textClean(text), _textDlgOptions); _toForwardNameVersion = version; } @@ -1087,7 +1087,7 @@ void MainWidget::onCacheBackground() { _cachedBackground = App::pixmapFromImageInPlace(std_::move(result)); } else { QRect to, from; - backgroundParams(_willCacheFor, to, from); + Window::Theme::ComputeBackgroundRects(_willCacheFor, bg.size(), to, from); _cachedX = to.x(); _cachedY = to.y(); _cachedBackground = App::pixmapFromImageInPlace(bg.toImage().copy(from).scaled(to.width() * cIntRetinaFactor(), to.height() * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); @@ -1854,31 +1854,6 @@ QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) { return QPixmap(); } -void MainWidget::backgroundParams(const QRect &forRect, QRect &to, QRect &from) const { - auto bg = Window::Theme::Background()->image().size(); - if (uint64(bg.width()) * forRect.height() > uint64(bg.height()) * forRect.width()) { - float64 pxsize = forRect.height() / float64(bg.height()); - int takewidth = qCeil(forRect.width() / pxsize); - if (takewidth > bg.width()) { - takewidth = bg.width(); - } else if ((bg.width() % 2) != (takewidth % 2)) { - ++takewidth; - } - to = QRect(int((forRect.width() - takewidth * pxsize) / 2.), 0, qCeil(takewidth * pxsize), forRect.height()); - from = QRect((bg.width() - takewidth) / 2, 0, takewidth, bg.height()); - } else { - float64 pxsize = forRect.width() / float64(bg.width()); - int takeheight = qCeil(forRect.height() / pxsize); - if (takeheight > bg.height()) { - takeheight = bg.height(); - } else if ((bg.height() % 2) != (takeheight % 2)) { - ++takeheight; - } - to = QRect(0, int((forRect.height() - takeheight * pxsize) / 2.), forRect.width(), qCeil(takeheight * pxsize)); - from = QRect(0, (bg.height() - takeheight) / 2, bg.width(), takeheight); - } -} - void MainWidget::updateScrollColors() { _history->updateScrollColors(); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index ab12d5423..244f3caf6 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -316,7 +316,6 @@ public: bool isIdle() const; QPixmap cachedBackground(const QRect &forRect, int &x, int &y); - void backgroundParams(const QRect &forRect, QRect &to, QRect &from) const; void updateScrollColors(); void setChatBackground(const App::WallPaper &wp); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 74f64226f..8472df3a9 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -262,6 +262,7 @@ void MainWindow::setupPasscode() { updateControlsGeometry(); if (_main) _main->hide(); + _mediaView->hide(); Ui::hideSettingsAndLayer(true); if (_intro) _intro->hide(); if (animated) { @@ -1291,7 +1292,7 @@ QImage MainWindow::iconLarge() const { return iconbig256; } -void MainWindow::placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) { +void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) { QPainter p(&img); QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 10, 1, 10, QChar('0')); @@ -1329,7 +1330,7 @@ void MainWindow::placeSmallCounter(QImage &img, int size, int count, const style } -QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) { +QImage MainWindow::iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) { bool layer = false; if (size < 0) { size = -size; diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index f69587007..41535c0ba 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -144,7 +144,7 @@ public: bool isActive(bool cached = true) const; void hideMediaview(); - QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) override; + QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) override; bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(QWidget *w, QPaintEvent *e) { @@ -226,7 +226,7 @@ private: QPixmap grabInner(); - void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) override; + void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) override; QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64; struct DelayedServiceMsg { diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 067147e94..210dae4aa 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -54,7 +54,6 @@ mediaPlayerCloseRight: 0px; mediaPlayerName: FlatLabel(defaultFlatLabel) { maxHeight: 20px; - textFg: windowFg; } mediaPlayerTime: LabelSimple(defaultLabelSimple) { textFg: windowSubTextFg; diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 417904fa3..9159541f5 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -229,7 +229,7 @@ void CoverWidget::updatePlayPrevNextPositions() { } void CoverWidget::updateLabelPositions() { - _nameLabel->moveToLeft(st::mediaPlayerPanelPadding, st::mediaPlayerPanelNameTop - st::mediaPlayerName.font->ascent); + _nameLabel->moveToLeft(st::mediaPlayerPanelPadding, st::mediaPlayerPanelNameTop - st::mediaPlayerName.style.font->ascent); _timeLabel->moveToRight(st::mediaPlayerPanelPadding, st::mediaPlayerPanelNameTop - st::mediaPlayerTime.font->ascent); } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index dabfd554d..b56bcda22 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -301,7 +301,7 @@ void Widget::updateLabelsGeometry() { widthForName -= _timeLabel->width() + 2 * st::normalFont->spacew; _nameLabel->resizeToWidth(widthForName); - _nameLabel->moveToLeft(left, st::mediaPlayerNameTop - st::mediaPlayerName.font->ascent); + _nameLabel->moveToLeft(left, st::mediaPlayerNameTop - st::mediaPlayerName.style.font->ascent); _timeLabel->moveToRight(right, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent); } diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index ddad573d3..847e9f645 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -81,13 +81,13 @@ void Controller::handleSeekFinished(float64 progress) { void Controller::showAnimated() { startFading([this]() { - _fadeAnimation->fadeIn(st::mvShowDuration); + _fadeAnimation->fadeIn(st::mediaviewShowDuration); }); } void Controller::hideAnimated() { startFading([this]() { - _fadeAnimation->fadeOut(st::mvHideDuration); + _fadeAnimation->fadeOut(st::mediaviewHideDuration); }); } diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index a18ee47de..0fd201b43 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -150,3 +150,70 @@ mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { shadow: mediaviewMenuShadow; } } + +mediaviewSaveMsgCheck: icon {{ "mediaview_save_check", mediaviewSaveMsgFg }}; +mediaviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); +mediaviewSaveMsgCheckPos: point(23px, 21px); +mediaviewSaveMsgShowing: 200; +mediaviewSaveMsgShown: 2000; +mediaviewSaveMsgHiding: 2500; +mediaviewSaveMsgStyle: TextStyle(defaultTextStyle) { + font: font(16px); + linkFont: font(16px); + linkFontOver: font(16px underline); +} +mediaviewTextPalette: TextPalette(defaultTextPalette) { + linkFg: mediaviewTextLinkFg; +} + +mediaviewCaptionStyle: defaultTextStyle; + +mediaviewThickFont: semiboldFont; +mediaviewFont: normalFont; +mediaviewTextStyle: defaultTextStyle; + +mediaviewTextLeft: 16px; +mediaviewTextSkip: 10px; +mediaviewHeaderTop: 48px; +mediaviewTextTop: 24px; +mediaviewTextOpacity: 0.5; +mediaviewTextOverOpacity: 1; + +mediaviewIconOpacity: 0.45; +mediaviewIconOverOpacity: 1; +mediaviewControlBgOpacity: 0.3; +mediaviewControlMargin: 0px; +mediaviewControlSize: 90px; +mediaviewIconSize: size(60px, 56px); + +mediaviewWaitHide: 2000; +mediaviewHideDuration: 1000; +mediaviewShowDuration: 200; +mediaviewFadeDuration: 150; + +mediaviewDeltaFromLastAction: 5px; +mediaviewSwipeDistance: 80px; + +mediaviewCaptionPadding: margins(18px, 10px, 18px, 10px); +mediaviewCaptionMargin: size(11px, 11px); +mediaviewCaptionRadius: 2px; + +themePreviewSize: size(903px, 584px); +themePreviewBg: windowBg; +themePreviewOverlayOpacity: 0.7; +themePreviewMargin: margins(36px, 52px, 36px, 88px); +themePreviewTitleTop: 14px; +themePreviewTitleFg: windowBoldFg; +themePreviewTitleFont: font(17px semibold); +themePreviewLoadingFont: font(16px); +themePreviewLoadingFg: windowSubTextFg; +themePreviewApplyButton: RoundButton(defaultActiveButton) { + height: 38px; + font: font(15px semibold); +} +themePreviewCancelButton: RoundButton(defaultLightButton) { + height: 38px; + font: font(15px semibold); +} +themePreviewButtonsSkip: 20px; +themePreviewDialogsWidth: 312px; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 8e5446ae5..c46551c52 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -34,6 +34,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "media/media_audio.h" #include "history/history_media_types.h" +#include "window/window_theme_preview.h" +#include "core/task_queue.h" namespace { @@ -71,13 +73,13 @@ MediaView::MediaView() : TWidget(App::wnd()) , _docSaveAs(this, lang(lng_mediaview_save_as), st::mediaviewFileLink) , _docCancel(this, lang(lng_cancel), st::mediaviewFileLink) , _radial(animation(this, &MediaView::step_radial)) -, _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction) +, _lastAction(-st::mediaviewDeltaFromLastAction, -st::mediaviewDeltaFromLastAction) , _a_state(animation(this, &MediaView::step_state)) , _dropdown(this, st::mediaviewDropdownMenu) { TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); - _saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom); - _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::medviewSaveMsgPadding.left() + st::medviewSaveMsgPadding.right(), st::medviewSaveMsgFont->height + st::medviewSaveMsgPadding.top() + st::medviewSaveMsgPadding.bottom()); + _saveMsgText.setRichText(st::mediaviewSaveMsgStyle, lang(lng_mediaview_saved), _textDlgOptions, custom); + _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::mediaviewSaveMsgPadding.left() + st::mediaviewSaveMsgPadding.right(), st::mediaviewSaveMsgStyle.font->height + st::mediaviewSaveMsgPadding.top() + st::mediaviewSaveMsgPadding.bottom()); _saveMsgText.setLink(1, MakeShared([this] { showSaveMsgFile(); })); connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(onScreenResized(int))); @@ -132,12 +134,12 @@ void MediaView::moveToScreen() { setGeometry(avail); } - int32 navSkip = 2 * st::mvControlMargin + st::mvControlSize; - _closeNav = myrtlrect(width() - st::mvControlMargin - st::mvControlSize, st::mvControlMargin, st::mvControlSize, st::mvControlSize); + int32 navSkip = 2 * st::mediaviewControlMargin + st::mediaviewControlSize; + _closeNav = myrtlrect(width() - st::mediaviewControlMargin - st::mediaviewControlSize, st::mediaviewControlMargin, st::mediaviewControlSize, st::mediaviewControlSize); _closeNavIcon = centerrect(_closeNav, st::mediaviewClose); - _leftNav = myrtlrect(st::mvControlMargin, navSkip, st::mvControlSize, height() - 2 * navSkip); + _leftNav = myrtlrect(st::mediaviewControlMargin, navSkip, st::mediaviewControlSize, height() - 2 * navSkip); _leftNavIcon = centerrect(_leftNav, st::mediaviewLeft); - _rightNav = myrtlrect(width() - st::mvControlMargin - st::mvControlSize, navSkip, st::mvControlSize, height() - 2 * navSkip); + _rightNav = myrtlrect(width() - st::mediaviewControlMargin - st::mediaviewControlSize, navSkip, st::mediaviewControlSize, height() - 2 * navSkip); _rightNavIcon = centerrect(_rightNav, st::mediaviewRight); _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2); @@ -194,6 +196,10 @@ bool MediaView::fileShown() const { return !_current.isNull() || gifShown(); } +bool MediaView::fileBubbleShown() const { + return _doc && !fileShown() && !_themePreviewShown; +} + bool MediaView::gifShown() const { if (_gif && _gif->ready()) { if (!_gif->started()) { @@ -220,7 +226,7 @@ void MediaView::stopGif() { } void MediaView::documentUpdated(DocumentData *doc) { - if (_doc && _doc == doc && !fileShown()) { + if (fileBubbleShown() && _doc == doc) { if ((_doc->loading() && _docCancel->isHidden()) || (!_doc->loading() && !_docCancel->isHidden())) { updateControls(); } else if (_doc->loading()) { @@ -238,7 +244,7 @@ void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { } void MediaView::updateDocSize() { - if (!_doc || fileShown()) return; + if (!fileBubbleShown()) return; if (_doc->loading()) { quint64 ready = _doc->loadOffset(), total = _doc->size; @@ -262,16 +268,16 @@ void MediaView::updateDocSize() { } else { _docSize = formatSizeText(_doc->size); } - _docSizeWidth = st::mvFont->width(_docSize); + _docSizeWidth = st::mediaviewFont->width(_docSize); int32 maxw = st::mediaviewFileSize.width() - st::mediaviewFileIconSize - st::mediaviewFilePadding * 3; if (_docSizeWidth > maxw) { - _docSize = st::mvFont->elided(_docSize, maxw); - _docSizeWidth = st::mvFont->width(_docSize); + _docSize = st::mediaviewFont->elided(_docSize, maxw); + _docSizeWidth = st::mediaviewFont->width(_docSize); } } void MediaView::updateControls() { - if (_doc && !fileShown()) { + if (fileBubbleShown()) { if (_doc->loading()) { _docDownload->hide(); _docSaveAs->hide(); @@ -299,10 +305,12 @@ void MediaView::updateControls() { } radialStart(); + updateThemePreviewGeometry(); + _saveVisible = ((_photo && _photo->loaded()) || (_doc && (_doc->loaded(DocumentData::FilePathResolveChecked) || (!fileShown() && (_photo || _doc))))); - _saveNav = myrtlrect(width() - st::mvIconSize.width() * 2, height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height()); + _saveNav = myrtlrect(width() - st::mediaviewIconSize.width() * 2, height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height()); _saveNavIcon = centerrect(_saveNav, st::mediaviewSave); - _moreNav = myrtlrect(width() - st::mvIconSize.width(), height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height()); + _moreNav = myrtlrect(width() - st::mediaviewIconSize.width(), height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height()); _moreNavIcon = centerrect(_moreNav, st::mediaviewMore); QDateTime d, dNow(date(unixtime())); @@ -321,12 +329,12 @@ void MediaView::updateControls() { _dateText = lng_mediaview_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(cTimeFormat())); } if (_from) { - _fromName.setText(st::mvFont, (_from->migrateTo() ? _from->migrateTo() : _from)->name, _textNameOptions); - _nameNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mvFont->height); - _dateNav = myrtlrect(st::mvTextLeft + _nameNav.width() + st::mvTextSkip, height() - st::mvTextTop, st::mvFont->width(_dateText), st::mvFont->height); + _fromName.setText(st::mediaviewTextStyle, (_from->migrateTo() ? _from->migrateTo() : _from)->name, _textNameOptions); + _nameNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mediaviewFont->height); + _dateNav = myrtlrect(st::mediaviewTextLeft + _nameNav.width() + st::mediaviewTextSkip, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height); } else { _nameNav = QRect(); - _dateNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, st::mvFont->width(_dateText), st::mvFont->height); + _dateNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height); } updateHeader(); if (_photo || (_history && (_overview == OverviewPhotos || _overview == OverviewChatPhotos || _overview == OverviewFiles || _overview == OverviewVideos))) { @@ -354,9 +362,9 @@ void MediaView::updateControls() { if (!_caption.isEmpty()) { int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width()); - int32 maxw = qMin(qMax(width() - 2 * skipw - st::mvCaptionPadding.left() - st::mvCaptionPadding.right() - 2 * st::mvCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth()); - int32 maxh = qMin(_caption.countHeight(maxw), int(height() / 4 - st::mvCaptionPadding.top() - st::mvCaptionPadding.bottom() - 2 * st::mvCaptionMargin.height())); - _captionRect = QRect((width() - maxw) / 2, height() - maxh - st::mvCaptionPadding.bottom() - st::mvCaptionMargin.height(), maxw, maxh); + int32 maxw = qMin(qMax(width() - 2 * skipw - st::mediaviewCaptionPadding.left() - st::mediaviewCaptionPadding.right() - 2 * st::mediaviewCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth()); + int32 maxh = qMin(_caption.countHeight(maxw), int(height() / 4 - st::mediaviewCaptionPadding.top() - st::mediaviewCaptionPadding.bottom() - 2 * st::mediaviewCaptionMargin.height())); + _captionRect = QRect((width() - maxw) / 2, height() - maxh - st::mediaviewCaptionPadding.bottom() - st::mediaviewCaptionMargin.height(), maxw, maxh); } else { _captionRect = QRect(); } @@ -410,7 +418,7 @@ void MediaView::step_state(TimeMs ms, bool timer) { case OverMore: update(_moreNav); break; default: break; } - float64 dt = float64(ms - start) / st::mvFadeDuration; + float64 dt = float64(ms - start) / st::mediaviewFadeDuration; if (dt >= 1) { _animOpacities.remove(i.key()); i = _animations.erase(i); @@ -420,7 +428,7 @@ void MediaView::step_state(TimeMs ms, bool timer) { } } if (_controlsState == ControlsShowing || _controlsState == ControlsHiding) { - float64 dt = float64(ms - _controlsAnimStarted) / (_controlsState == ControlsShowing ? st::mvShowDuration : st::mvHideDuration); + float64 dt = float64(ms - _controlsAnimStarted) / (_controlsState == ControlsShowing ? st::mediaviewShowDuration : st::mediaviewHideDuration); if (dt >= 1) { a_cOpacity.finish(); _controlsState = (_controlsState == ControlsShowing ? ControlsShown : ControlsHidden); @@ -428,7 +436,7 @@ void MediaView::step_state(TimeMs ms, bool timer) { } else { a_cOpacity.update(dt, anim::linear); } - QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav + _captionRect.marginsAdded(st::mvCaptionPadding); + QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav + _captionRect.marginsAdded(st::mediaviewCaptionPadding); update(toUpdate); if (dt < 1) result = true; } @@ -500,7 +508,7 @@ void MediaView::step_radial(TimeMs ms, bool timer) { } else { const FileLocation &location(_doc->location(true)); if (location.accessEnable()) { - if (_doc->isAnimation() || _doc->isVideo() || QImageReader(location.name()).canRead()) { + if (_doc->isAnimation() || _doc->isVideo() || _doc->isTheme() || QImageReader(location.name()).canRead()) { displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); } location.accessDisable(); @@ -626,7 +634,7 @@ void MediaView::close() { void MediaView::activateControls() { if (!_menu && !_mousePressed) { - _controlsHideTimer.start(int(st::mvWaitHide)); + _controlsHideTimer.start(int(st::mediaviewWaitHide)); } if (_fullScreenVideo) { if (_clipController) { @@ -1120,6 +1128,7 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { stopGif(); + destroyThemePreview(); _doc = nullptr; _fullScreenVideo = false; _photo = photo; @@ -1132,7 +1141,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { _caption = Text(); if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : nullptr) { if (HistoryPhoto *photoMsg = dynamic_cast(itemMsg->getMedia())) { - _caption.setMarkedText(st::mvCaptionFont, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions); + _caption.setMarkedText(st::mediaviewCaptionStyle, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions); } } @@ -1166,14 +1175,25 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { displayFinished(); } +void MediaView::destroyThemePreview() { + _themePreviewShown = false; + _themePreview.reset(); + _themeApply.destroy(); + _themeCancel.destroy(); +} + void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty messages shown as docs: doc can be NULL - if (!doc || (!doc->isAnimation() && !doc->isVideo()) || doc != _doc || (item && (item->id != _msgid || (item->history() != (_msgmigrated ? _migrated : _history))))) { + auto documentChanged = (!doc || doc != _doc || (item && (item->id != _msgid || (item->history() != (_msgmigrated ? _migrated : _history))))); + if (documentChanged || (!doc->isAnimation() && !doc->isVideo())) { _fullScreenVideo = false; _current = QPixmap(); stopGif(); } else if (gifShown()) { _current = QPixmap(); } + if (documentChanged || !doc->isTheme()) { + destroyThemePreview(); + } _doc = doc; _photo = nullptr; _radial.stop(); @@ -1196,6 +1216,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty if (_doc->isAnimation() || _doc->isVideo()) { initAnimation(); + } else if (_doc->isTheme()) { + initThemePreview(); } else { const FileLocation &location(_doc->location(true)); if (location.accessEnable()) { @@ -1209,10 +1231,10 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty } _docIconRect = QRect((width() - st::mediaviewFileIconSize) / 2, (height() - st::mediaviewFileIconSize) / 2, st::mediaviewFileIconSize, st::mediaviewFileIconSize); - if (!fileShown()) { + if (fileBubbleShown()) { if (!_doc || _doc->thumb->isNull()) { int32 colorIndex = documentColorIndex(_doc, _docExt); - _docIconColor = &documentColor(colorIndex); + _docIconColor = documentColor(colorIndex); const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; _docIcon = thumbs[colorIndex]; @@ -1255,6 +1277,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _docRect = QRect((width() - st::mediaviewFileSize.width()) / 2, (height() - st::mediaviewFileSize.height()) / 2, st::mediaviewFileSize.width(), st::mediaviewFileSize.height()); _docIconRect = myrtlrect(_docRect.x() + st::mediaviewFilePadding, _docRect.y() + st::mediaviewFilePadding, st::mediaviewFileIconSize, st::mediaviewFileIconSize); + } else if (_themePreviewShown) { + updateThemePreviewGeometry(); } else if (!_current.isNull()) { _current.setDevicePixelRatio(cRetinaFactor()); _w = convertScale(_current.width()); @@ -1304,6 +1328,25 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty displayFinished(); } +void MediaView::updateThemePreviewGeometry() { + if (_themePreviewShown) { + auto previewRect = QRect((width() - st::themePreviewSize.width()) / 2, (height() - st::themePreviewSize.height()) / 2, st::themePreviewSize.width(), st::themePreviewSize.height()); + _themePreviewRect = previewRect.marginsAdded(st::themePreviewMargin); + if (_themeApply) { + auto right = width() - _themePreviewRect.x() - _themePreviewRect.width() + st::themePreviewMargin.right(); + _themeApply->moveToRight(right, _themePreviewRect.y() + _themePreviewRect.height() - st::themePreviewMargin.bottom() + (st::themePreviewMargin.bottom() - _themeApply->height()) / 2); + right += _themeApply->width() + st::themePreviewButtonsSkip; + _themeCancel->moveToRight(right, _themeApply->y()); + } + + // For context menu event. + _x = _themePreviewRect.x(); + _y = _themePreviewRect.y(); + _w = _themePreviewRect.width(); + _h = _themePreviewRect.height(); + } +} + void MediaView::displayFinished() { updateControls(); if (isHidden()) { @@ -1363,6 +1406,59 @@ void MediaView::createClipReader() { createClipController(); } +void MediaView::initThemePreview() { + t_assert(_doc && _doc->isTheme()); + + auto &location = _doc->location(); + if (!location.isEmpty() && location.accessEnable()) { + _themePreviewShown = true; + auto path = _doc->location().name(); + auto id = _themePreviewId = rand_value(); + auto ready = base::lambda_guarded(this, [this, id](std_::unique_ptr result) { + if (id != _themePreviewId) { + return; + } + _themePreviewId = 0; + _themePreview = std_::move(result); + if (_themePreview) { + _themeApply.create(this, lang(lng_theme_preview_apply), st::themePreviewApplyButton); + _themeApply->show(); + _themeApply->setClickedCallback([this] { + auto preview = std_::move(_themePreview); + close(); + Window::Theme::Apply(std_::move(preview)); + }); + _themeCancel.create(this, lang(lng_cancel), st::themePreviewCancelButton); + _themeCancel->show(); + _themeCancel->setClickedCallback([this] { close(); }); + updateControls(); + } + update(); + }); + struct mutable_ready { + mutable_ready(decltype(ready) value) : value(std_::move(value)) { + } + mutable decltype(ready) value; + }; + struct mutable_result { + mutable_result(std_::unique_ptr value) : value(std_::move(value)) { + } + mutable std_::unique_ptr value; + }; + Window::Theme::CurrentData current; + current.backgroundId = Window::Theme::Background()->id(); + current.backgroundImage = Window::Theme::Background()->image(); + current.backgroundTiled = Window::Theme::Background()->tile(); + base::TaskQueue::Normal().Put([path, current, callback = mutable_ready(std_::move(ready))]() { + auto preview = Window::Theme::GeneratePreview(path, current); + base::TaskQueue::Main().Put([result = mutable_result(std_::move(preview)), callback = std_::move(callback.value)]() { + callback(std_::move(result.value)); + }); + }); + location.accessDisable(); + } +} + void MediaView::createClipController() { if (!_doc->isVideo()) return; @@ -1389,7 +1485,7 @@ void MediaView::setClipControllerGeometry() { int controllerBottom = _captionRect.isEmpty() ? height() : _captionRect.y(); _clipController->setGeometry( (width() - _clipController->width()) / 2, - controllerBottom - _clipController->height() - st::mvCaptionPadding.bottom() - st::mvCaptionMargin.height(), + controllerBottom - _clipController->height() - st::mediaviewCaptionPadding.bottom() - st::mediaviewCaptionMargin.height(), st::mediaviewControllerSize.width(), st::mediaviewControllerSize.height()); myEnsureResized(_clipController); @@ -1603,26 +1699,26 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_saveMsgStarted) { auto ms = getms(); - float64 dt = float64(ms) - _saveMsgStarted, hidingDt = dt - st::medviewSaveMsgShowing - st::medviewSaveMsgShown; - if (dt < st::medviewSaveMsgShowing + st::medviewSaveMsgShown + st::medviewSaveMsgHiding) { + float64 dt = float64(ms) - _saveMsgStarted, hidingDt = dt - st::mediaviewSaveMsgShowing - st::mediaviewSaveMsgShown; + if (dt < st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + st::mediaviewSaveMsgHiding) { if (hidingDt >= 0 && _saveMsgOpacity.to() > 0.5) { _saveMsgOpacity.start(0); } - float64 progress = (hidingDt >= 0) ? (hidingDt / st::medviewSaveMsgHiding) : (dt / st::medviewSaveMsgShowing); + float64 progress = (hidingDt >= 0) ? (hidingDt / st::mediaviewSaveMsgHiding) : (dt / st::mediaviewSaveMsgShowing); _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); if (_saveMsgOpacity.current() > 0) { p.setOpacity(_saveMsgOpacity.current()); App::roundRect(p, _saveMsg, st::mediaviewSaveMsgBg, MediaviewSaveCorners); - st::medviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::medviewSaveMsgCheckPos, width()); + st::mediaviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::mediaviewSaveMsgCheckPos, width()); p.setPen(st::mediaviewSaveMsgFg); - textstyleSet(&st::mediaviewTextStyle); - _saveMsgText.draw(p, _saveMsg.x() + st::medviewSaveMsgPadding.left(), _saveMsg.y() + st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); - textstyleRestore(); + p.setTextPalette(st::mediaviewTextPalette); + _saveMsgText.draw(p, _saveMsg.x() + st::mediaviewSaveMsgPadding.left(), _saveMsg.y() + st::mediaviewSaveMsgPadding.top(), _saveMsg.width() - st::mediaviewSaveMsgPadding.left() - st::mediaviewSaveMsgPadding.right()); + p.restoreTextPalette(); p.setOpacity(1); } if (_full >= 1) { - auto nextFrame = (dt < st::medviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::medviewSaveMsgShowing + st::medviewSaveMsgShown + 1 - dt); + auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); _saveMsgUpdater.start(nextFrame); } } else { @@ -1630,6 +1726,8 @@ void MediaView::paintEvent(QPaintEvent *e) { } } } + } else if (_themePreviewShown) { + paintThemePreview(p, r); } else { if (_docRect.intersects(r)) { p.fillRect(_docRect, st::mediaviewFileBg); @@ -1642,7 +1740,7 @@ void MediaView::paintEvent(QPaintEvent *e) { radialOpacity = _radial.opacity(); } if (!_doc || _doc->thumb->isNull()) { - p.fillRect(_docIconRect, (*_docIconColor)->b); + p.fillRect(_docIconRect, _docIconColor); if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); p.setPen(st::mediaviewFileExtFg); @@ -1666,7 +1764,7 @@ void MediaView::paintEvent(QPaintEvent *e) { p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileNameTop, width(), _docName, _docNameWidth); p.setPen(st::mediaviewFileSizeFg); - p.setFont(st::mvFont); + p.setFont(st::mediaviewFont); p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileSizeTop, width(), _docSize, _docSizeWidth); } } @@ -1685,7 +1783,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } if (_leftNavIcon.intersects(r)) { - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); st::mediaviewLeft.paintInCenter(p, _leftNavIcon); } } @@ -1701,7 +1799,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } if (_rightNavIcon.intersects(r)) { - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); st::mediaviewRight.paintInCenter(p, _rightNavIcon); } } @@ -1717,7 +1815,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } if (_closeNavIcon.intersects(r)) { - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); st::mediaviewClose.paintInCenter(p, _closeNavIcon); } } @@ -1725,71 +1823,71 @@ void MediaView::paintEvent(QPaintEvent *e) { // save button if (_saveVisible && _saveNavIcon.intersects(r)) { auto o = overLevel(OverSave); - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); st::mediaviewSave.paintInCenter(p, _saveNavIcon); } // more area if (_moreNavIcon.intersects(r)) { auto o = overLevel(OverMore); - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); st::mediaviewMore.paintInCenter(p, _moreNavIcon); } p.setPen(st::mediaviewControlFg); - p.setFont(st::mvThickFont); + p.setFont(st::mediaviewThickFont); // header if (_headerNav.intersects(r)) { auto o = _headerHasLink ? overLevel(OverHeader) : 0; - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); - p.drawText(_headerNav.left(), _headerNav.top() + st::mvThickFont->ascent, _headerText); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); + p.drawText(_headerNav.left(), _headerNav.top() + st::mediaviewThickFont->ascent, _headerText); if (o > 0) { p.setOpacity(o * co); - p.drawLine(_headerNav.left(), _headerNav.top() + st::mvThickFont->ascent + 1, _headerNav.right(), _headerNav.top() + st::mvThickFont->ascent + 1); + p.drawLine(_headerNav.left(), _headerNav.top() + st::mediaviewThickFont->ascent + 1, _headerNav.right(), _headerNav.top() + st::mediaviewThickFont->ascent + 1); } } - p.setFont(st::mvFont); + p.setFont(st::mediaviewFont); // name if (_from && _nameNav.intersects(r)) { float64 o = overLevel(OverName); - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); _fromName.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width()); if (o > 0) { p.setOpacity(o * co); - p.drawLine(_nameNav.left(), _nameNav.top() + st::mvFont->ascent + 1, _nameNav.right(), _nameNav.top() + st::mvFont->ascent + 1); + p.drawLine(_nameNav.left(), _nameNav.top() + st::mediaviewFont->ascent + 1, _nameNav.right(), _nameNav.top() + st::mediaviewFont->ascent + 1); } } // date if (_dateNav.intersects(r)) { float64 o = overLevel(OverDate); - p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); - p.drawText(_dateNav.left(), _dateNav.top() + st::mvFont->ascent, _dateText); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); + p.drawText(_dateNav.left(), _dateNav.top() + st::mediaviewFont->ascent, _dateText); if (o > 0) { p.setOpacity(o * co); - p.drawLine(_dateNav.left(), _dateNav.top() + st::mvFont->ascent + 1, _dateNav.right(), _dateNav.top() + st::mvFont->ascent + 1); + p.drawLine(_dateNav.left(), _dateNav.top() + st::mediaviewFont->ascent + 1, _dateNav.right(), _dateNav.top() + st::mediaviewFont->ascent + 1); } } // caption if (!_caption.isEmpty()) { - QRect outer(_captionRect.marginsAdded(st::mvCaptionPadding)); + QRect outer(_captionRect.marginsAdded(st::mediaviewCaptionPadding)); if (outer.intersects(r)) { p.setOpacity(co); p.setBrush(st::mediaviewCaptionBg); p.setPen(Qt::NoPen); - p.drawRoundedRect(outer, st::mvCaptionRadius, st::mvCaptionRadius); + p.drawRoundedRect(outer, st::mediaviewCaptionRadius, st::mediaviewCaptionRadius); if (_captionRect.intersects(r)) { - textstyleSet(&st::mediaviewTextStyle); + p.setTextPalette(st::mediaviewTextPalette); p.setPen(st::mediaviewCaptionFg); - _caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mvCaptionFont->height); - textstyleRestore(); + _caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mediaviewCaptionStyle.font->height); + p.restoreTextPalette(); } } } @@ -1828,6 +1926,49 @@ void MediaView::paintDocRadialLoading(Painter &p, bool radial, float64 radialOpa } } +void MediaView::paintThemePreview(Painter &p, QRect clip) { + auto fill = _themePreviewRect.intersected(clip); + if (!fill.isEmpty()) { + if (_themePreview) { + p.drawPixmapLeft(_themePreviewRect.x(), _themePreviewRect.y(), width(), _themePreview->preview); + } else { + p.fillRect(fill, st::themePreviewBg); + p.setFont(st::themePreviewLoadingFont); + p.setPen(st::themePreviewLoadingFg); + p.drawText(_themePreviewRect, lang(_themePreviewId ? lng_theme_preview_generating : lng_theme_preview_invalid), QTextOption(style::al_center)); + } + } + + auto fillOverlay = [this, &p, clip](QRect fill) { + auto clipped = fill.intersected(clip); + if (!clipped.isEmpty()) { + p.setOpacity(st::themePreviewOverlayOpacity); + p.fillRect(clipped, st::themePreviewBg); + p.setOpacity(1.); + } + }; + auto titleRect = QRect(_themePreviewRect.x(), _themePreviewRect.y(), _themePreviewRect.width(), st::themePreviewMargin.top()); + if (titleRect.x() < 0) { + titleRect = QRect(0, _themePreviewRect.y(), width(), st::themePreviewMargin.top()); + } + if (auto fillTitleRect = (titleRect.y() < 0)) { + titleRect.moveTop(0); + fillOverlay(titleRect); + } + titleRect = titleRect.marginsRemoved(QMargins(st::themePreviewMargin.left(), st::themePreviewTitleTop, st::themePreviewMargin.right(), titleRect.height() - st::themePreviewTitleTop - st::themePreviewTitleFont->height)); + if (titleRect.intersects(clip)) { + p.setFont(st::themePreviewTitleFont); + p.setPen(st::themePreviewTitleFg); + p.drawTextLeft(titleRect.x(), titleRect.y(), width(), lang(lng_theme_preview_title)); + } + + auto buttonsRect = QRect(_themePreviewRect.x(), _themePreviewRect.y() + _themePreviewRect.height() - st::themePreviewMargin.bottom(), _themePreviewRect.width(), st::themePreviewMargin.bottom()); + if (auto fillButtonsRect = (buttonsRect.y() + buttonsRect.height() > height())) { + buttonsRect.moveTop(height() - buttonsRect.height()); + fillOverlay(buttonsRect); + } +} + void MediaView::keyPressEvent(QKeyEvent *e) { if (_clipController) { auto toggle1 = (e->key() == Qt::Key_F && e->modifiers().testFlag(Qt::ControlModifier)); @@ -1856,7 +1997,7 @@ void MediaView::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) { onCopy(); } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) { - if (_doc && !_doc->loading() && (!fileShown() || !_doc->loaded())) { + if (_doc && !_doc->loading() && (fileBubbleShown() || !_doc->loaded())) { onDocClick(); } else if (_doc && _doc->isVideo()) { onVideoPauseResume(); @@ -1957,8 +2098,8 @@ bool MediaView::moveToNext(int32 delta) { _channel = _history ? _history->channelId() : NoChannel; _canForward = _msgid > 0; _canDelete = lastChatPhoto.item->canDelete(); - stopGif(); - displayPhoto(lastChatPhoto.photo, lastChatPhoto.item); preloadData(delta); + displayPhoto(lastChatPhoto.photo, lastChatPhoto.item); + preloadData(delta); return true; } else if (_history && (_history->overviewCount(OverviewChatPhotos) != 0 || ( _migrated && _migrated->overviewCount(OverviewChatPhotos) != 0))) { @@ -1994,7 +2135,7 @@ bool MediaView::moveToNext(int32 delta) { _canForward = _msgid > 0; _canDelete = item->canDelete(); stopGif(); - if (HistoryMedia *media = item->getMedia()) { + if (auto media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; case MediaTypeFile: @@ -2013,7 +2154,6 @@ bool MediaView::moveToNext(int32 delta) { _msgmigrated = false; _canForward = false; _canDelete = false; - stopGif(); displayPhoto(_additionalChatPhoto, 0); } if (delta < 0 && _index < MediaOverviewStartPerPage) { @@ -2189,8 +2329,8 @@ void MediaView::snapXY() { void MediaView::mouseMoveEvent(QMouseEvent *e) { updateOver(e->pos()); - if (_lastAction.x() >= 0 && (e->pos() - _lastAction).manhattanLength() >= st::mvDeltaFromLastAction) { - _lastAction = QPoint(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction); + if (_lastAction.x() >= 0 && (e->pos() - _lastAction).manhattanLength() >= st::mediaviewDeltaFromLastAction) { + _lastAction = QPoint(-st::mediaviewDeltaFromLastAction, -st::mediaviewDeltaFromLastAction); } if (_pressed) { if (!_dragging && (e->pos() - _mStart).manhattanLength() >= QApplication::startDragDistance()) { @@ -2267,7 +2407,7 @@ void MediaView::updateOver(QPoint pos) { ClickHandlerHost *lnkhost = nullptr; if (_saveMsgStarted && _saveMsg.contains(pos)) { - auto textState = _saveMsgText.getState(pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); + auto textState = _saveMsgText.getState(pos.x() - _saveMsg.x() - st::mediaviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::mediaviewSaveMsgPadding.top(), _saveMsg.width() - st::mediaviewSaveMsgPadding.left() - st::mediaviewSaveMsgPadding.right()); lnk = textState.link; lnkhost = this; } else if (_captionRect.contains(pos)) { @@ -2302,7 +2442,7 @@ void MediaView::updateOver(QPoint pos) { updateOverState(OverHeader); } else if (_saveVisible && _saveNav.contains(pos)) { updateOverState(OverSave); - } else if (_doc && !fileShown() && _docIconRect.contains(pos)) { + } else if (_doc && fileBubbleShown() && _docIconRect.contains(pos)) { updateOverState(OverIcon); } else if (_moreNav.contains(pos)) { updateOverState(OverMore); @@ -2358,8 +2498,14 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) { } _dragging = 0; setCursor(style::cur_default); - } else if ((e->pos() - _lastAction).manhattanLength() >= st::mvDeltaFromLastAction && (!_doc || fileShown() || !_docRect.contains(e->pos()))) { - close(); + } else if ((e->pos() - _lastAction).manhattanLength() >= st::mediaviewDeltaFromLastAction) { + if (_themePreviewShown) { + if (!_themePreviewRect.contains(e->pos())) { + close(); + } + } else if (!_doc || fileShown() || !_docRect.contains(e->pos())) { + close(); + } } _pressed = false; } @@ -2422,7 +2568,7 @@ void MediaView::touchEvent(QTouchEvent *e) { } else if (_touchMove) { if ((!_leftNavVisible || !_leftNav.contains(mapFromGlobal(_touchStart))) && (!_rightNavVisible || !_rightNav.contains(mapFromGlobal(_touchStart)))) { QPoint d = (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart); - if (d.x() * d.x() > d.y() * d.y() && (d.x() > st::mvSwipeDistance || d.x() < -st::mvSwipeDistance)) { + if (d.x() * d.x() > d.y() * d.y() && (d.x() > st::mediaviewSwipeDistance || d.x() < -st::mediaviewSwipeDistance)) { moveToNext(d.x() > 0 ? -1 : 1); } } @@ -2500,6 +2646,7 @@ void MediaView::setVisible(bool visible) { Sandbox::removeEventFilter(this); stopGif(); + destroyThemePreview(); _radial.stop(); Notify::clipStopperHidden(ClipStopperMediaview); } @@ -2746,12 +2893,12 @@ void MediaView::updateHeader() { } } _headerHasLink = _history && typeHasMediaOverview(_overview); - int32 hwidth = st::mvThickFont->width(_headerText); + int32 hwidth = st::mediaviewThickFont->width(_headerText); if (hwidth > width() / 3) { hwidth = width() / 3; - _headerText = st::mvThickFont->elided(_headerText, hwidth, Qt::ElideMiddle); + _headerText = st::mediaviewThickFont->elided(_headerText, hwidth, Qt::ElideMiddle); } - _headerNav = myrtlrect(st::mvTextLeft, height() - st::mvHeaderTop, hwidth, st::mvThickFont->height); + _headerNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewHeaderTop, hwidth, st::mediaviewThickFont->height); } float64 MediaView::overLevel(OverState control) const { diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 2271554cb..60cd2a8f3 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -32,8 +32,15 @@ class Controller; namespace Ui { class PopupMenu; class LinkButton; +class RoundButton; } // namespace Ui +namespace Window { +namespace Theme { +struct Preview; +} // namespace Theme +} // namespace Window + struct AudioPlaybackState; class MediaView : public TWidget, private base::Subscriber, public RPCSender, public ClickHandlerHost { @@ -127,6 +134,20 @@ private slots: void onVideoPlayProgress(const AudioMsgId &audioId); private: + enum OverState { + OverNone, + OverLeftNav, + OverRightNav, + OverClose, + OverHeader, + OverName, + OverDate, + OverSave, + OverMore, + OverIcon, + OverVideo, + }; + void showSaveMsgFile(); void dropdownHidden(); @@ -155,6 +176,10 @@ private: void initAnimation(); void createClipReader(); + void initThemePreview(); + void destroyThemePreview(); + void updateThemePreviewGeometry(); + // Radial animation interface. float64 radialProgress() const; bool radialLoading() const; @@ -187,6 +212,11 @@ private: void zoomUpdate(int32 &newZoom); void paintDocRadialLoading(Painter &p, bool radial, float64 radialOpacity); + void paintThemePreview(Painter &p, QRect clip); + + void updateOverRect(OverState state); + bool updateOverState(OverState newState); + float64 overLevel(OverState control) const; QBrush _transparentBrush; @@ -236,10 +266,11 @@ private: bool fileShown() const; bool gifShown() const; + bool fileBubbleShown() const; void stopGif(); const style::icon *_docIcon = nullptr; - const style::color *_docIconColor = nullptr; + style::color _docIconColor; QString _docName, _docSize, _docExt; int _docNameWidth = 0, _docSizeWidth = 0, _docExtWidth = 0; QRect _docRect, _docIconRect; @@ -278,19 +309,6 @@ private: mtpRequestId _loadRequest = 0; - enum OverState { - OverNone, - OverLeftNav, - OverRightNav, - OverClose, - OverHeader, - OverName, - OverDate, - OverSave, - OverMore, - OverIcon, - OverVideo, - }; OverState _over = OverNone; OverState _down = OverNone; QPoint _lastAction, _lastMouseMovePos; @@ -340,8 +358,11 @@ private: int _verticalWheelDelta = 0; - void updateOverRect(OverState state); - bool updateOverState(OverState newState); - float64 overLevel(OverState control) const; + bool _themePreviewShown = false; + uint64 _themePreviewId = 0; + QRect _themePreviewRect; + std_::unique_ptr _themePreview; + object_ptr _themeApply = { nullptr }; + object_ptr _themeCancel = { nullptr }; }; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 1169d85f3..3640f2a3d 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -493,7 +493,7 @@ Voice::Voice(DocumentData *voice, HistoryItem *parent, const style::OverviewFile updateName(); QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; - _details.setText(st::normalFont, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); + _details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); _details.setLink(1, goToMessageClickHandler(parent)); } @@ -578,9 +578,9 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg); int32 unreadx = nameleft; if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) { - textstyleSet(&(selected ? st::mediaInStyleSelected : st::mediaInStyle)); + p.setTextPalette(selected ? st::mediaInPaletteSelected : st::mediaInPalette); _details.drawLeftElided(p, nameleft, statustop, namewidth, _width); - textstyleRestore(); + p.restoreTextPalette(); unreadx += _details.maxWidth(); } else { int32 statusw = st::normalFont->width(_status.text()); @@ -631,12 +631,12 @@ void Voice::updateName() { int32 version = 0; if (const HistoryMessageForwarded *fwd = _parent->Get()) { if (_parent->fromOriginal()->isChannel()) { - _name.setText(st::semiboldFont, lng_forwarded_channel(lt_channel, App::peerName(_parent->fromOriginal())), _textNameOptions); + _name.setText(st::semiboldTextStyle, lng_forwarded_channel(lt_channel, App::peerName(_parent->fromOriginal())), _textNameOptions); } else { - _name.setText(st::semiboldFont, lng_forwarded(lt_user, App::peerName(_parent->fromOriginal())), _textNameOptions); + _name.setText(st::semiboldTextStyle, lng_forwarded(lt_user, App::peerName(_parent->fromOriginal())), _textNameOptions); } } else { - _name.setText(st::semiboldFont, App::peerName(_parent->from()), _textNameOptions); + _name.setText(st::semiboldTextStyle, App::peerName(_parent->from()), _textNameOptions); } version = _parent->fromOriginal()->nameVersion; _nameVersion = version; @@ -675,7 +675,7 @@ Document::Document(DocumentData *document, HistoryItem *parent, const style::Ove , _date(langDateTime(date(_data->date))) , _datew(st::normalFont->width(_date)) , _colorIndex(documentColorIndex(_data, _ext)) { - _name.setMarkedText(st::normalFont, documentNameWithEntities(_data), _documentNameOptions); + _name.setMarkedText(st::defaultTextStyle, documentNameWithEntities(_data), _documentNameOptions); AddComponents(Info::Bit()); @@ -806,7 +806,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } } if (selected) { - p.fillRect(rthumb, textstyleCurrent()->selectOverlay); + p.fillRect(rthumb, st::defaultTextPalette.selectOverlay); } if (radial || (!loaded && !_data->loading())) { @@ -1029,7 +1029,7 @@ Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) { } if (till > from) { TextParseOptions opts = { TextParseMultiline, int32(st::linksMaxWidth), 3 * st::normalFont->height, Qt::LayoutDirectionAuto }; - _text.setText(st::normalFont, text.mid(from, till - from), opts); + _text.setText(st::defaultTextStyle, text.mid(from, till - from), opts); } int32 tw = 0, th = 0; if (_page && _page->photo) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index dd904f3b5..bb2e39aba 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -87,8 +87,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, P using Update = Window::Theme::BackgroundUpdate; subscribe(Window::Theme::Background(), [this](const Update &update) { - if (update.type == Update::Type::TestingTheme - || update.type == Update::Type::RevertingTheme) { + if (update.paletteChanged()) { invalidateCache(); } }); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 17a0a0199..7246f6cb7 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -45,7 +45,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; static void LibsLoaded(); @@ -71,7 +71,7 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); - virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; + virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; private: void updateIconCounters(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index da0490c5c..a9feffd74 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -49,7 +49,7 @@ public: return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); } - virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; void closeWithoutDestroy() override; @@ -99,7 +99,7 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); - virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; + virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; QTimer psUpdatedPositionTimer; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 73359553a..aa60566c5 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -205,9 +205,8 @@ MainWindow::MainWindow() , iconbig256(qsl(":/gui/art/iconbig256.png")) , wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)) , _private(std_::make_unique(this)) { - QImage tray(qsl(":/gui/art/osxtray.png")); - trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); - trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); + trayImg = st::macTrayIcon.instance(QColor(0, 0, 0, 180), dbisOne); + trayImgSel = st::macTrayIcon.instance(QColor(255, 255, 255), dbisOne); _hideAfterFullScreenTimer.setSingleShot(true); connect(&_hideAfterFullScreenTimer, SIGNAL(timeout()), this, SLOT(onHideAfterFullScreen())); @@ -292,39 +291,45 @@ void MainWindow::psUpdateWorkmode() { } } -void _placeCounter(QImage &img, int size, int count, const style::color &bg, const style::color &color) { +void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) { if (!count) return; + auto savedRatio = img.devicePixelRatio(); + img.setDevicePixelRatio(1.); - QPainter p(&img); - QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0')); - int32 cntSize = cnt.size(); + { + Painter p(&img); + PainterHighQualityEnabler hq(p); - p.setBrush(bg->b); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing); - int32 fontSize, skip; - if (size == 22) { - skip = 1; - fontSize = 8; - } else { - skip = 2; - fontSize = 16; + auto cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0')); + auto cntSize = cnt.size(); + + p.setBrush(bg); + p.setPen(Qt::NoPen); + int32 fontSize, skip; + if (size == 22) { + skip = 1; + fontSize = 8; + } else { + skip = 2; + fontSize = 16; + } + style::font f(fontSize, 0, 0); + int32 w = f->width(cnt), d, r; + if (size == 22) { + d = (cntSize < 2) ? 3 : 2; + r = (cntSize < 2) ? 6 : 5; + } else { + d = (cntSize < 2) ? 6 : 5; + r = (cntSize < 2) ? 9 : 11; + } + p.drawRoundedRect(QRect(size - w - d * 2 - skip, size - f->height - skip, w + d * 2, f->height), r, r); + + p.setCompositionMode(QPainter::CompositionMode_Source); + p.setFont(f); + p.setPen(color); + p.drawText(size - w - d - skip, size - f->height + f->ascent - skip, cnt); } - style::font f(fontSize, 0, 0); - int32 w = f->width(cnt), d, r; - if (size == 22) { - d = (cntSize < 2) ? 3 : 2; - r = (cntSize < 2) ? 6 : 5; - } else { - d = (cntSize < 2) ? 6 : 5; - r = (cntSize < 2) ? 9 : 11; - } - p.drawRoundedRect(QRect(size - w - d * 2 - skip, size - f->height - skip, w + d * 2, f->height), r, r); - - p.setCompositionMode(QPainter::CompositionMode_Source); - p.setFont(f->f); - p.setPen(color->p); - p.drawText(size - w - d - skip, size - f->height + f->ascent - skip, cnt); + img.setDevicePixelRatio(savedRatio); } void MainWindow::updateTitleCounter() { diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.h b/Telegram/SourceFiles/platform/mac/window_title_mac.h index 48d165ed5..3e2477545 100644 --- a/Telegram/SourceFiles/platform/mac/window_title_mac.h +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.h @@ -47,4 +47,7 @@ private: object_ptr CreateTitleWidget(QWidget *parent); +int PreviewTitleHeight(); +void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth); + } // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.mm b/Telegram/SourceFiles/platform/mac/window_title_mac.mm index a0cdc64e1..decbad759 100644 --- a/Telegram/SourceFiles/platform/mac/window_title_mac.mm +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.mm @@ -21,10 +21,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/mac/window_title_mac.h" +#include "mainwindow.h" #include "ui/widgets/shadow.h" #include "styles/style_window.h" +#include "styles/style_mediaview.h" #include "platform/platform_main_window.h" +#include +#include + namespace Platform { TitleWidget::TitleWidget(MainWindow *parent, int height) : Window::TitleWidget(parent) @@ -84,4 +89,138 @@ object_ptr CreateTitleWidget(QWidget *parent) { return { nullptr }; } +// All the window decorations preview is done without taking cScale() into +// account, with dbisOne scale and without "px" dimensions, because thats +// how it will look in real launched macOS app. +int PreviewTitleHeight() { + if (auto window = qobject_cast(App::wnd())) { + if (auto height = window->getCustomTitleHeight()) { + return height; + } + } + return 22; +} + +QImage PreviewWindowSystemButton(QColor inner, QColor border) { + auto buttonSize = 12; + auto fullSize = buttonSize * cIntRetinaFactor(); + auto result = QImage(fullSize, fullSize, QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + { + Painter p(&result); + PainterHighQualityEnabler hq(p); + + p.setPen(border); + p.setBrush(inner); + p.drawEllipse(QRectF(0.5, 0.5, fullSize - 1., fullSize - 1.)); + } + result.setDevicePixelRatio(cRetinaFactor()); + return std_::move(result); +} + +void PreviewWindowTitle(Painter &p, const style::palette &palette, QRect body, int titleHeight, int outerWidth) { + auto titleRect = QRect(body.x(), body.y() - titleHeight, body.width(), titleHeight); + p.fillRect(titleRect, QColor(0, 0, 0)); + p.fillRect(titleRect, st::titleBg[palette]); + p.fillRect(titleRect.x(), titleRect.y() + titleRect.height() - st::lineWidth, titleRect.width(), st::lineWidth, st::titleShadow[palette]); + + auto useSystemFont = false; + QFont font; +#ifndef OS_MAC_OLD + QStringList families = { qsl(".SF NS Text"), qsl("Helvetica Neue") }; + for (auto family : families) { + font.setFamily(family); + if (QFontInfo(font).family() == font.family()) { + useSystemFont = true; + break; + } + } +#endif // OS_MAC_OLD + + if (useSystemFont) { + font.setPixelSize((titleHeight * 15) / 24); + } else { + font = st::normalFont; + } + + p.setPen(st::titleFgActive[palette]); + p.setFont(font); + + p.drawText(titleRect, qsl("Telegram"), style::al_center); + + auto isGraphite = ([NSColor currentControlTint] == NSGraphiteControlTint); + auto buttonSkip = 8; + auto graphiteInner = QColor(141, 141, 146); + auto graphiteBorder = QColor(104, 104, 109); + auto closeInner = isGraphite ? graphiteInner : QColor(252, 96, 92); + auto closeBorder = isGraphite ? graphiteBorder : QColor(222, 64, 59); + auto minimizeInner = isGraphite ? graphiteInner : QColor(254, 192, 65); + auto minimizeBorder = isGraphite ? graphiteBorder : QColor(221, 152, 25); + auto maximizeInner = isGraphite ? graphiteInner : QColor(52, 200, 74); + auto maximizeBorder = isGraphite ? graphiteBorder : QColor(21, 164, 41); + auto close = PreviewWindowSystemButton(closeInner, closeBorder); + auto left = buttonSkip; + p.drawImage(titleRect.x() + left, titleRect.y() + (titleRect.height() - (close.height() / cIntRetinaFactor())) / 2, close); + left += (close.width() / cIntRetinaFactor()) + buttonSkip; + auto minimize = PreviewWindowSystemButton(minimizeInner, minimizeBorder); + p.drawImage(titleRect.x() + left, titleRect.y() + (titleRect.height() - (minimize.height() / cIntRetinaFactor())) / 2, minimize); + left += (minimize.width() / cIntRetinaFactor()) + buttonSkip; + auto maximize = PreviewWindowSystemButton(maximizeInner, maximizeBorder); + p.drawImage(titleRect.x() + left, titleRect.y() + (titleRect.height() - (maximize.height() / cIntRetinaFactor())) / 2, maximize); +} + +void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) { + auto retina = cIntRetinaFactor(); + auto titleHeight = PreviewTitleHeight(); + { + Painter p(&preview); + PreviewWindowTitle(p, palette, body, titleHeight, outerWidth); + } + auto inner = QRect(body.x(), body.y() - titleHeight, body.width(), body.height() + titleHeight); + + auto retinaRadius = st::macWindowRoundRadius * retina; + auto roundMask = QImage(2 * retinaRadius, 2 * retinaRadius, QImage::Format_ARGB32_Premultiplied); + roundMask.setDevicePixelRatio(cRetinaFactor()); + roundMask.fill(Qt::transparent); + { + Painter p(&roundMask); + PainterHighQualityEnabler hq(p); + + p.setPen(Qt::NoPen); + p.setBrush(QColor(255, 255, 255)); + p.drawRoundedRect(0, 0, 2 * st::macWindowRoundRadius, 2 * st::macWindowRoundRadius, st::macWindowRoundRadius, st::macWindowRoundRadius); + } + QImage corners[4]; + corners[0] = roundMask.copy(0, 0, retinaRadius, retinaRadius); + corners[1] = roundMask.copy(retinaRadius, 0, retinaRadius, retinaRadius); + corners[2] = roundMask.copy(0, retinaRadius, retinaRadius, retinaRadius); + corners[3] = roundMask.copy(retinaRadius, retinaRadius, retinaRadius, retinaRadius); + QImage *cornersPointers[] = { &corners[0], &corners[1], &corners[2], &corners[3] }; + auto rounded = preview.copy(inner.x() * retina, inner.y() * retina, inner.width() * retina, inner.height() * retina); + Images::prepareRound(rounded, cornersPointers); + preview.fill(st::themePreviewBg->c); + + auto topLeft = st::macWindowShadowTopLeft.instance(QColor(0, 0, 0), dbisOne); + auto topRight = topLeft.mirrored(true, false); + auto bottomLeft = topLeft.mirrored(false, true); + auto bottomRight = bottomLeft.mirrored(true, false); + auto extend = QMargins(37, 28, 37, 28); + auto left = topLeft.copy(0, topLeft.height() - retina, extend.left() * retina, retina); + auto top = topLeft.copy(topLeft.width() - retina, 0, retina, extend.top() * retina); + auto right = topRight.copy(topRight.width() - (extend.right() * retina), topRight.height() - retina, extend.right() * retina, retina); + auto bottom = bottomRight.copy(0, bottomRight.height() - (extend.bottom() * retina), retina, extend.bottom() * retina); + { + Painter p(&preview); + p.drawImage(inner.x() - extend.left(), inner.y() - extend.top(), topLeft); + p.drawImage(inner.x() + inner.width() + extend.right() - (topRight.width() / retina), inner.y() - extend.top(), topRight); + p.drawImage(inner.x() - extend.left(), inner.y() + inner.height() + extend.bottom() - (bottomLeft.height() / retina), bottomLeft); + p.drawImage(inner.x() + inner.width() + extend.right() - (bottomRight.width() / retina), inner.y() + inner.height() + extend.bottom() - (bottomRight.height() / retina), bottomRight); + p.drawImage(QRect(inner.x() - extend.left(), inner.y() - extend.top() + (topLeft.height() / retina), extend.left(), extend.top() + inner.height() + extend.bottom() - (topLeft.height() / retina) - (bottomLeft.height() / retina)), left); + p.drawImage(QRect(inner.x() - extend.left() + (topLeft.width() / retina), inner.y() - extend.top(), extend.left() + inner.width() + extend.right() - (topLeft.width() / retina) - (topRight.width() / retina), extend.top()), top); + p.drawImage(QRect(inner.x() + inner.width(), inner.y() - extend.top() + (topRight.height() / retina), extend.right(), extend.top() + inner.height() + extend.bottom() - (topRight.height() / retina) - (bottomRight.height() / retina)), right); + p.drawImage(QRect(inner.x() - extend.left() + (bottomLeft.width() / retina), inner.y() + inner.height(), extend.left() + inner.width() + extend.right() - (bottomLeft.width() / retina) - (bottomRight.width() / retina), extend.bottom()), bottom); + p.drawImage(inner.x(), inner.y(), rounded); + } +} + } // namespace Platform diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index 2a1f45752..711289cda 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -22,6 +22,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_title.h" +namespace Window { +namespace Theme { + +int DefaultPreviewTitleHeight(); +void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth); + +} // namespace Theme +} // namespace Window + #ifdef Q_OS_MAC #include "platform/mac/window_title_mac.h" #elif defined Q_OS_WIN // Q_OS_MAC @@ -34,6 +43,14 @@ inline object_ptr CreateTitleWidget(QWidget *parent) { return { nullptr }; } +inline int PreviewTitleHeight() { + return Window::Theme::DefaultPreviewTitleHeight(); +} + +inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) { + return Window::Theme::DefaultPreviewWindowFramePaint(preview, palette, body, outerWidth); +} + } // namespace Platform #endif // Q_OS_MAC || Q_OS_WIN || Q_OS_WINRT || Q_OS_LINUX diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index bd128bf19..a83cfc8a2 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "localstorage.h" #include "ui/widgets/popup_menu.h" +#include "window/window_theme.h" #include @@ -143,11 +144,11 @@ public: bool init(QColor c) { _fullsize = st::windowShadow.width(); _shift = st::windowShadowShift; - QImage cornersImage(_fullsize, _fullsize, QImage::Format_ARGB32_Premultiplied); + auto cornersImage = QImage(_fullsize, _fullsize, QImage::Format_ARGB32_Premultiplied); { Painter p(&cornersImage); p.setCompositionMode(QPainter::CompositionMode_Source); - st::windowShadow.paint(p, 0, 0, _fullsize); + st::windowShadow.paint(p, 0, 0, _fullsize, QColor(0, 0, 0)); } if (rtl()) cornersImage = cornersImage.mirrored(true, false); @@ -529,7 +530,6 @@ private: }; _PsShadowWindows _psShadowWindows; -QColor _shActive(0, 0, 0)/*, _shInactive(0, 0, 0)*/; LRESULT CALLBACK _PsShadowWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { auto wnd = App::wnd(); @@ -618,6 +618,12 @@ MainWindow::MainWindow() if (!_taskbarCreatedMsgId) { _taskbarCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated"); } + using Update = Window::Theme::BackgroundUpdate; + subscribe(Window::Theme::Background(), [this](const Update &update) { + if (update.paletteChanged()) { + _psShadowWindows.setColor(st::windowShadowFg->c); + } + }); } void MainWindow::TaskbarCreated() { @@ -791,7 +797,7 @@ bool MainWindow::psHasNativeNotifications() { Q_DECLARE_METATYPE(QMargins); void MainWindow::psFirstShow() { - _psShadowWindows.init(_shActive); + _psShadowWindows.init(st::windowShadowFg->c); _shadowsWorking = true; psUpdateMargins(); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 4a00efd6e..4659fd100 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -52,7 +52,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; static UINT TaskbarCreatedMsgId() { return _taskbarCreatedMsgId; @@ -104,13 +104,13 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); - virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; + virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; QTimer psUpdatedPositionTimer; private: void updateIconCounters(); - + void psDestroyIcons(); static UINT _taskbarCreatedMsgId; diff --git a/Telegram/SourceFiles/platform/win/window_title_win.h b/Telegram/SourceFiles/platform/win/window_title_win.h index 261bb2f5b..a33985df6 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.h +++ b/Telegram/SourceFiles/platform/win/window_title_win.h @@ -27,6 +27,15 @@ class IconButton; class PlainShadow; } // namespace Ui +namespace Window { +namespace Theme { + +int DefaultPreviewTitleHeight(); +void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth); + +} // namespace Theme +} // namespace Window + namespace Platform { class TitleWidget : public Window::TitleWidget, private base::Subscriber { @@ -64,4 +73,12 @@ inline object_ptr CreateTitleWidget(QWidget *parent) { return object_ptr(parent); } +inline int PreviewTitleHeight() { + return Window::Theme::DefaultPreviewTitleHeight(); +} + +inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) { + return Window::Theme::DefaultPreviewWindowFramePaint(preview, palette, body, outerWidth); +} + } // namespace Platform diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index b0e323d90..da08e063c 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -19,7 +19,6 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; -using "basic_types.style"; using "ui/widgets/widgets.style"; using "window/window.style"; @@ -38,12 +37,14 @@ profileNameLeft: 26px; profileNameTop: 9px; profileNameLabel: FlatLabel(defaultFlatLabel) { margin: margins(10px, 5px, 10px, 5px); - font: font(16px semibold); width: 160px; maxHeight: 24px; textFg: windowBoldFg; -} -profileNameTextStyle: TextStyle(defaultTextStyle) { + style: TextStyle(defaultTextStyle) { + font: font(16px semibold); + linkFont: font(16px semibold); + linkFontOver: font(16px semibold underline); + } } profileStatusLeft: 27px; profileStatusTop: 35px; @@ -135,9 +136,9 @@ profileMemberAdminIcon: icon {{ "profile_admin_star", windowBgActive, point(4px, profileLimitReachedLabel: FlatLabel(defaultFlatLabel) { width: 180px; margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px); -} -profileLimitReachedStyle: TextStyle(defaultTextStyle) { - lineHeight: 19px; + style: TextStyle(defaultTextStyle) { + lineHeight: 19px; + } } profileReportReasonOther: InputField(defaultInputField) { diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index d4da91f86..ba51b5ee3 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -209,7 +209,7 @@ void GroupMembersWidget::refreshLimitReached() { bool limitReachedShown = (itemsCount() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle(); if (limitReachedShown && !_limitReachedInfo) { - _limitReachedInfo.create(this, st::profileLimitReachedLabel, st::profileLimitReachedStyle); + _limitReachedInfo.create(this, st::profileLimitReachedLabel); QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); QString body = textRichPrepare(lang(lng_profile_migrate_body)); QString link = textRichPrepare(lang(lng_profile_migrate_learn_more)); diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index e9b5f4f5c..069b413c3 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -99,7 +99,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select item->peer->paintUserpicLeft(p, st::profileMemberPhotoSize, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width()); if (item->name.isEmpty()) { - item->name.setText(st::semiboldFont, App::peerName(item->peer), _textNameOptions); + item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions); } int nameLeft = x + st::profileMemberNamePosition.x(); int nameTop = y + st::profileMemberNamePosition.y(); diff --git a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp index 25be4db04..4930077ab 100644 --- a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp +++ b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp @@ -242,7 +242,7 @@ void InnerWidget::paintRow(Painter &p, int index, TimeMs ms) { y += st::profileCommonGroupsNameTop; auto nameWidth = _contentWidth - (x - _contentLeft) - st::profileCommonGroupsPadding.right(); if (item->name.isEmpty()) { - item->name.setText(st::semiboldFont, App::peerName(item->peer), _textNameOptions); + item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions); } _items[index]->name.drawLeftElided(p, x, y, nameWidth, width()); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 3012bbe32..d14de8753 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -19,7 +19,6 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; -using "basic_types.style"; using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; using "boxes/boxes.style"; @@ -50,12 +49,14 @@ settingsNameLeft: 26px; settingsNameTop: 9px; settingsNameLabel: FlatLabel(defaultFlatLabel) { margin: margins(10px, 5px, 10px, 5px); - font: font(16px semibold); width: 160px; maxHeight: 24px; textFg: windowBoldFg; -} -settingsNameTextStyle: TextStyle(defaultTextStyle) { + style: TextStyle(defaultTextStyle) { + font: font(16px semibold); + linkFont: font(16px semibold); + linkFontOver: font(16px semibold underline); + } } settingsStatusLeft: 27px; settingsStatusTop: 35px; @@ -87,7 +88,11 @@ settingsBlockTitleFont: font(15px semibold); settingsBlockTitleFg: windowBoldFg; settingsBlockTitleTop: 0px; settingsPrimaryLabel: FlatLabel(defaultFlatLabel) { - font: boxTextFont; + style: TextStyle(defaultTextStyle) { + font: boxTextFont; + linkFont: boxTextFont; + linkFontOver: font(boxFontSize underline); + } } settingsBlockLabel: FlatLabel(settingsPrimaryLabel) { textFg: windowSubTextFg; diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 3a53d4a17..04e7c13dd 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -66,7 +66,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/style/style_core.h" #include "styles/palette.h" -#include "styles/style_basic_types.h" #include "styles/style_basic.h" #include "ui/animation.h" diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp index 107611106..f97612ab9 100644 --- a/Telegram/SourceFiles/stickers/stickers.cpp +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -27,6 +27,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "localstorage.h" #include "mainwidget.h" +#include "mainwindow.h" +#include "ui/toast/toast.h" +#include "styles/style_stickers.h" namespace Stickers { namespace { @@ -78,16 +81,19 @@ void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { } Local::writeInstalledStickers(); Local::writeArchivedStickers(); - Ui::show(Box(archived), KeepOtherLayers); + + Ui::Toast::Config toast; + toast.text = lang(lng_stickers_packs_archived); + toast.maxWidth = st::stickersToastMaxWidth; + toast.padding = st::stickersToastPadding; + Ui::Toast::Show(App::wnd(), toast); +// Ui::show(Box(archived), KeepOtherLayers); emit App::main()->stickersUpdated(); } // For testing: Just apply random subset or your sticker sets as archived. bool applyArchivedResultFake() { - if (rand_value() % 128 < 64) { - return false; - } auto sets = QVector(); for (auto &set : Global::RefStickerSets()) { if ((set.flags & MTPDstickerSet::Flag::f_installed) && !(set.flags & MTPDstickerSet_ClientFlag::f_special)) { diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 7a9308c47..9c370f7b6 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -202,3 +202,6 @@ hashtagClose: IconButton { color: windowBgOver; } } + +stickersToastMaxWidth: 340px; +stickersToastPadding: margins(16px, 13px, 16px, 12px); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index b2d513412..b0bc174ed 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -60,18 +60,18 @@ ImagePtr generateUserpicImage(const style::icon &icon) { } // namespace -const style::color &peerColor(int index) { - static const style::color *peerColors[kUserColorsCount] = { - &st::historyPeer1NameFg, - &st::historyPeer2NameFg, - &st::historyPeer3NameFg, - &st::historyPeer4NameFg, - &st::historyPeer5NameFg, - &st::historyPeer6NameFg, - &st::historyPeer7NameFg, - &st::historyPeer8NameFg, +style::color peerColor(int index) { + static style::color peerColors[kUserColorsCount] = { + st::historyPeer1NameFg, + st::historyPeer2NameFg, + st::historyPeer3NameFg, + st::historyPeer4NameFg, + st::historyPeer5NameFg, + st::historyPeer6NameFg, + st::historyPeer7NameFg, + st::historyPeer8NameFg, }; - return *peerColors[index]; + return peerColors[index]; } ImagePtr userDefPhoto(int index) { @@ -117,7 +117,7 @@ PeerData::PeerData(const PeerId &id) : id(id) , colorIndex(peerColorIndex(id)) , color(peerColor(colorIndex)) , _userpic(isUser() ? userDefPhoto(colorIndex) : ((isChat() || isMegagroup()) ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex))) { - nameText.setText(st::msgNameFont, QString(), _textNameOptions); + nameText.setText(st::msgNameStyle, QString(), _textNameOptions); } void PeerData::updateNameDelayed(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) { @@ -137,7 +137,7 @@ void PeerData::updateNameDelayed(const QString &newName, const QString &newNameO ++nameVersion; name = newName; - nameText.setText(st::msgNameFont, name, _textNameOptions); + nameText.setText(st::msgNameStyle, name, _textNameOptions); Notify::PeerUpdate update(this); update.flags |= UpdateFlag::NameChanged; @@ -207,7 +207,7 @@ QPixmap PeerData::genUserpic(int size) const { const Text &BotCommand::descriptionText() const { if (_descriptionText.isEmpty() && !_description.isEmpty()) { - _descriptionText.setText(st::mentionFont, _description, _textNameOptions); + _descriptionText.setText(st::defaultTextStyle, _description, _textNameOptions); } return _descriptionText; } @@ -387,7 +387,7 @@ void UserData::setBotInfo(const MTPBotInfo &info) { void UserData::setNameOrPhone(const QString &newNameOrPhone) { if (nameOrPhone != newNameOrPhone) { nameOrPhone = newNameOrPhone; - phoneText.setText(st::msgNameFont, nameOrPhone, _textNameOptions); + phoneText.setText(st::msgNameStyle, nameOrPhone, _textNameOptions); } } @@ -986,11 +986,9 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, bool playVideo = data->isVideo() && audioPlayer(); bool playAnimation = data->isAnimation(); auto &location = data->location(true); - if (auto applyTheme = data->name.endsWith(qstr(".tdesktop-theme"))) { + if (auto applyTheme = data->isTheme()) { if (!location.isEmpty() && location.accessEnable()) { - if (!Window::Theme::Apply(location.name())) { - // show error? - } + App::wnd()->showDocument(data, context); location.accessDisable(); return; } @@ -1295,11 +1293,9 @@ void DocumentData::performActionOnLoad() { bool playVoice = voice() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playMusic = song() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); - if (auto applyTheme = name.endsWith(qstr(".tdesktop-theme"))) { + if (auto applyTheme = isTheme()) { if (!loc.isEmpty() && loc.accessEnable()) { - if (!Window::Theme::Apply(loc.name())) { - // show error? - } + App::wnd()->showDocument(this, item); loc.accessDisable(); return; } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 8265c26a7..f30c21c4e 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -203,7 +203,7 @@ static constexpr int kUserColorsCount = 8; static constexpr int kChatColorsCount = 4; static constexpr int kChannelColorsCount = 4; -const style::color &peerColor(int index); +style::color peerColor(int index); ImagePtr userDefPhoto(int index); ImagePtr chatDefPhoto(int index); ImagePtr channelDefPhoto(int index); @@ -289,7 +289,7 @@ public: MTPinputPeer input; int colorIndex; - const style::color &color; + style::color color; void setUserpic(ImagePtr userpic); void paintUserpic(Painter &p, int size, int x, int y) const; @@ -1136,6 +1136,9 @@ public: bool isGifv() const { return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive); } + bool isTheme() const { + return name.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive); + } bool isMusic() const { if (auto s = song()) { return (s->duration > 0); diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 508689120..dce5c34fb 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -337,15 +337,15 @@ FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) { #endif // SHIFTED_USE_32BIT -FORCE_INLINE QColor color(const style::color &a, QColor b, float64 b_ratio) { +FORCE_INLINE QColor color(style::color a, QColor b, float64 b_ratio) { return color(a->c, b, b_ratio); } -FORCE_INLINE QColor color(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QColor color(QColor a, style::color b, float64 b_ratio) { return color(a, b->c, b_ratio); } -FORCE_INLINE QColor color(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QColor color(style::color a, style::color b, float64 b_ratio) { return color(a->c, b->c, b_ratio); } @@ -353,15 +353,15 @@ FORCE_INLINE QPen pen(QColor a, QColor b, float64 b_ratio) { return color(a, b, b_ratio); } -FORCE_INLINE QPen pen(const style::color &a, QColor b, float64 b_ratio) { +FORCE_INLINE QPen pen(style::color a, QColor b, float64 b_ratio) { return (b_ratio > 0) ? pen(a->c, b, b_ratio) : a; } -FORCE_INLINE QPen pen(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QPen pen(QColor a, style::color b, float64 b_ratio) { return (b_ratio < 1) ? pen(a, b->c, b_ratio) : b; } -FORCE_INLINE QPen pen(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QPen pen(style::color a, style::color b, float64 b_ratio) { return (b_ratio > 0) ? ((b_ratio < 1) ? pen(a->c, b->c, b_ratio) : b) : a; } @@ -369,15 +369,15 @@ FORCE_INLINE QBrush brush(QColor a, QColor b, float64 b_ratio) { return color(a, b, b_ratio); } -FORCE_INLINE QBrush brush(const style::color &a, QColor b, float64 b_ratio) { +FORCE_INLINE QBrush brush(style::color a, QColor b, float64 b_ratio) { return (b_ratio > 0) ? brush(a->c, b, b_ratio) : a; } -FORCE_INLINE QBrush brush(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QBrush brush(QColor a, style::color b, float64 b_ratio) { return (b_ratio < 1) ? brush(a, b->c, b_ratio) : b; } -FORCE_INLINE QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QBrush brush(style::color a, style::color b, float64 b_ratio) { return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a; } diff --git a/Telegram/SourceFiles/ui/effects/cross_animation.cpp b/Telegram/SourceFiles/ui/effects/cross_animation.cpp index d1d6d9c4e..d12cd7f6a 100644 --- a/Telegram/SourceFiles/ui/effects/cross_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/cross_animation.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, const style::color &color, int x, int y, int outerWidth, float64 shown) { +void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown) { PainterHighQualityEnabler hq(p); auto deleteScale = shown + st.minScale * (1. - shown); diff --git a/Telegram/SourceFiles/ui/effects/cross_animation.h b/Telegram/SourceFiles/ui/effects/cross_animation.h index 33583b783..45a9fdbd2 100644 --- a/Telegram/SourceFiles/ui/effects/cross_animation.h +++ b/Telegram/SourceFiles/ui/effects/cross_animation.h @@ -26,7 +26,7 @@ namespace Ui { class CrossAnimation { public: - static void paint(Painter &p, const style::CrossAnimation &st, const style::color &color, int x, int y, int outerWidth, float64 shown); + static void paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown); }; diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.cpp b/Telegram/SourceFiles/ui/effects/radial_animation.cpp index 80cb84b96..f721b539e 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/radial_animation.cpp @@ -70,7 +70,7 @@ void RadialAnimation::step(TimeMs ms) { _animation.step(ms); } -void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) { +void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, style::color color) { float64 o = p.opacity(); p.setOpacity(o * _opacity); diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.h b/Telegram/SourceFiles/ui/effects/radial_animation.h index addf33698..15ac27e09 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.h +++ b/Telegram/SourceFiles/ui/effects/radial_animation.h @@ -42,7 +42,7 @@ public: step(getms()); } - void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color); + void draw(Painter &p, const QRect &inner, int32 thickness, style::color color); private: TimeMs _firstStart = 0; diff --git a/Telegram/SourceFiles/ui/effects/send_action_animations.cpp b/Telegram/SourceFiles/ui/effects/send_action_animations.cpp index 18004ed1f..af73c9cb2 100644 --- a/Telegram/SourceFiles/ui/effects/send_action_animations.cpp +++ b/Telegram/SourceFiles/ui/effects/send_action_animations.cpp @@ -51,13 +51,13 @@ public: } private: - void paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) override; + void paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) override; }; const TypingAnimation::MetaData TypingAnimation::kMeta = { 0, &TypingAnimation::create }; -void TypingAnimation::paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) { +void TypingAnimation::paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) { PainterHighQualityEnabler hq(p); p.setPen(Qt::NoPen); p.setBrush(color); @@ -96,13 +96,13 @@ public: } private: - void paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) override; + void paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) override; }; const RecordAnimation::MetaData RecordAnimation::kMeta = { 0, &RecordAnimation::create }; -void RecordAnimation::paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) { +void RecordAnimation::paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) { PainterHighQualityEnabler hq(p); auto pen = color->p; pen.setWidth(st::historySendActionRecordStrokeNumerator / st::historySendActionRecordDenominator); @@ -140,13 +140,13 @@ public: } private: - void paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) override; + void paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) override; }; const UploadAnimation::MetaData UploadAnimation::kMeta = { 0, &UploadAnimation::create }; -void UploadAnimation::paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) { +void UploadAnimation::paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) { PainterHighQualityEnabler hq(p); auto pen = color->p; pen.setWidth(st::historySendActionUploadStrokeNumerator / st::historySendActionUploadDenominator); diff --git a/Telegram/SourceFiles/ui/effects/send_action_animations.h b/Telegram/SourceFiles/ui/effects/send_action_animations.h index bfb802e50..d29f263bf 100644 --- a/Telegram/SourceFiles/ui/effects/send_action_animations.h +++ b/Telegram/SourceFiles/ui/effects/send_action_animations.h @@ -32,7 +32,7 @@ public: int width() const { return _impl ? _impl->width() : 0; } - void paint(Painter &p, const style::color &color, int x, int y, int outerWidth, TimeMs ms) { + void paint(Painter &p, style::color color, int x, int y, int outerWidth, TimeMs ms) { if (_impl) { _impl->paint(p, color, x, y, outerWidth, ms); } @@ -57,14 +57,14 @@ public: bool supports(Type type) const; virtual int width() const = 0; - void paint(Painter &p, const style::color &color, int x, int y, int outerWidth, TimeMs ms) { + void paint(Painter &p, style::color color, int x, int y, int outerWidth, TimeMs ms) { paintFrame(p, color, x, y, outerWidth, qMax(ms - _started, 0LL) % _period); } virtual ~Impl() = default; private: - virtual void paintFrame(Painter &p, const style::color &color, int x, int y, int outerWidth, int frameMs) = 0; + virtual void paintFrame(Painter &p, style::color color, int x, int y, int outerWidth, int frameMs) = 0; int _period = 1; TimeMs _started = 0; diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index ab705545a..36c86bca6 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -200,14 +200,15 @@ void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corn t_assert(!image.isNull()); QImage **masks = App::cornersMask(radius); - auto cornerWidth = masks[0]->width(); - auto cornerHeight = masks[0]->height(); + prepareRound(image, masks, corners); +} + +void prepareRound(QImage &image, QImage **cornerMasks, ImageRoundCorners corners) { + auto cornerWidth = cornerMasks[0]->width(); + auto cornerHeight = cornerMasks[0]->height(); auto imageWidth = image.width(); auto imageHeight = image.height(); if (imageWidth < 2 * cornerWidth || imageHeight < 2 * cornerHeight) { - if (radius == ImageRoundRadius::Large) { - return prepareRound(image, ImageRoundRadius::Small, corners); - } return; } constexpr auto imageIntsPerPixel = 1; @@ -242,13 +243,13 @@ void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corn imageInts += imageIntsAdded; } }; - if (corners & ImageRoundCorner::TopLeft) maskCorner(intsTopLeft, masks[0]); - if (corners & ImageRoundCorner::TopRight) maskCorner(intsTopRight, masks[1]); - if (corners & ImageRoundCorner::BottomLeft) maskCorner(intsBottomLeft, masks[2]); - if (corners & ImageRoundCorner::BottomRight) maskCorner(intsBottomRight, masks[3]); + if (corners & ImageRoundCorner::TopLeft) maskCorner(intsTopLeft, cornerMasks[0]); + if (corners & ImageRoundCorner::TopRight) maskCorner(intsTopRight, cornerMasks[1]); + if (corners & ImageRoundCorner::BottomLeft) maskCorner(intsBottomLeft, cornerMasks[2]); + if (corners & ImageRoundCorner::BottomRight) maskCorner(intsBottomRight, cornerMasks[3]); } -QImage prepareColored(const style::color &add, QImage image) { +QImage prepareColored(style::color add, QImage image) { auto format = image.format(); if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32_Premultiplied) { image = std_::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); @@ -547,7 +548,7 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { return i.value(); } -const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) const { +const QPixmap &Image::pixColored(style::color add, int32 w, int32 h) const { checkload(); if (w <= 0 || !width() || !height()) { @@ -570,7 +571,7 @@ const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) cons return i.value(); } -const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 h) const { +const QPixmap &Image::pixBlurredColored(style::color add, int32 w, int32 h) const { checkload(); if (w <= 0 || !width() || !height()) { @@ -721,7 +722,7 @@ QPixmap Image::pixNoCache(int w, int h, Images::Options options, int outerw, int return Images::pixmap(_data.toImage(), w, h, options, outerw, outerh); } -QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const { +QPixmap Image::pixColoredNoCache(style::color add, int32 w, int32 h, bool smooth) const { const_cast(this)->load(); restore(); if (_data.isNull()) return blank()->pix(); @@ -734,7 +735,7 @@ QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); } -QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h) const { +QPixmap Image::pixBlurredColoredNoCache(style::color add, int32 w, int32 h) const { const_cast(this)->load(); restore(); if (_data.isNull()) return blank()->pix(); diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index cf8ca01b4..bf54efad0 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -124,8 +124,9 @@ namespace Images { QImage prepareBlur(QImage image); void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All); +void prepareRound(QImage &image, QImage **cornerMasks, ImageRoundCorners corners = ImageRoundCorner::All); void prepareCircle(QImage &image); -QImage prepareColored(const style::color &add, QImage image); +QImage prepareColored(style::color add, QImage image); QImage prepareOpaque(QImage image); enum class Option { @@ -188,15 +189,15 @@ public: const QPixmap &pix(int32 w = 0, int32 h = 0) const; const QPixmap &pixRounded(int32 w = 0, int32 h = 0, ImageRoundRadius radius = ImageRoundRadius::None, ImageRoundCorners corners = ImageRoundCorner::All) const; const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; - const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const; - const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const; + const QPixmap &pixColored(style::color add, int32 w = 0, int32 h = 0) const; + const QPixmap &pixBlurredColored(style::color add, int32 w = 0, int32 h = 0) const; const QPixmap &pixSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All) const; const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All) const; const QPixmap &pixCircled(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurredCircled(int32 w = 0, int32 h = 0) const; QPixmap pixNoCache(int w = 0, int h = 0, Images::Options options = 0, int outerw = -1, int outerh = -1) const; - QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const; - QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const; + QPixmap pixColoredNoCache(style::color add, int32 w = 0, int32 h = 0, bool smooth = false) const; + QPixmap pixBlurredColoredNoCache(style::color add, int32 w, int32 h = 0) const; int32 width() const { return qMax(countWidth(), 1); diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 08659f8cf..c2b22c2e8 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -71,7 +71,6 @@ void startManager() { void stopManager() { internal::stopModules(); internal::destroyFonts(); - internal::destroyColors(); internal::destroyIcons(); } diff --git a/Telegram/SourceFiles/ui/style/style_core_color.cpp b/Telegram/SourceFiles/ui/style/style_core_color.cpp index edaa9533b..586440528 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_color.cpp @@ -23,63 +23,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace style { namespace internal { -namespace { -typedef QMap ColorDatas; -ColorDatas colorsMap; - -uint32 colorKey(uchar r, uchar g, uchar b, uchar a) { - return (((((uint32(r) << 8) | uint32(g)) << 8) | uint32(b)) << 8) | uint32(a); -} - -} // namespace - -void destroyColors() { - for (auto colorData : colorsMap) { - delete colorData; - } - colorsMap.clear(); -} - -Color::Color(ColorData *data) : ptr(data) { -} - -Color::Color(uchar r, uchar g, uchar b, uchar a) { - init(r, g, b, a); -} - -Color::Color(Color &&other) : ptr(other.ptr) { -} - -Color &Color::operator=(Color &&other) { - ptr = other.ptr; - return *this; -} - -void Color::set(uchar r, uchar g, uchar b, uchar a) const { - ptr->set(r, g, b, a); -} - -void Color::init(uchar r, uchar g, uchar b, uchar a) { - if (ptr) { - return set(r, g, b, a); - } - auto key = colorKey(r, g, b, a); - auto i = colorsMap.constFind(key); - if (i == colorsMap.cend()) { - i = colorsMap.insert(key, new ColorData(r, g, b, a)); - } - ptr = i.value(); -} - -ColorData::ColorData() : p(Qt::NoPen), b(Qt::NoBrush) { +Color::Proxy Color::operator[](const style::palette &paletteOverride) const { + auto index = main_palette::indexOfColor(*this); + return Proxy((index >= 0) ? paletteOverride.colorAtIndex(index) : (*this)); } ColorData::ColorData(uchar r, uchar g, uchar b, uchar a) : c(int(r), int(g), int(b), int(a)), p(c), b(c) { } void ColorData::set(uchar r, uchar g, uchar b, uchar a) { - this->c = QColor(r, g, b, a); + this->c = QColor(int(r), int(g), int(b), int(a)); this->p = QPen(c); this->b = QBrush(c); } diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index 0c8fed354..f40de92e7 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -26,8 +26,6 @@ class palette; namespace internal { -void destroyColors(); - class Color; class ColorData { public: @@ -40,8 +38,10 @@ public: } private: - ColorData(); ColorData(uchar r, uchar g, uchar b, uchar a); + ColorData(const ColorData &other) = default; + ColorData &operator=(const ColorData &other) = default; + void set(uchar r, uchar g, uchar b, uchar a); friend class Color; @@ -53,52 +53,67 @@ class Color { public: Color(Qt::Initialization = Qt::Uninitialized) { } - Color(uchar r, uchar g, uchar b, uchar a); - Color(const Color &other) = delete; - Color &operator=(const Color &other) = delete; - Color(Color &&other); - Color &operator=(Color &&other); + Color(const Color &other) = default; + Color &operator=(const Color &other) = default; - Color clone() const { - return Color(ptr); + void set(uchar r, uchar g, uchar b, uchar a) const { + _data->set(r, g, b, a); } - void set(uchar r, uchar g, uchar b, uchar a) const; - operator const QBrush &() const { - return ptr->b; + return _data->b; } operator const QPen &() const { - return ptr->p; + return _data->p; } ColorData *operator->() const { - return ptr; + return _data; } ColorData *v() const { - return ptr; + return _data; } explicit operator bool() const { - return !!ptr; + return !!_data; } + class Proxy; + Proxy operator[](const style::palette &paletteOverride) const; + private: - Color(ColorData *data); - void init(uchar r, uchar g, uchar b, uchar a); - - ColorData *ptr = nullptr; - friend class style::palette; + Color(ColorData *data) : _data(data) { + } + + ColorData *_data = nullptr; }; -inline bool operator==(const Color &a, const Color &b) { +class Color::Proxy { +public: + Proxy(Color color) : _color(color) { + } + Proxy(const Proxy &other) = default; + + operator const QBrush &() const { return _color; } + operator const QPen &() const { return _color; } + ColorData *operator->() const { return _color.v(); } + ColorData *v() const { return _color.v(); } + explicit operator bool() const { return _color ? true : false; } + Color clone() const { return _color; } + +private: + Color _color; + +}; + +inline bool operator==(Color a, Color b) { return a->c == b->c; } -inline bool operator!=(const Color &a, const Color &b) { +inline bool operator!=(Color a, Color b) { return a->c != b->c; } diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.cpp b/Telegram/SourceFiles/ui/style/style_core_icon.cpp index 0e6d80da1..2b4edd278 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_icon.cpp @@ -43,7 +43,7 @@ inline int pxAdjust(int value, int scale) { return qFloor((value * scale / 4.) + 0.1); } -QImage createIconMask(const IconMask *mask) { +QImage createIconMask(const IconMask *mask, DBIScale scale) { auto maskImage = QImage::fromData(mask->data(), mask->size(), "PNG"); maskImage.setDevicePixelRatio(cRetinaFactor()); t_assert(!maskImage.isNull()); @@ -54,15 +54,15 @@ QImage createIconMask(const IconMask *mask) { int width = maskImage.width() / 3; int height = qRound((maskImage.height() * 2) / 7.); auto r = QRect(0, 0, width * 2, height * 2); - if (!cRetina() && cScale() != dbisTwo) { - if (cScale() == dbisOne) { + if (!cRetina() && scale != dbisTwo) { + if (scale == dbisOne) { r = QRect(width * 2, 0, width, height); } else { int width125 = pxAdjust(width, 5); int height125 = pxAdjust(height, 5); int width150 = pxAdjust(width, 6); int height150 = pxAdjust(height, 6); - if (cScale() == dbisOneAndQuarter) { + if (scale == dbisOneAndQuarter) { r = QRect(width150, height * 2, width125, height125); } else { r = QRect(0, height * 2, width150, height150); @@ -72,9 +72,45 @@ QImage createIconMask(const IconMask *mask) { return maskImage.copy(r); } +QSize readGeneratedSize(const IconMask *mask, DBIScale scale) { + auto data = mask->data(); + auto size = mask->size(); + + auto generateTag = qstr("GENERATE:"); + if (size > generateTag.size() && !memcmp(data, generateTag.data(), generateTag.size())) { + size -= generateTag.size(); + data += generateTag.size(); + auto sizeTag = qstr("SIZE:"); + if (size > sizeTag.size() && !memcmp(data, sizeTag.data(), sizeTag.size())) { + size -= sizeTag.size(); + data += sizeTag.size(); + auto baForStream = QByteArray::fromRawData(reinterpret_cast(data), size); + QBuffer buffer(&baForStream); + buffer.open(QIODevice::ReadOnly); + + QDataStream stream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + + qint32 width = 0, height = 0; + stream >> width >> height; + t_assert(stream.status() == QDataStream::Ok); + + switch (scale) { + case dbisOne: return QSize(width, height); + case dbisOneAndQuarter: return QSize(pxAdjust(width, 5), pxAdjust(height, 5)); + case dbisOneAndHalf: return QSize(pxAdjust(width, 6), pxAdjust(height, 6)); + case dbisTwo: return QSize(width * 2, height * 2); + } + } else { + t_assert(!"Bad data in generated icon!"); + } + } + return QSize(); +} + } // namespace -MonoIcon::MonoIcon(const IconMask *mask, Color &&color, QPoint offset) +MonoIcon::MonoIcon(const IconMask *mask, Color color, QPoint offset) : _mask(mask) , _color(std_::move(color)) , _offset(offset) { @@ -152,6 +188,57 @@ void MonoIcon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const } } +void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw, const style::palette &paletteOverride) const { + int w = width(), h = height(); + QPoint fullOffset = pos + offset(); + int partPosX = rtl() ? (outerw - fullOffset.x() - w) : fullOffset.x(); + int partPosY = fullOffset.y(); + + ensureLoaded(); + if (_pixmap.isNull()) { + p.fillRect(partPosX, partPosY, w, h, _color[paletteOverride]); + } else { + ensureColorizedImage(_color[paletteOverride]->c); + p.drawImage(partPosX, partPosY, _colorizedImage); + } +} + +void MonoIcon::fill(QPainter &p, const QRect &rect, const style::palette &paletteOverride) const { + ensureLoaded(); + if (_pixmap.isNull()) { + p.fillRect(rect, _color[paletteOverride]); + } else { + ensureColorizedImage(_color[paletteOverride]->c); + p.drawImage(rect, _colorizedImage, _colorizedImage.rect()); + } +} + +QImage MonoIcon::instance(QColor colorOverride, DBIScale scale) const { + if (scale == dbisAuto) { + ensureLoaded(); + auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + if (_pixmap.isNull()) { + result.fill(colorOverride); + } else { + colorizeImage(_maskImage, colorOverride, &result); + } + return std_::move(result); + } + auto size = readGeneratedSize(_mask, scale); + if (!size.isEmpty()) { + auto result = QImage(size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(colorOverride); + return std_::move(result); + } + auto mask = createIconMask(_mask, scale); + auto result = QImage(mask.size(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + colorizeImage(mask, colorOverride, &result); + return std_::move(result); +} + void MonoIcon::ensureLoaded() const { if (_size.isValid()) { return; @@ -161,42 +248,12 @@ void MonoIcon::ensureLoaded() const { return; } - auto data = _mask->data(); - auto size = _mask->size(); - - auto generateTag = qstr("GENERATE:"); - if (size > generateTag.size() && !memcmp(data, generateTag.data(), generateTag.size())) { - size -= generateTag.size(); - data += generateTag.size(); - auto sizeTag = qstr("SIZE:"); - if (size > sizeTag.size() && !memcmp(data, sizeTag.data(), sizeTag.size())) { - size -= sizeTag.size(); - data += sizeTag.size(); - auto baForStream = QByteArray::fromRawData(reinterpret_cast(data), size); - QBuffer buffer(&baForStream); - buffer.open(QIODevice::ReadOnly); - - QDataStream stream(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - - qint32 width = 0, height = 0; - stream >> width >> height; - t_assert(stream.status() == QDataStream::Ok); - - switch (cScale()) { - case dbisOne: _size = QSize(width, height); break; - case dbisOneAndQuarter: _size = QSize(pxAdjust(width, 5), pxAdjust(height, 5)); break; - case dbisOneAndHalf: _size = QSize(pxAdjust(width, 6), pxAdjust(height, 6)); break; - case dbisTwo: _size = QSize(width * 2, height * 2); break; - } - } else { - t_assert(!"Bad data in generated icon!"); - } - } else { + _size = readGeneratedSize(_mask, cScale()); + if (_size.isEmpty()) { iconMasks.createIfNull(); auto i = iconMasks->constFind(_mask); if (i == iconMasks->cend()) { - i = iconMasks->insert(_mask, createIconMask(_mask)); + i = iconMasks->insert(_mask, createIconMask(_mask, cScale())); } _maskImage = i.value(); @@ -229,7 +286,7 @@ void IconData::created() { void IconData::fill(QPainter &p, const QRect &rect) const { if (_parts.isEmpty()) return; - auto partSize = _parts.at(0).size(); + auto partSize = _parts[0].size(); for_const (auto &part, _parts) { t_assert(part.offset() == QPoint(0, 0)); t_assert(part.size() == partSize); @@ -240,7 +297,7 @@ void IconData::fill(QPainter &p, const QRect &rect) const { void IconData::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { if (_parts.isEmpty()) return; - auto partSize = _parts.at(0).size(); + auto partSize = _parts[0].size(); for_const (auto &part, _parts) { t_assert(part.offset() == QPoint(0, 0)); t_assert(part.size() == partSize); @@ -248,6 +305,13 @@ void IconData::fill(QPainter &p, const QRect &rect, QColor colorOverride) const } } +QImage IconData::instance(QColor colorOverride, DBIScale scale) const { + t_assert(_parts.size() == 1); + auto &part = _parts[0]; + t_assert(part.offset() == QPoint(0, 0)); + return part.instance(colorOverride, scale); +} + int IconData::width() const { if (_width < 0) { _width = 0; diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.h b/Telegram/SourceFiles/ui/style/style_core_icon.h index f2799d7ab..ba88511dd 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.h +++ b/Telegram/SourceFiles/ui/style/style_core_icon.h @@ -53,7 +53,7 @@ public: MonoIcon &operator=(const MonoIcon &other) = delete; MonoIcon(MonoIcon &&other) = default; MonoIcon &operator=(MonoIcon &&other) = default; - MonoIcon(const IconMask *mask, Color &&color, QPoint offset); + MonoIcon(const IconMask *mask, Color color, QPoint offset); void reset() const; int width() const; @@ -64,9 +64,15 @@ public: void paint(QPainter &p, const QPoint &pos, int outerw) const; void fill(QPainter &p, const QRect &rect) const; + void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const; void fill(QPainter &p, const QRect &rect, QColor colorOverride) const; + void paint(QPainter &p, const QPoint &pos, int outerw, const style::palette &paletteOverride) const; + void fill(QPainter &p, const QRect &rect, const style::palette &paletteOverride) const; + + QImage instance(QColor colorOverride, DBIScale scale) const; + ~MonoIcon() { } @@ -116,6 +122,15 @@ public: } void fill(QPainter &p, const QRect &rect, QColor colorOverride) const; + void paint(QPainter &p, const QPoint &pos, int outerw, const style::palette &paletteOverride) const { + for_const (auto &part, _parts) { + part.paint(p, pos, outerw, paletteOverride); + } + } + void fill(QPainter &p, const QRect &rect, const style::palette &paletteOverride) const; + + QImage instance(QColor colorOverride, DBIScale scale) const; + int width() const; int height() const; @@ -167,6 +182,16 @@ public: return _data->empty(); } + int width() const { + return _data->width(); + } + int height() const { + return _data->height(); + } + QSize size() const { + return QSize(width(), height()); + } + void paint(QPainter &p, const QPoint &pos, int outerw) const { return _data->paint(p, pos, outerw); } @@ -193,14 +218,41 @@ public: return _data->fill(p, rect, colorOverride); } - int width() const { - return _data->width(); + QImage instance(QColor colorOverride, DBIScale scale = dbisAuto) const { + return _data->instance(colorOverride, scale); } - int height() const { - return _data->height(); - } - QSize size() const { - return QSize(width(), height()); + + class Proxy { + public: + Proxy(const Icon &icon, const style::palette &palette) : _icon(icon), _palette(palette) { + } + Proxy(const Proxy &other) = default; + + bool empty() const { return _icon.empty(); } + int width() const { return _icon.width(); } + int height() const { return _icon.height(); } + QSize size() const { return _icon.size(); } + + void paint(QPainter &p, const QPoint &pos, int outerw) const { + return _icon.paintWithPalette(p, pos, outerw, _palette); + } + void paint(QPainter &p, int x, int y, int outerw) const { + return _icon.paintWithPalette(p, x, y, outerw, _palette); + } + void paintInCenter(QPainter &p, const QRect &outer) const { + return _icon.paintInCenterWithPalette(p, outer, _palette); + } + void fill(QPainter &p, const QRect &rect) const { + return _icon.fillWithPalette(p, rect, _palette); + } + + private: + const Icon &_icon; + const style::palette &_palette; + + }; + Proxy operator[](const style::palette &paletteOverride) const { + return Proxy(*this, paletteOverride); } ~Icon() { @@ -212,6 +264,21 @@ public: } private: + friend class Proxy; + + void paintWithPalette(QPainter &p, const QPoint &pos, int outerw, const style::palette &paletteOverride) const { + return _data->paint(p, pos, outerw, paletteOverride); + } + void paintWithPalette(QPainter &p, int x, int y, int outerw, const style::palette &paletteOverride) const { + return _data->paint(p, QPoint(x, y), outerw, paletteOverride); + } + void paintInCenterWithPalette(QPainter &p, const QRect &outer, const style::palette &paletteOverride) const { + return _data->paint(p, QPoint(outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2), outer.x() * 2 + outer.width(), paletteOverride); + } + void fillWithPalette(QPainter &p, const QRect &rect, const style::palette &paletteOverride) const { + return _data->fill(p, rect, paletteOverride); + } + IconData *_data = nullptr; bool _owner = false; diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index a4d18eb5e..3a46416d5 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -32,26 +32,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace { -const style::TextStyle *_textStyle = nullptr; - -void initDefault() { - _textStyle = &st::defaultTextStyle; -} - -inline int32 countBlockHeight(const ITextBlock *b, const style::font &font) { - return (b->type() == TextBlockTSkip) ? static_cast(b)->height() : (_textStyle->lineHeight > font->height) ? _textStyle->lineHeight : font->height; +inline int32 countBlockHeight(const ITextBlock *b, const style::TextStyle *st) { + return (b->type() == TextBlockTSkip) ? static_cast(b)->height() : (st->lineHeight > st->font->height) ? st->lineHeight : st->font->height; } } // namespace -const style::TextStyle *textstyleCurrent() { - return _textStyle; -} - -void textstyleSet(const style::TextStyle *style) { - _textStyle = style ? style : &st::defaultTextStyle; -} - QString textcmdSkipBlock(ushort w, ushort h) { static QString cmd(5, TextCommand); cmd[1] = QChar(TextCommandSkipBlock); @@ -194,13 +180,13 @@ public: } lastSkipped = false; if (emoji) { - _t->_blocks.push_back(new EmojiBlock(_t->_font, _t->_text, blockStart, len, flags, lnkIndex, emoji)); + _t->_blocks.push_back(new EmojiBlock(_t->_st->font, _t->_text, blockStart, len, flags, lnkIndex, emoji)); emoji = 0; lastSkipped = true; } else if (newline) { - _t->_blocks.push_back(new NewlineBlock(_t->_font, _t->_text, blockStart, len)); + _t->_blocks.push_back(new NewlineBlock(_t->_st->font, _t->_text, blockStart, len)); } else { - _t->_blocks.push_back(new TextBlock(_t->_font, _t->_text, _t->_minResizeWidth, blockStart, len, flags, lnkIndex)); + _t->_blocks.push_back(new TextBlock(_t->_st->font, _t->_text, _t->_minResizeWidth, blockStart, len, flags, lnkIndex)); } blockStart += len; blockCreated(); @@ -210,7 +196,7 @@ public: void createSkipBlock(int32 w, int32 h) { createBlock(); _t->_text.push_back('_'); - _t->_blocks.push_back(new SkipBlock(_t->_font, _t->_text, blockStart++, w, h, lnkIndex)); + _t->_blocks.push_back(new SkipBlock(_t->_st->font, _t->_text, blockStart++, w, h, lnkIndex)); blockCreated(); } @@ -557,7 +543,7 @@ public: } void parse(const TextParseOptions &options) { if (options.maxw > 0 && options.maxh > 0) { - stopAfterWidth = ((options.maxh / _t->_font->height) + 1) * options.maxw; + stopAfterWidth = ((options.maxh / _t->_st->font->height) + 1) * options.maxw; } start = src.constData(); @@ -587,7 +573,7 @@ public: ch = emojiLookback = 0; lastSkipped = false; - checkTilde = !cRetina() && _t->_font->size() == 13 && _t->_font->flags() == 0; // tilde Open Sans fix + checkTilde = !cRetina() && _t->_st->font->size() == 13 && _t->_st->font->flags() == 0; // tilde Open Sans fix for (; ptr <= end; ++ptr) { while (checkEntities() || (rich && checkCommand())) { } @@ -682,7 +668,7 @@ private: void computeLinkText(const QString &linkData, QString *outLinkText, LinkDisplayStatus *outDisplayStatus) { QUrl url(linkData), good(url.isValid() ? url.toEncoded() : ""); QString readable = good.isValid() ? good.toDisplayString() : linkData; - *outLinkText = _t->_font->elided(readable, st::linkCropLimit); + *outLinkText = _t->_st->font->elided(readable, st::linkCropLimit); *outDisplayStatus = (*outLinkText == readable) ? LinkDisplayedFull : LinkDisplayedElided; } @@ -822,7 +808,7 @@ public: return _blockEnd(t, i, e) - (*i)->from(); } - TextPainter(QPainter *p, const Text *t) : _p(p), _t(t) { + TextPainter(Painter *p, const Text *t) : _p(p), _t(t) { } ~TextPainter() { @@ -894,10 +880,8 @@ public: if (_t->isEmpty()) return; _blocksSize = _t->_blocks.size(); - if (!_textStyle) initDefault(); - if (_p) { - _p->setFont(_t->_font->f); + _p->setFont(_t->_st->font); _originalPen = _p->pen(); } @@ -911,7 +895,7 @@ public: if (_elideLast) { _yToElide = _yTo; if (_elideRemoveFromEnd > 0 && !_t->_blocks.isEmpty()) { - int firstBlockHeight = countBlockHeight(_t->_blocks.front(), _t->_font); + int firstBlockHeight = countBlockHeight(_t->_blocks.front(), _t->_st); if (_y + firstBlockHeight >= _yToElide) { _wLeft -= _elideRemoveFromEnd; } @@ -920,7 +904,7 @@ public: _str = _t->_text.unicode(); if (_p) { - QRectF clip = _p->clipBoundingRect(); + auto clip = _p->hasClipping() ? _p->clipBoundingRect() : QRectF(); if (clip.width() > 0 || clip.height() > 0) { if (_yFrom < clip.y()) _yFrom = clip.y(); if (_yTo < 0 || _yTo > clip.y() + clip.height()) _yTo = clip.y() + clip.height(); @@ -939,7 +923,7 @@ public: _lineStartBlock = 0; _lineHeight = 0; - _fontHeight = _t->_font->height; + _fontHeight = _t->_st->font->height; QFixed last_rBearing = 0, last_rPadding = 0; int32 blockIndex = 0; @@ -948,7 +932,7 @@ public: for (Text::TextBlocks::const_iterator i = _t->_blocks.cbegin(); i != e; ++i, ++blockIndex) { ITextBlock *b = *i; TextBlockType _btype = b->type(); - int32 blockHeight = countBlockHeight(b, _t->_font); + int32 blockHeight = countBlockHeight(b, _t->_st); if (_btype == TextBlockTNewline) { if (!_lineHeight) _lineHeight = blockHeight; @@ -1091,8 +1075,8 @@ public: void drawElided(int32 left, int32 top, int32 w, style::align align, int32 lines, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) { if (lines <= 0 || _t->isNull()) return; - if (yTo < 0 || (lines - 1) * _t->_font->height < yTo) { - yTo = lines * _t->_font->height; + if (yTo < 0 || (lines - 1) * _t->_st->font->height < yTo) { + yTo = lines * _t->_st->font->height; _elideLast = true; _elideRemoveFromEnd = removeFromEnd; } @@ -1127,8 +1111,8 @@ public: _lookupLink = (_lookupRequest.flags & Text::StateRequest::Flag::LookupLink); if (_lookupSymbol || (_lookupX >= 0 && _lookupX < w)) { int yTo = _lookupY + 1; - if (yTo < 0 || (request.lines - 1) * _t->_font->height < yTo) { - yTo = request.lines * _t->_font->height; + if (yTo < 0 || (request.lines - 1) * _t->_st->font->height < yTo) { + yTo = request.lines * _t->_st->font->height; _elideLast = true; _elideRemoveFromEnd = request.removeFromEnd; } @@ -1140,13 +1124,10 @@ public: const QPen &blockPen(ITextBlock *block) { if (block->lnkIndex()) { - if (ClickHandler::showAsPressed(_t->_links.at(block->lnkIndex() - 1))) { - return _textStyle->linkFgDown->p; - } - return _textStyle->linkFg->p; + return _p->textPalette().linkFg->p; } if ((block->flags() & TextBlockFCode) || (block->flags() & TextBlockFPre)) { - return _textStyle->monoFg->p; + return _p->textPalette().monoFg->p; } return _originalPen; } @@ -1249,12 +1230,12 @@ public: if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) { if (x > _x) { - _p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _p->textPalette().selectBg->b); } } if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) { if (x < _x + _wLeft) { - _p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _p->textPalette().selectBg->b); } } } @@ -1262,7 +1243,7 @@ public: if (!elidedLine) initParagraphBidi(); // if was not inited - _f = _t->_font; + _f = _t->_st->font; QStackTextEngine engine(lineText, _f->f); engine.option.setTextDirection(_parDirection); _e = &engine; @@ -1315,7 +1296,7 @@ public: currentBlock = _t->_blocks[blockIndex]; nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex] : 0; - int32 textY = _y + _yDelta + _t->_font->ascent, emojiY = (_t->_font->height - st::emojiSize) / 2; + int32 textY = _y + _yDelta + _t->_st->font->ascent, emojiY = (_t->_st->font->height - st::emojiSize) / 2; eSetFont(currentBlock); if (_p) _p->setPen(blockPen(currentBlock)); @@ -1391,15 +1372,15 @@ public: const QChar *chFrom = _str + currentBlock->from(), *chTo = chFrom + ((nextBlock ? nextBlock->from() : _t->_text.size()) - currentBlock->from()); if (_localFrom + si.position >= _selection.from) { // could be without space if (chTo == chFrom || (chTo - 1)->unicode() != QChar::Space || _selection.to >= (chTo - _str)) { - _p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _p->textPalette().selectBg); } else { // or with space - _p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _p->textPalette().selectBg); } } else if (chTo > chFrom && (chTo - 1)->unicode() == QChar::Space && (chTo - 1 - _str) >= _selection.from) { if (rtl) { // rtl space only - _p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _p->textPalette().selectBg->b); } else { // ltr space only - _p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _p->textPalette().selectBg); } } } @@ -1522,7 +1503,7 @@ public: } } if (rtl) selX = x + itemWidth - (selX - x) - selWidth; - _p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBg->b); + _p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _p->textPalette().selectBg->b); } _p->drawTextItem(QPointF(x.toReal(), textY), gf); @@ -1540,7 +1521,7 @@ public: _elideSavedIndex = blockIndex; _elideSavedBlock = _t->_blocks[blockIndex]; - const_cast(_t)->_blocks[blockIndex] = new TextBlock(_t->_font, _t->_text, QFIXED_MAX, elideStart, 0, _elideSavedBlock->flags(), _elideSavedBlock->lnkIndex()); + const_cast(_t)->_blocks[blockIndex] = new TextBlock(_t->_st->font, _t->_text, QFIXED_MAX, elideStart, 0, _elideSavedBlock->flags(), _elideSavedBlock->lnkIndex()); _blocksSize = blockIndex + 1; _endBlock = (blockIndex + 1 < _t->_blocks.size() ? _t->_blocks[blockIndex + 1] : 0); } @@ -1558,7 +1539,7 @@ public: void prepareElidedLine(QString &lineText, int32 lineStart, int32 &lineLength, ITextBlock *&_endBlock, int repeat = 0) { static const QString _Elide = qsl("..."); - _f = _t->_font; + _f = _t->_st->font; QStackTextEngine engine(lineText, _f->f); engine.option.setTextDirection(_parDirection); _e = &engine; @@ -1718,25 +1699,25 @@ public: } void eSetFont(ITextBlock *block) { - style::font newFont = _t->_font; + style::font newFont = _t->_st->font; int flags = block->flags(); if (flags) { - newFont = applyFlags(flags, _t->_font); + newFont = applyFlags(flags, _t->_st->font); } if (block->lnkIndex()) { if (ClickHandler::showAsActive(_t->_links.at(block->lnkIndex() - 1))) { - if (_t->_font != _textStyle->linkFlagsOver) { - newFont = _textStyle->linkFlagsOver; + if (_t->_st->font != _t->_st->linkFontOver) { + newFont = _t->_st->linkFontOver; } } else { - if (_t->_font != _textStyle->linkFlags) { - newFont = _textStyle->linkFlags; + if (_t->_st->font != _t->_st->linkFont) { + newFont = _t->_st->linkFont; } } } if (newFont != _f) { - if (newFont->family() == _t->_font->family()) { - newFont = applyFlags(flags | newFont->flags(), _t->_font); + if (newFont->family() == _t->_st->font->family()) { + newFont = applyFlags(flags | newFont->flags(), _t->_st->font); } _f = newFont; _e->fnt = _f->f; @@ -2291,7 +2272,7 @@ public: private: - QPainter *_p; + Painter *_p; const Text *_t; bool _elideLast = false; bool _breakEverywhere = false; @@ -2348,14 +2329,14 @@ const TextParseOptions _textPlainOptions = { Qt::LayoutDirectionAuto, // dir }; -Text::Text(int32 minResizeWidth) : _minResizeWidth(minResizeWidth), _maxWidth(0), _minHeight(0), _startDir(Qt::LayoutDirectionAuto) { +Text::Text(int32 minResizeWidth) : _minResizeWidth(minResizeWidth) { } -Text::Text(style::font font, const QString &text, const TextParseOptions &options, int32 minResizeWidth, bool richText) : _minResizeWidth(minResizeWidth) { +Text::Text(const style::TextStyle &st, const QString &text, const TextParseOptions &options, int32 minResizeWidth, bool richText) : _minResizeWidth(minResizeWidth) { if (richText) { - setRichText(font, text, options); + setRichText(st, text, options); } else { - setText(font, text, options); + setText(st, text, options); } } @@ -2364,7 +2345,7 @@ Text::Text(const Text &other) , _maxWidth(other._maxWidth) , _minHeight(other._minHeight) , _text(other._text) -, _font(other._font) +, _st(other._st) , _blocks(other._blocks.size()) , _links(other._links) , _startDir(other._startDir) { @@ -2378,7 +2359,7 @@ Text::Text(Text &&other) , _maxWidth(other._maxWidth) , _minHeight(other._minHeight) , _text(other._text) -, _font(other._font) +, _st(other._st) , _blocks(other._blocks) , _links(other._links) , _startDir(other._startDir) { @@ -2390,7 +2371,7 @@ Text &Text::operator=(const Text &other) { _maxWidth = other._maxWidth; _minHeight = other._minHeight; _text = other._text; - _font = other._font; + _st = other._st; _blocks = TextBlocks(other._blocks.size()); _links = other._links; _startDir = other._startDir; @@ -2405,7 +2386,7 @@ Text &Text::operator=(Text &&other) { _maxWidth = other._maxWidth; _minHeight = other._minHeight; _text = other._text; - _font = other._font; + _st = other._st; _blocks = other._blocks; _links = other._links; _startDir = other._startDir; @@ -2413,9 +2394,8 @@ Text &Text::operator=(Text &&other) { return *this; } -void Text::setText(style::font font, const QString &text, const TextParseOptions &options) { - if (!_textStyle) initDefault(); - _font = font; +void Text::setText(const style::TextStyle &st, const QString &text, const TextParseOptions &options) { + _st = &st; clear(); { TextParser parser(this, text, options); @@ -2433,7 +2413,7 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) { for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) { ITextBlock *b = *i; TextBlockType _btype = b->type(); - int32 blockHeight = countBlockHeight(b, _font); + int32 blockHeight = countBlockHeight(b, _st); if (_btype == TextBlockTNewline) { if (!lineHeight) lineHeight = blockHeight; if (initial) { @@ -2483,7 +2463,7 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) { } } if (_width > 0) { - if (!lineHeight) lineHeight = countBlockHeight(_blocks.back(), _font); + if (!lineHeight) lineHeight = countBlockHeight(_blocks.back(), _st); _minHeight += lineHeight; if (_maxWidth < _width) { _maxWidth = _width; @@ -2491,9 +2471,8 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) { } } -void Text::setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options) { - if (!_textStyle) initDefault(); - _font = font; +void Text::setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options) { + _st = &st; clear(); { // QString newText; // utf16 of the text for emoji @@ -2531,7 +2510,7 @@ void Text::setMarkedText(style::font font, const TextWithEntities &textWithEntit recountNaturalSize(true, options.dir); } -void Text::setRichText(style::font font, const QString &text, TextParseOptions options, const TextCustomTagsMap &custom) { +void Text::setRichText(const style::TextStyle &st, const QString &text, TextParseOptions options, const TextCustomTagsMap &custom) { QString parsed; parsed.reserve(text.size()); const QChar *s = text.constData(), *ch = s; @@ -2606,7 +2585,7 @@ void Text::setRichText(style::font font, const QString &text, TextParseOptions o s = ch; options.flags |= TextParseRichText; - setText(font, parsed, options); + setText(st, parsed, options); } void Text::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) { @@ -2630,7 +2609,7 @@ void Text::setSkipBlock(int32 width, int32 height) { _blocks.pop_back(); } _text.push_back('_'); - _blocks.push_back(new SkipBlock(_font, _text, _text.size() - 1, width, height, 0)); + _blocks.push_back(new SkipBlock(_st->font, _text, _text.size() - 1, width, height, 0)); recountNaturalSize(false); } @@ -2683,7 +2662,7 @@ void Text::enumerateLines(int w, Callback callback) const { bool longWordLine = true; for_const (auto b, _blocks) { TextBlockType _btype = b->type(); - int blockHeight = countBlockHeight(b, _font); + int blockHeight = countBlockHeight(b, _st); if (_btype == TextBlockTNewline) { if (!lineHeight) lineHeight = blockHeight; @@ -2785,17 +2764,13 @@ void Text::enumerateLines(int w, Callback callback) const { } } -void Text::replaceFont(style::font f) { - _font = f; -} - -void Text::draw(QPainter &painter, int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection, bool fullWidthSelection) const { +void Text::draw(Painter &painter, int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection, bool fullWidthSelection) const { // painter.fillRect(QRect(left, top, w, countHeight(w)), QColor(0, 0, 0, 32)); // debug TextPainter p(&painter, this); p.draw(left, top, w, align, yFrom, yTo, selection, fullWidthSelection); } -void Text::drawElided(QPainter &painter, int32 left, int32 top, int32 w, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) const { +void Text::drawElided(Painter &painter, int32 left, int32 top, int32 w, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) const { // painter.fillRect(QRect(left, top, w, countHeight(w)), QColor(0, 0, 0, 32)); // debug TextPainter p(&painter, this); p.drawElided(left, top, w, align, lines, yFrom, yTo, removeFromEnd, breakEverywhere, selection); diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 4a86dd80c..87f973ade 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -85,7 +85,7 @@ class Text { public: Text(int32 minResizeWidth = QFIXED_MAX); - Text(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false); + Text(const style::TextStyle &st, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false); Text(const Text &other); Text(Text &&other); Text &operator=(const Text &other); @@ -94,9 +94,9 @@ public: int countWidth(int width) const; int countHeight(int width) const; void countLineWidths(int width, QVector *lineWidths) const; - void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions); - void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap()); - void setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions); + void setText(const style::TextStyle &st, const QString &text, const TextParseOptions &options = _defaultOptions); + void setRichText(const style::TextStyle &st, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap()); + void setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions); void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); bool hasLinks() const; @@ -112,20 +112,18 @@ public: return _minHeight; } - void replaceFont(style::font f); // does not recount anything, use at your own risk! - - void draw(QPainter &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(QPainter &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(QPainter &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 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(QPainter &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 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(QPainter &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 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(QPainter &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 { + 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); } @@ -173,7 +171,7 @@ public: bool isEmpty() const; bool isNull() const { - return !_font; + return !_st; } int length() const { return _text.size(); @@ -224,11 +222,12 @@ private: // it is also called from move constructor / assignment operator void clearFields(); - QFixed _minResizeWidth, _maxWidth; - int32 _minHeight; + QFixed _minResizeWidth; + QFixed _maxWidth = 0; + int32 _minHeight = 0; QString _text; - style::font _font; + const style::TextStyle *_st = nullptr; typedef QVector TextBlocks; TextBlocks _blocks; @@ -236,7 +235,7 @@ private: typedef QVector TextLinks; TextLinks _links; - Qt::LayoutDirection _startDir; + Qt::LayoutDirection _startDir = Qt::LayoutDirectionAuto; friend class TextParser; friend class TextPainter; @@ -263,13 +262,6 @@ const QRegularExpression &reMailStart(); const QRegularExpression &reHashtag(); const QRegularExpression &reBotCommand(); -// text style -const style::TextStyle *textstyleCurrent(); -void textstyleSet(const style::TextStyle *style); -inline void textstyleRestore() { - textstyleSet(nullptr); -} - // textcmd QString textcmdSkipBlock(ushort w, ushort h); QString textcmdStartLink(ushort lnkIndex); diff --git a/Telegram/SourceFiles/ui/toast/toast.h b/Telegram/SourceFiles/ui/toast/toast.h index 4acc538cc..d9e9f5c80 100644 --- a/Telegram/SourceFiles/ui/toast/toast.h +++ b/Telegram/SourceFiles/ui/toast/toast.h @@ -32,6 +32,8 @@ static constexpr const int DefaultDuration = 1500; struct Config { QString text; int durationMs = DefaultDuration; + int maxWidth = 0; + QMargins padding; }; void Show(QWidget *parent, const Config &config); diff --git a/Telegram/SourceFiles/ui/toast/toast_widget.cpp b/Telegram/SourceFiles/ui/toast/toast_widget.cpp index f8af6c05e..a9ec95504 100644 --- a/Telegram/SourceFiles/ui/toast/toast_widget.cpp +++ b/Telegram/SourceFiles/ui/toast/toast_widget.cpp @@ -24,10 +24,23 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { namespace Toast { namespace internal { +namespace { -Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) { - TextParseOptions toastOptions = { 0, int(st::toastMaxWidth), st::toastFont->height, Qt::LayoutDirectionAuto }; - _text.setText(st::toastFont, textOneLine(config.text), toastOptions); +constexpr auto kToastMaxLines = 16; + +} // namespace + +Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) +, _multiline(config.maxWidth > 0) +, _maxWidth((config.maxWidth > 0) ? config.maxWidth : st::toastMaxWidth) +, _padding((config.padding.left() > 0) ? config.padding : st::toastPadding) +, _maxTextWidth(_maxWidth - _padding.left() - _padding.right()) +, _text(_multiline ? _maxTextWidth : QFIXED_MAX) { + TextParseOptions toastOptions = { 0, _maxTextWidth, st::toastTextStyle.font->height, Qt::LayoutDirectionAuto }; + if (_multiline) { + toastOptions.maxh *= kToastMaxLines; + } + _text.setText(st::toastTextStyle, _multiline ? config.text : textOneLine(config.text), toastOptions); setAttribute(Qt::WA_TransparentForMouseEvents); @@ -36,11 +49,14 @@ Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) { } void Widget::onParentResized() { - int width = st::toastMaxWidth; - accumulate_min(width, st::toastPadding.left() + _text.maxWidth() + st::toastPadding.right()); - accumulate_min(width, parentWidget()->width() - 2 * int(st::toastMinMargin)); - int height = st::toastPadding.top() + _text.minHeight() + st::toastPadding.bottom(); - setGeometry((parentWidget()->width() - width) / 2, (parentWidget()->height() - height) / 2, width, height); + auto newWidth = _maxWidth; + accumulate_min(newWidth, _padding.left() + _text.maxWidth() + _padding.right()); + accumulate_min(newWidth, parentWidget()->width() - 2 * st::toastMinMargin); + _textWidth = newWidth - _padding.left() - _padding.right(); + auto maxHeight = kToastMaxLines * st::toastTextStyle.font->height; + auto textHeight = _multiline ? qMin(_text.countHeight(_textWidth), maxHeight) : _text.minHeight(); + auto newHeight = _padding.top() + textHeight + _padding.bottom(); + setGeometry((parentWidget()->width() - newWidth) / 2, (parentWidget()->height() - newHeight) / 2, newWidth, newHeight); } void Widget::setShownLevel(float64 shownLevel) { @@ -49,14 +65,14 @@ void Widget::setShownLevel(float64 shownLevel) { void Widget::paintEvent(QPaintEvent *e) { Painter p(this); + PainterHighQualityEnabler hq(p); p.setOpacity(_shownLevel); App::roundRect(p, rect(), st::toastBg, ImageRoundRadius::Large); + auto lines = _multiline ? kToastMaxLines : 1; p.setPen(st::toastFg); - textstyleSet(&st::defaultTextStyle); - _text.drawElided(p, st::toastPadding.left(), st::toastPadding.top(), width() - st::toastPadding.left() - st::toastPadding.right()); - textstyleRestore(); + _text.drawElided(p, _padding.left(), _padding.top(), _textWidth + 1, lines); } } // namespace internal diff --git a/Telegram/SourceFiles/ui/toast/toast_widget.h b/Telegram/SourceFiles/ui/toast/toast_widget.h index 1d255e2c1..a1529a096 100644 --- a/Telegram/SourceFiles/ui/toast/toast_widget.h +++ b/Telegram/SourceFiles/ui/toast/toast_widget.h @@ -42,6 +42,12 @@ protected: private: float64 _shownLevel = 0; + bool _multiline = false; + int _maxWidth = 0; + QMargins _padding; + + int _maxTextWidth = 0; + int _textWidth = 0; Text _text; }; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index 990d81851..1482725ec 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -76,6 +76,19 @@ public: return drawPixmapRight(p.x(), p.y(), outerw, pix); } + void setTextPalette(const style::TextPalette &palette) { + _textPalette = &palette; + } + void restoreTextPalette() { + _textPalette = nullptr; + } + const style::TextPalette &textPalette() const { + return _textPalette ? *_textPalette : st::defaultTextPalette; + } + +private: + const style::TextPalette *_textPalette = nullptr; + }; class PainterHighQualityEnabler { @@ -278,16 +291,14 @@ protected: }; template -class WeakPointed { -public: - QPointer weak() { - return QPointer(static_cast(this)); - } - QPointer weak() const { - return QPointer(static_cast(this)); - } +QPointer weak(Widget *object) { + return QPointer(object); +} -}; +template +QPointer weak(const Widget *object) { + return QPointer(object); +} void myEnsureResized(QWidget *target); QPixmap myGrab(TWidget *target, QRect rect = QRect(), QColor bg = QColor(255, 255, 255, 0)); diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp index 237350907..03106237c 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -937,7 +937,7 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) { if (c.charFormat().isAnchor()) { imageFormat.setAnchor(true); imageFormat.setAnchorName(c.charFormat().anchorName()); - imageFormat.setForeground(st::defaultTextStyle.linkFg); + imageFormat.setForeground(st::defaultTextPalette.linkFg); } static QString objectReplacement(QChar::ObjectReplacementCharacter); c.insertText(objectReplacement, imageFormat); @@ -969,7 +969,7 @@ void prepareFormattingOptimization(QTextDocument *document) { } } -void removeTags(const style::color &textFg, QTextDocument *document, int from, int end) { +void removeTags(style::color textFg, QTextDocument *document, int from, int end) { QTextCursor c(document->docHandle(), 0); c.setPosition(from); c.setPosition(end, QTextCursor::KeepAnchor); @@ -982,7 +982,7 @@ void removeTags(const style::color &textFg, QTextDocument *document, int from, i } // Returns the position of the first inserted tag or "changedEnd" value if none found. -int processInsertedTags(const style::color &textFg, QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { +int processInsertedTags(style::color textFg, QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { int firstTagStart = changedEnd; int applyNoTagFrom = changedEnd; for_const (auto &tag, tags) { @@ -1006,7 +1006,7 @@ int processInsertedTags(const style::color &textFg, QTextDocument *document, int QTextCharFormat format; format.setAnchor(true); format.setAnchorName(tagId + '/' + QString::number(rand_value())); - format.setForeground(st::defaultTextStyle.linkFg); + format.setForeground(st::defaultTextPalette.linkFg); c.mergeCharFormat(format); applyNoTagFrom = tagTo; diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp index 87bab310b..1a9c788d8 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -43,7 +43,7 @@ TextParseOptions _labelMarkedOptions = { } // namespace -CrossFadeAnimation::CrossFadeAnimation(const style::color &bg) : _bg(bg) { +CrossFadeAnimation::CrossFadeAnimation(style::color bg) : _bg(bg) { } void CrossFadeAnimation::addLine(Part was, Part now) { @@ -132,18 +132,16 @@ void LabelSimple::paintEvent(QPaintEvent *e) { p.drawTextLeft(0, 0, width(), _text, _textWidth); } -FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st, const style::TextStyle &tst) : TWidget(parent) +FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st) : TWidget(parent) , _text(st.width ? st.width : QFIXED_MAX) , _st(st) -, _tst(tst) , _contextCopyText(lang(lng_context_copy_text)) { init(); } -FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st, const style::TextStyle &tst) : TWidget(parent) +FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st) : TWidget(parent) , _text(st.width ? st.width : QFIXED_MAX) , _st(st) -, _tst(tst) , _contextCopyText(lang(lng_context_copy_text)) { if (initType == InitType::Rich) { setRichText(text); @@ -162,29 +160,27 @@ void FlatLabel::init() { template void FlatLabel::setTextByCallback(SetCallback callback) { - textstyleSet(&_tst); callback(); refreshSize(); - textstyleRestore(); setMouseTracking(_selectable || _text.hasLinks()); update(); } void FlatLabel::setText(const QString &text) { setTextByCallback([this, &text]() { - _text.setText(_st.font, text, _labelOptions); + _text.setText(_st.style, text, _labelOptions); }); } void FlatLabel::setRichText(const QString &text) { setTextByCallback([this, &text]() { - _text.setRichText(_st.font, text, _labelOptions); + _text.setRichText(_st.style, text, _labelOptions); }); } void FlatLabel::setMarkedText(const TextWithEntities &textWithEntities) { setTextByCallback([this, &textWithEntities]() { - _text.setMarkedText(_st.font, textWithEntities, _labelMarkedOptions); + _text.setMarkedText(_st.style, textWithEntities, _labelMarkedOptions); }); } @@ -211,10 +207,8 @@ void FlatLabel::setBreakEverywhere(bool breakEverywhere) { int FlatLabel::resizeGetHeight(int newWidth) { _allowedWidth = newWidth; - textstyleSet(&_tst); int textWidth = countTextWidth(); int textHeight = countTextHeight(textWidth); - textstyleRestore(); return _st.margin.top() + textHeight + _st.margin.bottom(); } @@ -599,7 +593,7 @@ void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool a update(); } -std_::unique_ptr FlatLabel::CrossFade(FlatLabel *from, FlatLabel *to, const style::color &bg, QPoint fromPosition, QPoint toPosition) { +std_::unique_ptr FlatLabel::CrossFade(FlatLabel *from, FlatLabel *to, style::color bg, QPoint fromPosition, QPoint toPosition) { auto result = std_::make_unique(bg); struct Data { @@ -613,8 +607,8 @@ std_::unique_ptr FlatLabel::CrossFade(FlatLabel *from, FlatL result.full = myGrabImage(label, QRect(), bg->c); auto textWidth = label->width() - label->_st.margin.left() - label->_st.margin.right(); label->_text.countLineWidths(textWidth, &result.lineWidths); - result.lineHeight = label->_st.font->height; - auto addedHeight = (label->_tst.lineHeight - result.lineHeight); + result.lineHeight = label->_st.style.font->height; + auto addedHeight = (label->_st.style.lineHeight - result.lineHeight); if (addedHeight > 0) { result.lineAddTop = addedHeight / 2; result.lineHeight += addedHeight; @@ -646,7 +640,7 @@ std_::unique_ptr FlatLabel::CrossFade(FlatLabel *from, FlatL } 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.font->height * cIntRetinaFactor())); + auto snapshotRect = data.full.rect().intersected(QRect(left * cIntRetinaFactor(), top * cIntRetinaFactor(), lineWidth * cIntRetinaFactor(), label->_st.style.font->height * cIntRetinaFactor())); if (!snapshotRect.isEmpty()) { result.snapshot = App::pixmapFromImageInPlace(data.full.copy(snapshotRect)); result.snapshot.setDevicePixelRatio(cRetinaFactor()); @@ -746,12 +740,11 @@ Text::StateResult FlatLabel::getTextState(const QPoint &m) const { } int textWidth = width() - _st.margin.left() - _st.margin.right(); - textstyleSet(&_tst); Text::StateResult state; bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth()); bool renderElided = _breakEverywhere || heightExceeded; if (renderElided) { - auto lineHeight = qMax(_tst.lineHeight, _st.font->height); + auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height); auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2); request.lines = lines; if (_breakEverywhere) { @@ -761,7 +754,6 @@ Text::StateResult FlatLabel::getTextState(const QPoint &m) const { } else { state = _text.getState(m.x() - _st.margin.left(), m.y() - _st.margin.top(), textWidth, request); } - textstyleRestore(); return state; } @@ -775,19 +767,18 @@ void FlatLabel::paintEvent(QPaintEvent *e) { Painter p(this); p.setOpacity(_opacity); p.setPen(_st.textFg); - textstyleSet(&_tst); + p.setTextPalette(_st.palette); int textWidth = width() - _st.margin.left() - _st.margin.right(); auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection; bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth()); bool renderElided = _breakEverywhere || heightExceeded; if (renderElided) { - auto lineHeight = qMax(_tst.lineHeight, _st.font->height); + auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height); auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2); _text.drawElided(p, _st.margin.left(), _st.margin.top(), textWidth, lines, _st.align, e->rect().y(), e->rect().bottom(), 0, _breakEverywhere, selection); } else { _text.draw(p, _st.margin.left(), _st.margin.top(), textWidth, _st.align, e->rect().y(), e->rect().bottom(), selection); } - textstyleRestore(); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/labels.h b/Telegram/SourceFiles/ui/widgets/labels.h index d0da4da7e..c1726f8bd 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.h +++ b/Telegram/SourceFiles/ui/widgets/labels.h @@ -28,7 +28,7 @@ class PopupMenu; class CrossFadeAnimation { public: - CrossFadeAnimation(const style::color &bg); + CrossFadeAnimation(style::color bg); struct Part { QPixmap snapshot; @@ -52,7 +52,7 @@ private: }; void paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow); - const style::color &_bg; + style::color _bg; QList _lines; }; @@ -82,13 +82,13 @@ class FlatLabel : public TWidget, public ClickHandlerHost { Q_OBJECT public: - FlatLabel(QWidget *parent, const style::FlatLabel &st = st::defaultFlatLabel, const style::TextStyle &tst = st::defaultTextStyle); + FlatLabel(QWidget *parent, const style::FlatLabel &st = st::defaultFlatLabel); enum class InitType { Simple, Rich, }; - FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st = st::defaultFlatLabel, const style::TextStyle &tst = st::defaultTextStyle); + FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st = st::defaultFlatLabel); void setOpacity(float64 o); @@ -112,7 +112,7 @@ public: void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; - static std_::unique_ptr CrossFade(FlatLabel *from, FlatLabel *to, const style::color &bg, QPoint fromPosition = QPoint(), QPoint toPosition = QPoint()); + static std_::unique_ptr CrossFade(FlatLabel *from, FlatLabel *to, style::color bg, QPoint fromPosition = QPoint(), QPoint toPosition = QPoint()); protected: void paintEvent(QPaintEvent *e) override; @@ -166,7 +166,6 @@ private: Text _text; const style::FlatLabel &_st; - const style::TextStyle &_tst; float64 _opacity = 1.; int _allowedWidth = 0; diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index 42200e994..28bdc8b78 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -37,7 +37,7 @@ constexpr int kWideScale = 3; class MultiSelect::Inner::Item { public: - Item(const style::MultiSelectItem &st, uint64 id, const QString &text, const style::color &color, PaintRoundImage &&paintRoundImage); + Item(const style::MultiSelectItem &st, uint64 id, const QString &text, style::color color, PaintRoundImage &&paintRoundImage); uint64 id() const { return _id; @@ -104,7 +104,7 @@ private: int _y = -1; int _width = 0; Text _text; - const style::color &_color; + style::color _color; bool _over = false; QPixmap _cache; Animation _visibility; @@ -117,7 +117,7 @@ private: }; -MultiSelect::Inner::Item::Item(const style::MultiSelectItem &st, uint64 id, const QString &text, const style::color &color, PaintRoundImage &&paintRoundImage) +MultiSelect::Inner::Item::Item(const style::MultiSelectItem &st, uint64 id, const QString &text, style::color color, PaintRoundImage &&paintRoundImage) : _st(st) , _id(id) , _color(color) @@ -126,7 +126,7 @@ MultiSelect::Inner::Item::Item(const style::MultiSelectItem &st, uint64 id, cons } void MultiSelect::Inner::Item::setText(const QString &text) { - _text.setText(_st.font, text, _textNameOptions); + _text.setText(_st.style, text, _textNameOptions); _width = _st.height + _st.padding.left() + _text.maxWidth() + _st.padding.right(); accumulate_min(_width, _st.maxWidth); } @@ -402,7 +402,7 @@ QString MultiSelect::getQuery() const { return _inner->getQuery(); } -void MultiSelect::addItem(uint64 itemId, const QString &text, const style::color &color, PaintRoundImage &&paintRoundImage, AddItemWay way) { +void MultiSelect::addItem(uint64 itemId, const QString &text, style::color color, PaintRoundImage &&paintRoundImage, AddItemWay way) { _inner->addItem(std_::make_unique(_st.item, itemId, text, color, std_::move(paintRoundImage)), way); } diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 32fc912b1..fce6686d1 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -45,7 +45,7 @@ public: SkipAnimation, }; using PaintRoundImage = base::lambda; - void addItem(uint64 itemId, const QString &text, const style::color &color, PaintRoundImage &&paintRoundImage, AddItemWay way = AddItemWay::Default); + void addItem(uint64 itemId, const QString &text, style::color color, PaintRoundImage &&paintRoundImage, AddItemWay way = AddItemWay::Default); void setItemText(uint64 itemId, const QString &text); void setItemRemovedCallback(base::lambda &&callback); diff --git a/Telegram/SourceFiles/ui/widgets/shadow.h b/Telegram/SourceFiles/ui/widgets/shadow.h index a5a20f810..777210aaa 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.h +++ b/Telegram/SourceFiles/ui/widgets/shadow.h @@ -23,7 +23,7 @@ namespace Ui { class PlainShadow : public TWidget { public: - PlainShadow(QWidget *parent, const style::color &color) : TWidget(parent), _color(color) { + PlainShadow(QWidget *parent, style::color color) : TWidget(parent), _color(color) { } protected: @@ -32,7 +32,7 @@ protected: } private: - const style::color &_color; + style::color _color; }; diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.cpp b/Telegram/SourceFiles/ui/widgets/tooltip.cpp index a18da282c..2ba943a6c 100644 --- a/Telegram/SourceFiles/ui/widgets/tooltip.cpp +++ b/Telegram/SourceFiles/ui/widgets/tooltip.cpp @@ -19,11 +19,16 @@ #include "ui/widgets/tooltip.h" #include "application.h" +#include "styles/style_widgets.h" namespace Ui { Tooltip *TooltipInstance = nullptr; +const style::Tooltip *AbstractTooltipShower::tooltipSt() const { + return &st::defaultTooltip; +} + AbstractTooltipShower::~AbstractTooltipShower() { if (TooltipInstance && TooltipInstance->_shower == this) { TooltipInstance->_shower = 0; @@ -35,6 +40,7 @@ Tooltip::Tooltip() : TWidget(nullptr) { setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint); setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); _showTimer.setSingleShot(true); connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow())); @@ -92,7 +98,10 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip * _point = m; _st = st; - _text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true); + _text = Text(_st->textStyle, text, _textPlainOptions, _st->widthMax, true); + + _useTransparency = Platform::TransparentWindowsSupported(_point); + setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right(); int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom(); @@ -103,7 +112,7 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip * s.setWidth(addw + _text.countWidth(_st->widthMax - addw)); s.setHeight(addh + _text.countHeight(s.width() - addw)); } - int32 maxh = addh + (_st->linesMax * _st->textFont->height); + int32 maxh = addh + (_st->linesMax * _st->textStyle.font->height); if (s.height() > maxh) { s.setHeight(maxh); } @@ -141,14 +150,20 @@ void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip * void Tooltip::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(rect(), _st->textBg); + if (_useTransparency) { + p.setPen(_st->textBorder); + p.setBrush(_st->textBg); + PainterHighQualityEnabler hq(p); + p.drawRoundedRect(QRectF(0.5, 0.5, width() - 1., height() - 1.), st::buttonRadius, st::buttonRadius); + } else { + p.fillRect(rect(), _st->textBg); - p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - - int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height); + p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder); + p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder); + p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); + p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); + } + int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textStyle.font->height); p.setPen(_st->textFg); _text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines); diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.h b/Telegram/SourceFiles/ui/widgets/tooltip.h index eb1611014..a5fcc4d63 100644 --- a/Telegram/SourceFiles/ui/widgets/tooltip.h +++ b/Telegram/SourceFiles/ui/widgets/tooltip.h @@ -17,15 +17,17 @@ */ #pragma once +namespace style { +struct Tooltip; +} // namespace style + namespace Ui { class AbstractTooltipShower { public: virtual QString tooltipText() const = 0; virtual QPoint tooltipPos() const = 0; - virtual const style::Tooltip *tooltipSt() const { - return &st::defaultTooltip; - } + virtual const style::Tooltip *tooltipSt() const; virtual ~AbstractTooltipShower(); }; @@ -36,7 +38,7 @@ public: static void Show(int32 delay, const AbstractTooltipShower *shower); static void Hide(); - private slots: +private slots: void onShow(); void onWndActiveChanged(); void onHideByLeave(); @@ -63,6 +65,7 @@ private: const style::Tooltip *_st = nullptr; QTimer _hideByLeaveTimer; + bool _useTransparency = true; }; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index b63f42395..2a9d6cbd0 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -28,12 +28,13 @@ LabelSimple { } FlatLabel { - font: font; margin: margins; width: pixels; align: align; textFg: color; maxHeight: pixels; + style: TextStyle; + palette: TextPalette; } LinkButton { @@ -321,7 +322,7 @@ MultiSelectItem { padding: margins; maxWidth: pixels; height: pixels; - font: font; + style: TextStyle; textBg: color; textFg: color; textActiveBg: color; @@ -421,6 +422,20 @@ DropdownMenu { menu: Menu; } +Tooltip { + textBg: color; + textFg: color; + textStyle: TextStyle; + textBorder: color; + textPadding: margins; + + shift: point; + skip: pixels; + + widthMax: pixels; + linesMax: int; +} + defaultLabelSimple: LabelSimple { font: normalFont; maxWidth: 0px; @@ -428,11 +443,12 @@ defaultLabelSimple: LabelSimple { } defaultFlatLabel: FlatLabel { - font: font(fsize); width: 0px; maxHeight: 0px; align: align(left); textFg: windowFg; + style: defaultTextStyle; + palette: defaultTextPalette; } defaultLinkButton: LinkButton { @@ -784,6 +800,20 @@ defaultDropdownMenu: DropdownMenu { menu: defaultMenu; } +defaultTooltip: Tooltip { + textBg: tooltipBg; + textFg: tooltipFg; + textStyle: defaultTextStyle; + textBorder: tooltipBorderFg; + textPadding: margins(5px, 2px, 5px, 2px); + + shift: point(-20px, 20px); + skip: 10px; + + widthMax: 800px; + linesMax: 12; +} + historyToDownBelow: icon { { "history_down_shadow", historyToDownShadow }, { "history_down_circle", windowBg, point(4px, 4px) }, diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index af2f0c879..3d4a00ee8 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -34,8 +34,7 @@ MainWindow::MainWindow() : QWidget() , _titleText(qsl("Telegram")) { using Update = Theme::BackgroundUpdate; subscribe(Theme::Background(), [this](const Update &data) { - if (data.type == Update::Type::TestingTheme - || data.type == Update::Type::RevertingTheme) { + if (data.paletteChanged()) { if (_title) { _title->update(); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index dee5d70d1..cfed32531 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -639,7 +639,7 @@ void Notification::updateNotifyDisplay() { } else if (_forwardedCount > 1) { p.setFont(st::dialogsTextFont); if (_author) { - itemTextCache.setText(st::dialogsTextFont, _author->name); + itemTextCache.setText(st::dialogsTextStyle, _author->name); p.setPen(st::dialogsTextFgService); itemTextCache.drawElided(p, r.left(), r.top(), r.width(), st::dialogsTextFont->height); r.setTop(r.top() + st::dialogsTextFont->height); diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index b0f1c4d26..e57a02659 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -106,7 +106,7 @@ void TopBarWidget::showMenu() { if (auto peer = main->peer()) { if (!_menu) { _menu.create(App::main()); - _menu->setHiddenCallback([that = weak(), menu = _menu.data()] { + _menu->setHiddenCallback([that = weak(this), menu = _menu.data()] { menu->deleteLater(); if (that && that->_menu == menu) { that->_menu = nullptr; @@ -159,10 +159,12 @@ void TopBarWidget::paintEvent(QPaintEvent *e) { p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBg); if (_clearSelection->isHidden()) { p.save(); - int decreaseWidth = 0; + auto decreaseWidth = 0; if (!_info->isHidden()) { decreaseWidth += _info->width(); - decreaseWidth -= st::topBarArrowPadding.left(); + } + if (!_menuToggle->isHidden()) { + decreaseWidth += _menuToggle->width(); } if (!_search->isHidden()) { decreaseWidth += _search->width(); diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 8af590e37..caae98021 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -31,7 +31,7 @@ class DropdownMenu; namespace Window { -class TopBarWidget : public TWidget, private base::Subscriber, public WeakPointed { +class TopBarWidget : public TWidget, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 5ecc22dea..7db7391ee 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -119,24 +119,22 @@ mainMenuHelp: icon {{ "menu_help", menuIconFg }}; mainMenuHelpOver: icon {{ "menu_help", menuIconFgOver }}; mainMenuFooterLeft: 30px; mainMenuTelegramLabel: FlatLabel(defaultFlatLabel) { - font: semiboldFont; align: align(left); textFg: windowSubTextFg; + style: TextStyle(defaultTextStyle) { + font: semiboldFont; + linkFont: semiboldFont; + linkFontOver: font(fsize semibold underline); + } } -mainMenuTelegramStyle: TextStyle(defaultTextStyle) { - linkFlags: semiboldFont; - linkFlagsOver: font(fsize semibold underline); +mainMenuTelegramPalette: TextPalette(defaultTextPalette) { linkFg: windowSubTextFg; - linkFgDown: windowSubTextFg; } mainMenuTelegramBottom: 43px; mainMenuVersionLabel: FlatLabel(mainMenuTelegramLabel) { - font: normalFont; -} -mainMenuVersionStyle: TextStyle(mainMenuTelegramStyle) { - linkFlags: normalFont; - linkFlagsOver: font(fsize underline); + style: defaultTextStyle; } +mainMenuVersionPalette: mainMenuTelegramPalette; mainMenuVersionBottom: 21px; // Windows specific title @@ -233,3 +231,7 @@ macSelectorTop: 6; macAlwaysThisAppTop: 4; macAppHintTop: 8; macCautionIconSize: 16; + +macWindowRoundRadius: 5; +macWindowShadowTopLeft: icon {{ "mac_window_shadow_top_left", windowShadowFg }}; +macTrayIcon: icon {{ "mac_tray_icon", windowFg }}; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 1b7573796..42dec52e0 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -36,8 +36,8 @@ namespace Window { MainMenu::MainMenu(QWidget *parent) : TWidget(parent) , _menu(this, st::mainMenu) -, _telegram(this, st::mainMenuTelegramLabel, st::mainMenuTelegramStyle) -, _version(this, st::mainMenuVersionLabel, st::mainMenuVersionStyle) { +, _telegram(this, st::mainMenuTelegramLabel) +, _version(this, st::mainMenuVersionLabel) { setAttribute(Qt::WA_OpaquePaintEvent); subscribe(Global::RefSelfChanged(), [this] { diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index dfa65f878..a7fc6bbaa 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -328,7 +328,7 @@ QImage prepareBackgroundImage(QImage &&image) { return std_::move(image); } -void initColor(const style::color &color, float64 hue, float64 saturation) { +void initColor(style::color color, float64 hue, float64 saturation) { auto original = color->c; original.setHslF(hue, saturation, original.lightnessF(), original.alphaF()); color.set(original.red(), original.green(), original.blue(), original.alpha()); @@ -562,19 +562,23 @@ void Unload() { } bool Apply(const QString &filepath) { - QByteArray content; - Instance theme; - if (!LoadFromFile(filepath, &theme, &content)) { + auto preview = std_::make_unique(); + preview->path = filepath; + if (!LoadFromFile(preview->path, &preview->instance, &preview->content)) { return false; } + return Apply(std_::move(preview)); +} + +bool Apply(std_::unique_ptr preview) { instance.createIfNull(); - instance->applying.path = filepath; - instance->applying.content = content; - instance->applying.cached = theme.cached; + instance->applying.path = std_::move(preview->path); + instance->applying.content = std_::move(preview->content); + instance->applying.cached = std_::move(preview->instance.cached); if (instance->applying.paletteForRevert.isEmpty()) { instance->applying.paletteForRevert = style::main_palette::save(); } - Background()->setTestingTheme(std_::move(theme)); + Background()->setTestingTheme(std_::move(preview->instance)); return true; } @@ -619,5 +623,29 @@ bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) { return loadTheme(*outContent, out->cached, out); } +void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) { + if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) { + float64 pxsize = wholeFill.height() / float64(imageSize.height()); + int takewidth = qCeil(wholeFill.width() / pxsize); + if (takewidth > imageSize.width()) { + takewidth = imageSize.width(); + } else if ((imageSize.width() % 2) != (takewidth % 2)) { + ++takewidth; + } + to = QRect(int((wholeFill.width() - takewidth * pxsize) / 2.), 0, qCeil(takewidth * pxsize), wholeFill.height()); + from = QRect((imageSize.width() - takewidth) / 2, 0, takewidth, imageSize.height()); + } else { + float64 pxsize = wholeFill.width() / float64(imageSize.width()); + int takeheight = qCeil(wholeFill.height() / pxsize); + if (takeheight > imageSize.height()) { + takeheight = imageSize.height(); + } else if ((imageSize.height() % 2) != (takeheight % 2)) { + ++takeheight; + } + to = QRect(0, int((wholeFill.height() - takeheight * pxsize) / 2.), wholeFill.width(), qCeil(takeheight * pxsize)); + from = QRect(0, (imageSize.height() - takeheight) / 2, imageSize.width(), takeheight); + } +} + } // namespace Theme } // namespace Window diff --git a/Telegram/SourceFiles/window/window_theme.h b/Telegram/SourceFiles/window/window_theme.h index f2e1800ad..9171ef5a5 100644 --- a/Telegram/SourceFiles/window/window_theme.h +++ b/Telegram/SourceFiles/window/window_theme.h @@ -45,17 +45,26 @@ struct Cached { bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache); void Unload(); -bool Apply(const QString &filepath); -void ApplyDefault(); -void KeepApplied(); -void Revert(); - struct Instance { style::palette palette; QImage background; Cached cached; bool tiled = false; }; + +struct Preview { + QString path; + Instance instance; + QByteArray content; + QPixmap preview; +}; + +bool Apply(const QString &filepath); +bool Apply(std_::unique_ptr preview); +void ApplyDefault(); +void KeepApplied(); +void Revert(); + bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent); struct BackgroundUpdate { @@ -70,6 +79,9 @@ struct BackgroundUpdate { BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) { } + bool paletteChanged() const { + return (type == Type::TestingTheme || type == Type::RevertingTheme); + } Type type; bool tiled; }; @@ -116,5 +128,7 @@ private: ChatBackground *Background(); +void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from); + } // namespace Theme } // namespace Window diff --git a/Telegram/SourceFiles/window/window_theme_preview.cpp b/Telegram/SourceFiles/window/window_theme_preview.cpp new file mode 100644 index 000000000..ea4a0facd --- /dev/null +++ b/Telegram/SourceFiles/window/window_theme_preview.cpp @@ -0,0 +1,906 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "window/window_theme_preview.h" + +#include "window/window_theme.h" +#include "lang.h" +#include "platform/platform_window_title.h" +#include "styles/style_widgets.h" +#include "styles/style_window.h" +#include "styles/style_mediaview.h" +#include "styles/style_history.h" +#include "styles/style_dialogs.h" + +namespace Window { +namespace Theme { +namespace { + +class Generator { +public: + Generator(const Instance &theme, const CurrentData ¤t); + + QPixmap generate(); + +private: + enum class Status { + None, + Sent, + Received + }; + struct Row { + Text name; + enum class Type { + User, + Group, + Channel + }; + Type type = Type::User; + int peerIndex = 0; + int unreadCounter = 0; + bool muted = false; + bool pinned = false; + QString date; + Text text; + Status status = Status::None; + bool selected = false; + bool active = false; + }; + struct Bubble { + int width = 0; + int height = 0; + bool outbg = false; + Status status = Status::None; + QString date; + bool attached = false; + bool tail = true; + Text text = { st::msgMinWidth }; + QVector waveform; + int waveactive = 0; + QString wavestatus; + QImage photo; + int photoWidth = 0; + int photoHeight = 0; + Text replyName = { st::msgMinWidth }; + Text replyText = { st::msgMinWidth }; + }; + + void prepare(); + + void addRow(QString name, int peerIndex, QString date, QString text); + void addBubble(Bubble bubble, int width, int height, QString date, Status status); + void addAudioBubble(QVector waveform, int waveactive, QString wavestatus, QString date, Status status); + void addTextBubble(QString text, QString date, Status status); + void addDateBubble(QString date); + void addPhotoBubble(QString image, QString caption, QString date, Status status); + QSize computeSkipBlock(Status status, QString date); + int computeInfoWidth(Status status, QString date); + + void generateData(); + + void paintHistoryList(); + void paintHistoryBackground(); + void paintTopBar(); + void paintComposeArea(); + void paintDialogs(); + void paintDialogsList(); + void paintHistoryShadows(); + void paintRow(const Row &row); + void paintBubble(const Bubble &bubble); + void paintService(QString text); + + void paintUserpic(int x, int y, Row::Type type, int index); + + void setTextPalette(const style::TextPalette &st); + void restoreTextPalette(); + + const Instance &_theme; + const style::palette &_palette; + const CurrentData &_current; + Painter *_p = nullptr; + + QRect _rect; + QRect _inner; + QRect _body; + QRect _dialogs; + QRect _dialogsList; + QRect _topBar; + QRect _composeArea; + QRect _history; + + int _rowsTop = 0; + std_::vector_of_moveable _rows; + + Text _topBarName; + QString _topBarStatus; + bool _topBarStatusActive = false; + + int _historyBottom = 0; + std_::vector_of_moveable _bubbles; + + style::TextPalette _textPalette; + +}; + +void Generator::prepare() { + _rect = QRect(0, 0, st::themePreviewMargin.left() + st::themePreviewSize.width() + st::themePreviewMargin.right(), st::themePreviewMargin.top() + st::themePreviewSize.height() + st::themePreviewMargin.bottom()); + _inner = _rect.marginsRemoved(st::themePreviewMargin); + _body = _inner.marginsRemoved(QMargins(0, Platform::PreviewTitleHeight(), 0, 0)); + _dialogs = QRect(_body.x(), _body.y(), st::themePreviewDialogsWidth, _body.height()); + _dialogsList = _dialogs.marginsRemoved(QMargins(0, st::dialogsFilterPadding.y() + st::dialogsMenuToggle.height + st::dialogsFilterPadding.y(), 0, st::dialogsPadding.y())); + _topBar = QRect(_dialogs.x() + _dialogs.width(), _dialogs.y(), _body.width() - _dialogs.width(), st::topBarHeight); + _composeArea = QRect(_topBar.x(), _body.y() + _body.height() - st::historySend.height, _topBar.width(), st::historySend.height); + _history = QRect(_topBar.x(), _topBar.y() + _topBar.height(), _topBar.width(), _body.height() - _topBar.height() - _composeArea.height()); + + generateData(); +} + +void Generator::addRow(QString name, int peerIndex, QString date, QString text) { + Row row; + row.name.setText(st::msgNameStyle, name, _textNameOptions); + row.peerIndex = peerIndex; + row.date = date; + row.text.setRichText(st::dialogsTextStyle, text, _textDlgOptions); + _rows.push_back(std_::move(row)); +} + +void Generator::addBubble(Bubble bubble, int width, int height, QString date, Status status) { + bubble.width = width; + bubble.height = height; + bubble.date = date; + bubble.status = status; + _bubbles.push_back(std_::move(bubble)); +} + +void Generator::addAudioBubble(QVector waveform, int waveactive, QString wavestatus, QString date, Status status) { + Bubble bubble; + bubble.waveform = waveform; + bubble.waveactive = waveactive; + bubble.wavestatus = wavestatus; + + auto skipBlock = computeSkipBlock(status, date); + + auto width = st::msgFileMinWidth; + auto tleft = 0, tright = 0; + tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + tright = st::msgFileThumbPadding.left(); + accumulate_max(width, tleft + st::normalFont->width(wavestatus) + skipBlock.width() + st::msgPadding.right()); + accumulate_min(width, st::msgMaxWidth); + + auto height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + addBubble(std_::move(bubble), width, height, date, status); +} + +QSize Generator::computeSkipBlock(Status status, QString date) { + auto infoWidth = computeInfoWidth(status, date); + auto width = st::msgDateSpace + infoWidth - st::msgDateDelta.x(); + auto height = st::msgDateFont->height - st::msgDateDelta.y(); + return QSize(width, height); +} + +int Generator::computeInfoWidth(Status status, QString date) { + auto result = st::msgDateFont->width(date); + if (status != Status::None) { + result += st::historySendStateSpace; + } + return result; +} + +void Generator::addTextBubble(QString text, QString date, Status status) { + Bubble bubble; + auto skipBlock = computeSkipBlock(status, date); + bubble.text.setRichText(st::messageTextStyle, text + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), _historyTextOptions); + + auto width = _history.width() - st::msgMargin.left() - st::msgMargin.right(); + accumulate_min(width, st::msgPadding.left() + bubble.text.maxWidth() + st::msgPadding.right()); + accumulate_min(width, st::msgMaxWidth); + + auto textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1); + auto textHeight = bubble.text.countHeight(textWidth); + + auto height = st::msgPadding.top() + textHeight + st::msgPadding.bottom(); + addBubble(std_::move(bubble), width, height, date, status); +} + +void Generator::addDateBubble(QString date) { + Bubble bubble; + addBubble(std_::move(bubble), 0, 0, date, Status::None); +} + +void Generator::addPhotoBubble(QString image, QString caption, QString date, Status status) { + Bubble bubble; + bubble.photo.load(image); + bubble.photoWidth = convertScale(bubble.photo.width() / 2); + bubble.photoHeight = convertScale(bubble.photo.height() / 2); + auto skipBlock = computeSkipBlock(status, date); + bubble.text.setRichText(st::messageTextStyle, caption + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), _historyTextOptions); + + auto width = _history.width() - st::msgMargin.left() - st::msgMargin.right(); + accumulate_min(width, bubble.photoWidth); + accumulate_min(width, st::msgMaxWidth); + + auto textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1); + auto textHeight = bubble.text.countHeight(textWidth); + + auto height = st::mediaCaptionSkip + textHeight + st::msgPadding.bottom(); + addBubble(std_::move(bubble), width, height, date, status); +} + +void Generator::generateData() { + _rows.reserve(9); + addRow("Eva Summer", 0, "11:00", "Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago."); + _rows.back().active = true; + _rows.back().pinned = true; + addRow("Alexandra Smith", 1, "10:00", "This is amazing!"); + _rows.back().unreadCounter = 2; + addRow("Mike Apple", 2, "9:00", textcmdLink(1, QChar(55357) + QString() + QChar(56836) + " Sticker")); + _rows.back().unreadCounter = 2; + _rows.back().muted = true; + addRow("Evening Club", 3, "8:00", textcmdLink(1, "Eva: Photo")); + _rows.back().type = Row::Type::Group; + addRow("Old Pirates", 4, "7:00", textcmdLink(1, "Max:") + " Yo-ho-ho!"); + _rows.back().type = Row::Type::Group; + addRow("Max Bright", 5, "6:00", "How about some coffee?"); + _rows.back().status = Status::Received; + addRow("Natalie Parker", 6, "5:00", "OK, great)"); + _rows.back().status = Status::Received; + addRow("Davy Jones", 7, "4:00", textcmdLink(1, "Keynote.pdf")); + addRow("Eva Summer", 8, "3:00", "Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago."); + + _topBarName.setText(st::msgNameStyle, "Eva Summer", _textNameOptions); + _topBarStatus = "online"; + _topBarStatusActive = true; + + addPhotoBubble(":/gui/art/sunrise.jpg", "Nearly missed this sunrise", "7:00", Status::None); + int wavedata[] = { 0, 0, 0, 0, 27, 31, 4, 1, 0, 0, 23, 30, 18, 9, 7, 19, 4, 2, 2, 2, 0, 0, 15, 15, 15, 15, 3, 15, 19, 3, 2, 0, 0, 0, 0, 0, 3, 12, 16, 6, 4, 6, 14, 12, 2, 12, 12, 11, 3, 0, 7, 5, 7, 4, 7, 5, 2, 4, 0, 9, 5, 7, 6, 2, 2, 0, 0 }; + auto waveform = QVector(base::array_size(wavedata)); + memcpy(waveform.data(), wavedata, sizeof(wavedata)); + addAudioBubble(waveform, 33, "0:07", "8:00", Status::None); + _bubbles.back().outbg = true; + _bubbles.back().status = Status::Received; + addDateBubble("December 26"); + addTextBubble("Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do, so throw off the bowlines, sail away from safe harbor, catch the trade winds in your sails.", "9:00", Status::Received); + _bubbles.back().tail = false; + _bubbles.back().outbg = true; + addTextBubble("Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), "9:00", Status::Received); + _bubbles.back().outbg = true; + _bubbles.back().attached = true; + _bubbles.back().tail = true; + addTextBubble("Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago. The second best time is now.", "9:00", Status::None); + _bubbles.back().replyName.setText(st::msgNameStyle, "Alex Cassio", _textNameOptions); + _bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), _textDlgOptions); +} + +Generator::Generator(const Instance &theme, const CurrentData ¤t) +: _theme(theme) +, _palette(_theme.palette) +, _current(current) { +} + +QPixmap Generator::generate() { + prepare(); + + auto result = QImage(_rect.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(st::themePreviewBg->c); + + { + Painter p(&result); + PainterHighQualityEnabler hq(p); + _p = &p; + + _p->fillRect(_body, QColor(0, 0, 0)); + _p->fillRect(_body, st::windowBg[_palette]); + + paintHistoryList(); + paintTopBar(); + paintComposeArea(); + paintDialogs(); + paintHistoryShadows(); + } + Platform::PreviewWindowFramePaint(result, _palette, _body, _rect.width()); + + return App::pixmapFromImageInPlace(std_::move(result)); +} + +void Generator::paintHistoryList() { + paintHistoryBackground(); + + _historyBottom = _history.y() + _history.height(); + _historyBottom -= st::historyPaddingBottom; + for (auto i = _bubbles.size(); i != 0;) { + auto &bubble = _bubbles[--i]; + if (bubble.width > 0) { + paintBubble(bubble); + } else { + paintService(bubble.date); + } + } + + _p->setClipping(false); +} + +void Generator::paintHistoryBackground() { + auto fromy = (-st::topBarHeight); + auto background = _theme.background; + auto tiled = _theme.tiled; + if (background.isNull()) { + if (_current.backgroundId == Window::Theme::kThemeBackground) { + background.load(qsl(":/gui/art/bg.jpg")); + tiled = false; + } else { + background = _current.backgroundImage.toImage(); + tiled = _current.backgroundTiled; + } + } + _p->setClipRect(_history); + if (tiled) { + auto left = _history.x(), top = _history.y(), right = _history.x() + _history.width(), bottom = _history.y() + _history.height(); + auto w = background.width() / cRetinaFactor(); + auto h = background.height() / cRetinaFactor(); + auto sx = qFloor(left / w); + auto sy = qFloor((top - fromy) / h); + auto cx = qCeil(right / w); + auto cy = qCeil((bottom - fromy) / h); + for (auto i = sx; i != cx; ++i) { + for (auto j = sy; j != cy; ++j) { + _p->drawImage(QPointF(_history.x() + i * w, _history.y() + fromy + j * h), background); + } + } + } else { + PainterHighQualityEnabler hq(*_p); + + auto fill = QRect(_topBar.x(), _topBar.y(), _topBar.width(), _body.height()); + QRect to, from; + ComputeBackgroundRects(fill, background.size(), to, from); + to.moveTop(to.top() + fromy); + to.moveTopLeft(to.topLeft() + _history.topLeft()); + _p->drawImage(to, background, from); + } + _p->setClipping(false); +} + +void Generator::paintTopBar() { + _p->fillRect(_topBar, st::topBarBg[_palette]); + + auto right = st::topBarMenuToggle.width; + st::topBarMenuToggle.icon[_palette].paint(*_p, _topBar.x() + _topBar.width() - right + st::topBarMenuToggle.iconPosition.x(), _topBar.y() + st::topBarMenuToggle.iconPosition.y(), _rect.width()); + right += st::topBarSearch.width; + st::topBarSearch.icon[_palette].paint(*_p, _topBar.x() + _topBar.width() - right + st::topBarSearch.iconPosition.x(), _topBar.y() + st::topBarSearch.iconPosition.y(), _rect.width()); + + auto decreaseWidth = st::topBarSearch.width + st::topBarMenuToggle.width; + auto nameleft = _topBar.x() + st::topBarArrowPadding.right(); + auto nametop = _topBar.y() + st::topBarArrowPadding.top(); + auto statustop = _topBar.y() + st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height; + auto namewidth = _topBar.x() + _topBar.width() - decreaseWidth - nameleft - st::topBarArrowPadding.right(); + _p->setFont(st::dialogsTextFont); + _p->setPen(_topBarStatusActive ? st::historyStatusFgActive[_palette] : st::historyStatusFg[_palette]); + _p->drawText(nameleft, statustop + st::dialogsTextFont->ascent, _topBarStatus); + + _p->setPen(st::dialogsNameFg[_palette]); + _topBarName.drawElided(*_p, nameleft, nametop, namewidth); +} + +void Generator::paintComposeArea() { + _p->fillRect(_composeArea, st::historyReplyBg[_palette]); + + auto controlsTop = _composeArea.y() + _composeArea.height() - st::historySend.height; + st::historyAttach.icon[_palette].paint(*_p, _composeArea.x() + st::historyAttach.iconPosition.x(), controlsTop + st::historyAttach.iconPosition.y(), _rect.width()); + auto right = st::historySendRight + st::historySend.width; + st::historyRecordVoice[_palette].paintInCenter(*_p, QRect(_composeArea.x() + _composeArea.width() - right, controlsTop, st::historySend.width, st::historySend.height)); + + right += st::historyAttachEmoji.width; + auto attachEmojiLeft = _composeArea.x() + _composeArea.width() - right; + _p->fillRect(attachEmojiLeft, controlsTop, st::historyAttachEmoji.width, st::historyAttachEmoji.height, st::historyComposeAreaBg[_palette]); + st::historyAttachEmoji.icon[_palette].paint(*_p, attachEmojiLeft + st::historyAttachEmoji.iconPosition.x(), controlsTop + st::historyAttachEmoji.iconPosition.y(), _rect.width()); + + auto pen = st::historyEmojiCircleFg[_palette]->p; + pen.setWidth(st::historyEmojiCircleLine); + pen.setCapStyle(Qt::RoundCap); + _p->setPen(pen); + _p->setBrush(Qt::NoBrush); + + PainterHighQualityEnabler hq(*_p); + auto inner = QRect(QPoint(attachEmojiLeft + (st::historyAttachEmoji.width - st::historyEmojiCircle.width()) / 2, controlsTop + st::historyEmojiCircleTop), st::historyEmojiCircle); + _p->drawEllipse(inner); + + auto fakeMargin = 0; + switch (cScale()) { + case dbisOneAndQuarter: fakeMargin = 1; break; + case dbisOneAndHalf: fakeMargin = 2; break; + case dbisTwo: fakeMargin = 4; break; + } + + auto fieldLeft = _composeArea.x() + st::historyAttach.width + fakeMargin; + auto fieldTop = _composeArea.y() + _composeArea.height() - st::historyAttach.height + st::historySendPadding + fakeMargin; + auto fieldWidth = _composeArea.width() - st::historyAttach.width - st::historySend.width - st::historySendRight - st::historyAttachEmoji.width - 2 * fakeMargin; + auto fieldHeight = st::historySend.height - 2 * st::historySendPadding - 2 * fakeMargin; + auto field = QRect(fieldLeft, fieldTop, fieldWidth, fieldHeight); + _p->fillRect(field, st::historyComposeField.bgColor[_palette]); + + _p->save(); + _p->setClipRect(field); + _p->setFont(st::historyComposeField.font); + _p->setPen(st::historyComposeField.phColor[_palette]); + + auto phRect = QRect(field.x() + st::historyComposeField.textMrg.left() - fakeMargin + st::historyComposeField.phPos.x(), field.y() + st::historyComposeField.textMrg.top() - fakeMargin + st::historyComposeField.phPos.y(), field.width() - st::historyComposeField.textMrg.left() - st::historyComposeField.textMrg.right(), field.height() - st::historyComposeField.textMrg.top() - st::historyComposeField.textMrg.bottom()); + _p->drawText(phRect, lang(lng_message_ph), QTextOption(st::historyComposeField.phAlign)); + + _p->restore(); + _p->setClipping(false); +} + +void Generator::paintDialogs() { + _p->fillRect(_dialogs, st::dialogsBg[_palette]); + + st::dialogsMenuToggle.icon[_palette].paint(*_p, _dialogs.x() + st::dialogsFilterPadding.x() + st::dialogsMenuToggle.iconPosition.x(), _dialogs.y() + st::dialogsFilterPadding.y() + st::dialogsMenuToggle.iconPosition.y(), _rect.width()); + + auto filterLeft = _dialogs.x() + st::dialogsFilterPadding.x() + st::dialogsMenuToggle.width + st::dialogsFilterPadding.x(); + auto filterRight = st::dialogsFilterSkip + st::dialogsFilterPadding.x(); + auto filterWidth = _dialogs.width() - filterLeft - filterRight; + auto filterAreaHeight = st::dialogsFilterPadding.y() + st::dialogsMenuToggle.height + st::dialogsFilterPadding.y(); + auto filterTop = _dialogs.y() + (filterAreaHeight - st::dialogsFilter.height) / 2; + auto filter = QRect(filterLeft, filterTop, filterWidth, st::dialogsFilter.height); + + auto pen = st::dialogsFilter.borderColor[_palette]->p; + pen.setWidth(st::dialogsFilter.borderWidth); + _p->setPen(pen); + _p->setBrush(st::dialogsFilter.bgColor[_palette]); + { + PainterHighQualityEnabler hq(*_p); + _p->drawRoundedRect(QRectF(filter).marginsRemoved(QMarginsF(st::dialogsFilter.borderWidth / 2., st::dialogsFilter.borderWidth / 2., st::dialogsFilter.borderWidth / 2., st::dialogsFilter.borderWidth / 2.)), st::buttonRadius - (st::dialogsFilter.borderWidth / 2.), st::buttonRadius - (st::dialogsFilter.borderWidth / 2.)); + } + + if (!st::dialogsFilter.icon.empty()) { + st::dialogsFilter.icon[_palette].paint(*_p, filter.x(), filter.y(), _rect.width()); + } + + _p->save(); + _p->setClipRect(filter); + auto phRect = QRect(filter.x() + st::dialogsFilter.textMrg.left() + st::dialogsFilter.phPos.x(), filter.y() + st::dialogsFilter.textMrg.top() + st::dialogsFilter.phPos.y(), filter.width() - st::dialogsFilter.textMrg.left() - st::dialogsFilter.textMrg.right(), filter.height() - st::dialogsFilter.textMrg.top() - st::dialogsFilter.textMrg.bottom());; + _p->setFont(st::dialogsFilter.font); + _p->setPen(st::dialogsFilter.phColor[_palette]); + _p->drawText(phRect, lang(lng_dlg_filter), QTextOption(st::dialogsFilter.phAlign)); + _p->restore(); + _p->setClipping(false); + + paintDialogsList(); +} + +void Generator::paintDialogsList() { + _p->setClipRect(_dialogsList); + _rowsTop = _dialogsList.y(); + for (auto &row : _rows) { + paintRow(row); + _rowsTop += st::dialogsRowHeight; + } + _p->setClipping(false); +} + +void Generator::paintRow(const Row &row) { + auto x = _dialogsList.x(); + auto y = _rowsTop; + auto fullWidth = _dialogsList.width(); + auto fullRect = QRect(x, y, fullWidth, st::dialogsRowHeight); + if (row.active || row.selected) { + _p->fillRect(fullRect, row.active ? st::dialogsBgActive[_palette] : st::dialogsBgOver[_palette]); + } + paintUserpic(x + st::dialogsPadding.x(), y + st::dialogsPadding.y(), row.type, row.peerIndex); + + auto nameleft = x + st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; + auto namewidth = x + fullWidth - nameleft - st::dialogsPadding.x(); + auto rectForName = QRect(nameleft, y + st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); + + auto chatTypeIcon = ([&row]() -> const style::icon * { + if (row.type == Row::Type::Group) { + return &(row.active ? st::dialogsChatIconActive : (row.selected ? st::dialogsChatIconOver : st::dialogsChatIcon)); + } else if (row.type == Row::Type::Channel) { + return &(row.active ? st::dialogsChannelIconActive : (row.selected ? st::dialogsChannelIconOver : st::dialogsChannelIcon)); + } + return nullptr; + })(); + if (chatTypeIcon) { + (*chatTypeIcon)[_palette].paint(*_p, rectForName.topLeft(), fullWidth); + rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); + } + + auto texttop = y + st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; + + auto dateWidth = st::dialogsDateFont->width(row.date); + rectForName.setWidth(rectForName.width() - dateWidth - st::dialogsDateSkip); + _p->setFont(st::dialogsDateFont); + _p->setPen(row.active ? st::dialogsDateFgActive[_palette] : (row.selected ? st::dialogsDateFgOver[_palette] : st::dialogsDateFg[_palette])); + _p->drawText(rectForName.left() + rectForName.width() + st::dialogsDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, row.date); + + auto availableWidth = namewidth; + if (row.unreadCounter) { + auto counter = QString::number(row.unreadCounter); + auto mutedCounter = row.muted; + auto unreadRight = x + fullWidth - st::dialogsPadding.x(); + auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; + + auto unreadWidth = st::dialogsUnreadFont->width(counter); + auto unreadRectWidth = unreadWidth + 2 * st::dialogsUnreadPadding; + auto unreadRectHeight = st::dialogsUnreadHeight; + accumulate_max(unreadRectWidth, unreadRectHeight); + + auto unreadRectLeft = unreadRight - unreadRectWidth; + auto unreadRectTop = unreadTop; + availableWidth -= unreadRectWidth + st::dialogsUnreadPadding; + + style::color bg[] = { + st::dialogsUnreadBg, + st::dialogsUnreadBgOver, + st::dialogsUnreadBgActive, + st::dialogsUnreadBgMuted, + st::dialogsUnreadBgMutedOver, + st::dialogsUnreadBgMutedActive + }; + + auto index = (row.active ? 2 : row.selected ? 1 : 0) + (row.muted ? 3 : 0); + _p->setPen(Qt::NoPen); + _p->setBrush(bg[index][_palette]); + _p->drawRoundedRect(QRectF(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight), unreadRectHeight / 2., unreadRectHeight / 2.); + + auto textTop = (unreadRectHeight - st::dialogsUnreadFont->height) / 2; + _p->setFont(st::dialogsUnreadFont); + _p->setPen(row.active ? st::dialogsUnreadFgActive[_palette] : (row.selected ? st::dialogsUnreadFgOver[_palette] : st::dialogsUnreadFg[_palette])); + _p->drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st::dialogsUnreadFont->ascent, counter); + } else if (row.pinned) { + auto icon = (row.active ? st::dialogsPinnedIconActive[_palette] : (row.selected ? st::dialogsPinnedIconOver[_palette] : st::dialogsPinnedIcon[_palette])); + icon.paint(*_p, x + fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); + availableWidth -= icon.width() + st::dialogsUnreadPadding; + } + auto textRect = QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height); + setTextPalette(row.active ? st::dialogsTextPaletteActive : (row.selected ? st::dialogsTextPaletteOver : st::dialogsTextPalette)); + _p->setFont(st::dialogsTextFont); + _p->setPen(row.active ? st::dialogsTextFgActive[_palette] : (row.selected ? st::dialogsTextFgOver[_palette] : st::dialogsTextFg[_palette])); + row.text.drawElided(*_p, textRect.left(), textRect.top(), textRect.width(), textRect.height() / st::dialogsTextFont->height); + restoreTextPalette(); + + auto sendStateIcon = ([&row]() -> const style::icon* { + if (row.status == Status::Sent) { + return &(row.active ? st::dialogsSentIconActive : (row.selected ? st::dialogsSentIconOver : st::dialogsSentIcon)); + } else if (row.status == Status::Received) { + return &(row.active ? st::dialogsReceivedIconActive : (row.selected ? st::dialogsReceivedIconOver : st::dialogsReceivedIcon)); + } + return nullptr; + })(); + if (sendStateIcon) { + rectForName.setWidth(rectForName.width() - st::dialogsSendStateSkip); + (*sendStateIcon)[_palette].paint(*_p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth); + } + _p->setPen(row.active ? st::dialogsNameFgActive[_palette] : (row.selected ? st::dialogsNameFgOver[_palette] : st::dialogsNameFg[_palette])); + row.name.drawElided(*_p, rectForName.left(), rectForName.top(), rectForName.width()); +} + +void Generator::paintBubble(const Bubble &bubble) { + auto height = bubble.height; + if (!bubble.replyName.isEmpty()) { + height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + } + auto isPhoto = !bubble.photo.isNull(); + + auto x = _history.x(); + auto y = _historyBottom - st::msgMargin.bottom() - height; + auto bubbleTop = y; + auto bubbleHeight = height; + if (isPhoto) { + bubbleTop -= st::historyMessageRadius + 1; + bubbleHeight += st::historyMessageRadius + 1; + } + + auto left = bubble.outbg ? st::msgMargin.right() : st::msgMargin.left(); + if (bubble.outbg) { + left += _history.width() - st::msgMargin.left() - st::msgMargin.right() - bubble.width; + } + x += left; + + _p->setPen(Qt::NoPen); + auto tailclip = st::historyMessageRadius + 1; + if (bubble.tail) { + if (bubble.outbg) { + _p->setClipRegion(QRegion(_history) - QRect(x + bubble.width - tailclip, bubbleTop + bubbleHeight - tailclip, tailclip + st::historyMessageRadius, tailclip + st::historyMessageRadius)); + } else { + _p->setClipRegion(QRegion(_history) - QRect(x - st::historyMessageRadius, bubbleTop + bubbleHeight - tailclip, tailclip + st::historyMessageRadius, tailclip + st::historyMessageRadius)); + } + } + auto sh = bubble.outbg ? st::msgOutShadow[_palette] : st::msgInShadow[_palette]; + _p->setBrush(sh); + _p->drawRoundedRect(x, bubbleTop + st::msgShadow, bubble.width, bubbleHeight, st::historyMessageRadius, st::historyMessageRadius); + auto bg = bubble.outbg ? st::msgOutBg[_palette] : st::msgInBg[_palette]; + _p->setBrush(bg); + _p->drawRoundedRect(x, bubbleTop, bubble.width, bubbleHeight, st::historyMessageRadius, st::historyMessageRadius); + if (bubble.tail) { + _p->setClipRect(_history); + if (bubble.outbg) { + _p->fillRect(QRect(x + bubble.width - tailclip, bubbleTop + bubbleHeight - tailclip, tailclip, tailclip), bg); + _p->fillRect(QRect(x + bubble.width - tailclip, bubbleTop + bubbleHeight, tailclip + st::historyBubbleTailOutRight.width(), st::msgShadow), sh); + st::historyBubbleTailOutRight[_palette].paint(*_p, x + bubble.width, bubbleTop + bubbleHeight - st::historyBubbleTailOutRight.height(), _rect.width()); + } else { + _p->fillRect(QRect(x, bubbleTop + bubbleHeight - tailclip, tailclip, tailclip), bg); + _p->fillRect(QRect(x - st::historyBubbleTailInLeft.width(), bubbleTop + bubbleHeight, tailclip + st::historyBubbleTailInLeft.width(), st::msgShadow), sh); + st::historyBubbleTailInLeft[_palette].paint(*_p, x - st::historyBubbleTailInLeft.width(), bubbleTop + bubbleHeight - st::historyBubbleTailOutRight.height(), _rect.width()); + } + } + + auto trect = QRect(x, y, bubble.width, bubble.height); + if (isPhoto) { + trect = trect.marginsRemoved(QMargins(st::msgPadding.left(), st::mediaCaptionSkip, st::msgPadding.right(), st::msgPadding.bottom())); + } else { + trect = trect.marginsRemoved(st::msgPadding); + } + if (!bubble.replyName.isEmpty()) { + auto h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + + auto bar = (bubble.outbg ? st::msgOutReplyBarColor[_palette] : st::msgInReplyBarColor[_palette]); + auto rbar = rtlrect(trect.x() + st::msgReplyBarPos.x(), trect.y() + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), _rect.width()); + _p->fillRect(rbar, bar); + + _p->setPen(bubble.outbg ? st::msgOutServiceFg[_palette] : st::msgInServiceFg[_palette]); + bubble.replyName.drawLeftElided(*_p, trect.x() + st::msgReplyBarSkip, trect.y() + st::msgReplyPadding.top(), bubble.width - st::msgReplyBarSkip, _rect.width()); + + _p->setPen(bubble.outbg ? st::historyTextOutFg[_palette] : st::historyTextInFg[_palette]); + bubble.replyText.drawLeftElided(*_p, trect.x() + st::msgReplyBarSkip, trect.y() + st::msgReplyPadding.top() + st::msgServiceNameFont->height, bubble.width - st::msgReplyBarSkip, _rect.width()); + + trect.setY(trect.y() + h); + } + + if (!bubble.text.isEmpty()) { + setTextPalette(bubble.outbg ? st::outTextPalette : st::inTextPalette); + _p->setPen(bubble.outbg ? st::historyTextOutFg[_palette] : st::historyTextInFg[_palette]); + _p->setFont(st::msgFont); + bubble.text.draw(*_p, trect.x(), trect.y(), trect.width()); + } else if (!bubble.waveform.isEmpty()) { + auto nameleft = x + st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + auto nametop = y + st::msgFileNameTop; + auto nameright = st::msgFilePadding.left(); + auto statustop = y + st::msgFileStatusTop; + auto bottom = y + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + + auto inner = rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _rect.width()); + _p->setPen(Qt::NoPen); + _p->setBrush(bubble.outbg ? st::msgFileOutBg[_palette] : st::msgFileInBg[_palette]); + + _p->drawEllipse(inner); + + auto icon = ([&bubble] { + return &(bubble.outbg ? st::historyFileOutPlay : st::historyFileInPlay); + })(); + (*icon)[_palette].paintInCenter(*_p, inner); + + auto namewidth = x + bubble.width - nameleft - nameright; + + // rescale waveform by going in waveform.size * bar_count 1D grid + auto active = bubble.outbg ? st::msgWaveformOutActive[_palette] : st::msgWaveformInActive[_palette]; + auto inactive = bubble.outbg ? st::msgWaveformOutInactive[_palette] : st::msgWaveformInInactive[_palette]; + int32 wf_size = bubble.waveform.size(), availw = namewidth + st::msgWaveformSkip; + int32 bar_count = wf_size; + int32 max_delta = st::msgWaveformMax - st::msgWaveformMin; + auto wave_bottom = y + st::msgFilePadding.top() + st::msgWaveformMax; + _p->setPen(Qt::NoPen); + auto norm_value = uchar(31); + for (auto i = 0, bar_x = 0; i != wf_size; ++i) { + uchar value = bubble.waveform[i]; + auto max_value = value; + int32 bar_value = ((max_value * max_delta) + ((norm_value + 1) / 2)) / (norm_value + 1); + + if (i >= bubble.waveactive) { + _p->fillRect(nameleft + bar_x, wave_bottom - bar_value, st::msgWaveformBar, st::msgWaveformMin + bar_value, inactive); + } else { + _p->fillRect(nameleft + bar_x, wave_bottom - bar_value, st::msgWaveformBar, st::msgWaveformMin + bar_value, active); + } + bar_x += st::msgWaveformBar + st::msgWaveformSkip; + } + + auto status = bubble.outbg ? st::mediaOutFg[_palette] : st::mediaInFg[_palette]; + _p->setFont(st::normalFont); + _p->setPen(status); + _p->drawTextLeft(nameleft, statustop, _rect.width(), bubble.wavestatus); + } + + _p->setFont(st::msgDateFont); + auto infoRight = x + bubble.width - st::msgPadding.right() + st::msgDateDelta.x(); + auto infoBottom = y + height - st::msgPadding.bottom() + st::msgDateDelta.y(); + _p->setPen(bubble.outbg ? st::msgOutDateFg[_palette] : st::msgInDateFg[_palette]); + auto infoWidth = computeInfoWidth(bubble.status, bubble.date); + + auto dateX = infoRight - infoWidth; + auto dateY = infoBottom - st::msgDateFont->height; + _p->drawText(dateX, dateY + st::msgDateFont->ascent, bubble.date); + auto icon = ([&bubble]() -> const style::icon * { + if (bubble.status == Status::Sent) { + return &st::historySentIcon; + } else if (bubble.status == Status::Received) { + return &st::historyReceivedIcon; + } + return nullptr; + })(); + if (icon) { + (*icon)[_palette].paint(*_p, QPoint(infoRight, infoBottom) + st::historySendStatePosition, _rect.width()); + } + + _historyBottom = y - (bubble.attached ? st::msgMarginTopAttached : st::msgMargin.top()); + + if (isPhoto) { + auto image = bubble.photo.scaled(bubble.photoWidth * cIntRetinaFactor(), bubble.photoHeight * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + image.setDevicePixelRatio(cRetinaFactor()); + _p->drawImage(x, y - bubble.photoHeight, image); + _historyBottom -= bubble.photoHeight; + } +} + +void Generator::paintService(QString text) { + auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom(); + auto bubbleTop = _historyBottom - st::msgServiceMargin.bottom() - bubbleHeight; + auto textWidth = st::msgServiceFont->width(text); + auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right(); + auto radius = bubbleHeight / 2; + _p->setPen(Qt::NoPen); + _p->setBrush(st::msgServiceBg[_palette]); + auto bubbleLeft = _history.x() + (_history.width() - bubbleWidth) / 2; + _p->drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius); + _p->setPen(st::msgServiceFg[_palette]); + _p->drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text); + _historyBottom = bubbleTop - st::msgServiceMargin.top(); +} + +void Generator::paintUserpic(int x, int y, Row::Type type, int index) { + const style::icon *userIcons[] = { + &st::historyPeer1UserpicPerson, + &st::historyPeer2UserpicPerson, + &st::historyPeer3UserpicPerson, + &st::historyPeer4UserpicPerson, + &st::historyPeer5UserpicPerson, + &st::historyPeer6UserpicPerson, + &st::historyPeer7UserpicPerson, + &st::historyPeer8UserpicPerson, + }; + const style::icon *chatIcons[] = { + &st::historyPeer1UserpicChat, + &st::historyPeer2UserpicChat, + &st::historyPeer3UserpicChat, + &st::historyPeer4UserpicChat, + }; + const style::icon *channelIcons[] = { + &st::historyPeer1UserpicChannel, + &st::historyPeer2UserpicChannel, + &st::historyPeer3UserpicChannel, + &st::historyPeer4UserpicChannel, + }; + auto userpic = (type == Row::Type::User) ? userIcons[index % base::array_size(userIcons)] : (type == Row::Type::Group) ? chatIcons[index % base::array_size(chatIcons)] : channelIcons[index % base::array_size(channelIcons)]; + + auto image = QImage(userpic->width() * cIntRetinaFactor(), userpic->height() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&image); + userpic->paint(p, 0, 0, userpic->width()); + } + image = std_::move(image).scaled(st::dialogsPhotoSize * cIntRetinaFactor(), st::dialogsPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + Images::prepareCircle(image); + _p->drawImage(rtl() ? (_rect.width() - x - st::dialogsPhotoSize) : x, y, image); +} + +void Generator::paintHistoryShadows() { + _p->fillRect(_history.x() + st::lineWidth, _history.y(), _history.width() - st::lineWidth, st::lineWidth, st::shadowFg[_palette]); + _p->fillRect(_history.x() + st::lineWidth, _history.y() + _history.height() - st::lineWidth, _history.width() - st::lineWidth, st::lineWidth, st::shadowFg[_palette]); + _p->fillRect(_history.x(), _body.y(), st::lineWidth, _body.height(), st::shadowFg[_palette]); +} + +void Generator::setTextPalette(const style::TextPalette &st) { + _textPalette.linkFg = st.linkFg[_palette].clone(); + _textPalette.monoFg = st.monoFg[_palette].clone(); + _textPalette.selectBg = st.selectBg[_palette].clone(); + _textPalette.selectOverlay = st.selectOverlay[_palette].clone(); + _p->setTextPalette(_textPalette); +} + +void Generator::restoreTextPalette() { + _p->restoreTextPalette(); +} + +} // namespace + +std_::unique_ptr GeneratePreview(const QString &filepath, const CurrentData &data) { + auto result = std_::make_unique(); + result->path = filepath; + if (!LoadFromFile(filepath, &result->instance, &result->content)) { + return std_::unique_ptr(); + } + result->preview = Generator(result->instance, data).generate(); + return std_::move(result); +} + +int DefaultPreviewTitleHeight() { + return st::titleHeight; +} + +void DefaultPreviewWindowTitle(Painter &p, const style::palette &palette, QRect body, int outerWidth) { + auto titleRect = QRect(body.x(), body.y() - st::titleHeight, body.width(), st::titleHeight); + p.fillRect(titleRect, QColor(0, 0, 0)); + p.fillRect(titleRect, st::titleBg[palette]); + auto right = st::titleButtonClose.width; + st::titleButtonClose.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonClose.iconPosition.x(), titleRect.y() + st::titleButtonClose.iconPosition.y(), outerWidth); + right += st::titleButtonMaximize.width; + st::titleButtonMaximize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonMaximize.iconPosition.x(), titleRect.y() + st::titleButtonMaximize.iconPosition.y(), outerWidth); + right += st::titleButtonMinimize.width; + st::titleButtonMinimize.icon[palette].paint(p, titleRect.x() + titleRect.width() - right + st::titleButtonMinimize.iconPosition.x(), titleRect.y() + st::titleButtonMinimize.iconPosition.y(), outerWidth); + p.fillRect(titleRect.x(), titleRect.y() + titleRect.height() - st::lineWidth, titleRect.width(), st::lineWidth, st::titleShadow[palette]); +} + +void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) { + auto mask = QImage(st::windowShadow.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + mask.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&mask); + p.setCompositionMode(QPainter::CompositionMode_Source); + st::windowShadow.paint(p, 0, 0, st::windowShadow.width(), QColor(0, 0, 0)); + } + auto maxSize = 0; + auto currentInt = static_cast(0); + auto lastLineInts = reinterpret_cast(mask.constBits() + (mask.height() - 1) * mask.bytesPerLine()); + for (auto end = lastLineInts + mask.width(); lastLineInts != end; ++lastLineInts) { + if (*lastLineInts < currentInt) { + break; + } + currentInt = *lastLineInts; + ++maxSize; + } + if (cRetina() && (maxSize % cIntRetinaFactor())) { + maxSize -= (maxSize % cIntRetinaFactor()); + } + auto size = maxSize / cIntRetinaFactor(); + auto bottom = size; + auto left = size - st::windowShadowShift; + auto right = left; + auto top = size - 2 * st::windowShadowShift; + + auto sprite = st::windowShadow[palette]; + auto topLeft = QImage(sprite.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + topLeft.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&topLeft); + p.setCompositionMode(QPainter::CompositionMode_Source); + sprite.paint(p, 0, 0, sprite.width()); + } + auto width = sprite.width(); + auto height = sprite.height(); + auto topRight = topLeft.mirrored(true, false); + auto bottomRight = topLeft.mirrored(true, true); + auto bottomLeft = topLeft.mirrored(false, true); + + Painter p(&preview); + DefaultPreviewWindowTitle(p, palette, body, outerWidth); + + auto inner = QRect(body.x(), body.y() - st::titleHeight, body.width(), body.height() + st::titleHeight); + p.setClipRegion(QRegion(inner.marginsAdded(QMargins(size, size, size, size))) - inner); + p.drawImage(inner.x() - left, inner.y() - top, topLeft); + p.drawImage(inner.x() + inner.width() + right - width, inner.y() - top, topRight); + p.drawImage(inner.x() + inner.width() + right - width, inner.y() + inner.height() + bottom - height, bottomRight); + p.drawImage(inner.x() - left, inner.y() + inner.height() + bottom - height, bottomLeft); + p.drawImage(QRect(inner.x() - left, inner.y() - top + height, left, top + inner.height() + bottom - 2 * height), topLeft, QRect(0, topLeft.height() - cIntRetinaFactor(), left * cIntRetinaFactor(), cIntRetinaFactor())); + p.drawImage(QRect(inner.x() - left + width, inner.y() - top, left + inner.width() + right - 2 * width, top), topLeft, QRect(topLeft.width() - cIntRetinaFactor(), 0, cIntRetinaFactor(), top * cIntRetinaFactor())); + p.drawImage(QRect(inner.x() + inner.width(), inner.y() - top + height, right, top + inner.height() + bottom - 2 * height), topRight, QRect(topRight.width() - right * cIntRetinaFactor(), topRight.height() - cIntRetinaFactor(), right * cIntRetinaFactor(), cIntRetinaFactor())); + p.drawImage(QRect(inner.x() - left + width, inner.y() + inner.height(), left + inner.width() + right - 2 * width, bottom), bottomRight, QRect(0, bottomRight.height() - bottom * cIntRetinaFactor(), cIntRetinaFactor(), bottom * cIntRetinaFactor())); +} + +} // namespace Theme +} // namespace Window diff --git a/Telegram/Resources/basic_types.style b/Telegram/SourceFiles/window/window_theme_preview.h similarity index 72% rename from Telegram/Resources/basic_types.style rename to Telegram/SourceFiles/window/window_theme_preview.h index 07875c2a9..882e2ece5 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/SourceFiles/window/window_theme_preview.h @@ -18,28 +18,20 @@ to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ +#pragma once -TextStyle { - linkFlags: font; - linkFlagsOver: font; - linkFg: color; - linkFgDown: color; - monoFg: color; - selectBg: color; - selectOverlay: color; - lineHeight: pixels; -} +#include "window/window_theme.h" -Tooltip { - textBg: color; - textFg: color; - textFont: font; - textBorder: color; - textPadding: margins; +namespace Window { +namespace Theme { - shift: point; - skip: pixels; +struct CurrentData { + int32 backgroundId = 0; + QPixmap backgroundImage; + bool backgroundTiled = false; +}; - widthMax: pixels; - linesMax: int; -} +std_::unique_ptr GeneratePreview(const QString &filepath, const CurrentData &data); + +} // namespace Theme +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_theme_warning.cpp b/Telegram/SourceFiles/window/window_theme_warning.cpp index f52855174..250291fc6 100644 --- a/Telegram/SourceFiles/window/window_theme_warning.cpp +++ b/Telegram/SourceFiles/window/window_theme_warning.cpp @@ -38,7 +38,7 @@ constexpr int kWaitBeforeRevertMs = 15999; WarningWidget::WarningWidget(QWidget *parent) : TWidget(parent) , _secondsLeft(kWaitBeforeRevertMs / 1000) , _keepChanges(this, lang(lng_theme_keep_changes), st::defaultBoxButton) -, _revert(this, lang(lng_theme_revert), st::cancelBoxButton) { +, _revert(this, lang(lng_theme_revert), st::defaultBoxButton) { _keepChanges->setClickedCallback([] { Window::Theme::KeepApplied(); }); _revert->setClickedCallback([] { Window::Theme::Revert(); }); _timer.setTimeoutHandler([this] { handleTimer(); }); diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 5c746da56..28d112845 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -36,7 +36,6 @@ 'style_files': [ '<(res_loc)/colors.palette', '<(res_loc)/basic.style', - '<(res_loc)/basic_types.style', '<(src_loc)/boxes/boxes.style', '<(src_loc)/dialogs/dialogs.style', '<(src_loc)/history/history.style', @@ -221,6 +220,8 @@ '<(src_loc)/core/single_timer.cpp', '<(src_loc)/core/single_timer.h', '<(src_loc)/core/stl_subset.h', + '<(src_loc)/core/task_queue.h', + '<(src_loc)/core/task_queue.cpp', '<(src_loc)/core/type_traits.h', '<(src_loc)/core/utils.cpp', '<(src_loc)/core/utils.h', @@ -566,6 +567,8 @@ '<(src_loc)/window/window_main_menu.h', '<(src_loc)/window/window_theme.cpp', '<(src_loc)/window/window_theme.h', + '<(src_loc)/window/window_theme_preview.cpp', + '<(src_loc)/window/window_theme_preview.h', '<(src_loc)/window/window_theme_warning.cpp', '<(src_loc)/window/window_theme_warning.h', '<(src_loc)/window/window_title.h', diff --git a/Telegram/prepare.bat b/Telegram/prepare.bat new file mode 100644 index 000000000..0c5d63d8c --- /dev/null +++ b/Telegram/prepare.bat @@ -0,0 +1,120 @@ +@echo off +setlocal enabledelayedexpansion +set "FullScriptPath=%~dp0" +set "FullExecPath=%cd%" + +set "Command=%1" +if "%Command%" == "module" ( + call :write_module %2 + exit /b %errorlevel% +) else if "%Command%" == "header" ( + call :write_header %2 + exit /b %errorlevel% +) else if "%Command%" == "source" ( + call :write_source %2 + exit /b %errorlevel% +) + +cd gyp +call refresh.bat +cd .. + +exit /b + +:write_module +( + set "CommandPath=%1" + set "CommandPathUnix=!CommandPath:\=/!" + if "!CommandPathUnix!" == "" ( + echo Provide module path. + exit /b 1 + ) + echo Generating module !CommandPathUnix!.. + call prepare.bat header !CommandPathUnix! + call prepare.bat source !CommandPathUnix! + exit /b +) + +:write_header +( + set "CommandPath=%1" + set "CommandPathUnix=!CommandPath:\=/!" + set "CommandPathWin=!CommandPath:/=\!" + + if "!CommandPathUnix!" == "" ( + echo Provide header path. + exit /b 1 + ) else if exist "SourceFiles\!CommandPathWin!.h" ( + echo This header already exists. + exit /b 1 + ) + echo Generating header !CommandPathUnix!.h.. + mkdir "SourceFiles\!CommandPathWin!.h" + rmdir "SourceFiles\!CommandPathWin!.h" + + call :write_comment !CommandPathWin!.h + set "header1=#pragma once" + ( + echo !header1! + echo. + )>> "SourceFiles\!CommandPathWin!.h" + exit /b +) + +:write_source +( + set "CommandPath=%1" + set "CommandPathUnix=!CommandPath:\=/!" + set "CommandPathWin=!CommandPath:/=\!" + + if "!CommandPathUnix!" == "" ( + echo Provide source path. + exit /b 1 + ) else if exist "SourceFiles\!CommandPathWin!.cpp" ( + echo This source already exists. + exit /b 1 + ) + echo Generating source !CommandPathUnix!.cpp.. + mkdir "SourceFiles\!CommandPathWin!.cpp" + rmdir "SourceFiles\!CommandPathWin!.cpp" + + call :write_comment !CommandPathWin!.cpp + set "quote=""" + set "quote=!quote:~0,1!" + set "source1=#include !quote!stdafx.h!quote!" + set "source2=#include !quote!!CommandPathUnix!.h!quote!" + ( + echo !source1! + echo !source2! + echo. + )>> "SourceFiles\!CommandPathWin!.cpp" + exit /b +) + +:write_comment +( + set "Path=%1" + ( + echo /* + echo This file is part of Telegram Desktop, + echo the official desktop version of Telegram messaging app, see https://telegram.org + echo. + echo Telegram Desktop is free software: you can redistribute it and/or modify + echo it under the terms of the GNU General Public License as published by + echo the Free Software Foundation, either version 3 of the License, or + echo ^(at your option^) any later version. + echo. + echo It is distributed in the hope that it will be useful, + echo but WITHOUT ANY WARRANTY; without even the implied warranty of + echo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + echo GNU General Public License for more details. + echo. + echo In addition, as a special exception, the copyright holders give permission + echo to link the code of portions of this program with the OpenSSL library. + echo. + echo Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + echo Copyright ^(c^) 2014-2016 John Preston, https://desktop.telegram.org + echo */ + )> "SourceFiles\!Path!" + exit /b +)