diff --git a/Telegram.sln b/Telegram.sln index fe3eed1b1..b474402bc 100644 --- a/Telegram.sln +++ b/Telegram.sln @@ -9,6 +9,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Telegram", "Telegram\Telegr {E4DF8176-4DEF-4859-962F-B497E3E7A323} = {E4DF8176-4DEF-4859-962F-B497E3E7A323} {E417CAA4-259B-4C99-88E3-805F1300E8EB} = {E417CAA4-259B-4C99-88E3-805F1300E8EB} {EB7D16AC-EACF-4577-B05A-F28E5F356794} = {EB7D16AC-EACF-4577-B05A-F28E5F356794} + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6} = {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MetaStyle", "Telegram\MetaStyle.vcxproj", "{6F483617-7C84-4E7E-91D8-1FF28A4CE3A0}" @@ -25,6 +26,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Packer", "Telegram\Packer.v EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "codegen_style", "Telegram\build\vc\codegen_style\codegen_style.vcxproj", "{E4DF8176-4DEF-4859-962F-B497E3E7A323}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "codegen_numbers", "Telegram\build\vc\codegen_numbers\codegen_numbers.vcxproj", "{7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -95,11 +98,22 @@ Global {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|Win32.ActiveCfg = Release|Win32 {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|Win32.Build.0 = Release|Win32 {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|x64.ActiveCfg = Release|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Debug|Win32.ActiveCfg = Debug|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Debug|Win32.Build.0 = Debug|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Debug|x64.ActiveCfg = Debug|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Deploy|Win32.ActiveCfg = Debug|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Deploy|Win32.Build.0 = Debug|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Deploy|x64.ActiveCfg = Release|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Deploy|x64.Build.0 = Release|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Release|Win32.ActiveCfg = Release|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Release|Win32.Build.0 = Release|Win32 + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6}.Release|x64.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {E4DF8176-4DEF-4859-962F-B497E3E7A323} = {2F863EAD-33C9-4014-A573-93F085BA9CB1} + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6} = {2F863EAD-33C9-4014-A573-93F085BA9CB1} EndGlobalSection EndGlobal diff --git a/Telegram/Resources/all_files.style b/Telegram/Resources/all_files.style index 09d45ede9..8737bca36 100644 --- a/Telegram/Resources/all_files.style +++ b/Telegram/Resources/all_files.style @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ // Legacy styles -using "Resources/style_classes.txt"; -using "Resources/style.txt"; +using "Resources/basic_types.style"; +using "Resources/basic.style"; //using "overview/overview.style"; diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style new file mode 100644 index 000000000..92ed45667 --- /dev/null +++ b/Telegram/Resources/basic.style @@ -0,0 +1,2525 @@ +/* +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 +*/ +using "Resources/basic_types.style"; + +semibold: "Open Sans Semibold"; + +fsize: 13px; +normalFont: font(fsize); +semiboldFont: font(fsize semibold); + +emojiImgSize: 18px; // exceptional value for retina +emojiSize: 18px; +emojiPadding: 0px; + +counterBG: #f23c34; +counterMuteBG: #888; +counterColor: #fff; +counterMacInvColor: #ffffff01; + +lineWidth: 1px; + +transparent: #fff0; +white: #fff; +black: #000; + +color1: #c03d33; // red +color2: #4fad2d; // green +color3: #d09306; // yellow +color4: #348cd4; // blue +color5: #8544d6; // purple +color6: #cd4073; // pink +color7: #2996ad; // sea +color8: #ce671b; // orange + +wndMinWidth: 380px; + +adaptiveNormalWidth: 640px; +adaptiveWideWidth: 1366px; + +wndMinHeight: 480px; +wndDefWidth: 800px; +wndDefHeight: 600px; +wndBG: white; +wndShadow: sprite(209px, 46px, 19px, 19px); +wndShadowShift: 1px; + +layerAlpha: 0.5; +layerBg: black; + +overBg: #edf2f5; + +labelDefFlat: flatLabel { + font: font(fsize); + minWidth: 100px; + width: 0px; + align: align(left); +} + +boxBg: white; +boxVerticalMargin: 10px; +boxWidth: 320px; +boxWideWidth: 364px; +boxPadding: margins(26px, 30px, 34px, 8px); +boxMaxListHeight: 600px; +boxFontSize: 14px; +boxTextFont: font(boxFontSize); +boxLittleSkip: 10px; +boxMediumSkip: 20px; + +boxTitleFg: #444444; +boxTitleFont: font(boxFontSize bold); +boxTitlePosition: point(26px, 28px); +boxTitleHeight: 54px; + +boxBlueTitleBg: #6393b5; +boxBlueTitleAdditionalFg: #dae9f5; +boxBlueTitleAdditionalSkip: 12px; +boxBlueTitlePosition: point(23px, 18px); +boxBlueCloseIcon: sprite(120px, 108px, 12px, 12px); +boxBlueCloseBg: #c8e1f0; +boxBlueCloseDuration: 150; +boxBlueShadow: sprite(132px, 108px, 1px, 4px); + +boxButtonFont: font(boxFontSize semibold); +defaultBoxButton: BoxButton { + textFg: #2f9fea; + textFgOver: #2f9fea; + textBg: white; + textBgOver: #edf7ff; + + width: -24px; + height: 36px; + + textTop: 8px; + + font: boxButtonFont; + duration: 200; +} +cancelBoxButton: BoxButton(defaultBoxButton) { + textFg: #aeaeae; +} +attentionBoxButton: BoxButton(defaultBoxButton) { + textFg: #ea4b2f; + textFgOver: #ea4b2f; + textBgOver: #fff0ed; +} +boxButtonPadding: margins(12px, 16px, 22px, 16px); +defaultBoxLinkButton: linkButton { + color: #0080c0; + overColor: #0080c0; + downColor: #0073ad; + font: boxTextFont; + overFont: font(boxFontSize underline); +} +redBoxLinkButton: linkButton(defaultBoxLinkButton) { + color: #d15948; + overColor: #d15948; + downColor: #db6352; +} +boxLabel: flatLabel(labelDefFlat) { + font: font(boxFontSize); + align: align(topleft); +} + +defaultInputArea: InputArea { + textFg: black; + textMargins: margins(5px, 6px, 5px, 4px); + + placeholderFg: #999; + placeholderFgActive: #aaa; + placeholderMargins: margins(2px, 0px, 2px, 0px); + placeholderAlign: align(topleft); + placeholderShift: 50px; + duration: 120; + + borderFg: #e0e0e0; + borderFgActive: #62c0f7; + borderFgError: #e48383; + + border: 1px; + borderActive: 2px; + borderError: 2px; + + font: boxTextFont; + + heightMin: 32px; + heightMax: 128px; +} +defaultInputField: InputField { + textFg: black; + textMargins: margins(0px, 6px, 0px, 4px); + textAlign: align(topleft); + + placeholderFg: #999; + placeholderFgActive: #aaa; + placeholderMargins: margins(2px, 0px, 2px, 0px); + placeholderAlign: align(topleft); + placeholderShift: 50px; + duration: 120; + + borderFg: #e0e0e0; + borderFgActive: #62c0f7; + borderFgError: #e48383; + + border: 1px; + borderActive: 2px; + borderError: 2px; + + font: boxTextFont; + + height: 32px; +} +dialogsSearchField: InputField(defaultInputField) { + textMargins: margins(34px, 7px, 34px, 7px); + + iconSprite: sprite(227px, 21px, 24px, 24px); + iconPosition: point(6px, 5px); + + width: 240px; + height: 34px; +} +defaultCheckbox: Checkbox { + textFg: black; + textBg: white; + + checkFg: #d9d9d9; + checkFgOver: #bfbfbf; + checkFgActive: #4eb3ee; + + width: -46px; + height: 22px; + + textPosition: point(34px, 0px); + diameter: 22px; + thickness: 2px; + checkIcon: sprite(106px, 136px, 14px, 10px); + + font: boxTextFont; + duration: 120; +} +defaultRadiobutton: Radiobutton { + textFg: black; + textBg: white; + + checkFg: #d9d9d9; + checkFgOver: #bfbfbf; + checkFgActive: #4eb3ee; + + width: -46px; + height: 22px; + + textPosition: point(34px, 0px); + diameter: 22px; + thickness: 2px; + checkSkip: 65px; // * 0.1 + + font: boxTextFont; + duration: 120; +} +solidScroll: flatScroll { + barColor: #3f729734; + bgColor: #214f751a; + barOverColor: #3f729734; + bgOverColor: #214f751a; + + round: 0px; + minHeight: 20px; + + deltax: 5px; + width: 14px; + deltat: 6px; + deltab: 6px; + + topsh: 0px; + bottomsh: 0px; + shColor: rgba(0, 0, 0, 18); + + duration: 150; + hiding: 0; +} +defaultPopupMenu: PopupMenu { + skip: 5px; + + shadow: sprite(241px, 46px, 6px, 6px); + shadowShift: 1px; + + itemBg: white; + itemBgOver: overBg; + itemFg: black; + itemFgOver: black; + itemFgDisabled: #ccc; + itemFgShortcut: #999; + itemFgShortcutOver: #7c99b2; + itemFgShortcutDisabled: #ccc; + + itemPadding: margins(17px, 8px, 17px, 7px); + itemFont: normalFont; + + separatorPadding: margins(0px, 5px, 0px, 5px); + separatorWidth: 1px; + separatorFg: #f1f1f1; + + arrow: sprite(0px, 126px, 4px, 7px); + + duration: 120; + + widthMin: 180px; + widthMax: 300px; +} + +defaultTooltip: Tooltip { + textBg: #eef2f5; + textFg: #5d6c80; + textFont: normalFont; + textBorder: #c9d1db; + textPadding: margins(5px, 2px, 5px, 2px); + + shift: point(-20px, 20px); + skip: 10px; + + widthMax: 800px; + linesMax: 12; +} + +almostTransparent: #ffffff0d; +boxScroll: flatScroll(solidScroll) { + width: 18px; + deltax: 6px; +} +boxScrollSkip: 6px; +boxScrollShadowBg: #00000012; + +boxSearchField: InputField(defaultInputField) { + textMargins: margins(41px, 16px, 41px, 0px); + + placeholderFg: #999; + placeholderFgActive: #aaa; + placeholderMargins: margins(4px, 0px, 4px, 0px); + + border: 0px; + borderActive: 0px; + borderError: 0px; + + height: 48px; + + iconSprite: sprite(227px, 21px, 24px, 24px); + iconPosition: point(15px, 14px); + + font: normalFont; +} +boxSearchCancel: iconedButton { + color: white; + bgColor: white; + overBgColor: white; + font: font(fsize); + + opacity: 0.3; + overOpacity: 0.4; + + textPos: point(0px, 0px); + downTextPos: point(0px, 0px); + + duration: 150; + cursor: cursor(pointer); + + icon: sprite(133px, 108px, 12px, 12px); + iconPos: point(8px, 18px); + downIcon: sprite(133px, 108px, 12px, 12px); + downIconPos: point(8px, 18px); + + width: 41px; + height: 48px; +} + +titleBG: #6389a8; +titleColor: #0f8dcc;//rgb(20, 136, 210); +titleHeight: 39px; +titleIconPos: point(7px, 7px); +titleIconImg: sprite(161px, 100px, 26px, 26px); +titleFont: font(17px); +titlePos: point(44px, 29px); +titleMenuOffset: 36px; + +titleRed: #ee4928; +titleGray: #777; +titleGreen: #41a903; + +titleStatusColor: #999; +titleTypingColor: #0080c0; + +statusFont: font(fsize); +versionColor: #777; + +shadowColor: rgba(0, 0, 0, 24); + +slideDuration: 240; +slideShift: 0.3; +slideFadeOut: 0.3; +slideShadow: sprite(348px, 71px, 48px, 1px); +slideFunction: transition(easeOutCirc); + +btnDefIconed: iconedButton { + color: white; + bgColor: white; + overBgColor: white; + font: font(fsize); + + opacity: 0.78; + overOpacity: 1.; + + textPos: point(0px, 0px); + downTextPos: point(0px, 0px); + + duration: 150; + cursor: cursor(pointer); +} + +sysBtnDelta: 6px; +sysUpd: sysButton { + size: size(31px, 39px); + img: sprite(184px, 1px, 19px, 19px); + color: #c4d8e9; + overColor: white; + duration: 150; +} +updateBlinkDuration: 500; +sysMin: sysButton(sysUpd) { + img: sprite(207px, 1px, 19px, 19px); +} +sysMax: sysButton(sysUpd) { + img: sprite(230px, 1px, 19px, 19px); +} +sysRes: sysButton(sysUpd) { + img: sprite(253px, 1px, 19px, 19px); +} +sysCls: sysButton(sysUpd) { + img: sprite(276px, 1px, 19px, 19px); +} +sysLock: sysButton(sysUpd) { + img: sprite(184px, 22px, 19px, 19px); +} +sysUnlock: sysButton(sysUpd) { + img: sprite(207px, 22px, 19px, 19px); +} +titleBackButton: iconedButton(btnDefIconed) { + icon: sprite(9px, 104px, 13px, 20px); + iconPos: point(5px, 9px); + downIcon: sprite(9px, 104px, 13px, 20px); + downIconPos: point(5px, 10px); + + bgColor: #c4d8e9; + overBgColor: #fff; + + width: -30px; + height: 39px; + + opacity: 1.; + cursor: cursor(default); + + textPos: point(23px, 10px); + downTextPos: point(23px, 11px); +} + +btnWhiteHover: #f5f5f5; +btnBoxWhiteHover: #fafafa; +btnYesColor: #0080c0; +btnYesHover: #0073ad; +btnNoColor: #8b8b8b; +btnNoHover: #777; + +titleTextButton: flatButton { + color: #d4e3ef; + overColor: #fff; + downColor: #fff; + bgColor: transparent; + overBgColor: transparent; + downBgColor: transparent; + + width: -14px; + height: 39px; + + textTop: 10px; + overTextTop: 10px; + downTextTop: 11px; + + font: font(fsize); + overFont: font(fsize); + duration: 150; + cursor: cursor(default); +} + +btnDefFlat: flatButton { + duration: 200; + cursor: cursor(pointer); +} +btnDefBig: flatButton(btnDefFlat) { + textTop: 11px; + overTextTop: 11px; + downTextTop: 12px; + + font: font(23px); + overFont: font(23px); + height: 56px; +} +btnNextBG: #2fa9e2; +btnDefNext: flatButton(btnDefFlat) { + color: white; + overColor: white; + downColor: white; + bgColor: btnNextBG; + overBgColor: #279ad0; + downBgColor: #279ad0; +} +btnDefBack: flatButton(btnDefFlat) { + color: white; + overColor: white; + downColor: white; + bgColor: #c7c7c7; + overBgColor: #b9b9b9; + downBgColor: #b9b9b9; +} + +linkCropLimit: 360px; +linkFont: normalFont; +linkOverFont: font(fsize underline); +btnDefLink: linkButton { + color: btnYesColor; + overColor: btnYesColor; + downColor: btnYesHover; + font: linkFont; + overFont: linkOverFont; +} + +cbDefFlat: flatCheckbox { + textColor: #000; + bgColor: transparent; + disColor: #999; + + height: 22px; + textTop: 1px; + textLeft: 34px; + font: font(fsize); + duration: 200; + bgFunc: transition(easeOutCirc); + cursor: cursor(pointer); + + disabledCursor: cursor(default); + + imageRect: sprite(142px, 43px, 22px, 22px); + chkImageRect: sprite(120px, 68px, 22px, 22px); + overImageRect: sprite(142px, 68px, 22px, 22px); + chkOverImageRect: sprite(120px, 68px, 22px, 22px); + disImageRect: sprite(142px, 43px, 22px, 22px); + chkDisImageRect: sprite(120px, 43px, 22px, 22px); + + imagePos: point(0px, 0px); +} + +rbDefFlat: flatCheckbox(cbDefFlat) { + chkImageRect: sprite(165px, 68px, 22px, 22px); + chkOverImageRect: sprite(165px, 68px, 22px, 22px); + chkDisImageRect: sprite(165px, 43px, 22px, 22px); +} + +inpDefFont: font(17px); +inpDefFlat: flatInput { + textColor: #000; + bgColor: #FFF; + bgActive: #FFF; + width: 210px; + height: 40px; + align: align(left); + textMrg: margins(5px, 5px, 5px, 5px); + font: inpDefFont; + cursor: cursor(text); + + borderWidth: 0px; + borderColor: black; + borderActive: black; + borderError: black; + + phColor: #949494; + phFocusColor: #AAA; + phAlign: align(left); + phPos: point(2px, 0px); + phShift: 50px; + phDuration: 100; + phLeftFunc: transition(linear); + phAlphaFunc: transition(linear); + phColorFunc: transition(linear); +} + +inpDefGray: flatInput(inpDefFlat) { + bgColor: #f2f2f2; + borderWidth: 2px; + borderColor: #f2f2f2; + borderActive: #80cff9; + borderError: #ed8080; + phColor: #808080; +} + +taDefFlat: flatTextarea { + textColor: #000; + bgColor: #FFF; + align: align(left); + textMrg: margins(5px, 5px, 5px, 5px); + font: inpDefFont; + cursor: cursor(text); + + phColor: #999; + phFocusColor: #AAA; + phAlign: align(topleft); + phPos: point(2px, 0px); + phShift: 50px; + phDuration: 100; + phLeftFunc: transition(linear); + phAlphaFunc: transition(linear); + phColorFunc: transition(linear); +} + +scrollDef: flatScroll { + barColor: rgba(0, 0, 0, 83); + bgColor: rgba(0, 0, 0, 26); + barOverColor: rgba(0, 0, 0, 122); + bgOverColor: rgba(0, 0, 0, 44); + + round: 0px; + + width: 10px; + minHeight: 20px; + deltax: 3px; + deltat: 3px; + deltab: 3px; + + topsh: 2px; + bottomsh: 2px; + shColor: rgba(0, 0, 0, 18); + + duration: 150; + hiding: 1000; +} +scrollCountries: flatScroll(scrollDef) { + topsh: 0px; + bottomsh: -2px; +} + +lnkText: #0f7dc7; + +introBtnTop: 288px; +introSkip: 45px; +introFinishSkip: 15px; +introPhotoSize: 98px; +introHeaderFont: font(24px); +introHeaderSkip: 14px; +introIconSkip: 54px; +introFont: font(16px); +introLink: linkButton(btnDefLink) { + font: introFont; + overFont: font(16px underline); +} +introColor: black; +introLabel: flatLabel(labelDefFlat) { + font: introFont; + align: align(center); +} + +introPointsTop: -30px; // intro steps bottom points +introPointWidth: 4px; +introPointHeight: 4px; +introPointHoverWidth: 10px; +introPointHoverHeight: 10px; +introPointLeft: 3px; +introPointTop: 3px; +introPointDelta: 10px; +introPointColor: rgb(0, 0, 0); +introPointAlpha: 0.5; +introPointHoverColor: #86b4e3; +introPointStepT: transition(sineInOut); +introPointAlphaT: transition(linear); +introPointShowStepT: transition(easeOutCirc); +introPointHideStepT: transition(easeInCirc); +introPointShowAlphaT: transition(easeInCirc); +introPointHideAlphaT: transition(easeOutCirc); + +introStepSize: size(400px, 200px); +introSize: size(400px, 400px); +introSlideShift: 500px; // intro hiding animation +introSlideDuration: 200; +introSlideDelta: 0; // between hide start and show start +introHideFunc: transition(easeInCirc); +introShowFunc: transition(easeOutCirc); +introAlphaHideFunc: transition(easeOutCirc); +introAlphaShowFunc: transition(easeInCirc); +introTextTop: 22px; +introTextSize: size(400px, 93px); +introCallSkip: 15px; +introPwdTextSize: size(400px, 73px); + +btnIntroSep: 12px; +btnIntroNext: flatButton(btnDefNext, btnDefBig) { + textTop: 16px; + overTextTop: 16px; + downTextTop: 17px; + + font: font(17px); + overFont: font(17px); + + width: 300px; +} + +boxShadow: sprite(363px, 50px, 15px, 15px); +boxShadowShift: 2px; + +introCountry: countryInput { + width: 300px; + height: 41px; + top: 33px; + bgColor: #f2f2f2; + ptrSize: size(15px, 8px); + textMrg: margins(16px, 5px, 16px, 15px); + font: inpDefFont; + align: align(left); +} + +introPhoneTop: 8px; +inpIntroCountryCode: flatInput(inpDefGray) { + width: 70px; + height: 41px; + align: align(center); +} +inpIntroPhone: flatInput(inpDefGray) { + textMrg: margins(12px, 5px, 12px, 6px); + width: 225px; + height: 41px; +} +inpIntroCode: flatInput(inpDefGray) { + textMrg: margins(12px, 5px, 12px, 6px); + width: 106px; + height: 41px; + align: align(center); + + phPos: point(0px, 0px); + phAlign: align(center); + phShift: 0px; +} +inpIntroName: flatInput(inpIntroPhone) { + width: 192px; +} +inpIntroPassword: flatInput(inpIntroPhone) { + width: 300px; +} + +introSelectDelta: 30px; +btnSelectSep: #e0e0e0; +btnRedLink: linkButton(btnDefLink) { + color: #d15948; + overColor: #d15948; + downColor: #db6352; +} + +countryRowHeight: 36px; +countryRowNameFont: semiboldFont; +countryRowPadding: margins(22px, 9px, 8px, 0px); +countryRowCodeFont: font(fsize); +countryRowBgOver: overBg; +countryRowCodeFg: #808080; +countryRowCodeFgOver: #7c99b2; +countriesSkip: 12px; +countriesScroll: flatScroll(boxScroll) { + deltat: 9px; + deltab: 3px; +} + +introErrWidth: 450px; +introErrDuration: 200; +introErrFunc: transition(linear); +introErrColor: black; +introErrTop: 15px; +introErrHeight: 40px; +introErrFont: font(16px); + +introErrLabel: flatLabel(labelDefFlat) { + font: introErrFont; + align: align(center); +} + +setWidth: 356px; +setTop: 26px; +setNameLeft: 3px; +setNameTop: 5px; +setNameFont: font(18px); +setStatusTop: 35px; +setStatusLeft: 3px; +setStatusFont: font(14px); +setPhotoSize: 120px; +setHeaderFont: font(17px); +setHeaderColor: black; +setHeaderSkip: 60px; +setHeaderLeft: -1px; +setHeaderTop: 26px; +setLittleSkip: 9px; +setSectionSkip: 25px; +setContactInfoLeft: 150px; +setVersionHeight: 41px; +setVersionLeft: 36px; +setVersionTop: 3px; +setVersionColor: #999; +setBottom: 130px; +setScroll: flatScroll(scrollDef) { + bottomsh: 0px; + topsh: 0px; +} +setClose: iconedButton(btnDefIconed) { + icon: sprite(245px, 221px, 40px, 40px); + iconPos: point(0px, 0px); + downIcon: sprite(245px, 221px, 40px, 40px); + downIconPos: point(0px, 0px); + + opacity: 0.71; + + width: 43px; + height: 43px; +} +setClosePos: point(32px, 32px); +setPhotoImg: sprite(0px, 218px, 120px, 120px); +setOverPhotoImg: sprite(122px, 218px, 120px, 120px); +setPhotoDuration: 150; + +setPadding: 26px; +setBG: #FFF; +setSh: #000; +setTitleFrom: point(20px, 20px); +setTitleFont: font(24px); +setTitleColor: #000; +setNameInput: flatInput(inpDefFlat) { + font: font(fsize); + height: 25px; + width: 170px; + textMrg: margins(3px, 3px, 3px, 3px); +} +setErrBG: #ffa5a5; +setErrColor: #d84d4d; +setErrHeight: 30px; +setErrFont: font(fsize); +setGoodColor: #4ab44a; + +setBackgroundSize: 120px; + +btnSetUpload: flatButton(btnDefNext, btnDefBig) { + width: 206px; + height: 42px; + font: font(18px); + overFont: font(18px); + + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; +} + +btnEditSave: flatButton(btnSetUpload) { + width: 115px; +} +btnEditCancel: flatButton(btnDefFlat, btnDefBig) { + color: #666d78; + overColor: #666d78; + downColor: #50565e; + + bgColor: rgba(0, 0, 0, 63); + overBgColor: rgba(0, 0, 0, 47); + downBgColor: rgba(0, 0, 0, 95); + + width: 115px; + height: 40px; + + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; + + font: font(18px); + overFont: font(18px); +} + +btnLogout: flatButton(btnDefFlat, btnDefBig) { + color: white; + overColor: white; + downColor: white; + + bgColor: #db6352; + overBgColor: #d15948; + downBgColor: #c74d3b; + + width: 148px; + height: 42px; + + textTop: 8px; + overTextTop: 8px; + downTextTop: 9px; + + font: font(18px); + overFont: font(18px); +} + +//// dialogs +dlgFilterPadding: 10px; +dlgPhotoSize: 46px; +dlgPaddingHor: 10px; +dlgPaddingVer: 8px; +dlgHeight: 62px; +dlgPhotoPadding: 12px; + +dlgImportantHeight: 37px; + +noContactsHeight: 100px; +noContactsFont: font(fsize); +noContactsColor: #777; + +dlgSep: 8px; + +dlgMinWidth: 260px; +dlgRichMinWidth: 150px; +dlgMaxWidth: 540px; +dlgFilter: flatInput(inpDefGray) { + font: font(fsize); + height: 34px; + bgColor: #f2f2f2; + phColor: #949494; + phFocusColor: #a4a4a4; + textMrg: margins(34px, 2px, 34px, 4px); + imgRect: sprite(227px, 21px, 24px, 24px); + imgPos: point(6px, 5px); + width: 240px; + + borderWidth: 2px; + borderColor: #f2f2f2; + borderActive: #80cff9; + borderError: #ed8080; +} +dlgScroll: flatScroll(scrollDef) { + topsh: 0px; + bottomsh: 0px; +} +dlgFont: font(fsize); + +dlgDblCheckImg: sprite(302px, 23px, 17px, 11px); +dlgCheckImg: sprite(320px, 23px, 17px, 11px); +dlgActiveDblCheckImg: sprite(302px, 36px, 17px, 11px); +dlgActiveCheckImg: sprite(320px, 36px, 17px, 11px); +dlgSendImg: sprite(122px, 25px, 17px, 11px); +dlgActiveSendImg: sprite(142px, 25px, 17px, 11px); + +dlgChatImgPos: point(1px, 4px); +dlgChatImg: sprite(104px, 26px, 16px, 11px); +dlgActiveChatImg: sprite(104px, 37px, 16px, 11px); +dlgChannelImgPos: point(3px, 4px); +dlgChannelImg: sprite(105px, 1px, 12px, 11px); +dlgActiveChannelImg: sprite(105px, 14px, 12px, 11px); +dlgImgSkip: 22px; + +dlgCheckLeft: 5px; +dlgCheckTop: 4px; +dlgCheckSkip: 3px; + +dlgHistFont: font(fsize); +dlgNameColor: #000; +dlgNameTop: 2px; +dlgSystemColor: #4981af; +dlgTextColor: #888; + +dlgDateFont: font(13px); +dlgDateColor: #a8a8a8; +dlgDateSkip: 5px; + +dlgUnreadColor: #FFF; +dlgUnreadBG: #6fc766; +dlgUnreadMutedBG: #bbb; +dlgUnreadFont: font(12px bold); +dlgUnreadHeight: 19px; +dlgUnreadTop: 1px; +dlgUnreadPaddingHor: 5px; +dlgUnreadRadius: 2px; +dlgBG: #FFF; +dlgHoverBG: #f5f5f5; + +dlgActiveBG: #6a91b1; +dlgActiveUnreadColor: #5b94bf; +dlgActiveUnreadBG: white; +dlgActiveColor: white; +dlgActiveDateColor: #d3e2ee; +dlgActiveUnreadMutedBG: dlgActiveDateColor; + +topBarHeight: 54px; +topBarBG: white; +topBarDuration: 200; +topBarForwardPadding: margins(17px, 8px, 39px, 8px); +topBarForwardAlpha: 0.6; +topBarForwardImg: sprite(31px, 104px, 9px, 16px); +topBarBackwardImg: sprite(22px, 104px, 9px, 16px); +topBarBackPadding: margins(15px, 7px, 9px, 7px); +topBarBackAlpha: 0.8; +topBarBackImg: sprite(65px, 112px, 9px, 16px); +topBarBackColor: #005faf; +topBarBackFont: font(16px); +topBarSearch: iconedButton(btnDefIconed) { + bgColor: transparent; + overBgColor: transparent; + + icon: sprite(84px, 374px, 18px, 18px); + iconPos: point(13px, 18px); + downIcon: sprite(84px, 374px, 18px, 18px); + downIconPos: point(13px, 18px); + + opacity: 0.22; + overOpacity: 0.36; + + width: 44px; + height: topBarHeight; +} +topBarMinPadding: 5px; +topBarButton: flatButton(btnDefFlat) { + color: btnYesColor; + overColor: btnYesHover; + downColor: btnYesHover; + + bgColor: white; + overBgColor: white; + downBgColor: white; + + width: -40px; + height: 54px; + + textTop: 19px; + overTextTop: 19px; + downTextTop: 20px; + + font: font(fsize); + overFont: font(fsize underline); +} +topBarActionButton: flatButton(btnDefNext, btnDefBig) { + textTop: 8px; + overTextTop: 8px; + downTextTop: 9px; + + font: font(fsize); + overFont: font(fsize); + width: 101px; + height: 34px; +} +topBarActionSkip: 13px; + +historyToEnd: iconedButton(btnDefIconed) { + bgColor: transparent; + overBgColor: transparent; + + icon: sprite(252px, 41px, 44px, 44px); + iconPos: point(0px, 0px); + downIcon: sprite(252px, 41px, 44px, 44px); + downIconPos: point(0px, 0px); + + width: 44px; + height: 44px; +} +historyToEndSkip: 10px; + +activeFadeInDuration: 500; +activeFadeOutDuration: 3000; + +msgRadius: 3px; + +msgMaxWidth: 430px; +msgFont: font(fsize); +msgNameFont: semiboldFont; +msgServiceFont: semiboldFont; +msgServiceNameFont: semiboldFont; +msgServicePhotoWidth: 100px; +msgDateFont: font(13px); +msgMinWidth: 190px; +msgPhotoSize: 33px; +msgPhotoSkip: 40px; +msgPadding: margins(13px, 7px, 13px, 8px); +msgMargin: margins(13px, 10px, 53px, 2px); +msgMarginTopAttached: 3px; +msgLnkPadding: 2px; // for media open / save links +msgBorder: #f0f0f0; +msgInBg: #fff; +msgInBgSelected: #c2dcf2; // #358cd4 with 30% opacity +msgOutBg: #effdde; +msgOutBgSelected: #b7dbdb; +msgSelectOverlay: #358cd44c; +msgStickerOverlay: #358cd47f; +msgInServiceFg: #0e7acd; +msgInServiceFgSelected: #0e7acd; +msgOutServiceFg: #3a8e26; +msgOutServiceFgSelected: #367570; +msgShadow: 2px; +msgInShadow: #748ea229; +msgInShadowSelected: #548dbb29; +msgOutShadow: #3ac34740; +msgOutShadowSelected: #37a78e40; +msgInDateFg: #a0acb6; +msgInDateFgSelected: #6a9cc5; +msgOutDateFg: #6cc264; +msgOutDateFgSelected: #50a79c; + +msgReplyPadding: margins(6px, 6px, 11px, 6px); +msgReplyBarPos: point(1px, 0px); +msgReplyBarSize: size(2px, 36px); +msgReplyBarSkip: 10px; +msgOutReplyBarColor: #5dc452; +msgInReplyBarColor: #2fa9e2; +msgOutReplyBarSelColor: #4da79f; +msgInReplyBarSelColor: #2fa9e2; + +msgBotKbDuration: 200; +msgBotKbFont: semiboldFont; +msgBotKbOverOpacity: 0.1; +msgBotKbIconPadding: 2px; +msgBotKbUrlIcon: sprite(188px, 338px, 10px, 10px); +//msgBotKbRequestPhoneIcon: msgBotKbUrlIcon; +//msgBotKbRequestLocationIcon: msgBotKbUrlIcon; +msgBotKbSwitchPmIcon: sprite(188px, 348px, 10px, 10px); +msgBotKbButton: botKeyboardButton { + margin: 5px; + padding: 10px; + height: 36px; + textTop: 8px; + downTextTop: 9px; +} + +msgServiceBg: #89a0b47f; +msgServiceSelectBg: #bbc8d4a2; +msgServiceColor: #FFF; +msgServicePadding: margins(12px, 3px, 12px, 4px); +msgServiceMargin: margins(10px, 10px, 80px, 2px); + +msgColor: #000; +msgDateColor: #000; +msgLinkColor: #2a6dc2; +msgPressedLinkColor: #004bad; +msgSkip: 40px; +msgPtr: 8px; +msgBG: ":/gui/art/bg.jpg"; +msgBG0: ":/gui/art/bg0.png"; + +msgCheckPos: point(3px, 1px); +msgSendingImg: sprite(260px, 20px, 20px, 20px); +msgCheckImg: sprite(320px, 0px, 20px, 20px); +msgDblCheckImg: sprite(300px, 0px, 20px, 20px); +msgSelectCheckImg: sprite(162px, 0px, 20px, 20px); +msgSelectDblCheckImg: sprite(142px, 0px, 20px, 20px); +msgViewsPos: point(0px, -4px); +msgViewsImg: sprite(104px, 48px, 16px, 11px); +msgSelectViewsImg: sprite(104px, 70px, 16px, 11px); +msgOutViewsImg: sprite(104px, 81px, 16px, 11px); +msgSelectOutViewsImg: sprite(104px, 92px, 16px, 11px); +msgSendingViewsImg: sprite(104px, 103px, 16px, 11px); +msgSendingOutViewsImg: sprite(104px, 125px, 16px, 11px); +msgInvSendingImg: sprite(320px, 65px, 20px, 20px); +msgInvCheckImg: sprite(280px, 20px, 20px, 20px); +msgInvDblCheckImg: sprite(300px, 65px, 20px, 20px); +msgInvViewsImg: sprite(104px, 59px, 16px, 11px); +msgInvSendingViewsImg: sprite(104px, 114px, 16px, 11px); + +msgDateSpace: 19px; +msgDateCheckSpace: 4px; +msgDateViewsSpace: 11px; +msgDateDelta: point(2px, 5px); + +msgDateImgDelta: 4px; +msgDateImgColor: #fff; +msgDateImgBg: #00000054; +msgDateImgBgOver: #00000074; +msgDateImgBgSelected: #1c4a7187; +msgDateImgPadding: point(8px, 2px); +msgDateImgCheckSpace: 4px; + +msgDogImg: sprite(216px, 92px, 126px, 126px); +historyPadding: 10px; + +collapseButton: flatButton(btnDefFlat) { + font: msgServiceFont; + overFont: msgServiceFont; + width: -24px; + bgColor: transparent; + downBgColor: transparent; + overBgColor: transparent; + color: white; + overColor: white; + downColor: white; + textTop: 3px; + overTextTop: 3px; + downTextTop: 3px; + height: 25px; +} +collapseHideDuration: 200; +collapseShowDuration: 200; + +defaultTextStyle: textStyle { + linkFlags: font(fsize); + linkFlagsOver: font(fsize underline); + linkFg: btnYesColor; + linkFgDown: btnYesHover; + monoFg: #777; + selectBg: msgInBgSelected; + selectOverlay: msgSelectOverlay; + lineHeight: 0px; +} +boxTextStyle: textStyle(defaultTextStyle) { + lineHeight: 22px; +} +serviceTextStyle: textStyle(defaultTextStyle) { + linkFlags: msgServiceFont; + linkFlagsOver: font(fsize semibold underline); + linkFg: msgServiceColor; + linkFgDown: msgServiceColor; + monoFg: msgServiceColor; + selectBg: msgServiceSelectBg; + selectOverlay: msgServiceSelectBg; +} +inTextStyle: textStyle(defaultTextStyle) { + monoFg: #4e7391; + selectBg: msgInBgSelected; + selectOverlay: msgSelectOverlay; +} +outTextStyle: textStyle(defaultTextStyle) { + monoFg: #469165; + selectBg: msgOutBgSelected; + selectOverlay: msgSelectOverlay; +} +inFwdTextStyle: textStyle(defaultTextStyle) { + linkFlags: semiboldFont; + linkFlagsOver: semiboldFont; + linkFg: msgInServiceFg; + linkFgDown: msgInServiceFg; +} +outFwdTextStyle: textStyle(inFwdTextStyle) { + linkFg: msgOutServiceFg; + linkFgDown: msgOutServiceFg; +} +inFwdTextStyleSelected: textStyle(inFwdTextStyle) { + linkFg: msgInServiceFgSelected; + linkFgDown: msgInServiceFgSelected; +} +outFwdTextStyleSelected: textStyle(inFwdTextStyle) { + linkFg: msgOutServiceFgSelected; + linkFgDown: msgOutServiceFgSelected; +} +medviewSaveAsTextStyle: textStyle(defaultTextStyle) { + linkFg: #91d9ff; + linkFgDown: #91d9ff; +} + +dlgTextStyle: textStyle(defaultTextStyle) { + linkFg: dlgSystemColor; + linkFgDown: dlgSystemColor; + linkFlagsOver: font(fsize); +} +dlgActiveTextStyle: textStyle(defaultTextStyle) { + linkFg: dlgActiveColor; + linkFgDown: dlgActiveColor; + linkFlagsOver: font(fsize); +} +introLabelTextStyle: textStyle(defaultTextStyle) { + lineHeight: 30px; +} +introErrLabelTextStyle: textStyle(defaultTextStyle) { + lineHeight: 27px; +} + +mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px); +mediaCaptionSkip: 5px; +mediaHeaderSkip: 5px; +mediaThumbSize: 48px; +mediaNameTop: 3px; +mediaDetailsShift: 3px; +mediaInFg: msgInDateFg; +mediaInFgSelected: msgInDateFgSelected; +mediaOutFg: msgOutDateFg; +mediaOutFgSelected: msgOutDateFgSelected; +mediaInUnreadFg: #999; +mediaInUnreadFgSelected: #7b95aa; +mediaOutUnreadFg: #6aad60; +mediaOutUnreadFgSelected: #5aa382; +mediaUnreadSize: 7px; +mediaUnreadSkip: 5px; +mediaUnreadTop: 6px; + +mediaInStyle: textStyle(defaultTextStyle) { + linkFg: mediaInFg; + linkFgDown: mediaInFg; +} +mediaInStyleSelected: textStyle(defaultTextStyle) { + linkFg: mediaInFgSelected; + linkFgDown: mediaInFgSelected; +} + +msgFileRedColor: #e47272; +msgFileYellowColor: #efc274; +msgFileGreenColor: #61b96e; +msgFileBlueColor: #72b1df; +msgFileRedDark: #cd5b5e; +msgFileYellowDark: #e6a561; +msgFileGreenDark: #4da859; +msgFileBlueDark: #5c9ece; +msgFileRedOver: #c35154; +msgFileYellowOver: #dc9c5a; +msgFileGreenOver: #44a050; +msgFileBlueOver: #5294c4; +msgFileRedSelected: #9f6a82; +msgFileYellowSelected: #b19d84; +msgFileGreenSelected: #46a07e; +msgFileBlueSelected: #5099d0; + +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; +msgFileThumbLinkInFg: #3da5e0; +msgFileThumbLinkInFgSelected: #3da5e0; +msgFileThumbLinkOutFg: #5eba5b; +msgFileThumbLinkOutFgSelected: #31a298; +msgFileNameTop: 16px; +msgFileStatusTop: 37px; +msgFileMinWidth: 294px; +msgFileInBg: #59b6eb; +msgFileInBgOver: #4eade3; +msgFileInBgSelected: #51a3d3; +msgFileOutBg: #78c67f; +msgFileOutBgOver: #6bc272; +msgFileOutBgSelected: #5fb389; + +msgFileOutImage: sprite(0px, 146px, 18px, 18px); +msgFileOutImageSelected: sprite(18px, 146px, 18px, 18px); +msgFileInImage: sprite(0px, 164px, 18px, 18px); +msgFileInImageSelected: sprite(18px, 164px, 18px, 18px); +msgFileOutFile: sprite(36px, 146px, 18px, 18px); +msgFileOutFileSelected: sprite(54px, 146px, 18px, 18px); +msgFileInFile: sprite(36px, 164px, 18px, 18px); +msgFileInFileSelected: sprite(54px, 164px, 18px, 18px); +msgFileOutDownload: sprite(72px, 142px, 14px, 20px); +msgFileOutDownloadSelected: sprite(86px, 142px, 14px, 20px); +msgFileInDownload: sprite(72px, 162px, 14px, 20px); +msgFileInDownloadSelected: sprite(86px, 162px, 14px, 20px); +msgFileOutCancel: sprite(100px, 147px, 16px, 16px); +msgFileOutCancelSelected: sprite(116px, 147px, 16px, 16px); +msgFileInCancel: sprite(100px, 165px, 16px, 16px); +msgFileInCancelSelected: sprite(116px, 165px, 16px, 16px); +msgFileOutPause: sprite(132px, 147px, 14px, 16px); +msgFileOutPauseSelected: sprite(146px, 147px, 14px, 16px); +msgFileInPause: sprite(132px, 165px, 14px, 16px); +msgFileInPauseSelected: sprite(146px, 165px, 14px, 16px); +msgFileOutPlay: sprite(160px, 146px, 20px, 18px); +msgFileOutPlaySelected: sprite(180px, 146px, 20px, 18px); +msgFileInPlay: sprite(160px, 164px, 20px, 18px); +msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); + +msgFileRed: sprite(0px, 425px, 20px, 20px); +msgFileYellow: sprite(20px, 425px, 20px, 20px); +msgFileGreen: sprite(40px, 425px, 20px, 20px); +msgFileBlue: sprite(60px, 425px, 20px, 20px); + +msgFileOverDuration: 200; +msgFileRadialLine: 3px; + +msgVideoSize: size(320px, 240px); + +msgWaveformBar: 2px; +msgWaveformSkip: 1px; +msgWaveformMin: 2px; +msgWaveformMax: 20px; +msgWaveformInActive: #59b6eb; +msgWaveformInActiveSelected: #51a3d3; +msgWaveformInInactive: #d4dee6; +msgWaveformInInactiveSelected: #9cc1e1; +msgWaveformOutActive: #78c67f; +msgWaveformOutActiveSelected: #6badad; +msgWaveformOutInactive: #b3e2b4; +msgWaveformOutInactiveSelected: #91c3c3; + +sendPadding: 9px; +btnSend: flatButton(btnDefFlat) { + color: btnYesColor; + overColor: btnYesHover; + downColor: btnYesHover; + + bgColor: white; + overBgColor: btnWhiteHover; + downBgColor: btnWhiteHover; + + width: -32px; + height: 46px; + + textTop: 12px; + overTextTop: 12px; + downTextTop: 13px; + + font: font(16px); + overFont: font(16px); +} +btnUnblock: flatButton(btnSend) { + color: #d15948; + overColor: #d15948; + downColor: #db6352; +} + +btnAttachDocument: iconedButton(btnDefIconed) { + icon: sprite(218px, 68px, 24px, 24px); + iconPos: point(11px, 11px); + downIcon: sprite(218px, 68px, 24px, 24px); + downIconPos: point(11px, 12px); + + overBgColor: btnWhiteHover; + width: 46px; + height: 46px; +} +btnAttachPhoto: iconedButton(btnAttachDocument) { + icon: sprite(118px, 0px, 24px, 24px); + downIcon: sprite(118px, 0px, 24px, 24px); +} +btnAttachEmoji: iconedButton(btnAttachDocument) { + overBgColor: white; + icon: sprite(374px, 344px, 21px, 22px); + iconPos: point(6px, 12px); + downIcon: sprite(374px, 344px, 21px, 22px); + downIconPos: point(6px, 12px); + + width: 33px; +} +emojiCircle: size(19px, 19px); +emojiCirclePeriod: 1500; +emojiCircleDuration: 500; +emojiCircleTop: 13px; +emojiCircleLine: 2px; +emojiCircleFg: #b9b9b9; +emojiCirclePart: 3.5; +btnBotKbShow: iconedButton(btnAttachEmoji) { + icon: sprite(375px, 74px, 21px, 21px); + iconPos: point(6px, 12px); + downIcon: sprite(375px, 74px, 21px, 21px); + downIconPos: point(6px, 12px); +} +btnBotCmdStart: iconedButton(btnAttachEmoji) { + icon: sprite(354px, 74px, 21px, 21px); + iconPos: point(6px, 12px); + downIcon: sprite(354px, 74px, 21px, 21px); + downIconPos: point(6px, 12px); +} +btnBotKbHide: iconedButton(btnAttachEmoji) { + icon: sprite(373px, 95px, 23px, 14px); + iconPos: point(5px, 17px); + downIcon: sprite(373px, 95px, 23px, 14px); + downIconPos: point(5px, 17px); +} +broadcastToggle: flatCheckbox { + textColor: black; + bgColor: white; + disColor: black; + + width: 34px; + height: 46px; + duration: 200; + bgFunc: transition(easeOutCirc); + cursor: cursor(pointer); + + font: normalFont; + + imageRect: sprite(18px, 125px, 22px, 21px); + chkImageRect: sprite(40px, 125px, 22px, 21px); + overImageRect: sprite(40px, 104px, 22px, 21px); + chkOverImageRect: sprite(40px, 125px, 22px, 21px); + disImageRect: sprite(18px, 125px, 22px, 21px); + chkDisImageRect: sprite(18px, 125px, 22px, 21px); + + imagePos: point(6px, 12px); +} +silentToggle: flatCheckbox(broadcastToggle) { + width: 33px; + + imageRect: sprite(354px, 242px, 21px, 21px); + chkImageRect: sprite(354px, 221px, 21px, 21px); + overImageRect: sprite(375px, 242px, 21px, 21px); + chkOverImageRect: sprite(375px, 221px, 21px, 21px); + disImageRect: sprite(354px, 242px, 21px, 21px); + chkDisImageRect: sprite(354px, 221px, 21px, 21px); +} +btnRecordAudio: sprite(379px, 390px, 16px, 24px); +btnRecordAudioActive: sprite(379px, 366px, 16px, 24px); +recordSignalColor: #f17077; +recordSignalMin: 5px; +recordSignalMax: 12px; +recordCancel: #aaa; +recordCancelActive: #ec6466; +recordFont: font(13px); +recordTextTop: 14px; + +replySkip: 51px; +replyColor: #377aae; +replyHeight: 49px; +replyTop: 8px; +replyBottom: 6px; +replyIconPos: point(13px, 13px); +replyIcon: sprite(343px, 197px, 24px, 24px); +editIcon: sprite(371px, 286px, 24px, 24px); +replyCancel: iconedButton(btnDefIconed) { + icon: sprite(165px, 24px, 14px, 14px); + iconPos: point(17px, 17px); + downIcon: sprite(165px, 24px, 14px, 14px); + downIconPos: point(17px, 18px); + bgColor: white; + overBgColor: white; + width: 49px; + height: 49px; +} +inlineBotCancel: iconedButton(replyCancel) { + height: 46px; + iconPos: point(-1px, 16px); // < 0 means draw in the center of the button + downIconPos: point(-1px, 17px); +} +forwardIcon: sprite(368px, 197px, 24px, 24px); + +historyScroll: flatScroll(scrollDef) { + barColor: #89a0b47a; + bgColor: #89a0b44c; + barOverColor: #89a0b4bc; + bgOverColor: #89a0b46b; + + round: 0px; + + width: 12px; + deltax: 3px; + deltat: 3px; + deltab: 3px; + + topsh: 0px; + bottomsh: -1px; +} +textRectMargins: margins(-2px, -1px, -2px, -1px); +taMsgField: flatTextarea(taDefFlat) { + font: msgFont; +} +maxFieldHeight: 220px; +// historyMinHeight: 56px; + +reportSpamHide: flatButton(topBarButton) { + height: 46px; + + textTop: 15px; + overTextTop: 15px; + downTextTop: 16px; + + bgColor: transparent; + overBgColor: transparent; + downBgColor: transparent; +} +reportSpamButton: flatButton(reportSpamHide) { + textTop: 6px; + overTextTop: 6px; + downTextTop: 7px; + + width: -50px; + height: 30px; + + bgColor: #888; + overBgColor: #7b7b7b; + downBgColor: #7b7b7b; +} +reportSpamSeparator: 30px; +reportSpamBg: #fffffff0; + +newMsgSound: ":/gui/art/newmsg.wav"; + +unreadBarHeight: 32px; +unreadBarMargin: 8px; +unreadBarFont: semiboldFont; +unreadBarBG: #fcfbfa; +unreadBarBorder: shadowColor; +unreadBarColor: #538bb4; + +searchedBarHeight: unreadBarHeight; +searchedBarFont: unreadBarFont; +searchedBarBG: #ebeef1; +searchedBarBorder: unreadBarBorder; +searchedBarColor: #a2aeb7; + +layerSlideDuration: 200; +layerHideDuration: 200; +layerPadding: margins(10px, 10px, 10px, 10px); + +contactPadding: margins(49px, 22px, 0px, 6px); +contactSkip: 13px; +contactPhoneSkip: 30px; +contactUserIcon: sprite(120px, 90px, 18px, 18px); +contactPhoneIcon: sprite(138px, 90px, 18px, 18px); +contactIconTop: 10px; + +contactsPhotoSize: 42px; +contactsPadding: margins(16px, 7px, 16px, 7px); +contactsNameTop: 2px; +contactsNameFont: semiboldFont; +contactsStatusTop: 23px; +contactsStatusFont: font(fsize); +contactsStatusFg: #999999; +contactsStatusFgOver: #7c99b2; +contactsStatusFgOnline: #3b8dcc; +contactsBgOver: overBg; +contactsBgActive: #6f9cbd; +contactsCheckPosition: point(8px, 16px); +contactsCheckIcon: sprite(187px, 61px, 18px, 14px); +contactsCheckActiveIcon: sprite(187px, 75px, 18px, 14px); +contactsNewItemHeight: 53px; +contactsNewItemIcon: sprite(307px, 248px, 22px, 16px); +contactsNewItemIconPosition: point(29px, 19px); +contactsNewItemTop: 18px; +contactsNewItemFg: #4b82af; +contactsAboutBg: #f7f7f7; +contactsAboutShadow: #0000001F; +contactsAdminCheckbox: Checkbox(defaultCheckbox) { + font: semiboldFont; + textBg: #f7f7f7; + textPosition: point(34px, 1px); +} +contactsAboutHeight: 42px; +contactsAboutTop: 9px; +contactsScroll: flatScroll(boxScroll) { + deltab: 0px; +} + +btnNewGroup: iconedButton(btnDefIconed) { + icon: sprite(189px, 118px, 18px, 17px); + iconPos: point(8px, 8px); + downIcon: sprite(189px, 118px, 18px, 17px); + downIconPos: point(8px, 9px); + + bgColor: transparent; + overBgColor: transparent; + width: 36px; + height: 36px; +} +btnAddContact: iconedButton(btnNewGroup) { + icon: sprite(188px, 93px, 18px, 18px); + downIcon: sprite(188px, 93px, 18px, 18px); +} +btnCancelSearch: iconedButton(btnNewGroup) { + icon: sprite(188px, 43px, 18px, 18px); + downIcon: sprite(188px, 43px, 18px, 18px); +} + +notifyBG: white; +notifyBorder: #f1f1f1; +notifyBorderWidth: 1px; +notifySlowHide: 4000; +notifyPhotoSize: 62px; +notifyMacPhotoSize: 64px; +notifyPhotoPos: point(9px, 9px); +notifyClosePos: point(1px, 2px); +notifyClose: iconedButton(btnDefIconed) { + icon: sprite(167px, 130px, 10px, 10px); + iconPos: point(10px, 10px); + downIcon: sprite(167px, 130px, 10px, 10px); + downIconPos: point(10px, 11px); + + width: 30px; + height: 30px; +} +notifyItemTop: 12px; +notifyTextLeft: 12px; +notifyTextTop: 7px; +notifySlowHideFunc: transition(easeInCirc); +notifyWaitShortHide: 0; +notifyWaitLongHide: 20000; +notifyFastAnim: 150; +notifyWidth: 316px; +notifyHeight: 80px; +notifyDeltaX: 6px; +notifyDeltaY: 7px; + +boxPhotoPadding: margins(28px, 28px, 28px, 18px); +boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); +boxPhotoTextFg: #808080; +cropPointSize: 10px; +cropSkip: 13px; +cropMinSize: 20px; +confirmCaptionArea: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 56px; +} +confirmBg: #f2f2f2; +confirmMaxHeight: 245px; +confirmCompressedSkip: 10px; + +profileMaxWidth: 410px; +profilePadding: margins(28px, 30px, 28px, 0px); +profilePhotoSize: 120px; +profileNameLeft: 21px; +profileNameTop: -1px; +profileNameFont: font(20px); +profileStatusLeft: 22px; +profileStatusTop: 31px; +profileStatusFont: font(fsize); +profilePhoneLeft: 22px; +profilePhoneTop: 62px; +profilePhoneFont: font(16px); +profileButtonTop: 18px; +profileButtonSkip: 10px; +profileHeaderFont: font(20px); +profileHeaderColor: black; +profileHeaderSkip: 59px; +profileHeaderLeft: -1px; +profileHeaderTop: 22px; + +profileListPhotoSize: 46px; +profileListPadding: size(12px, 6px); +profileListNameTop: 8px; +profileListStatusBottom: 6px; +profileHoverBG: #f5f5f5; +profileActiveBG: #6294b9; +profileSubFont: font(fsize); +profileListNameFont: semiboldFont; +profileListNameColor: #000; +profileOnlineColor: titleTypingColor; +profileOfflineColor: titleStatusColor; +btnShareContact: flatButton(btnDefNext, btnDefBig) { + width: 145px; + height: 42px; + + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; + + font: font(17px); + overFont: font(17px); +} +btnMigrateToMega: flatButton(btnShareContact) { + width: -40px; +} +profileMinBtnPadding: 10px; + +membersPadding: margins(0px, 10px, 0px, 10px); + +forwardMargins: margins(30px, 10px, 30px, 10px); +forwardFont: font(16px); +forwardBg: rgba(0, 0, 0, 76); +btnProfileCancel: flatButton(btnDefFlat, btnDefBig) { + color: #666d78; + overColor: #666d78; + downColor: #50565e; + + bgColor: rgba(0, 0, 0, 63); + overBgColor: rgba(0, 0, 0, 47); + downBgColor: rgba(0, 0, 0, 95); + + width: 145px; + height: 40px; + + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; + + font: font(18px); + overFont: font(18px); +} + +btnDeleteContact: flatButton(btnDefFlat, btnDefBig) { + color: #fff; + overColor: #fff; + downColor: #ffcbc1; + + bgColor: #ee4928bf; + overBgColor: #ee4928; + downBgColor: #d14024; + + width: 300px; + height: 40px; + + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; + + font: font(18px); + overFont: font(18px); +} + +profileNameInput: flatInput(setNameInput) { + width: 230px; +} + +participantInnerAdd: flatButton(btnDefNext) { + width: 145px; + height: 40px; + font: font(18px); + overFont: font(18px); + textTop: 9px; + overTextTop: 9px; + downTextTop: 10px; +} +participantInnerCancel: flatButton(participantInnerAdd, btnDefBack) { +} +participantCancel: flatButton(participantInnerAdd, btnDefBack) { + width: 300px; +} +participantFilter: flatInput(inpDefFlat) { + width: 364px; + height: 52px; + font: font(16px); + textMrg: margins(39px, 11px, 10px, 10px); + imgRect: sprite(227px, 21px, 24px, 24px); + imgPos: point(10px, 15px); +} +participantDelta: 12px; + +contactsFilter: flatInput(dlgFilter) { + width: 340px; + height: 38px; + textMrg: margins(34px, 3px, 5px, 4px); + imgPos: point(6px, 7px); +} +inpCountry: flatInput(contactsFilter) { +} + +newGroupLimitFg: #a4a4a4; +newGroupAboutFg: #808080; +newGroupPadding: margins(4px, 6px, 4px, 3px); +newGroupSkip: 17px; +newGroupInfoPadding: margins(0px, -4px, 0px, 1px); + +newGroupLinkPadding: margins(4px, 27px, 4px, 12px); +newGroupLinkTop: 3px; +newGroupLinkFont: font(16px); + +newGroupPhotoSize: 76px; +newGroupPhotoBg: #4eb5f0; +newGroupPhotoBgOver: #3fa9e7; +newGroupPhotoIcon: sprite(74px, 104px, 30px, 27px); +newGroupPhotoIconPosition: point(23px, 25px); + +newGroupNamePosition: point(27px, 20px); + +newGroupDescriptionPadding: margins(0px, 23px, 0px, 14px); +newGroupDescription: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 115px; +} + +newGroupPublicLinkPadding: margins(0px, 20px, 0px, 5px); +newGroupLinkFadeDuration: 5000; + +connectionHostInputField: InputField(defaultInputField) { + width: 160px; +} +connectionPortInputField: InputField(defaultInputField) { + width: 55px; +} +connectionUserInputField: InputField(defaultInputField) { + width: 95px; +} +connectionPasswordInputField: InputField(defaultInputField) { + width: 120px; +} +connectionIPv6Skip: 11px; + +contactsAdd: flatButton(topBarButton) { + width: -40px; + height: 52px; + + textTop: 18px; + overTextTop: 18px; + downTextTop: 19px; +} + +aboutIcon: sprite(0px, 0px, 104px, 104px); +aboutWidth: 390px; +aboutVersionTop: -3px; +aboutVersionLink: linkButton(btnDefLink) { + color: #999; + overColor: #999; + downColor: #999; +} +aboutTextTop: 34px; +aboutSkip: 14px; +aboutLabel: flatLabel(labelDefFlat) { + font: normalFont; + width: 330px; + align: align(topleft); +} +aboutTextStyle: textStyle(defaultTextStyle) { + lineHeight: 22px; +} + +emojiTextFont: font(15px); +emojiReplaceWidth: 52px; +emojiReplaceHeight: 56px; +emojiReplaceInnerHeight: 42px; +emojiReplacePadding: 14px; + +connectingBG: #fffe; +connectingColor: #777; +connectingPadding: margins(5px, 5px, 5px, 5px); + +dropdownDef: dropdown { + border: 1px; + borderColor: #ebebeb; + + padding: margins(10px, 10px, 10px, 10px); + shadow: sprite(241px, 46px, 6px, 6px); + shadowShift: 1px; + + duration: 150; + width: 0px; +} + +dropdownAttachDocument: iconedButton(btnAttachDocument) { + iconPos: point(14px, 13px); + downIconPos: point(14px, 14px); + + width: 172px; + height: 49px; + + color: black; + + font: font(16px); + + textPos: point(50px, 13px); + downTextPos: point(50px, 14px); +} +dropdownAttachPhoto: iconedButton(dropdownAttachDocument) { + icon: sprite(118px, 0px, 24px, 24px); + downIcon: sprite(118px, 0px, 24px, 24px); +} +dropdownMediaPhotos: iconedButton(dropdownAttachPhoto) { + width: 200px; +} +dropdownMediaVideos: iconedButton(dropdownMediaPhotos) { + icon: sprite(92px, 348px, 24px, 24px); + downIcon: sprite(92px, 348px, 24px, 24px); +} +dropdownMediaSongs: iconedButton(dropdownMediaPhotos) { + icon: sprite(60px, 374px, 24px, 26px); + downIcon: sprite(60px, 374px, 24px, 26px); + iconPos: point(12px, 12px); + downIconPos: point(12px, 13px); +} +dropdownMediaDocuments: iconedButton(dropdownAttachDocument) { + width: 200px; +} +dropdownMediaAudios: iconedButton(dropdownMediaDocuments) { + icon: sprite(62px, 348px, 24px, 24px); + downIcon: sprite(62px, 348px, 24px, 24px); +} +dropdownMediaLinks: iconedButton(dropdownMediaDocuments) { + icon: sprite(372px, 414px, 24px, 24px); + downIcon: sprite(372px, 414px, 24px, 24px); +} + +dragFont: font(28px semibold); +dragSubfont: font(20px semibold); +dragColor: #777; +dragDropColor: btnYesColor; + +dragMargin: margins(0px, 10px, 0px, 10px); +dragPadding: margins(20px, 10px, 20px, 10px); + +dragHeight: 72px; + +dpiSlider: slider { + color: #ccc; + thikness: 2px; + + width: 260px; + bar: sprite(0px, 104px, 9px, 22px); +} +dpiActive: black; +dpiInactive: #999; +dpiFont1: linkFont; +dpiFont2: linkFont; +dpiFont3: linkFont; +dpiFont4: linkFont; + +stickersMaxHeight: 440px; +stickersPadding: margins(19px, 17px, 19px, 17px); +stickersSize: size(64px, 64px); +stickersScroll: flatScroll(boxScroll) { + deltax: 7px; + deltat: 23px; + deltab: 9px; +} +stickersReorderPadding: margins(0px, 12px, 0px, 12px); +stickersReorderFg: #777; +stickersRowDisabledOpacity: 0.4; +stickersRowDuration: 200; + +emojiScroll: flatScroll(solidScroll) { + deltat: 48px; +} +emojiRecentOver: sprite(0px, 196px, 21px, 22px); +emojiRecentActive: sprite(245px, 264px, 21px, 22px); +emojiPeopleOver: sprite(21px, 196px, 21px, 22px); +emojiPeopleActive: sprite(266px, 264px, 21px, 22px); +emojiNatureOver: sprite(42px, 196px, 21px, 22px); +emojiNatureActive: sprite(245px, 286px, 21px, 22px); +emojiFoodOver: sprite(63px, 196px, 21px, 22px); +emojiFoodActive: sprite(266px, 286px, 21px, 22px); +emojiActivityOver: sprite(126px, 196px, 21px, 22px); +emojiActivityActive: sprite(287px, 264px, 21px, 22px); +emojiTravelOver: sprite(105px, 196px, 21px, 22px); +emojiTravelActive: sprite(308px, 286px, 21px, 22px); +emojiObjectsOver: sprite(147px, 196px, 21px, 22px); +emojiObjectsActive: sprite(308px, 264px, 21px, 22px); +emojiSymbolsOver: sprite(84px, 196px, 21px, 22px); +emojiSymbolsActive: sprite(287px, 286px, 21px, 22px); +stickersSettings: sprite(140px, 124px, 21px, 22px); +savedGifsOver: sprite(329px, 286px, 21px, 22px); +savedGifsActive: sprite(350px, 286px, 21px, 22px); + +emojiPanCategories: #f7f7f7; + +rbEmoji: flatCheckbox { + textColor: transparent; + bgColor: emojiPanCategories; + disColor: emojiPanCategories; + + width: 42px; + height: 46px; + + textTop: 0px; + textLeft: 0px; + font: font(fsize); + duration: 200; + bgFunc: transition(easeOutCirc); + cursor: cursor(pointer); + + disabledCursor: cursor(default); + imagePos: point(11px, 12px); +} +rbEmojiRecent: flatCheckbox(rbEmoji) { + imageRect: emojiRecentOver; + chkImageRect: emojiRecentActive; + overImageRect: emojiRecentOver; + chkOverImageRect: emojiRecentActive; + disImageRect: emojiRecentOver; + chkDisImageRect: emojiRecentActive; +} +rbEmojiPeople: flatCheckbox(rbEmoji) { + imageRect: emojiPeopleOver; + chkImageRect: emojiPeopleActive; + overImageRect: emojiPeopleOver; + chkOverImageRect: emojiPeopleActive; + disImageRect: emojiPeopleOver; + chkDisImageRect: emojiPeopleActive; +} +rbEmojiNature: flatCheckbox(rbEmoji) { + imageRect: emojiNatureOver; + chkImageRect: emojiNatureActive; + overImageRect: emojiNatureOver; + chkOverImageRect: emojiNatureActive; + disImageRect: emojiNatureOver; + chkDisImageRect: emojiNatureActive; +} +rbEmojiFood: flatCheckbox(rbEmoji) { + imageRect: emojiFoodOver; + chkImageRect: emojiFoodActive; + overImageRect: emojiFoodOver; + chkOverImageRect: emojiFoodActive; + disImageRect: emojiFoodOver; + chkDisImageRect: emojiFoodActive; +} +rbEmojiActivity: flatCheckbox(rbEmoji) { + imageRect: emojiActivityOver; + chkImageRect: emojiActivityActive; + overImageRect: emojiActivityOver; + chkOverImageRect: emojiActivityActive; + disImageRect: emojiActivityOver; + chkDisImageRect: emojiActivityActive; +} +rbEmojiTravel: flatCheckbox(rbEmoji) { + imageRect: emojiTravelOver; + chkImageRect: emojiTravelActive; + overImageRect: emojiTravelOver; + chkOverImageRect: emojiTravelActive; + disImageRect: emojiTravelOver; + chkDisImageRect: emojiTravelActive; +} +rbEmojiObjects: flatCheckbox(rbEmoji) { + imageRect: emojiObjectsOver; + chkImageRect: emojiObjectsActive; + overImageRect: emojiObjectsOver; + chkOverImageRect: emojiObjectsActive; + disImageRect: emojiObjectsOver; + chkDisImageRect: emojiObjectsActive; +} +rbEmojiSymbols: flatCheckbox(rbEmoji) { + imageRect: emojiSymbolsOver; + chkImageRect: emojiSymbolsActive; + overImageRect: emojiSymbolsOver; + chkOverImageRect: emojiSymbolsActive; + disImageRect: emojiSymbolsOver; + chkDisImageRect: emojiSymbolsActive; +} +emojiPanPadding: 12px; +emojiPanSize: size(45px, 41px); +emojiPanWidth: 345px; +emojiPanMaxHeight: 366px; +emojiPanDuration: 200; +emojiPanHover: #f0f4f7; + +emojiPanHeader: 42px; +emojiPanHeaderFont: semiboldFont; +emojiPanHeaderColor: #999; +emojiPanHeaderLeft: 22px; +emojiPanHeaderTop: 12px; +emojiPanHeaderBg: #fffffff2; + +emojiColorsPadding: 5px; +emojiColorsSep: 1px; +emojiColorsSepColor: #d5d5d5; + +emojiSwitchSkip: 27px; +emojiSwitchImgSkip: 21px; +emojiSwitchStickers: sprite(318px, 328px, 8px, 12px); +emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px); +emojiSwitchColor: #42a8db; + +stickerPanSize: size(64px, 64px); +stickerPanPadding: 11px; +stickerPanDelete: sprite(128px, 132px, 12px, 12px); +stickerPanDeleteOpacity: 0.5; +stickerIconPadding: 5px; +stickerIconOpacity: 0.7; +stickerIconSel: 2px; +stickerIconSelColor: #58b2ed; +stickerIconLeft: sprite(342px, 72px, 40px, 1px); +stickerIconRight: sprite(342px, 73px, 40px, 1px); +stickerIconMove: 400; +stickerPreviewDuration: 150; +stickerPreviewBg: #FFFFFFB0; +stickerPreviewMin: 0.1; + +verifiedCheckProfile: sprite(285px, 235px, 18px, 18px); +verifiedCheckProfilePos: point(7px, 6px); +verifiedCheck: sprite(285px, 221px, 14px, 14px); +verifiedCheckInv: sprite(299px, 221px, 14px, 14px); +verifiedCheckPos: point(4px, 2px); + +botKbDuration: 200; +botKbBg: #edf1f5; +botKbOverBg: #d8e2ec; +botKbDownBg: #d8e2ec; +botKbColor: #4b565f; +botKbFont: font(15px semibold); +botKbButton: botKeyboardButton { + margin: 10px; + padding: 10px; + height: 38px; + textTop: 9px; + downTextTop: 9px; +} +botKbTinyButton: botKeyboardButton { + margin: 4px; + padding: 3px; + height: 25px; + textTop: 2px; + downTextTop: 2px; +} +botKbScroll: flatScroll(solidScroll) { + deltax: 3px; + width: 10px; +} +switchPmButton: BoxButton(defaultBoxButton) { + width: 320px; + height: 34px; + textTop: 7px; +} + +minPhotoSize: 100px; +maxMediaSize: 420px; +maxStickerSize: 256px; +maxGifSize: 320px; +maxSignatureSize: 144px; + +mvBgColor: #222; +mvBgOpacity: 0.92; +mvThickFont: semiboldFont; +mvFont: font(fsize); + +mvTextLeft: 16px; +mvTextSkip: 10px; +mvHeaderTop: 48px; +mvTextTop: 24px; +mvTextColor: white; +mvTextOpacity: 0.5; +mvTextOverOpacity: 1; + +mvIconOpacity: 0.45; +mvIconOverOpacity: 1; +mvControlBgColor: black; +mvControlBgOpacity: 0.3; +mvControlMargin: 0px; +mvControlSize: 90px; +mvIconSize: size(60px, 56px); + +mvLeft: sprite(320px, 445px, 12px, 22px); +mvRight: sprite(332px, 445px, 12px, 22px); +mvClose: sprite(344px, 445px, 18px, 18px); +mvSave: sprite(362px, 445px, 14px, 19px); +mvMore: sprite(376px, 445px, 5px, 21px); + +mvDropdown: dropdown(dropdownDef) { + shadow: sprite(0px, 0px, 0px, 0px); + padding: margins(11px, 12px, 11px, 12px); + + border: 0px; + width: 182px; +} +mvButton: iconedButton(btnDefIconed) { + bgColor: #383838; + overBgColor: #505050; + font: font(fsize); + + opacity: 1.; + overOpacity: 1.; + + width: -32px; + height: 36px; + + color: white; + + textPos: point(16px, 9px); + downTextPos: point(16px, 10px); + + duration: 0; +} +mvPopupMenu: PopupMenu(defaultPopupMenu) { + shadow: sprite(0px, 0px, 0px, 0px); + + itemBg: #383838; + itemBgOver: #505050; + itemFg: white; + itemFgOver: white; + itemFgDisabled: #999; + itemFgShortcut: #eee; + itemFgShortcutOver: #fff; + itemFgShortcutDisabled: #999; + + separatorFg: #484848; +} +mvContextButton: iconedButton(mvButton) { + bgColor: #383838E6; + overBgColor: #505050E7; +} +mvWaitHide: 2000; +mvHideDuration: 1000; +mvShowDuration: 200; +mvFadeDuration: 150; + +mvDocPadding: 18px; +mvDocSize: size(340px, 116px); +mvDocBg: white; +mvDocNameTop: 4px; +mvDocNameFont: font(semibold 14px); +mvDocNameColor: black; +mvDocSizeTop: 29px; +mvDocSizeColor: #808080; +mvDocExtTop: 35px; +mvDocExtFont: font(semibold 18px); +mvDocExtColor: white; +mvDocExtPadding: 10px; +mvDocLinksTop: 57px; +mvDocRed: sprite(0px, 400px, 25px, 25px); +mvDocYellow: sprite(25px, 400px, 25px, 25px); +mvDocGreen: sprite(50px, 400px, 25px, 25px); +mvDocBlue: sprite(75px, 400px, 25px, 25px); +mvDocIconSize: 80px; + +mvDocLink: linkButton(btnDefLink) { + color: #4595d3; + overColor: #4595d3; + downColor: #4595d3; +} + +mvDeltaFromLastAction: 5px; +mvSwipeDistance: 80px; + +mvCaptionPadding: margins(18px, 10px, 18px, 10px); +mvCaptionMargin: size(11px, 11px); +mvCaptionRadius: 2px; +mvCaptionBg: #11111180; +mvCaptionFont: font(fsize); + +medviewSaveMsgCheck: sprite(311px, 309px, 22px, 18px); +medviewSaveMsgFont: font(16px); +medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); +medviewSaveMsgCheckPos: point(23px, 21px); +medviewSaveMsgShowing: 200; +medviewSaveMsgShown: 2000; +medviewSaveMsgHiding: 2500; +medviewSaveMsg: #000000b2; + +mvTransparentBrush: sprite(9px, 124px, 8px, 8px); + +overviewPhotoSkip: 10px; +overviewPhotoBg: #F1F1F1; +overviewPhotoMinSize: minPhotoSize; +overviewPhotoCheck: sprite(245px, 308px, 32px, 32px); +overviewPhotoChecked: sprite(278px, 308px, 32px, 32px); +overviewPhotoSelectOverlay: #0a7bb03f; + +overviewFilePadding: margins(0px, 3px, 16px, 3px); +overviewFileSize: 70px; +overviewFileNameTop: 7px; +overviewFileStatusTop: 27px; +overviewFileDateTop: 49px; +overviewFileChecked: #2fa9e2; +overviewFileCheck: #00000066; +overviewFileExtPadding: 5px; +overviewFileExtTop: 24px; +overviewFileExtFont: font(18px semibold); + +// Mac specific + +macAccessoryWidth: 450.; +macAccessoryHeight: 90.; +macEnableFilterAdd: 2; +macEnableFilterTop: 5; +macSelectorTop: 6; +macAlwaysThisAppTop: 4; +macAppHintTop: 8; +macCautionIconSize: 16; + +btnContext: iconedButton(btnDefIconed) { + bgColor: white; + overBgColor: btnWhiteHover; + font: font(14px); + + opacity: 1.; + overOpacity: 1.; + + width: -32px; + height: 36px; + + color: black; + + textPos: point(16px, 7px); + downTextPos: point(16px, 8px); +} + +photoLoader: size(52px, 22px); +photoLoaderBg: #00000054; +photoLoaderCnt: 3; +photoLoaderPoint: size(6px, 6px); +photoLoaderSkip: 6px; +photoLoaderPeriod: 600; // ms full period +photoLoaderDelta: 150; // ms between points +photoLoaderDuration1: 150; // ms fade in +photoLoaderDuration2: 150; // ms fade out +photoLoaderAlphaMin: 0.1; // not less than that + +radialSize: size(50px, 50px); +radialLine: 2px; +radialDuration: 350; +radialPeriod: 3000; +radialBgOpacity: 0.4; +radialDownload: sprite(346px, 0px, 50px, 50px); +radialDownloadOpacity: 0.8; +radialCancel: sprite(378px, 50px, 18px, 18px); +radialCancelOpacity: 1.0; + +overviewLoader: size(34px, 14px); +overviewLoaderPoint: size(4px, 4px); +overviewLoaderSkip: 4px; + +mediaviewLoader: size(78px, 33px); +mediaviewLoaderPoint: size(9px, 9px); +mediaviewLoaderSkip: 9px; + +downloadPathSkip: 10px; + +usernamePadding: margins(23px, 22px, 21px, 12px); +usernameSkip: 49px; +usernameTextStyle: textStyle(defaultTextStyle) { + lineHeight: 20px; +} +usernameDefaultFg: #777; + +youtubeIcon: sprite(116px, 338px, 72px, 50px); +videoIcon: sprite(0px, 340px, 60px, 60px); +locationSize: size(320px, 240px); + +boxOptionListPadding: margins(2px, 20px, 2px, 2px); + +langsWidth: 256px; +langsButton: Radiobutton(defaultRadiobutton) { + width: 200px; +} + +backgroundPadding: 10px; +backgroundSize: size(108px, 193px); +backgroundScroll: flatScroll(boxScroll) { + width: 10px; + deltax: 3px; + deltat: 10px; + deltab: 0px; +} + +passcodeHeaderFont: font(19px); +passcodeHeaderHeight: 80px; +passcodeInput: flatInput(inpIntroPhone) { +} +passcodeSubmit: flatButton(btnIntroNext) { + textTop: 15px; + overTextTop: 15px; + downTextTop: 16px; + width: 225px; + font: font(19px); + overFont: font(19px); +} +passcodeSubmitSkip: 40px; +passcodePadding: margins(0px, 22px, 0px, 3px); +passcodeSkip: 31px; + +mentionHeight: 40px; +mentionScroll: flatScroll(scrollDef) { + topsh: 0px; + bottomsh: 0px; +} +mentionPadding: margins(8px, 5px, 8px, 5px); +mentionTop: 11px; +mentionFont: linkFont; +mentionPhotoSize: msgPhotoSize; +mentionBgOver: #f5f5f5; +mentionFg: #777; +mentionFgOver: #707070; +mentionFgActive: #0080c0; +mentionFgOverActive: #0077b3; + +sessionsScroll: flatScroll(boxScroll) { + deltax: 5px; + width: 14px; +} +sessionsHeight: 440px; +sessionHeight: 70px; +sessionCurrentPadding: margins(0px, 7px, 0px, 4px); +sessionCurrentHeight: 118px; +sessionPadding: margins(21px, 10px, 21px, 0px); +sessionNameFont: msgNameFont; +sessionActiveFont: msgDateFont; +sessionActiveColor: #aaa; +sessionInfoFont: msgFont; +sessionInfoColor: dlgTextColor; +sessionTerminateTop: 30px; +sessionTerminateSkip: 18px; +sessionTerminate: iconedButton(notifyClose) { + iconPos: point(3px, 3px); + downIconPos: point(3px, 4px); + width: 16px; + height: 16px; +} + +webPageLeft: 10px; +webPageBar: 2px; +webPageTitleFont: semiboldFont; +webPageDescriptionFont: normalFont; +webPagePhotoSkip: 5px; +webPagePhotoSize: 100px; +webPagePhotoDelta: 8px; + +botDescSkip: 8px; + +suppressAll: 0.2; +suppressSong: 0.05; + +playerHeight: 44px; +playerBg: #e4e9ef; +playerFg: #54748f; +playerTimeFg: #a4afba; +playerLineHeight: 3px; +playerMoverSize: size(2px, 7px); +playerLineActive: #6389a8; +playerLineInactive: #bac7d4; +playerSkip: 8px; +playerNameStyle: textStyle(defaultTextStyle) { + linkFg: #6389a8; + linkFgDown: #6389a8; + linkFlags: semiboldFont; + linkFlagsOver: semiboldFont; +} +playerPlay: sprite(377px, 109px, 19px, 22px); +playerPause: sprite(379px, 131px, 17px, 20px); +playerNext: sprite(374px, 151px, 22px, 14px); +playerPrev: sprite(374px, 165px, 22px, 14px); +playerClose: sprite(361px, 97px, 12px, 12px); +playerFull: sprite(365px, 109px, 12px, 12px); +playerRepeat: sprite(365px, 121px, 12px, 14px); +playerVolume: sprite(352px, 179px, 44px, 12px); +playerInactiveOpacity: 0.8; +playerUnavailableOpacity: 0.3; +playerDuration: 200; + +playlistHoverBg: #f2f2f2; +playlistPadding: 10px; + +linksSearchMargin: margins(20px, 20px, 20px, 0px); +linksMaxWidth: 520px; +linksLetterFont: font(24px); +linksMargin: margins(0px, 7px, 0px, 5px); +linksTextTop: 6px; +linksBorder: 1px; +linksBorderFg: #eaeaea; +linksDateColor: #808080; +linksDateMargin: margins(0px, 15px, 0px, 2px); + +linksPhotoCheck: sprite(184px, 196px, 16px, 16px); +linksPhotoChecked: sprite(168px, 196px, 16px, 16px); + +inlineResultsLeft: 11px; +inlineResultsSkip: 3px; +inlineMediaHeight: 96px; +inlineThumbSize: 64px; +inlineThumbSkip: 10px; +inlineDescriptionFg: #8a8a8a; +inlineRowMargin: 6px; +inlineRowBorder: linksBorder; +inlineRowBorderFg: linksBorderFg; +inlineRowFileNameTop: 2px; +inlineRowFileDescriptionTop: 23px; +inlineResultsMinWidth: 64px; +inlineDurationMargin: 3px; + +editTextArea: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 256px; +} + +toastFont: normalFont; +toastMaxWidth: 480px; +toastMinMargin: 13px; +toastBg: medviewSaveMsg; +toastFg: #FFF; +toastPadding: margins(19px, 13px, 19px, 12px); +toastFadeInDuration: 200; +toastFadeOutDuration: 1000; + +infoButton: PeerAvatarButton { + size: topBarHeight; + photoSize: 42px; +} diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style new file mode 100644 index 000000000..750a52cea --- /dev/null +++ b/Telegram/Resources/basic_types.style @@ -0,0 +1,413 @@ +/* +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 +*/ + +textStyle { + linkFlags: font; + linkFlagsOver: font; + linkFg: color; + linkFgDown: color; + monoFg: color; + selectBg: color; + selectOverlay: color; + lineHeight: pixels; +} + +linkButton { + color: color; + overColor: color; + downColor: color; + font: font; + overFont: font; +} + +sysButton { + size: size; + img: sprite; + color: color; + overColor: color; + duration: int; +} + +flatButton { + color: color; + overColor: color; + downColor: color; + + bgColor: color; + overBgColor: color; + downBgColor: color; + + width: pixels; + height: pixels; + + textTop: pixels; + overTextTop: pixels; + downTextTop: pixels; + + font: font; + overFont: font; + duration: int; + cursor: cursor; +} + +iconedButton { + icon: sprite; + iconPos: point; + downIcon: sprite; + downIconPos: point; + + color: color; + bgColor: color; + overBgColor: color; + width: pixels; + height: pixels; + font: font; + + opacity: double; + overOpacity: double; + + textPos: point; + downTextPos: point; + + duration: int; + cursor: cursor; +} + +flatCheckbox { + textColor: color; + bgColor: color; + disColor: color; + + width: pixels; + height: pixels; + textTop: pixels; + textLeft: pixels; + font: font; + duration: int; + bgFunc: transition; + cursor: cursor; + + disabledCursor: cursor; + + imageRect: sprite; + chkImageRect: sprite; + overImageRect: sprite; + chkOverImageRect: sprite; + disImageRect: sprite; + chkDisImageRect: sprite; + + imagePos: point; +} + +flatInput { + textColor: color; + bgColor: color; + bgActive: color; + width: pixels; + height: pixels; + textMrg: margins; + align: align; + font: font; + cursor: cursor; + + imgRect: sprite; + imgPos: point; + + borderWidth: pixels; + borderColor: color; + borderActive: color; + borderError: color; + + phColor: color; + phFocusColor: color; + phPos: point; + phAlign: align; + phShift: pixels; + phDuration: int; + phLeftFunc: transition; + phAlphaFunc: transition; + phColorFunc: transition; +} + +flatTextarea { + textColor: color; + bgColor: color; + width: pixels; + textMrg: margins; + align: align; + font: font; + cursor: cursor; + + phColor: color; + phFocusColor: color; + phPos: point; + phAlign: align; + phShift: pixels; + phDuration: int; + phLeftFunc: transition; + phAlphaFunc: transition; + phColorFunc: transition; +} + +flatScroll { + barColor: color; + bgColor: color; + barOverColor: color; + bgOverColor: color; + + round: pixels; + + width: pixels; + minHeight: pixels; + deltax: pixels; + deltat: pixels; + deltab: pixels; + + topsh: pixels; + bottomsh: pixels; + shColor: color; + + duration: int; + hiding: int; +} + +countryInput { + width: pixels; + height: pixels; + top: pixels; + bgColor: color; + ptrSize: size; + textMrg: margins; + font: font; + align: align; +} + +slider { + color: color; + thikness: pixels; + + width: pixels; + bar: sprite; +} + +flatLabel { + font: font; + minWidth: pixels; + width: pixels; + align: align; +} + +switcher { + border: pixels; + borderColor: color; + + bgColor: color; + bgHovered: color; + bgActive: color; + + height: pixels; + + font: font; + textColor: color; + activeColor: color; + + duration: int; +} + +dropdown { + border: pixels; + borderColor: color; + + padding: margins; + shadow: sprite; + shadowShift: pixels; + + duration: int; + width: pixels; +} + +PopupMenu { + skip: pixels; + + shadow: sprite; + shadowShift: pixels; + + itemBg: color; + itemBgOver: color; + itemFg: color; + itemFgOver: color; + itemFgDisabled: color; + itemFgShortcut: color; + itemFgShortcutOver: color; + itemFgShortcutDisabled: color; + itemPadding: margins; + itemFont: font; + + separatorPadding: margins; + separatorWidth: pixels; + separatorFg: color; + + arrow: sprite; + + duration: int; + + widthMin: pixels; + widthMax: pixels; +} + +Tooltip { + textBg: color; + textFg: color; + textFont: font; + textBorder: color; + textPadding: margins; + + shift: point; + skip: pixels; + + widthMax: pixels; + linesMax: int; +} + +botKeyboardButton { + margin: pixels; + padding: pixels; + height: pixels; + textTop: pixels; + downTextTop: pixels; +} + +BoxButton { + textFg: color; + textFgOver: color; + textBg: color; // rect of textBg with rounded rect of textBgOver upon it + textBgOver: color; + + width: pixels; + height: pixels; + + textTop: pixels; + + font: font; + duration: int; +} + +Checkbox { + textFg: color; + textBg: color; + + checkFg: color; + checkFgOver: color; + checkFgActive: color; + + width: pixels; + height: pixels; + + textPosition: point; + diameter: pixels; + thickness: pixels; + checkIcon: sprite; + + font: font; + duration: int; +} + +Radiobutton { + textFg: color; + textBg: color; + + checkFg: color; + checkFgOver: color; + checkFgActive: color; + + width: pixels; + height: pixels; + + textPosition: point; + diameter: pixels; + thickness: pixels; + checkSkip: pixels; + + font: font; + duration: int; +} + +InputArea { + textFg: color; + textMargins: margins; + + placeholderFg: color; + placeholderFgActive: color; + placeholderMargins: margins; + placeholderAlign: align; + placeholderShift: pixels; + + duration: int; + + borderFg: color; + borderFgActive: color; + borderFgError: color; + + border: pixels; + borderActive: pixels; + borderError: pixels; + + font: font; + + width: pixels; + heightMin: pixels; + heightMax: pixels; +} + +InputField { + textFg: color; + textMargins: margins; + textAlign: align; + + placeholderFg: color; + placeholderFgActive: color; + placeholderMargins: margins; + placeholderAlign: align; + placeholderShift: pixels; + + duration: int; + + borderFg: color; + borderFgActive: color; + borderFgError: color; + + border: pixels; + borderActive: pixels; + borderError: pixels; + + font: font; + + width: pixels; + height: pixels; + + iconSprite: sprite; + iconPosition: point; +} + +PeerAvatarButton { + size: pixels; + photoSize: pixels; +} diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index f63bb4db3..003796f0c 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -18,10 +18,8 @@ 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 */ -using "Resources/style_classes.txt"; - -defaultFontFamily: "Open Sans"; -semibold: "Open Sans Semibold"; +defaultFontFamily: 'Open Sans'; +semibold: 'Open Sans Semibold'; fsize: 13px; normalFont: font(fsize); @@ -385,7 +383,7 @@ btnDefIconed: iconedButton { font: font(fsize); opacity: 0.78; - overOpacity: 1.; + overOpacity: 1; textPos: point(0px, 0px); downTextPos: point(0px, 0px); @@ -433,7 +431,7 @@ titleBackButton: iconedButton(btnDefIconed) { width: -30px; height: 39px; - opacity: 1.; + opacity: 1; cursor: cursor(default); textPos: point(23px, 10px); @@ -1117,8 +1115,8 @@ msgLinkColor: #2a6dc2; msgPressedLinkColor: #004bad; msgSkip: 40px; msgPtr: 8px; -msgBG: ":/gui/art/bg.jpg"; -msgBG0: ":/gui/art/bg0.png"; +msgBG: ':/gui/art/bg.jpg'; +msgBG0: ':/gui/art/bg0.png'; msgCheckPos: point(3px, 1px); msgSendingImg: sprite(260px, 20px, 20px, 20px); @@ -1549,7 +1547,7 @@ reportSpamButton: flatButton(reportSpamHide) { reportSpamSeparator: 30px; reportSpamBg: #fffffff0; -newMsgSound: ":/gui/art/newmsg.wav"; +newMsgSound: ':/gui/art/newmsg.wav'; unreadBarHeight: 32px; unreadBarMargin: 8px; @@ -2189,7 +2187,7 @@ mvDropdown: dropdown(dropdownDef) { shadow: sprite(0px, 0px, 0px, 0px); padding: margins(11px, 12px, 11px, 12px); - border: 0px; + border: 0; width: 182px; } mvButton: iconedButton(btnDefIconed) { @@ -2197,8 +2195,8 @@ mvButton: iconedButton(btnDefIconed) { overBgColor: #505050; font: font(fsize); - opacity: 1.; - overOpacity: 1.; + opacity: 1; + overOpacity: 1; width: -32px; height: 36px; @@ -2298,8 +2296,8 @@ overviewFileExtFont: font(18px semibold); // Mac specific -macAccessoryWidth: 450.; -macAccessoryHeight: 90.; +macAccessoryWidth: 450; +macAccessoryHeight: 90; macEnableFilterAdd: 2; macEnableFilterTop: 5; macSelectorTop: 6; @@ -2312,8 +2310,8 @@ btnContext: iconedButton(btnDefIconed) { overBgColor: btnWhiteHover; font: font(14px); - opacity: 1.; - overOpacity: 1.; + opacity: 1; + overOpacity: 1; width: -32px; height: 36px; @@ -2400,8 +2398,8 @@ passcodeSkip: 31px; mentionHeight: 40px; mentionScroll: flatScroll(scrollDef) { - topsh: 0px; - bottomsh: 0px; + topsh: 0; + bottomsh: 0; } mentionPadding: margins(8px, 5px, 8px, 5px); mentionTop: 11px; @@ -2421,7 +2419,7 @@ sessionsHeight: 440px; sessionHeight: 70px; sessionCurrentPadding: margins(0px, 7px, 0px, 4px); sessionCurrentHeight: 118px; -sessionPadding: margins(21px, 10px, 21px, 0px); +sessionPadding: margins(21px, 10px, 21px, 0); sessionNameFont: msgNameFont; sessionActiveFont: msgDateFont; sessionActiveColor: #aaa; diff --git a/Telegram/Resources/style_classes.txt b/Telegram/Resources/style_classes.txt index 27079bf5b..a3405a048 100644 --- a/Telegram/Resources/style_classes.txt +++ b/Telegram/Resources/style_classes.txt @@ -26,7 +26,7 @@ textStyle { monoFg: color; selectBg: color; selectOverlay: color; - lineHeight: pixels; + lineHeight: number; } linkButton { @@ -42,7 +42,7 @@ sysButton { img: sprite; color: color; overColor: color; - duration: int; + duration: number; } flatButton { @@ -54,16 +54,16 @@ flatButton { overBgColor: color; downBgColor: color; - width: pixels; - height: pixels; + width: number; + height: number; - textTop: pixels; - overTextTop: pixels; - downTextTop: pixels; + textTop: number; + overTextTop: number; + downTextTop: number; font: font; overFont: font; - duration: int; + duration: number; cursor: cursor; } @@ -76,17 +76,17 @@ iconedButton { color: color; bgColor: color; overBgColor: color; - width: pixels; - height: pixels; + width: number; + height: number; font: font; - opacity: double; - overOpacity: double; + opacity: number; + overOpacity: number; textPos: point; downTextPos: point; - duration: int; + duration: number; cursor: cursor; } @@ -95,12 +95,12 @@ flatCheckbox { bgColor: color; disColor: color; - width: pixels; - height: pixels; - textTop: pixels; - textLeft: pixels; + width: number; + height: number; + textTop: number; + textLeft: number; font: font; - duration: int; + duration: number; bgFunc: transition; cursor: cursor; @@ -120,8 +120,8 @@ flatInput { textColor: color; bgColor: color; bgActive: color; - width: pixels; - height: pixels; + width: number; + height: number; textMrg: margins; align: align; font: font; @@ -130,7 +130,7 @@ flatInput { imgRect: sprite; imgPos: point; - borderWidth: pixels; + borderWidth: number; borderColor: color; borderActive: color; borderError: color; @@ -139,8 +139,8 @@ flatInput { phFocusColor: color; phPos: point; phAlign: align; - phShift: pixels; - phDuration: int; + phShift: number; + phDuration: number; phLeftFunc: transition; phAlphaFunc: transition; phColorFunc: transition; @@ -149,7 +149,7 @@ flatInput { flatTextarea { textColor: color; bgColor: color; - width: pixels; + width: number; textMrg: margins; align: align; font: font; @@ -159,8 +159,8 @@ flatTextarea { phFocusColor: color; phPos: point; phAlign: align; - phShift: pixels; - phDuration: int; + phShift: number; + phDuration: number; phLeftFunc: transition; phAlphaFunc: transition; phColorFunc: transition; @@ -172,26 +172,26 @@ flatScroll { barOverColor: color; bgOverColor: color; - round: pixels; + round: number; - width: pixels; - minHeight: pixels; - deltax: pixels; - deltat: pixels; - deltab: pixels; + width: number; + minHeight: number; + deltax: number; + deltat: number; + deltab: number; - topsh: pixels; - bottomsh: pixels; + topsh: number; + bottomsh: number; shColor: color; - duration: int; - hiding: int; + duration: number; + hiding: number; } countryInput { - width: pixels; - height: pixels; - top: pixels; + width: number; + height: number; + top: number; bgColor: color; ptrSize: size; textMrg: margins; @@ -201,53 +201,53 @@ countryInput { slider { color: color; - thikness: pixels; + thikness: number; - width: pixels; + width: number; bar: sprite; } flatLabel { font: font; - minWidth: pixels; - width: pixels; + minWidth: number; + width: number; align: align; } switcher { - border: pixels; + border: number; borderColor: color; bgColor: color; bgHovered: color; bgActive: color; - height: pixels; + height: number; font: font; textColor: color; activeColor: color; - duration: int; + duration: number; } dropdown { - border: pixels; + border: number; borderColor: color; padding: margins; shadow: sprite; - shadowShift: pixels; + shadowShift: number; - duration: int; - width: pixels; + duration: number; + width: number; } PopupMenu { - skip: pixels; + skip: number; shadow: sprite; - shadowShift: pixels; + shadowShift: number; itemBg: color; itemBgOver: color; @@ -261,15 +261,15 @@ PopupMenu { itemFont: font; separatorPadding: margins; - separatorWidth: pixels; + separatorWidth: number; separatorFg: color; arrow: sprite; - duration: int; + duration: number; - widthMin: pixels; - widthMax: pixels; + widthMin: number; + widthMax: number; } Tooltip { @@ -280,18 +280,18 @@ Tooltip { textPadding: margins; shift: point; - skip: pixels; + skip: number; - widthMax: pixels; - linesMax: int; + widthMax: number; + linesMax: number; } botKeyboardButton { - margin: pixels; - padding: pixels; - height: pixels; - textTop: pixels; - downTextTop: pixels; + margin: number; + padding: number; + height: number; + textTop: number; + downTextTop: number; } BoxButton { @@ -300,13 +300,13 @@ BoxButton { textBg: color; // rect of textBg with rounded rect of textBgOver upon it textBgOver: color; - width: pixels; - height: pixels; + width: number; + height: number; - textTop: pixels; + textTop: number; font: font; - duration: int; + duration: number; } Checkbox { @@ -317,16 +317,16 @@ Checkbox { checkFgOver: color; checkFgActive: color; - width: pixels; - height: pixels; + width: number; + height: number; textPosition: point; - diameter: pixels; - thickness: pixels; + diameter: number; + thickness: number; checkIcon: sprite; font: font; - duration: int; + duration: number; } Radiobutton { @@ -337,16 +337,16 @@ Radiobutton { checkFgOver: color; checkFgActive: color; - width: pixels; - height: pixels; + width: number; + height: number; textPosition: point; - diameter: pixels; - thickness: pixels; - checkSkip: pixels; + diameter: number; + thickness: number; + checkSkip: number; font: font; - duration: int; + duration: number; } InputArea { @@ -357,23 +357,23 @@ InputArea { placeholderFgActive: color; placeholderMargins: margins; placeholderAlign: align; - placeholderShift: pixels; + placeholderShift: number; - duration: int; + duration: number; borderFg: color; borderFgActive: color; borderFgError: color; - border: pixels; - borderActive: pixels; - borderError: pixels; + border: number; + borderActive: number; + borderError: number; font: font; - width: pixels; - heightMin: pixels; - heightMax: pixels; + width: number; + heightMin: number; + heightMax: number; } InputField { @@ -385,28 +385,28 @@ InputField { placeholderFgActive: color; placeholderMargins: margins; placeholderAlign: align; - placeholderShift: pixels; + placeholderShift: number; - duration: int; + duration: number; borderFg: color; borderFgActive: color; borderFgError: color; - border: pixels; - borderActive: pixels; - borderError: pixels; + border: number; + borderActive: number; + borderError: number; font: font; - width: pixels; - height: pixels; + width: number; + height: number; iconSprite: sprite; iconPosition: point; } PeerAvatarButton { - size: pixels; - photoSize: pixels; + size: number; + photoSize: number; } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index d91437298..c61401d69 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -82,7 +82,7 @@ namespace { HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0; - QPixmap *sprite = 0, *emoji = 0, *emojiLarge = 0; + QPixmap *emoji = 0, *emojiLarge = 0; style::font monofont; struct CornersPixmaps { @@ -1993,23 +1993,6 @@ namespace { if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family(); ::monofont = style::font(st::normalFont->f.pixelSize(), 0, family); } - if (!::sprite) { - QString spriteFilePostfix; - if (cRetina() || cScale() == dbisTwo) { - spriteFilePostfix = qsl("_200x"); - } else if (cScale() == dbisOneAndQuarter) { - spriteFilePostfix = qsl("_125x"); - } else if (cScale() == dbisOneAndHalf) { - spriteFilePostfix = qsl("_150x"); - } - QString spriteFile = qsl(":/gui/art/sprite") + spriteFilePostfix + qsl(".png"); - if (rtl()) { - ::sprite = new QPixmap(QPixmap::fromImage(QImage(spriteFile).mirrored(true, false))); - } else { - ::sprite = new QPixmap(spriteFile); - } - if (cRetina()) ::sprite->setDevicePixelRatio(cRetinaFactor()); - } emojiInit(); if (!::emoji) { ::emoji = new QPixmap(QLatin1String(EName)); @@ -2070,8 +2053,6 @@ namespace { void deinitMedia() { audioFinish(); - delete ::sprite; - ::sprite = 0; delete ::emoji; ::emoji = 0; delete ::emojiLarge; @@ -2149,7 +2130,7 @@ namespace { } const QPixmap &sprite() { - return *::sprite; + return style::spritePixmap(); } const QPixmap &emoji() { diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h index 400fc9051..d33e06ee4 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h @@ -36,7 +36,7 @@ class LogStream; // Interface for reading a cleaned from comments file by basic tokens. class BasicTokenizedFile { public: - BasicTokenizedFile(const QString &filepath); + explicit BasicTokenizedFile(const QString &filepath); BasicTokenizedFile(const BasicTokenizedFile &other) = delete; BasicTokenizedFile &operator=(const BasicTokenizedFile &other) = delete; diff --git a/Telegram/SourceFiles/codegen/common/checked_utf8_string.h b/Telegram/SourceFiles/codegen/common/checked_utf8_string.h index 5ce217067..69441f838 100644 --- a/Telegram/SourceFiles/codegen/common/checked_utf8_string.h +++ b/Telegram/SourceFiles/codegen/common/checked_utf8_string.h @@ -36,9 +36,9 @@ public: CheckedUtf8String(const CheckedUtf8String &other) = default; CheckedUtf8String &operator=(const CheckedUtf8String &other) = default; - CheckedUtf8String(const char *string, int size = -1); - CheckedUtf8String(const QByteArray &string); - CheckedUtf8String(const ConstUtf8String &string); + explicit CheckedUtf8String(const char *string, int size = -1); + explicit CheckedUtf8String(const QByteArray &string); + explicit CheckedUtf8String(const ConstUtf8String &string); bool isValid() const { return valid_; diff --git a/Telegram/SourceFiles/codegen/common/clean_file.h b/Telegram/SourceFiles/codegen/common/clean_file.h index 80d4f73e3..82edf5a79 100644 --- a/Telegram/SourceFiles/codegen/common/clean_file.h +++ b/Telegram/SourceFiles/codegen/common/clean_file.h @@ -32,7 +32,7 @@ namespace common { // Reads a file removing all C-style comments. class CleanFile { public: - CleanFile(const QString &filepath); + explicit CleanFile(const QString &filepath); CleanFile(const CleanFile &other) = delete; CleanFile &operator=(const CleanFile &other) = delete; diff --git a/Telegram/SourceFiles/codegen/common/const_utf8_string.h b/Telegram/SourceFiles/codegen/common/const_utf8_string.h index 18f0849c8..d9b3ac03a 100644 --- a/Telegram/SourceFiles/codegen/common/const_utf8_string.h +++ b/Telegram/SourceFiles/codegen/common/const_utf8_string.h @@ -31,7 +31,7 @@ namespace common { // Not null-terminated! It does not hold any ownership. class ConstUtf8String { public: - ConstUtf8String(const char *string, int size = -1) : string_(string) { + explicit ConstUtf8String(const char *string, int size = -1) : string_(string) { if (size < 0) { size = strlen(string); } diff --git a/Telegram/SourceFiles/codegen/common/cpp_file.cpp b/Telegram/SourceFiles/codegen/common/cpp_file.cpp new file mode 100644 index 000000000..14d4d87f6 --- /dev/null +++ b/Telegram/SourceFiles/codegen/common/cpp_file.cpp @@ -0,0 +1,135 @@ +/* +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 "codegen/common/cpp_file.h" + +#include +#include + +namespace codegen { +namespace common { +namespace { + +void writeLicense(QTextStream &stream, const ProjectInfo &project) { + stream << "\ +/*\n\ +WARNING! All changes made in this file will be lost!\n\ +Created from '" << project.source << "' by '" << project.name << "'\n\ +\n\ +This file is part of Telegram Desktop,\n\ +the official desktop version of Telegram messaging app, see https://telegram.org\n\ +\n\ +Telegram Desktop is free software: you can redistribute it and/or modify\n\ +it under the terms of the GNU General Public License as published by\n\ +the Free Software Foundation, either version 3 of the License, or\n\ +(at your option) any later version.\n\ +\n\ +It is distributed in the hope that it will be useful,\n\ +but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ +GNU General Public License for more details.\n\ +\n\ +In addition, as a special exception, the copyright holders give permission\n\ +to link the code of portions of this program with the OpenSSL library.\n\ +\n\ +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\ +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\ +*/\n"; +} + +} // namespace + +CppFile::CppFile(const QString &path, const ProjectInfo &project) +: stream_(&content_) +, forceReGenerate_(project.forceReGenerate) { + bool cpp = path.toLower().endsWith(".cpp"); + + QFileInfo info(path); + info.dir().mkpath("."); + filepath_ = info.absoluteFilePath(); + + writeLicense(stream_, project); + if (cpp) { + if (!project.precompiledHeader.isEmpty()) { + include(project.precompiledHeader); + } + include(info.baseName() + ".h").newline(); + } else { + stream() << "#pragma once"; + newline().newline(); + } +} + +CppFile &CppFile::include(const QString &header) { + stream() << "#include \"" << header << "\""; + return newline(); +} + +CppFile &CppFile::pushNamespace(const QString &name) { + namespaces_.push_back(name); + + stream() << "namespace"; + if (!name.isEmpty()) { + stream() << ' ' << name; + } + stream() << " {"; + return newline(); +} + +CppFile &CppFile::popNamespace() { + if (namespaces_.isEmpty()) { + return *this; + } + auto name = namespaces_.back(); + namespaces_.pop_back(); + + stream() << "} // namespace"; + if (!name.isEmpty()) { + stream() << ' ' << name; + } + return newline(); +} + +bool CppFile::finalize() { + while (!namespaces_.isEmpty()) { + popNamespace(); + } + stream_.flush(); + + QFile file(filepath_); + if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) { + if (file.readAll() == content_) { + file.close(); + return true; + } + file.close(); + } + + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + if (file.write(content_) != content_.size()) { + return false; + } + return true; +} + +} // namespace common +} // namespace codegen \ No newline at end of file diff --git a/Telegram/SourceFiles/codegen/common/cpp_file.h b/Telegram/SourceFiles/codegen/common/cpp_file.h new file mode 100644 index 000000000..31691103a --- /dev/null +++ b/Telegram/SourceFiles/codegen/common/cpp_file.h @@ -0,0 +1,70 @@ +/* +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 + +#include +#include +#include + +namespace codegen { +namespace common { + +struct ProjectInfo { + QString name; + QString source; + QString precompiledHeader; + bool forceReGenerate; +}; + +// Creates a file with license header and codegen warning. +class CppFile { +public: + // If "basepath" is empty the folder containing "path" will be chosen. + // File ending with .cpp will be treated as source, otherwise like header. + CppFile(const QString &path, const ProjectInfo &project); + + QTextStream &stream() { + return stream_; + } + + CppFile &newline() { + stream() << "\n"; + return *this; + } + CppFile &include(const QString &header); + + // Empty name adds anonymous namespace. + CppFile &pushNamespace(const QString &name = QString()); + CppFile &popNamespace(); + + bool finalize(); + +private: + QString filepath_; + QByteArray content_; + QTextStream stream_; + QVector namespaces_; + bool forceReGenerate_; + +}; + +} // namespace common +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/generator.cpp b/Telegram/SourceFiles/codegen/numbers/generator.cpp new file mode 100644 index 000000000..e80f48297 --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/generator.cpp @@ -0,0 +1,54 @@ +/* +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 "codegen/numbers/generator.h" + +#include +#include +#include + +namespace codegen { +namespace numbers { +namespace { + +} // namespace + +Generator::Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project) +: rules_(rules) +, basePath_(destBasePath) +, project_(project) { +} + +bool Generator::writeHeader() { + header_ = std::make_unique(basePath_ + ".h", project_); + + header_->stream() << "QVector phoneNumberParse(const QString &number);\n"; + + return header_->finalize(); +} + +bool Generator::writeSource() { + source_ = std::make_unique(basePath_ + ".cpp", project_); + + return source_->finalize(); +} + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/generator.h b/Telegram/SourceFiles/codegen/numbers/generator.h new file mode 100644 index 000000000..f7bff73e9 --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/generator.h @@ -0,0 +1,50 @@ +/* +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 + +#include +#include +#include +#include "codegen/common/cpp_file.h" +#include "codegen/numbers/parsed_file.h" + +namespace codegen { +namespace numbers { + +class Generator { +public: + Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project); + Generator(const Generator &other) = delete; + Generator &operator=(const Generator &other) = delete; + + bool writeHeader(); + bool writeSource(); + +private: + const Rules &rules_; + QString basePath_; + const common::ProjectInfo &project_; + std::unique_ptr source_, header_; + +}; + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/main.cpp b/Telegram/SourceFiles/codegen/numbers/main.cpp new file mode 100644 index 000000000..ac7cddc0c --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/main.cpp @@ -0,0 +1,36 @@ +/* +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 + +#include "codegen/numbers/options.h" +#include "codegen/numbers/processor.h" + +int main(int argc, char *argv[]) { + QCoreApplication app(argc, argv); + + auto options = codegen::numbers::parseOptions(); + if (options.inputPath.isEmpty()) { + return -1; + } + + codegen::numbers::Processor processor(options); + return processor.launch(); +} diff --git a/Telegram/SourceFiles/codegen/numbers/options.cpp b/Telegram/SourceFiles/codegen/numbers/options.cpp new file mode 100644 index 000000000..abf3e776b --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/options.cpp @@ -0,0 +1,74 @@ +/* +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 "codegen/numbers/options.h" + +#include +#include +#include "codegen/common/logging.h" + +namespace codegen { +namespace numbers { +namespace { + +constexpr int kErrorOutputPathExpected = 902; +constexpr int kErrorInputPathExpected = 903; +constexpr int kErrorSingleInputPathExpected = 904; + +} // namespace + +using common::logError; + +Options parseOptions() { + Options result; + auto args(QCoreApplication::instance()->arguments()); + for (int i = 1, count = args.size(); i < count; ++i) { // skip first + const auto &arg(args.at(i)); + + // Output path + if (arg == "-o") { + if (++i == count) { + logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; + return Options(); + } else { + result.outputPath = args.at(i); + } + } else if (arg.startsWith("-o")) { + result.outputPath = arg.mid(2); + + // Input path + } else { + if (result.inputPath.isEmpty()) { + result.inputPath = arg; + } else { + logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected"; + return Options(); + } + } + } + if (result.inputPath.isEmpty()) { + logError(kErrorInputPathExpected, "Command Line") << "input path expected"; + return Options(); + } + return result; +} + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/options.h b/Telegram/SourceFiles/codegen/numbers/options.h new file mode 100644 index 000000000..b2ed1aae4 --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/options.h @@ -0,0 +1,38 @@ +/* +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 + +#include +#include + +namespace codegen { +namespace numbers { + +struct Options { + QString outputPath = "."; + QString inputPath; +}; + +// Parsing failed if inputPath is empty in the result. +Options parseOptions(); + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp b/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp new file mode 100644 index 000000000..3dc5508af --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/parsed_file.cpp @@ -0,0 +1,66 @@ +/* +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 "codegen/numbers/parsed_file.h" + +#include +#include +#include +#include +#include "codegen/common/basic_tokenized_file.h" +#include "codegen/common/logging.h" + +using BasicToken = codegen::common::BasicTokenizedFile::Token; +using BasicType = BasicToken::Type; + +namespace codegen { +namespace numbers { +namespace { + +} // namespace + +ParsedFile::ParsedFile(const Options &options) +: file_(options.inputPath) +, options_(options) { +} + +bool ParsedFile::read() { + if (!file_.read()) { + return false; + } + + auto filepath = QFileInfo(options_.inputPath).absoluteFilePath(); + do { + if (auto startToken = file_.getToken(BasicType::Name)) { + } + if (file_.atEnd()) { + break; + } + logErrorUnexpectedToken() << "numbers rule"; + } while (!failed()); + + if (failed()) { + result_.data.clear(); + } + return !failed(); +} + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/parsed_file.h b/Telegram/SourceFiles/codegen/numbers/parsed_file.h new file mode 100644 index 000000000..7f158b972 --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/parsed_file.h @@ -0,0 +1,74 @@ +/* +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 + +#include +#include +#include "codegen/common/basic_tokenized_file.h" +#include "codegen/numbers/options.h" + +namespace codegen { +namespace numbers { + +struct Rule { +}; +struct Rules { + QVector data; +}; + +// Parses an input file to the internal struct. +class ParsedFile { +public: + explicit ParsedFile(const Options &options); + ParsedFile(const ParsedFile &other) = delete; + ParsedFile &operator=(const ParsedFile &other) = delete; + + bool read(); + + Rules getResult() { + return result_; + } + +private: + + bool failed() const { + return failed_ || file_.failed(); + } + + // Log error to std::cerr with 'code' at the current position in file. + common::LogStream logError(int code) { + failed_ = true; + return file_.logError(code); + } + common::LogStream logErrorUnexpectedToken() { + failed_ = true; + return file_.logErrorUnexpectedToken(); + } + + common::BasicTokenizedFile file_; + Options options_; + bool failed_ = false; + Rules result_; + +}; + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/processor.cpp b/Telegram/SourceFiles/codegen/numbers/processor.cpp new file mode 100644 index 000000000..e7d0baf3e --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/processor.cpp @@ -0,0 +1,86 @@ +/* +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 "codegen/numbers/processor.h" + +#include +#include +#include "codegen/common/cpp_file.h" +#include "codegen/numbers/parsed_file.h" +#include "codegen/numbers/generator.h" + +namespace codegen { +namespace numbers { +namespace { + +constexpr int kErrorCantWritePath = 851; + +} // namespace + +Processor::Processor(const Options &options) +: parser_(std::make_unique(options)) +, options_(options) { +} + +int Processor::launch() { + if (!parser_->read()) { + return -1; + } + + auto result = parser_->getResult(); + if (!write(result)) { + return -1; + } + + return 0; +} + +bool Processor::write(const Rules &rules) const { + QDir dir(options_.outputPath); + if (!dir.mkpath(".")) { + common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); + return false; + } + + QFileInfo srcFile(options_.inputPath); + QString dstFilePath = dir.absolutePath() + "/numbers"; + + common::ProjectInfo project = { + "codegen_style", + srcFile.fileName(), + "stdafx.h", + false, // forceReGenerate + }; + + Generator generator(rules, dstFilePath, project); + if (!generator.writeHeader()) { + return false; + } + if (!generator.writeSource()) { + return false; + } + + return true; +} + +Processor::~Processor() = default; + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/numbers/processor.h b/Telegram/SourceFiles/codegen/numbers/processor.h new file mode 100644 index 000000000..e455e17d4 --- /dev/null +++ b/Telegram/SourceFiles/codegen/numbers/processor.h @@ -0,0 +1,53 @@ +/* +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 + +#include +#include +#include "codegen/numbers/options.h" + +namespace codegen { +namespace numbers { +class ParsedFile; +struct Rules; + +// Walks through a file, parses it and generates number formatter. +class Processor { +public: + explicit Processor(const Options &options); + Processor(const Processor &other) = delete; + Processor &operator=(const Processor &other) = delete; + + // Returns 0 on success. + int launch(); + + ~Processor(); + +private: + bool write(const Rules &rules) const; + + std::unique_ptr parser_; + const Options &options_; + +}; + +} // namespace numbers +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 031b1ae3f..6609ef572 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -20,45 +20,533 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "codegen/style/generator.h" -#include +#include +#include +#include #include "codegen/style/parsed_file.h" +using Module = codegen::style::structure::Module; +using Struct = codegen::style::structure::Struct; +using Variable = codegen::style::structure::Variable; +using Tag = codegen::style::structure::TypeTag; + namespace codegen { namespace style { namespace { -} // namespace - -Generator::Generator(const Options &options) -: parser_(std::make_unique(options)) -, options_(options) { - +char hexChar(uchar ch) { + if (ch < 10) { + return '0' + ch; + } else if (ch < 16) { + return 'a' + (ch - 10); + } + return '0'; } -int Generator::process() { - if (!parser_->read()) { - return -1; - } +char hexSecondChar(char ch) { + return hexChar((*reinterpret_cast(&ch)) << 4); +} - const auto &result = parser_->data(); - if (!write(result)) { - return -1; - } - if (options_.rebuildDependencies) { - for (auto included : result.includes) { - if (!write(included)) { - return -1; - } +char hexFirstChar(char ch) { + return hexChar((*reinterpret_cast(&ch)) & 0x0F); +} + +QString stringToEncodedString(const std::string &str) { + QString result; + result.reserve(str.size() * 4); + for (auto ch : str) { + if (ch == '\n') { + result.append("\\n"); + } else if (ch == '\t') { + result.append("\\t"); + } else if (ch == '"' || ch == '\\') { + result.append('\\').append(ch); + } else if (ch < 32 || ch > 127) { + result.append("\\x").append(hexFirstChar(ch)).append(hexSecondChar(ch)); + } else { + result.append(ch); } } - return 0; + return '"' + result + '"'; } -bool Generator::write(const structure::Module &) const { +QString pxValueName(int value) { + QString result = "px"; + if (value < 0) { + value = -value; + result += 'm'; + } + return result + QString::number(value); +} + +} // namespace + +Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project) +: module_(module) +, basePath_(destBasePath) +, baseName_(QFileInfo(basePath_).baseName()) +, project_(project) { +} + +bool Generator::writeHeader() { + header_ = std::make_unique(basePath_ + ".h", project_); + + header_->include("ui/style_core.h").newline(); + + if (!writeHeaderStyleNamespace()) { + return false; + } + if (!writeRefsDeclarations()) { + return false; + } + + return header_->finalize(); +} + +bool Generator::writeSource() { + source_ = std::make_unique(basePath_ + ".cpp", project_); + + writeIncludesInSource(); + + if (module_.hasVariables()) { + source_->pushNamespace().newline(); + source_->stream() << "\ +bool inited = false;\n\ +\n\ +class Module_" << baseName_ << " : public style::internal::ModuleBase {\n\ +public:\n\ + Module_" << baseName_ << "() { style::internal::registerModule(this); }\n\ + ~Module_" << baseName_ << "() { style::internal::unregisterModule(this); }\n\ +\n\ + void start() override {\n\ + style::internal::init_" << baseName_ << "();\n\ + }\n\ + void stop() override {\n\ + }\n\ +};\n\ +Module_" << baseName_ << " registrator;\n"; + if (!writeVariableDefinitions()) { + return false; + } + source_->newline().popNamespace(); + + source_->newline().pushNamespace("st"); + if (!writeRefsDefinition()) { + return false; + } + + source_->popNamespace().newline(); + source_->newline().pushNamespace("style").pushNamespace("internal").newline(); + if (!writeVariableInit()) { + return false; + } + } + + return source_->finalize(); +} + +// Empty result means an error. +QString Generator::typeToString(structure::Type type) const { + switch (type.tag) { + case Tag::Invalid: return QString(); + case Tag::Int: return "int"; + case Tag::Double: return "double"; + case Tag::Pixels: return "int"; + case Tag::String: return "QString"; + case Tag::Color: return "style::color"; + case Tag::Point: return "style::point"; + case Tag::Sprite: return "style::sprite"; + case Tag::Size: return "style::size"; + case Tag::Transition: return "style::transition"; + case Tag::Cursor: return "style::cursor"; + case Tag::Align: return "style::align"; + case Tag::Margins: return "style::margins"; + case Tag::Font: return "style::font"; + case Tag::Struct: return "style::" + type.name.back(); + } + return QString(); +} + +// Empty result means an error. +QString Generator::typeToDefaultValue(structure::Type type) const { + switch (type.tag) { + case Tag::Invalid: return QString(); + case Tag::Int: return "0"; + case Tag::Double: return "0."; + case Tag::Pixels: return "0"; + case Tag::String: return "QString()"; + case Tag::Color: return "{ Qt::Uninitialized }"; + case Tag::Point: return "{ 0, 0 }"; + case Tag::Sprite: return "{ 0, 0, 0, 0 }"; + case Tag::Size: return "{ 0, 0 }"; + case Tag::Transition: return "anim::linear"; + case Tag::Cursor: return "style::cur_default"; + case Tag::Align: return "style::al_topleft"; + case Tag::Margins: return "{ 0, 0, 0, 0 }"; + case Tag::Font: return "{ Qt::Uninitialized }"; + case Tag::Struct: { + if (auto realType = module_.findStruct(type.name)) { + QStringList fields; + for (auto field : realType->fields) { + fields.push_back(typeToDefaultValue(field.type)); + } + return "{ " + fields.join(", ") + " }"; + } + return QString(); + } break; + } + return QString(); +} + +// Empty result means an error. +QString Generator::valueAssignmentCode(structure::Value value) const { + auto copy = value.copyOf(); + if (!copy.isEmpty()) { + return "st::" + copy.back(); + } + + switch (value.type().tag) { + case Tag::Invalid: return QString(); + case Tag::Int: return QString("%1").arg(value.Int()); + case Tag::Double: return QString("%1").arg(value.Double()); + case Tag::Pixels: return pxValueName(value.Int()); + case Tag::String: return QString("qsl(%1)").arg(stringToEncodedString(value.String())); + case Tag::Color: return QString("{ %1, %2, %3, %4 }").arg(value.Color().red).arg(value.Color().green).arg(value.Color().blue).arg(value.Color().alpha); + case Tag::Point: { + auto v(value.Point()); + return QString("{ %1, %2 }").arg(pxValueName(v.x)).arg(pxValueName(v.y)); + } + case Tag::Sprite: { + auto v(value.Sprite()); + return QString("{ %1, %2, %3, %4 }").arg(pxValueName(v.left)).arg(pxValueName(v.top)).arg(pxValueName(v.width)).arg(pxValueName(v.height)); + } + case Tag::Size: { + auto v(value.Size()); + return QString("{ %1, %2 }").arg(pxValueName(v.width)).arg(pxValueName(v.height)); + } + case Tag::Transition: return QString("anim::%1").arg(value.String().c_str()); + case Tag::Cursor: return QString("style::cur_%1").arg(value.String().c_str()); + case Tag::Align: return QString("style::al_%1").arg(value.String().c_str()); + case Tag::Margins: { + auto v(value.Margins()); + return QString("{ %1, %2, %3, %4 }").arg(pxValueName(v.left)).arg(pxValueName(v.top)).arg(pxValueName(v.right)).arg(pxValueName(v.bottom)); + } + case Tag::Font: { + auto v(value.Font()); + QString family = "0"; + if (!v.family.empty()) { + auto familyIndex = fontFamilyValues_.value(v.family, -1); + if (familyIndex < 0) { + return QString(); + } else { + family = QString("font%1index").arg(familyIndex); + } + } + return QString("{ %1, %2, %3 }").arg(pxValueName(v.size)).arg(v.flags).arg(family); + } + case Tag::Struct: { + if (!value.Fields()) return QString(); + + QStringList fields; + for (auto field : *value.Fields()) { + fields.push_back(valueAssignmentCode(field.variable.value)); + } + return "{ " + fields.join(", ") + " }"; + } break; + } + return QString(); +} + +bool Generator::writeHeaderStyleNamespace() { + if (!module_.hasStructs() && !module_.hasVariables()) { + return true; + } + header_->pushNamespace("style"); + + if (module_.hasVariables()) { + header_->pushNamespace("internal").newline(); + header_->stream() << "void init_" << baseName_ << "();\n\n"; + header_->popNamespace(); + } + if (module_.hasStructs()) { + header_->newline(); + if (!writeStructsDefinitions()) { + return false; + } + } + + header_->popNamespace().newline(); return true; } -Generator::~Generator() = default; +bool Generator::writeStructsDefinitions() { + if (!module_.hasStructs()) { + return true; + } + + bool result = module_.enumStructs([this](const Struct &value) -> bool { + header_->stream() << "struct " << value.name.back() << " {\n"; + for (const auto &field : value.fields) { + auto type = typeToString(field.type); + if (type.isEmpty()) { + return false; + } + header_->stream() << "\t" << type << " " << field.name.back() << ";\n"; + } + header_->stream() << "};\n\n"; + return true; + }); + + return result; +} + +bool Generator::writeRefsDeclarations() { + if (!module_.hasVariables()) { + return true; + } + + header_->pushNamespace("st"); + + bool result = module_.enumVariables([this](const Variable &value) -> bool { + auto name = value.name.back(); + auto type = typeToString(value.value.type()); + if (type.isEmpty()) { + return false; + } + + header_->stream() << "extern const " << type << " &" << name << ";\n"; + return true; + }); + + header_->popNamespace(); + + return result; +} + +bool Generator::writeIncludesInSource() { + if (!module_.hasIncludes()) { + return true; + } + + bool result = module_.enumIncludes([this](const Module &module) -> bool { + source_->include("style_" + QFileInfo(module.filepath()).baseName() + ".h"); + return true; + }); + source_->newline(); + return result; +} + +bool Generator::writeVariableDefinitions() { + if (!module_.hasVariables()) { + return true; + } + + source_->newline(); + bool result = module_.enumVariables([this](const Variable &variable) -> bool { + auto name = variable.name.back(); + auto type = typeToString(variable.value.type()); + if (type.isEmpty()) { + return false; + } + source_->stream() << type << " _" << name << " = " << typeToDefaultValue(variable.value.type()) << ";\n"; + return true; + }); + return result; +} + +bool Generator::writeRefsDefinition() { + if (!module_.hasVariables()) { + return true; + } + + source_->newline(); + bool result = module_.enumVariables([this](const Variable &variable) -> bool { + auto name = variable.name.back(); + auto type = typeToString(variable.value.type()); + if (type.isEmpty()) { + return false; + } + source_->stream() << "const " << type << " &" << name << "(_" << name << ");\n"; + return true; + }); + return result; +} + +bool Generator::writeVariableInit() { + if (!module_.hasVariables()) { + return true; + } + + if (!collectUniqueValues()) { + return false; + } + bool hasUniqueValues = (!pxValues_.isEmpty() || !fontFamilyValues_.isEmpty()); + if (hasUniqueValues) { + source_->pushNamespace(); + if (!writePxValues()) { + return false; + } + if (!writeFontFamilyValues()) { + return false; + } + source_->popNamespace().newline(); + } + + source_->stream() << "\ +void init_" << baseName_ << "() {\n\ + if (inited) return;\n\ + inited = true;\n\n"; + + if (module_.hasIncludes()) { + bool writtenAtLeastOne = false; + bool result = module_.enumIncludes([this,&writtenAtLeastOne](const Module &module) -> bool { + if (module.hasVariables()) { + source_->stream() << "\tinit_style_" + QFileInfo(module.filepath()).baseName() + "();\n"; + writtenAtLeastOne = true; + } + return true; + }); + if (!result) { + return false; + } + if (writtenAtLeastOne) { + source_->newline(); + } + } + + if (hasUniqueValues) { + if (!pxValues_.isEmpty()) { + source_->stream() << "\tinitPxValues();\n"; + } + if (!fontFamilyValues_.isEmpty()) { + source_->stream() << "\tinitFontFamilyValues();\n"; + } + source_->newline(); + } + + bool result = module_.enumVariables([this](const Variable &variable) -> bool { + auto name = variable.name.back(); + auto value = valueAssignmentCode(variable.value); + if (value.isEmpty()) { + return false; + } + source_->stream() << "\t_" << name << " = " << value << ";\n"; + return true; + }); + source_->stream() << "\ +}\n\n"; + return result; +} + +bool Generator::writePxValues() { + if (pxValues_.isEmpty()) { + return true; + } + + for (auto i = pxValues_.cbegin(), e = pxValues_.cend(); i != e; ++i) { + source_->stream() << "int " << pxValueName(i.key()) << " = " << i.key() << ";\n"; + } + source_->stream() << "\ +void initPxValues() {\n\ + if (cRetina()) return;\n\ +\n\ + switch (cScale()) {\n"; + for (int i = 1, scalesCount = scales.size(); i < scalesCount; ++i) { + source_->stream() << "\tcase " << scaleNames.at(i) << ":\n"; + for (auto it = pxValues_.cbegin(), e = pxValues_.cend(); it != e; ++it) { + auto value = it.key(); + int adjusted = structure::data::pxAdjust(value, scales.at(i)); + if (adjusted != value) { + source_->stream() << "\t\t" << pxValueName(value) << " = " << adjusted << ";\n"; + } + } + source_->stream() << "\tbreak;\n"; + } + source_->stream() << "\ + }\n\ +}\n\n"; + return true; +} + +bool Generator::writeFontFamilyValues() { + if (fontFamilyValues_.isEmpty()) { + return true; + } + + for (auto i = fontFamilyValues_.cbegin(), e = fontFamilyValues_.cend(); i != e; ++i) { + source_->stream() << "int font" << i.value() << "index;\n"; + } + source_->stream() << "void initFontFamilyValues() {\n"; + for (auto i = fontFamilyValues_.cbegin(), e = fontFamilyValues_.cend(); i != e; ++i) { + auto family = stringToEncodedString(i.key()); + source_->stream() << "\tfont" << i.value() << "index = style::internal::registerFontFamily(" << family << ");\n"; + } + source_->stream() << "}\n\n"; + return true; +} + +bool Generator::collectUniqueValues() { + int fontFamilyIndex = 0; + std::function collector = [this, &collector, &fontFamilyIndex](const Variable &variable) { + auto value = variable.value; + switch (value.type().tag) { + case Tag::Invalid: + case Tag::Int: + case Tag::Double: + case Tag::String: + case Tag::Color: + case Tag::Transition: + case Tag::Cursor: + case Tag::Align: break; + case Tag::Pixels: pxValues_.insert(value.Int(), true); break; + case Tag::Point: { + auto v(value.Point()); + pxValues_.insert(v.x, true); + pxValues_.insert(v.y, true); + } break; + case Tag::Sprite: { + auto v(value.Sprite()); + pxValues_.insert(v.left, true); + pxValues_.insert(v.top, true); + pxValues_.insert(v.width, true); + pxValues_.insert(v.height, true); + } break; + case Tag::Size: { + auto v(value.Size()); + pxValues_.insert(v.width, true); + pxValues_.insert(v.height, true); + } break; + case Tag::Margins: { + auto v(value.Margins()); + pxValues_.insert(v.left, true); + pxValues_.insert(v.top, true); + pxValues_.insert(v.right, true); + pxValues_.insert(v.bottom, true); + } break; + case Tag::Font: { + auto v(value.Font()); + pxValues_.insert(v.size, true); + if (!v.family.empty() && !fontFamilyValues_.contains(v.family)) { + fontFamilyValues_.insert(v.family, ++fontFamilyIndex); + } + } break; + case Tag::Struct: { + auto fields = variable.value.Fields(); + if (!fields) { + return false; + } + + for (auto field : *fields) { + if (!collector(field.variable)) { + return false; + } + } + } break; + } + return true; + }; + return module_.enumVariables(collector); +} } // namespace style } // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.h b/Telegram/SourceFiles/codegen/style/generator.h index 526a70912..b97460360 100644 --- a/Telegram/SourceFiles/codegen/style/generator.h +++ b/Telegram/SourceFiles/codegen/style/generator.h @@ -22,36 +22,53 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include -#include "codegen/style/options.h" +#include +#include "codegen/common/cpp_file.h" +#include "codegen/style/structure_types.h" namespace codegen { namespace style { namespace structure { -struct Module; +class Module; } // namespace structure -class ParsedFile; -// Walks through a file, parses it and parses dependency files if necessary. class Generator { public: - Generator(const Options &options); + Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project); Generator(const Generator &other) = delete; Generator &operator=(const Generator &other) = delete; - // Returns 0 on success. - int process(); - - ~Generator(); + bool writeHeader(); + bool writeSource(); private: - bool write(const structure::Module &module) const; + QString typeToString(structure::Type type) const; + QString typeToDefaultValue(structure::Type type) const; + QString valueAssignmentCode(structure::Value value) const; - std::unique_ptr parser_; - const Options &options_; + bool writeHeaderStyleNamespace(); + bool writeStructsDefinitions(); + bool writeRefsDeclarations(); - // List of files we need to generate with other instance of Generator. - // It is not empty only if rebuild_ flag is true. - QStringList dependenciesToGenerate_; + bool writeIncludesInSource(); + bool writeVariableDefinitions(); + bool writeRefsDefinition(); + bool writeVariableInit(); + bool writePxValues(); + bool writeFontFamilyValues(); + + bool collectUniqueValues(); + + const structure::Module &module_; + QString basePath_, baseName_; + const common::ProjectInfo &project_; + std::unique_ptr source_, header_; + + QMap pxValues_; + QMap fontFamilyValues_; + + std::vector scales = { 4, 5, 6, 8 }; // scale / 4 gives our 1.00, 1.25, 1.50, 2.00 + std::vectorscaleNames = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" }; }; diff --git a/Telegram/SourceFiles/codegen/style/main.cpp b/Telegram/SourceFiles/codegen/style/main.cpp index b61d9ce11..b4e8ab217 100644 --- a/Telegram/SourceFiles/codegen/style/main.cpp +++ b/Telegram/SourceFiles/codegen/style/main.cpp @@ -19,21 +19,18 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include -#include -#include "codegen/style/generator.h" #include "codegen/style/options.h" - -using namespace codegen::style; +#include "codegen/style/processor.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - Options options = parseOptions(); + auto options = codegen::style::parseOptions(); if (options.inputPath.isEmpty()) { return -1; } - Generator generator(options); - return generator.process(); + codegen::style::Processor processor(options); + return processor.launch(); } diff --git a/Telegram/SourceFiles/codegen/style/module.cpp b/Telegram/SourceFiles/codegen/style/module.cpp new file mode 100644 index 000000000..d11d4cdd0 --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/module.cpp @@ -0,0 +1,101 @@ +/* +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 "codegen/style/module.h" + +namespace codegen { +namespace style { +namespace structure { +namespace { + +QString fullNameKey(const FullName &name) { + return name.join('.'); +} + +} // namespace + +Module::Module(const QString &fullpath) : fullpath_(fullpath) { +} + +void Module::addIncluded(std::unique_ptr &&value) { + included_.push_back(std::move(value)); +} + +bool Module::addStruct(const Struct &value) { + if (findStruct(value.name)) { + return false; + } + structsByName_.insert(fullNameKey(value.name), structs_.size()); + structs_.push_back(value); + return true; +} + +const Struct *Module::findStruct(const FullName &name) const { + if (auto result = findStructInModule(name, *this)) { + return result; + } + for (const auto &module : included_) { + if (auto result = findStructInModule(name, *module)) { + return result; + } + } + return nullptr; +} + +bool Module::addVariable(const Variable &value) { + if (findVariable(value.name)) { + return false; + } + variablesByName_.insert(fullNameKey(value.name), variables_.size()); + variables_.push_back(value); + return true; +} + +const Variable *Module::findVariable(const FullName &name) const { + if (auto result = findVariableInModule(name, *this)) { + return result; + } + for (const auto &module : included_) { + if (auto result = findVariableInModule(name, *module)) { + return result; + } + } + return nullptr; +} + +const Struct *Module::findStructInModule(const FullName &name, const Module &module) const { + auto index = module.structsByName_.value(fullNameKey(name), -1); + if (index < 0) { + return nullptr; + } + return &module.structs_.at(index); +} + +const Variable *Module::findVariableInModule(const FullName &name, const Module &module) const { + auto index = module.variablesByName_.value(fullNameKey(name), -1); + if (index < 0) { + return nullptr; + } + return &module.variables_.at(index); +} + +} // namespace structure +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/module.h b/Telegram/SourceFiles/codegen/style/module.h new file mode 100644 index 000000000..a890fd7dd --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/module.h @@ -0,0 +1,112 @@ +/* +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 + +#include +#include +#include +#include +#include "codegen/style/structure_types.h" + +namespace codegen { +namespace style { +namespace structure { + +class Module { +public: + + explicit Module(const QString &fullpath); + + QString filepath() const { + return fullpath_; + } + + void addIncluded(std::unique_ptr &&value); + + bool hasIncludes() const { + return !included_.empty(); + } + template + bool enumIncludes(F functor) const { + for (const auto &module : included_) { + if (!functor(*module)) { + return false; + } + } + return true; + } + + // Returns false if there is a struct with such name already. + bool addStruct(const Struct &value); + // Returns nullptr if there is no such struct in result_ or any of included modules. + const Struct *findStruct(const FullName &name) const; + bool hasStructs() const { + return !structs_.isEmpty(); + } + + template + bool enumStructs(F functor) const { + for (const auto &value : structs_) { + if (!functor(value)) { + return false; + } + } + return true; + } + + // Returns false if there is a variable with such name already. + bool addVariable(const Variable &value); + // Returns nullptr if there is no such variable in result_ or any of included modules. + const Variable *findVariable(const FullName &name) const; + bool hasVariables() const { + return !variables_.isEmpty(); + } + + template + bool enumVariables(F functor) const { + for (const auto &value : variables_) { + if (!functor(value)) { + return false; + } + } + return true; + } + + explicit operator bool() const { + return !fullpath_.isEmpty(); + } + +private: + QString fullpath_; + std::vector> included_; + QList structs_; + QList variables_; + QMap structsByName_; + QMap variablesByName_; + + const Struct *findStructInModule(const FullName &name, const Module &module) const; + const Variable *findVariableInModule(const FullName &name, const Module &module) const; + +}; + +} // namespace structure +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index f5f7e2d66..037b7732a 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -32,6 +32,9 @@ using BasicType = BasicToken::Type; namespace codegen { namespace style { + +using structure::logFullName; + namespace { constexpr int kErrorInIncluded = 801; @@ -65,7 +68,7 @@ bool isValidColor(const QString &str) { uchar readHexUchar(QChar ch) { auto code = ch.unicode(); - return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code - 'a') & 0xFF); + return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code + 10 - 'a') & 0xFF); } uchar readHexUchar(QChar char1, QChar char2) { @@ -95,10 +98,6 @@ structure::data::color convertIntColor(int r, int g, int b, int a) { return { uchar(r & 0xFF), uchar(g & 0xFF), uchar(b & 0xFF), uchar(a & 0xFF) }; } -std::string logFullName(const structure::FullName &name) { - return name.join('.').toStdString(); -} - std::string logType(const structure::Type &type) { if (type.tag == structure::TypeTag::Struct) { return "struct " + logFullName(type.name); @@ -121,10 +120,6 @@ std::string logType(const structure::Type &type) { return builtInTypes->value(type.tag, "invalid"); } -QString fullNameKey(const structure::FullName &name) { - return name.join('.'); -} - bool validateAnsiString(const QString &value) { for (auto ch : value) { if (ch.unicode() > 127) { @@ -158,67 +153,61 @@ bool ParsedFile::read() { return false; } - bool noErrors = false; + auto filepath = QFileInfo(options_.inputPath).absoluteFilePath(); + module_ = std::make_unique(filepath); do { if (auto startToken = file_.getToken(BasicType::Name)) { if (tokenValue(startToken) == "using") { if (auto includedResult = readIncluded()) { - result_.includes.push_back(includedResult); + module_->addIncluded(std::move(includedResult)); continue; } } else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) { if (auto structResult = readStruct(tokenValue(startToken))) { - if (findStruct(structResult.name)) { - logError(kErrorAlreadyDefined) << "struct '" << logFullName(structResult.name) << "' already defined"; - break; + if (module_->addStruct(structResult)) { + continue; } - result_.structsByName.insert(fullNameKey(structResult.name), result_.structs.size()); - result_.structs.push_back(structResult); - continue; + logError(kErrorAlreadyDefined) << "struct '" << logFullName(structResult.name) << "' already defined"; + break; } } else if (auto colonToken = file_.getToken(BasicType::Colon)) { if (auto variableResult = readVariable(tokenValue(startToken))) { - if (findVariable(variableResult.name)) { - logError(kErrorAlreadyDefined) << "variable '" << logFullName(variableResult.name) << "' already defined"; - break; + if (module_->addVariable(variableResult)) { + continue; } - result_.variablesByName.insert(fullNameKey(variableResult.name), result_.variables.size()); - result_.variables.push_back(variableResult); - continue; + logError(kErrorAlreadyDefined) << "variable '" << logFullName(variableResult.name) << "' already defined"; + break; } } } - if (!file_.atEnd()) { - logErrorUnexpectedToken() << "using keyword, or struct definition, or variable definition"; - } else { - noErrors = !failed(); + if (file_.atEnd()) { break; } + logErrorUnexpectedToken() << "using keyword, or struct definition, or variable definition"; } while (!failed()); - if (noErrors) { - result_.fullpath = QFileInfo(options_.inputPath).absoluteFilePath(); + if (failed()) { + module_ = nullptr; } - return noErrors; + return !failed(); } common::LogStream ParsedFile::logErrorTypeMismatch() { return logError(kErrorTypeMismatch) << "type mismatch: "; } -structure::Module ParsedFile::readIncluded() { - structure::Module result; +ParsedFile::ModulePtr ParsedFile::readIncluded() { if (auto usingFile = assertNextToken(BasicType::String)) { if (assertNextToken(BasicType::Semicolon)) { ParsedFile included(includedOptions(tokenValue(usingFile))); if (included.read()) { - result = included.data(); + return included.getResult(); } else { logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'"; } } } - return result; + return nullptr; } structure::Struct ParsedFile::readStruct(const QString &name) { @@ -268,7 +257,7 @@ structure::Type ParsedFile::readType() { result = builtInType; } else { auto fullName = composeFullName(name); - if (findStruct(fullName)) { + if (module_->findStruct(fullName)) { result.tag = structure::TypeTag::Struct; result.name = fullName; } else { @@ -331,11 +320,17 @@ structure::Value ParsedFile::readStructValue() { } structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName &structName) { - if (auto pattern = findStruct(structName)) { - QList fields; + if (auto pattern = module_->findStruct(structName)) { + QList fields; fields.reserve(pattern->fields.size()); for (const auto &fieldType : pattern->fields) { - fields.push_back({ fieldType.name, { fieldType.type, Qt::Uninitialized } }); + fields.push_back({ + { // variable + fieldType.name, + { fieldType.type, Qt::Uninitialized }, // value + }, + structure::data::field::Status::Uninitialized, // status + }); } return { structName, fields }; } @@ -343,14 +338,14 @@ structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName } void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) { - if (auto parent = findVariable(parentName)) { + if (auto parent = module_->findVariable(parentName)) { if (parent->value.type() != result.type()) { logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(parent->value.type()) << "' while child value has type " << logType(result.type()); return; } - const auto *srcFields(parent->value.Complex()); - auto *dstFields(result.Complex()); + const auto *srcFields(parent->value.Fields()); + auto *dstFields(result.Fields()); if (!srcFields || !dstFields) { logAssert(false) << "struct data check failed"; return; @@ -358,10 +353,17 @@ void ParsedFile::applyStructParent(structure::Value &result, const structure::Fu logAssert(srcFields->size() == dstFields->size()) << "struct size check failed"; for (int i = 0, s = srcFields->size(); i != s; ++i) { - const auto &srcValue(srcFields->at(i).value); - auto &dstValue((*dstFields)[i].value); - logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed"; - dstValue = srcValue; + const auto &srcField(srcFields->at(i)); + auto &dstField((*dstFields)[i]); + using Status = structure::data::field::Status; + if (srcField.status == Status::Explicit || + dstField.status == Status::Uninitialized) { + const auto &srcValue(srcField.variable.value); + auto &dstValue(dstField.variable.value); + logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed"; + dstValue = srcValue; + dstField.status = Status::Implicit; + } } } else { logError(kErrorIdentifierNotFound) << "parent '" << logFullName(parentName) << "' not found"; @@ -388,18 +390,19 @@ bool ParsedFile::readStructValueInner(structure::Value &result) { } bool ParsedFile::assignStructField(structure::Value &result, const structure::Variable &field) { - auto *fields = result.Complex(); + auto *fields = result.Fields(); if (!fields) { logAssert(false) << "struct data check failed"; return false; } for (auto &already : *fields) { - if (already.name == field.name) { - if (already.value.type() == field.value.type()) { - already.value = field.value; + if (already.variable.name == field.name) { + if (already.variable.value.type() == field.value.type()) { + already.variable.value = field.value; + already.status = structure::data::field::Status::Explicit; return true; } else { - logErrorTypeMismatch() << "field '" << logFullName(already.name) << "' has type '" << logType(already.value.type()) << "' while value has type '" << logType(field.value.type()) << "'"; + logErrorTypeMismatch() << "field '" << logFullName(already.variable.name) << "' has type '" << logType(already.variable.value.type()) << "' while value has type '" << logType(field.value.type()) << "'"; return false; } } @@ -429,7 +432,7 @@ structure::Value ParsedFile::readPositiveValue() { if (numericToken.type == BasicType::Int) { return { structure::TypeTag::Int, tokenValue(numericToken).toInt() }; } else if (numericToken.type == BasicType::Double) { - return { tokenValue(numericToken).toDouble() }; + return { structure::TypeTag::Double, tokenValue(numericToken).toDouble() }; } else if (numericToken.type == BasicType::Name) { auto value = tokenValue(numericToken); auto match = QRegularExpression("^\\d+px$").match(value); @@ -717,7 +720,7 @@ structure::Value ParsedFile::readFontValue() { structure::Value ParsedFile::readCopyValue() { if (auto copyName = file_.getToken(BasicType::Name)) { structure::FullName name = { tokenValue(copyName) }; - if (auto variable = findVariable(name)) { + if (auto variable = module_->findVariable(name)) { return variable->value.makeCopy(variable->name); } logError(kErrorIdentifierNotFound) << "identifier '" << logFullName(name) << "' not found"; @@ -725,48 +728,6 @@ structure::Value ParsedFile::readCopyValue() { return {}; } -// Returns nullptr if there is no such struct in result_ or any of included modules. -const structure::Struct *ParsedFile::findStruct(const structure::FullName &name) { - if (auto result = findStructInModule(name, result_)) { - return result; - } - for (const auto &included : result_.includes) { - if (auto result = findStructInModule(name, included)) { - return result; - } - } - return nullptr; -} - -const structure::Struct *ParsedFile::findStructInModule(const structure::FullName &name, const structure::Module &module) { - auto index = module.structsByName.value(fullNameKey(name), -1); - if (index < 0) { - return nullptr; - } - return &module.structs.at(index); -} - -// Returns nullptr if there is no such variable in result_ or any of included modules. -const structure::Variable *ParsedFile::findVariable(const structure::FullName &name) { - if (auto result = findVariableInModule(name, result_)) { - return result; - } - for (const auto &included : result_.includes) { - if (auto result = findVariableInModule(name, included)) { - return result; - } - } - return nullptr; -} - -const structure::Variable *ParsedFile::findVariableInModule(const structure::FullName &name, const structure::Module &module) { - auto index = module.variablesByName.value(fullNameKey(name), -1); - if (index < 0) { - return nullptr; - } - return &module.variables.at(index); -} - BasicToken ParsedFile::assertNextToken(BasicToken::Type type) { auto result = file_.getToken(type); if (!result) { diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index 6106d728a..f98b41c7c 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include "codegen/common/basic_tokenized_file.h" #include "codegen/style/options.h" -#include "codegen/style/structure.h" +#include "codegen/style/module.h" namespace codegen { namespace style { @@ -32,14 +32,15 @@ namespace style { // Parses an input file to the internal struct. class ParsedFile { public: - ParsedFile(const Options &options); + explicit ParsedFile(const Options &options); ParsedFile(const ParsedFile &other) = delete; ParsedFile &operator=(const ParsedFile &other) = delete; bool read(); - const structure::Module &data() const { - return result_; + using ModulePtr = std::unique_ptr; + ModulePtr getResult() { + return std::move(module_); } private: @@ -66,7 +67,7 @@ private: } // Helper methods for context-dependent reading. - structure::Module readIncluded(); + ModulePtr readIncluded(); structure::Struct readStruct(const QString &name); structure::Variable readVariable(const QString &name); @@ -96,14 +97,6 @@ private: structure::Value readFontValue(); structure::Value readCopyValue(); - // Returns nullptr if there is no such struct in result_ or any of included modules. - const structure::Struct *findStruct(const structure::FullName &name); - const structure::Struct *findStructInModule(const structure::FullName &name, const structure::Module &module); - - // Returns nullptr if there is no such variable in result_ or any of included modules. - const structure::Variable *findVariable(const structure::FullName &name); - const structure::Variable *findVariableInModule(const structure::FullName &name, const structure::Module &module); - // Read next token and fire unexpected token error if it is not of "type". using BasicToken = common::BasicTokenizedFile::Token; BasicToken assertNextToken(BasicToken::Type type); @@ -117,7 +110,7 @@ private: common::BasicTokenizedFile file_; Options options_; bool failed_ = false; - structure::Module result_; + ModulePtr module_; QMap typeNames_ = { { "int" , { structure::TypeTag::Int } }, diff --git a/Telegram/SourceFiles/codegen/style/processor.cpp b/Telegram/SourceFiles/codegen/style/processor.cpp new file mode 100644 index 000000000..77c241b14 --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/processor.cpp @@ -0,0 +1,103 @@ +/* +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 "codegen/style/processor.h" + +#include +#include +#include "codegen/common/cpp_file.h" +#include "codegen/style/parsed_file.h" +#include "codegen/style/generator.h" +#include "codegen/style/sprite_generator.h" + +namespace codegen { +namespace style { +namespace { + +constexpr int kErrorCantWritePath = 851; + +QString destFileBaseName(const structure::Module &module) { + return "style_" + QFileInfo(module.filepath()).baseName(); +} + +} // namespace + +Processor::Processor(const Options &options) +: parser_(std::make_unique(options)) +, options_(options) { +} + +int Processor::launch() { + if (!parser_->read()) { + return -1; + } + + auto module = parser_->getResult(); + if (options_.rebuildDependencies) { + bool result = module->enumIncludes([this](const structure::Module &included) -> bool { + return write(included); + }); + if (!result) { + return -1; + } + } else if (!write(*module)) { + return -1; + } + + return 0; +} + +bool Processor::write(const structure::Module &module) const { + QDir dir(options_.outputPath); + if (!dir.mkpath(".")) { + common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); + return false; + } + + QFileInfo srcFile(module.filepath()); + QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module); + + common::ProjectInfo project = { + "codegen_style", + srcFile.fileName(), + "stdafx.h", + !options_.rebuildDependencies, // forceReGenerate + }; + + SpriteGenerator spriteGenerator(module); + if (!spriteGenerator.writeSprites()) { + return false; + } + + Generator generator(module, dstFilePath, project); + if (!generator.writeHeader()) { + return false; + } + if (!generator.writeSource()) { + return false; + } + + return true; +} + +Processor::~Processor() = default; + +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/processor.h b/Telegram/SourceFiles/codegen/style/processor.h new file mode 100644 index 000000000..d2c2cbd55 --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/processor.h @@ -0,0 +1,60 @@ +/* +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 + +#include +#include +#include "codegen/style/options.h" + +namespace codegen { +namespace style { +namespace structure { +class Module; +} // namespace structure +class ParsedFile; + +// Walks through a file, parses it and parses dependency files if necessary. +// Uses Generator class to produce the final output. +class Processor { +public: + explicit Processor(const Options &options); + Processor(const Processor &other) = delete; + Processor &operator=(const Processor &other) = delete; + + // Returns 0 on success. + int launch(); + + ~Processor(); + +private: + bool write(const structure::Module &module) const; + + std::unique_ptr parser_; + const Options &options_; + + // List of files we need to generate with other instance of Generator. + // It is not empty only if rebuild_ flag is true. + QStringList dependenciesToGenerate_; + +}; + +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/sprite_generator.cpp b/Telegram/SourceFiles/codegen/style/sprite_generator.cpp new file mode 100644 index 000000000..34a6e948a --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/sprite_generator.cpp @@ -0,0 +1,180 @@ +/* +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 "codegen/style/sprite_generator.h" + +#include +#include +#include +#include +#include +#include +#include "codegen/style/parsed_file.h" + +using Module = codegen::style::structure::Module; +using Struct = codegen::style::structure::Struct; +using Variable = codegen::style::structure::Variable; +using Tag = codegen::style::structure::TypeTag; + +namespace codegen { +namespace style { + +using structure::logFullName; + +namespace { + +constexpr int kErrorFileNotFound = 881; +constexpr int kErrorSpritesIntersect = 882; +constexpr int kErrorCouldNotGenerate = 886; +constexpr int kErrorCouldNotSerialize = 886; +constexpr int kErrorCouldNotOpen = 886; +constexpr int kErrorCouldNotWrite = 886; + +} // namespace + +SpriteGenerator::SpriteGenerator(const structure::Module &module) +: module_(module) +, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) { +} + +bool SpriteGenerator::writeSprites() { + if (!collectSprites()) { + return false; + } + if (sprites_.isEmpty()) { + return true; + } + + sprite2x_ = QImage(basePath_ + "/art/sprite_200x.png"); + if (sprite2x_.isNull()) { + common::logError(kErrorFileNotFound, "/art/sprite_200x.png") << "sprite file was not found"; + return false; + } + std::vector sizes = { 5, 6 }; + std::vector postfixes = { "125", "150" }; + for (int i = 0, l = sizes.size(); i < l; ++i) { + auto sprite = generateSprite(sizes[i]); + QString filepath = basePath_ + "/art/sprite_" + postfixes[i] + "x.png"; + if (sprite.isNull()) { + common::logError(kErrorCouldNotGenerate, filepath) << "could not generate sprite file"; + return false; + } + QByteArray spriteData; + { + QBuffer spriteBuffer(&spriteData); + if (!sprite.save(&spriteBuffer, "PNG")) { + common::logError(kErrorCouldNotSerialize, filepath) << "could not serialize sprite file"; + return false; + } + } + QFile file(filepath); + if (file.open(QIODevice::ReadOnly)) { + if (file.readAll() == spriteData) { + continue; + } + file.close(); + } + if (!file.open(QIODevice::WriteOnly)) { + common::logError(kErrorCouldNotOpen, filepath) << "could not open sprite file for write"; + return false; + } + if (file.write(spriteData) != spriteData.size()) { + common::logError(kErrorCouldNotWrite, filepath) << "could not write sprite file"; + return false; + } + + // Touch resource file. + filepath = basePath_ + "/telegram.qrc"; + QFile qrc(filepath); + if (qrc.open(QIODevice::ReadOnly)) { + auto qrcContent = qrc.readAll(); + qrc.close(); + if (!qrc.open(QIODevice::WriteOnly)) { + common::logError(kErrorCouldNotOpen, filepath) << "could not open .qrc file for write"; + return false; + } + if (qrc.write(qrcContent) != qrcContent.size()) { + common::logError(kErrorCouldNotWrite, filepath) << "could not write .qrc file"; + return false; + } + } + } + + return true; +} + +bool SpriteGenerator::collectSprites() { + std::function collector = [this, &collector](const Variable &variable) { + auto value = variable.value; + if (value.type().tag == Tag::Sprite) { + auto v(value.Sprite()); + if (!v.width || !v.height) return true; + + QRect vRect(v.left, v.top, v.width, v.height); + bool found = false; + for (auto var : sprites_) { + auto sprite = var.value.Sprite(); + QRect spriteRect(sprite.left, sprite.top, sprite.width, sprite.height); + if (spriteRect == vRect) { + found = true; + } else if (spriteRect.intersects(vRect)) { + common::logError(kErrorSpritesIntersect, module_.filepath()) << "sprite '" << logFullName(variable.name) << "' intersects with '" << logFullName(var.name) << "'"; + return false; + } + } + if (!found) { + sprites_.push_back(variable); + } + } else if (value.type().tag == Tag::Struct) { + auto fields = variable.value.Fields(); + if (!fields) { + return false; + } + + for (auto field : *fields) { + if (!collector(field.variable)) { + return false; + } + } + } + return true; + }; + return module_.enumVariables(collector); +} + +QImage SpriteGenerator::generateSprite(int scale) { + auto convert = [scale](int value) -> int { return structure::data::pxAdjust(value, scale); }; + QImage result(convert(sprite2x_.width() / 2), convert(sprite2x_.height() / 2), sprite2x_.format()); + { + QPainter p(&result); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(0, 0, result.width(), result.height(), QColor(0, 0, 0, 0)); + for (auto variable : sprites_) { + auto sprite = variable.value.Sprite(); + auto copy = sprite2x_.copy(sprite.left * 2, sprite.top * 2, sprite.width * 2, sprite.height * 2); + copy = copy.scaled(convert(sprite.width), convert(sprite.height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + p.drawImage(convert(sprite.left), convert(sprite.top), copy); + } + } + return result; +} + +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure.h b/Telegram/SourceFiles/codegen/style/sprite_generator.h similarity index 62% rename from Telegram/SourceFiles/codegen/style/structure.h rename to Telegram/SourceFiles/codegen/style/sprite_generator.h index 8f29350b4..832a71984 100644 --- a/Telegram/SourceFiles/codegen/style/structure.h +++ b/Telegram/SourceFiles/codegen/style/sprite_generator.h @@ -20,55 +20,37 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include #include -#include -#include +#include +#include #include "codegen/style/structure_types.h" namespace codegen { namespace style { namespace structure { - -struct Variable { - FullName name; - Value value; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -struct StructField { - FullName name; - Type type; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -struct Struct { - FullName name; - QList fields; - - explicit operator bool() const { - return !name.isEmpty(); - } -}; - -struct Module { - QString fullpath; - QList includes; - QList structs; - QList variables; - QMap structsByName; - QMap variablesByName; - - explicit operator bool() const { - return !fullpath.isEmpty(); - } -}; - +class Module; } // namespace structure + +class SpriteGenerator { +public: + SpriteGenerator(const structure::Module &module); + SpriteGenerator(const SpriteGenerator &other) = delete; + SpriteGenerator &operator=(const SpriteGenerator &other) = delete; + + bool writeSprites(); + +private: + + bool collectSprites(); + QImage generateSprite(int scale); // scale = 5 for 125% and 6 for 150%. + + const structure::Module &module_; + QString basePath_; + QImage sprite2x_; + QList sprites_; + +}; + } // namespace style } // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure_types.cpp b/Telegram/SourceFiles/codegen/style/structure_types.cpp index 15a973efd..0cb47e23e 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.cpp +++ b/Telegram/SourceFiles/codegen/style/structure_types.cpp @@ -20,8 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "codegen/style/structure_types.h" -#include "codegen/style/structure.h" - namespace codegen { namespace style { namespace structure { @@ -117,15 +115,15 @@ struct Value::DataTypes { data::font value_; }; - class TComplex : public DataBase { + class TFields : public DataBase { public: - TComplex(data::complex value) : value_(value) { + TFields(data::fields value) : value_(value) { } - const data::complex *Complex() const override { return &value_; } - data::complex *Complex() override { return &value_; } + const data::fields *Fields() const override { return &value_; } + data::fields *Fields() override { return &value_; } private: - data::complex value_; + data::fields value_; }; }; @@ -133,9 +131,6 @@ struct Value::DataTypes { Value::Value() : Value(TypeTag::Invalid, std::make_shared()) { } -Value::Value(double value) : Value(TypeTag::Double, std::make_shared(value)) { -} - Value::Value(data::point value) : Value(TypeTag::Point, std::make_shared(value)) { } @@ -154,9 +149,16 @@ Value::Value(data::margins value) : Value(TypeTag::Margins, std::make_shared(value)) { } -Value::Value(const FullName &type, data::complex value) +Value::Value(const FullName &type, data::fields value) : type_ { TypeTag::Struct, type } -, data_(std::make_shared(value)) { +, data_(std::make_shared(value)) { +} + +Value::Value(TypeTag type, double value) : Value(type, std::make_shared(value)) { + if (type_.tag != TypeTag::Double) { + type_.tag = TypeTag::Invalid; + data_ = std::make_shared(); + } } Value::Value(TypeTag type, int value) : Value(type, std::make_shared(value)) { @@ -189,10 +191,10 @@ Value::Value(Type type, Qt::Initialization) : type_(type) { case TypeTag::Size: data_ = std::make_shared(data::size { 0, 0 }); break; case TypeTag::Transition: data_ = std::make_shared("linear"); break; case TypeTag::Cursor: data_ = std::make_shared("default"); break; - case TypeTag::Align: data_ = std::make_shared("left"); break; + case TypeTag::Align: data_ = std::make_shared("topleft"); break; case TypeTag::Margins: data_ = std::make_shared(data::margins { 0, 0, 0, 0 }); break; case TypeTag::Font: data_ = std::make_shared(data::font { "", 13, 0 }); break; - case TypeTag::Struct: data_ = std::make_shared(data::complex {}); break; + case TypeTag::Struct: data_ = std::make_shared(data::fields {}); break; } } diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h index 2e2703b3c..9146621fd 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.h +++ b/Telegram/SourceFiles/codegen/style/structure_types.h @@ -30,6 +30,9 @@ namespace structure { // List of names, like overview.document.bg using FullName = QStringList; +inline std::string logFullName(const FullName &name) { + return name.join('.').toStdString(); +} struct Variable; @@ -68,6 +71,10 @@ inline bool operator!=(const Type &a, const Type &b) { namespace data { +inline int pxAdjust(int value, int scale) { + return qRound((value * scale / 4.) + (value > 0 ? -0.01 : 0.01)); +} + struct point { int x, y; }; @@ -85,29 +92,32 @@ struct margins { }; struct font { enum Flag { - Bold = 0x01, - Italic = 0x02, + Bold = 0x01, + Italic = 0x02, Underline = 0x04, }; std::string family; int size; int flags; }; -using complex = QList; +struct field; // defined after Variable is defined +using fields = QList; } // namespace data class Value { public: Value(); - Value(double value); Value(data::point value); Value(data::sprite value); Value(data::size value); Value(data::color value); Value(data::margins value); Value(data::font value); - Value(const FullName &type, data::complex value); + Value(const FullName &type, data::fields value); + + // Can be only double. + Value(TypeTag type, double value); // Can be int / pixels. Value(TypeTag type, int value); @@ -128,8 +138,8 @@ public: data::color Color() const { return data_->Color(); }; data::margins Margins() const { return data_->Margins(); }; data::font Font() const { return data_->Font(); }; - const data::complex *Complex() const { return data_->Complex(); }; - data::complex *Complex() { return data_->Complex(); }; + const data::fields *Fields() const { return data_->Fields(); }; + data::fields *Fields() { return data_->Fields(); }; explicit operator bool() const { return type_.tag != TypeTag::Invalid; @@ -141,6 +151,10 @@ public: return result; } + const FullName ©Of() const { + return copyOf_; + } + private: class DataBase { public: @@ -153,8 +167,8 @@ private: virtual data::color Color() const { return {}; }; virtual data::margins Margins() const { return {}; }; virtual data::font Font() const { return {}; }; - virtual const data::complex *Complex() const { return nullptr; }; - virtual data::complex *Complex() { return nullptr; }; + virtual const data::fields *Fields() const { return nullptr; }; + virtual data::fields *Fields() { return nullptr; }; virtual ~DataBase() { } }; @@ -169,6 +183,45 @@ private: }; +struct Variable { + FullName name; + Value value; + + explicit operator bool() const { + return !name.isEmpty(); + } +}; + +namespace data { +struct field { + enum class Status { + Uninitialized, + Implicit, + Explicit + }; + Variable variable; + Status status; +}; +} // namespace data + +struct StructField { + FullName name; + Type type; + + explicit operator bool() const { + return !name.isEmpty(); + } +}; + +struct Struct { + FullName name; + QList fields; + + explicit operator bool() const { + return !name.isEmpty(); + } +}; + } // namespace structure } // namespace style } // namespace codegen diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index 75757ba02..41d42cc57 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -813,8 +813,7 @@ inline QSharedPointer MakeShared(Args&&... args) { template class NeverFreedPointer { public: - explicit NeverFreedPointer() { - } + NeverFreedPointer() = default; NeverFreedPointer(const NeverFreedPointer &other) = delete; NeverFreedPointer &operator=(const NeverFreedPointer &other) = delete; @@ -861,7 +860,7 @@ public: } private: - T *_p = nullptr; + T *_p; }; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 676ff0025..ceae67a46 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -2876,7 +2876,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { } if (rtl()) selx = width() - selx - st::rbEmoji.width; - p.setOpacity(skip ? qMax(1., selx / (skip * st::rbEmoji.width)) : 1.); + p.setOpacity(skip ? qMax(1., selx / float64(skip * st::rbEmoji.width)) : 1.); p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 7079c2cb6..383c05d85 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -375,7 +375,7 @@ void BoxButton::resizeToText() { resize(_textWidth - _st.width, _st.height); } else { if (_st.width < _textWidth + (_st.height - _st.font->height)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1.)); + _text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1)); _textWidth = _st.font->width(_text); } resize(_st.width, _st.height); diff --git a/Telegram/SourceFiles/ui/flatcheckbox.cpp b/Telegram/SourceFiles/ui/flatcheckbox.cpp index ea118134a..ddeb4f55e 100644 --- a/Telegram/SourceFiles/ui/flatcheckbox.cpp +++ b/Telegram/SourceFiles/ui/flatcheckbox.cpp @@ -250,7 +250,7 @@ Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const sty resize(_textWidth - _st.width, _st.height); } else { if (_st.width < _st.textPosition.x() + _textWidth + (_st.textPosition.x() - _st.diameter)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1.)); + _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1)); _textWidth = _st.font->width(_text); } resize(_st.width, _st.height); @@ -392,7 +392,7 @@ Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, con resize(_textWidth - _st.width, _st.height); } else { if (_st.width < _st.textPosition.x() + _textWidth + (_st.textPosition.x() - _st.diameter)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1.)); + _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1)); _textWidth = _st.font->width(_text); } resize(_st.width, _st.height); diff --git a/Telegram/SourceFiles/ui/style.h b/Telegram/SourceFiles/ui/style.h index b3108ab22..414541118 100644 --- a/Telegram/SourceFiles/ui/style.h +++ b/Telegram/SourceFiles/ui/style.h @@ -21,5 +21,5 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/style_core.h" -#include "GeneratedFiles/style_classes.h" -#include "GeneratedFiles/style_auto.h" +#include "GeneratedFiles/styles/style_basic_types.h" +#include "GeneratedFiles/styles/style_basic.h" diff --git a/Telegram/SourceFiles/ui/style_core.cpp b/Telegram/SourceFiles/ui/style_core.cpp index f1b52a7fa..e6ad6cbab 100644 --- a/Telegram/SourceFiles/ui/style_core.cpp +++ b/Telegram/SourceFiles/ui/style_core.cpp @@ -20,13 +20,63 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +namespace style { namespace { - typedef QMap FontFamilyMap; - FontFamilyMap _fontFamilyMap; +using ModulesList = QList; +NeverFreedPointer styleModules; + +typedef QMap FontFamilyMap; +FontFamilyMap fontFamilyMap; + +typedef QVector FontFamilies; +FontFamilies _fontFamilies; + +typedef QMap FontDatas; +FontDatas fontsMap; + +typedef QMap ColorDatas; +ColorDatas colorsMap; + +int spriteWidthValue = 0; +QPixmap *spriteData = nullptr; + +inline uint32 fontKey(int size, uint32 flags, int family) { + return (((uint32(family) << 10) | uint32(size)) << 3) | flags; } -namespace style { - FontData::FontData(uint32 size, uint32 flags, uint32 family, Font *other) : f(_fontFamilies[family]), m(f), _size(size), _flags(flags), _family(family) { +} // namespace + +namespace internal { + +void registerModule(ModuleBase *module) { + styleModules.makeIfNull(); + styleModules->push_back(module); +} + +void unregisterModule(ModuleBase *module) { + styleModules->removeOne(module); + if (styleModules->isEmpty()) { + styleModules.clear(); + } +} + +int registerFontFamily(const QString &family) { + auto result = fontFamilyMap.value(family, -1); + if (result < 0) { + result = _fontFamilies.size(); + fontFamilyMap.insert(family, result); + _fontFamilies.push_back(family); + } + return result; +} + +int spriteWidth() { + return spriteWidthValue; +} + +} // namespace internal + + FontData::FontData(int size, uint32 flags, int family, Font *other) : f(_fontFamilies[family]), m(f), _size(size), _flags(flags), _family(family) { if (other) { memcpy(modified, other, sizeof(modified)); } else { @@ -60,7 +110,7 @@ namespace style { return otherFlagsFont(FontUnderline, set); } - uint32 FontData::size() const { + int FontData::size() const { return _size; } @@ -68,7 +118,7 @@ namespace style { return _flags; } - uint32 FontData::family() const { + int FontData::family() const { return _family; } @@ -80,34 +130,34 @@ namespace style { return modified[newFlags]; } - Font::Font(uint32 size, uint32 flags, const QString &family) { - if (_fontFamilyMap.isEmpty()) { + Font::Font(int size, uint32 flags, const QString &family) { + if (fontFamilyMap.isEmpty()) { for (uint32 i = 0, s = style::_fontFamilies.size(); i != s; ++i) { - _fontFamilyMap.insert(style::_fontFamilies.at(i), i); + fontFamilyMap.insert(style::_fontFamilies.at(i), i); } } - FontFamilyMap::const_iterator i = _fontFamilyMap.constFind(family); - if (i == _fontFamilyMap.cend()) { + auto i = fontFamilyMap.constFind(family); + if (i == fontFamilyMap.cend()) { style::_fontFamilies.push_back(family); - i = _fontFamilyMap.insert(family, style::_fontFamilies.size() - 1); + i = fontFamilyMap.insert(family, style::_fontFamilies.size() - 1); } init(size, flags, i.value(), 0); } - Font::Font(uint32 size, uint32 flags, uint32 family) { + Font::Font(int size, uint32 flags, int family) { init(size, flags, family, 0); } - Font::Font(uint32 size, uint32 flags, uint32 family, Font *modified) { + Font::Font(int size, uint32 flags, int family, Font *modified) { init(size, flags, family, modified); } - void Font::init(uint32 size, uint32 flags, uint32 family, Font *modified) { - uint32 key = _fontKey(size, flags, family); - FontDatas::const_iterator i = _fontsMap.constFind(key); - if (i == _fontsMap.cend()) { - i = _fontsMap.insert(key, new FontData(size, flags, family, modified)); + void Font::init(int size, uint32 flags, int family, Font *modified) { + uint32 key = fontKey(size, flags, family); + auto i = fontsMap.constFind(key); + if (i == fontsMap.cend()) { + i = fontsMap.insert(key, new FontData(size, flags, family, modified)); } ptr = i.value(); } @@ -141,9 +191,9 @@ namespace style { } void Color::init(uchar r, uchar g, uchar b, uchar a) { uint32 key = colorKey(r, g, b, a); - ColorDatas::const_iterator i = _colorsMap.constFind(key); - if (i == _colorsMap.cend()) { - i = _colorsMap.insert(key, new ColorData(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(); } @@ -163,16 +213,57 @@ namespace style { b = QBrush(color); } - void stopManager() { - for (FontDatas::const_iterator i = _fontsMap.cbegin(), e = _fontsMap.cend(); i != e; ++i) { - delete i.value(); + void startManager() { + if (cRetina()) { + cSetRealScale(dbisOne); } - _fontsMap.clear(); - for (ColorDatas::const_iterator i = _colorsMap.cbegin(), e = _colorsMap.cend(); i != e; ++i) { - delete i.value(); + internal::registerFontFamily(qsl("Open Sans")); + QString spriteFilePostfix; + if (cRetina() || cScale() == dbisTwo) { + spriteFilePostfix = qsl("_200x"); + } else if (cScale() == dbisOneAndQuarter) { + spriteFilePostfix = qsl("_125x"); + } else if (cScale() == dbisOneAndHalf) { + spriteFilePostfix = qsl("_150x"); } - _colorsMap.clear(); + QString spriteFile = qsl(":/gui/art/sprite") + spriteFilePostfix + qsl(".png"); + if (rtl()) { + spriteData = new QPixmap(QPixmap::fromImage(QImage(spriteFile).mirrored(true, false))); + } else { + spriteData = new QPixmap(spriteFile); + } + if (cRetina()) spriteData->setDevicePixelRatio(cRetinaFactor()); + spriteWidthValue = spriteData->width(); + + if (styleModules) { + for_const (auto module, *styleModules) { + module->start(); + } + } + _fontFamilies.push_back(qsl("Open Sans")); + } + + void stopManager() { + if (styleModules) { + for_const (auto module, *styleModules) { + module->stop(); + } + } + + for (auto fontData : fontsMap) { + delete fontData; + } + fontsMap.clear(); + + for (auto colorData : colorsMap) { + delete colorData; + } + colorsMap.clear(); + } + + const QPixmap &spritePixmap() { + return *spriteData; } }; diff --git a/Telegram/SourceFiles/ui/style_core.h b/Telegram/SourceFiles/ui/style_core.h index 1528284d1..e440dd414 100644 --- a/Telegram/SourceFiles/ui/style_core.h +++ b/Telegram/SourceFiles/ui/style_core.h @@ -46,14 +46,31 @@ inline QRect centerrect(const QRect &inRect, const QRect &rect) { } namespace style { +namespace internal { + +// Objects of derived classes are created in global scope. +// They call [un]registerModule() in [de|con]structor. +class ModuleBase { +public: + virtual void start() = 0; + virtual void stop() = 0; +}; +void registerModule(ModuleBase *module); +void unregisterModule(ModuleBase *module); + +int registerFontFamily(const QString &family); + +int spriteWidth(); + +} // namespace internal class FontData; class Font { public: Font(Qt::Initialization = Qt::Uninitialized) : ptr(0) { } - Font(uint32 size, uint32 flags, const QString &family); - Font(uint32 size, uint32 flags = 0, uint32 family = 0); + Font(int size, uint32 flags, const QString &family); + Font(int size, uint32 flags = 0, int family = 0); Font &operator=(const Font &other) { ptr = other.ptr; @@ -76,36 +93,24 @@ namespace style { private: FontData *ptr; - void init(uint32 size, uint32 flags, uint32 family, Font *modified); + void init(int size, uint32 flags, int family, Font *modified); friend void startManager(); Font(FontData *p) : ptr(p) { } - Font(uint32 size, uint32 flags, uint32 family, Font *modified); + Font(int size, uint32 flags, int family, Font *modified); friend class FontData; }; - enum FontFlagBits { - FontBoldBit, - FontItalicBit, - FontUnderlineBit, - - FontFlagsBits - }; - enum FontFlags { - FontBold = (1 << FontBoldBit), - FontItalic = (1 << FontItalicBit), - FontUnderline = (1 << FontUnderlineBit), + FontBold = 0x01, + FontItalic = 0x02, + FontUnderline = 0x04, - FontDifferentFlags = (1 << FontFlagsBits) + FontDifferentFlags = 0x08, }; - inline uint32 _fontKey(uint32 size, uint32 flags, uint32 family) { - return (((family << 10) | size) << FontFlagsBits) | flags; - } - class FontData { public: @@ -126,9 +131,9 @@ namespace style { Font italic(bool set = true) const; Font underline(bool set = true) const; - uint32 size() const; + int size() const; uint32 flags() const; - uint32 family() const; + int family() const; QFont f; QFontMetrics m; @@ -138,10 +143,12 @@ namespace style { mutable Font modified[FontDifferentFlags]; Font otherFlagsFont(uint32 flag, bool set) const; - FontData(uint32 size, uint32 flags, uint32 family, Font *other); + FontData(int size, uint32 flags, int family, Font *other); friend class Font; - uint32 _size, _flags, _family; + int _size; + uint32 _flags; + int _family; }; @@ -159,7 +166,7 @@ namespace style { class ColorData; class Color { public: - Color(Qt::Initialization = Qt::Uninitialized) : ptr(0), owner(false) { + Color(Qt::Initialization = Qt::Uninitialized) { } Color(const Color &c); Color(const QColor &c); @@ -185,8 +192,8 @@ namespace style { } private: - ColorData *ptr; - bool owner; + ColorData *ptr = nullptr; + bool owner = false; void init(uchar r, uchar g, uchar b, uchar a); @@ -233,17 +240,6 @@ namespace style { return ptr->p; } - typedef QVector FontFamilies; - extern FontFamilies _fontFamilies; - - typedef QMap FontDatas; - extern FontDatas _fontsMap; - - typedef QMap ColorDatas; - extern ColorDatas _colorsMap; - - extern int _spriteWidth; - typedef float64 number; typedef QString string; typedef QRect rect; @@ -252,7 +248,7 @@ namespace style { public: sprite() { } - sprite(int left, int top, int width, int height) : rect(rtl() ? (_spriteWidth - left - width) : left, top, width, height) { + sprite(int left, int top, int width, int height) : rect(rtl() ? (internal::spriteWidth() - left - width) : left, top, width, height) { } inline int pxWidth() const { return rect::width() / cIntRetinaFactor(); @@ -314,8 +310,9 @@ namespace style { void startManager(); void stopManager(); + const QPixmap &spritePixmap(); -}; +} // namespace style inline QRect centersprite(const QRect &inRect, const style::sprite &sprite) { return centerrect(inRect, QRect(QPoint(0, 0), sprite.pxSize())); diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 3f3b24616..ff3e6f773 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -1051,7 +1051,13 @@ true true - + + + + true + true + true + @@ -1245,6 +1251,8 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/core/basic_types.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\..\..\Libraries\breakpad\src" "-I.\ThirdParty\minizip" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" + + @@ -1608,8 +1616,16 @@ $(QTDIR)\bin\moc.exe;%(FullPath) - - + + true + true + true + + + true + true + true + $(QTDIR)\bin\moc.exe;%(FullPath) @@ -2379,7 +2395,13 @@ - + + true + true + true + + + diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 964d49b76..bd8a0352a 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -82,6 +82,9 @@ cpp;moc False + + {3397dfda-79f1-4a4c-940b-be26b001baf5} + @@ -1077,6 +1080,12 @@ SourceFiles\ui\text + + GeneratedFiles\styles + + + GeneratedFiles\styles + @@ -1235,6 +1244,12 @@ SourceFiles\ui\text + + GeneratedFiles\styles + + + GeneratedFiles\styles + @@ -1539,5 +1554,11 @@ Resources + + Resources + + + Resources + \ No newline at end of file diff --git a/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj b/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj new file mode 100644 index 000000000..9e5a0eee3 --- /dev/null +++ b/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {7C25BFBD-7930-4DE2-AF33-27CE1CC521E6} + Qt4VSv1.0 + 8.1 + + + + Application + v140 + + + Application + v140 + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24730.2 + + + $(SolutionDir)$(Platform)\codegen\$(Configuration)\ + $(SolutionDir)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\codegen\$(Configuration)\ + $(SolutionDir)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + + + + UNICODE;WIN32;QT_CORE_LIB;%(PreprocessorDefinitions) + Disabled + ProgramDatabase + MultiThreadedDebug + .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);.\..\..\..\SourceFiles;$(QTDIR)\include\QtCore;.\..\%(AdditionalIncludeDirectories) + true + Level4 + true + /w44062 /wd4127 %(AdditionalOptions) + + + Console + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + true + ws2_32.lib;qtmaind.lib;qtharfbuzzngd.lib;qtpcred.lib;qtfreetyped.lib;Qt5Cored.lib;%(AdditionalDependencies) + + + + + UNICODE;WIN32;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;%(PreprocessorDefinitions) + + MultiThreaded + .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);.\..\..\..\SourceFiles;$(QTDIR)\include\QtCore;.\..\%(AdditionalIncludeDirectories) + true + Level4 + true + /w44062 /wd4127 %(AdditionalOptions) + + + Console + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + ws2_32.lib;qtmain.lib;qtharfbuzzng.lib;qtpcre.lib;qtfreetype.lib;Qt5Core.lib;%(AdditionalDependencies) + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj.filters b/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj.filters new file mode 100644 index 000000000..ed647d66b --- /dev/null +++ b/Telegram/build/vc/codegen_numbers/codegen_numbers.vcxproj.filters @@ -0,0 +1,82 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;cxx;c;def + + + {77524fac-2d55-437d-810f-c271326c7f95} + + + {48ba9931-338f-4476-8d83-600d0ac4440e} + + + + + src\common + + + src\common + + + src\common + + + src\common + + + src\common + + + src\numbers + + + src\numbers + + + src\numbers + + + src\numbers + + + src\numbers + + + + + src\common + + + src\common + + + src\common + + + src\common + + + src\common + + + src\common + + + src\common + + + src\numbers + + + src\numbers + + + src\numbers + + + src\numbers + + + \ No newline at end of file diff --git a/Telegram/build/vc/codegen_style/codegen_style.targets b/Telegram/build/vc/codegen_style/codegen_style.targets index 541943db3..f43879e86 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.targets +++ b/Telegram/build/vc/codegen_style/codegen_style.targets @@ -15,6 +15,6 @@ Inputs="%(CodegenStyleItem.Identity)" Outputs=".\GeneratedFiles\styles\style_%(CodegenStyleItem.Filename).h;.\GeneratedFiles\styles\style_%(CodegenStyleItem.Filename).cpp"> - + diff --git a/Telegram/build/vc/codegen_style/codegen_style.vcxproj b/Telegram/build/vc/codegen_style/codegen_style.vcxproj index 29b8674b4..aa95a1a6b 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.vcxproj +++ b/Telegram/build/vc/codegen_style/codegen_style.vcxproj @@ -14,11 +14,15 @@ + + + + @@ -27,11 +31,14 @@ + + - + + @@ -86,7 +93,7 @@ $(OutDir)\$(ProjectName).exe $(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories) true - ws2_32.lib;qtmaind.lib;qtharfbuzzngd.lib;qtpcred.lib;qtfreetyped.lib;imageformats\qwebpd.lib;Qt5Cored.lib;%(AdditionalDependencies) + ws2_32.lib;qtmaind.lib;qtharfbuzzngd.lib;qtpcred.lib;qtfreetyped.lib;imageformats\qwebpd.lib;Qt5Cored.lib;Qt5Guid.lib;%(AdditionalDependencies) @@ -94,7 +101,7 @@ UNICODE;WIN32;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;%(PreprocessorDefinitions) MultiThreadedDLL - .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;%(AdditionalIncludeDirectories) + .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);.\..\..\..\SourceFiles;$(QTDIR)\include\QtCore;.\..\%(AdditionalIncludeDirectories) true Level4 true @@ -105,7 +112,7 @@ $(OutDir)\$(ProjectName).exe $(QTDIR)\lib;%(AdditionalLibraryDirectories) false - qtmain.lib;Qt5Core.lib;%(AdditionalDependencies) + ws2_32.lib;qtmain.lib;qtharfbuzzng.lib;qtpcre.lib;qtfreetype.lib;imageformats\qwebp.lib;Qt5Core.lib;Qt5Gui.lib;%(AdditionalDependencies) diff --git a/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters b/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters index 20abd6169..ba57a5469 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters +++ b/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters @@ -22,9 +22,6 @@ src\common - - src\style - src\common @@ -40,11 +37,23 @@ src\style + + src\style + + + src\common + + + src\style + + + src\style + + + src\style + - - src\style - src\common @@ -66,14 +75,26 @@ src\style - - src\style - src\style src\style + + src\style + + + src\common + + + src\style + + + src\style + + + src\style + \ No newline at end of file