settings_common.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "settings/settings_common.h"
  8. #include "lottie/lottie_icon.h"
  9. #include "ui/painter.h"
  10. #include "ui/widgets/buttons.h"
  11. #include "ui/widgets/continuous_sliders.h"
  12. #include "ui/widgets/labels.h"
  13. #include "ui/wrap/vertical_layout.h"
  14. #include "styles/style_settings.h"
  15. #include <QAction>
  16. namespace Settings {
  17. Icon::Icon(IconDescriptor descriptor) : _icon(descriptor.icon) {
  18. const auto background = [&]() -> const style::color* {
  19. if (descriptor.type == IconType::Simple) {
  20. return nullptr;
  21. }
  22. return descriptor.background;
  23. }();
  24. if (background) {
  25. const auto radius = (descriptor.type == IconType::Rounded)
  26. ? st::settingsIconRadius
  27. : (std::min(_icon->width(), _icon->height()) / 2);
  28. _background.emplace(radius, *background);
  29. } else if (const auto brush = descriptor.backgroundBrush) {
  30. const auto radius = (descriptor.type == IconType::Rounded)
  31. ? st::settingsIconRadius
  32. : (std::min(_icon->width(), _icon->height()) / 2);
  33. _backgroundBrush.emplace(radius, std::move(*brush));
  34. }
  35. }
  36. void Icon::paint(QPainter &p, QPoint position) const {
  37. paint(p, position.x(), position.y());
  38. }
  39. void Icon::paint(QPainter &p, int x, int y) const {
  40. if (_background) {
  41. _background->paint(p, { { x, y }, _icon->size() });
  42. } else if (_backgroundBrush) {
  43. PainterHighQualityEnabler hq(p);
  44. p.setPen(Qt::NoPen);
  45. p.setBrush(_backgroundBrush->second);
  46. p.drawRoundedRect(
  47. QRect(QPoint(x, y), _icon->size()),
  48. _backgroundBrush->first,
  49. _backgroundBrush->first);
  50. }
  51. _icon->paint(p, { x, y }, 2 * x + _icon->width());
  52. }
  53. int Icon::width() const {
  54. return _icon->width();
  55. }
  56. int Icon::height() const {
  57. return _icon->height();
  58. }
  59. QSize Icon::size() const {
  60. return _icon->size();
  61. }
  62. void AddButtonIcon(
  63. not_null<Ui::AbstractButton*> button,
  64. const style::SettingsButton &st,
  65. IconDescriptor &&descriptor) {
  66. Expects(descriptor.icon != nullptr);
  67. struct IconWidget {
  68. IconWidget(QWidget *parent, IconDescriptor &&descriptor)
  69. : widget(parent)
  70. , icon(std::move(descriptor)) {
  71. }
  72. Ui::RpWidget widget;
  73. Icon icon;
  74. };
  75. const auto icon = button->lifetime().make_state<IconWidget>(
  76. button,
  77. std::move(descriptor));
  78. icon->widget.setAttribute(Qt::WA_TransparentForMouseEvents);
  79. icon->widget.resize(icon->icon.size());
  80. icon->widget.show();
  81. button->sizeValue(
  82. ) | rpl::start_with_next([=, left = st.iconLeft](QSize size) {
  83. icon->widget.moveToLeft(
  84. left,
  85. (size.height() - icon->widget.height()) / 2,
  86. size.width());
  87. }, icon->widget.lifetime());
  88. icon->widget.paintRequest(
  89. ) | rpl::start_with_next([=] {
  90. auto p = QPainter(&icon->widget);
  91. icon->icon.paint(p, 0, 0);
  92. }, icon->widget.lifetime());
  93. }
  94. object_ptr<Button> CreateButtonWithIcon(
  95. not_null<QWidget*> parent,
  96. rpl::producer<QString> text,
  97. const style::SettingsButton &st,
  98. IconDescriptor &&descriptor) {
  99. auto result = object_ptr<Button>(parent, std::move(text), st);
  100. const auto button = result.data();
  101. if (descriptor) {
  102. AddButtonIcon(button, st, std::move(descriptor));
  103. }
  104. return result;
  105. }
  106. not_null<Button*> AddButtonWithIcon(
  107. not_null<Ui::VerticalLayout*> container,
  108. rpl::producer<QString> text,
  109. const style::SettingsButton &st,
  110. IconDescriptor &&descriptor) {
  111. return container->add(
  112. CreateButtonWithIcon(container, std::move(text), st, std::move(descriptor)));
  113. }
  114. void CreateRightLabel(
  115. not_null<Button*> button,
  116. rpl::producer<QString> label,
  117. const style::SettingsButton &st,
  118. rpl::producer<QString> buttonText) {
  119. const auto name = Ui::CreateChild<Ui::FlatLabel>(
  120. button.get(),
  121. st.rightLabel);
  122. name->show();
  123. rpl::combine(
  124. button->widthValue(),
  125. std::move(buttonText),
  126. std::move(label)
  127. ) | rpl::start_with_next([=, &st](
  128. int width,
  129. const QString &button,
  130. const QString &text) {
  131. const auto available = width
  132. - st.padding.left()
  133. - st.padding.right()
  134. - st.style.font->width(button)
  135. - st::settingsButtonRightSkip;
  136. name->setText(text);
  137. name->resizeToNaturalWidth(available);
  138. name->moveToRight(st::settingsButtonRightSkip, st.padding.top());
  139. }, name->lifetime());
  140. name->setAttribute(Qt::WA_TransparentForMouseEvents);
  141. }
  142. not_null<Button*> AddButtonWithLabel(
  143. not_null<Ui::VerticalLayout*> container,
  144. rpl::producer<QString> text,
  145. rpl::producer<QString> label,
  146. const style::SettingsButton &st,
  147. IconDescriptor &&descriptor) {
  148. const auto button = AddButtonWithIcon(
  149. container,
  150. rpl::duplicate(text),
  151. st,
  152. std::move(descriptor));
  153. CreateRightLabel(button, std::move(label), st, std::move(text));
  154. return button;
  155. }
  156. void AddDividerTextWithLottie(
  157. not_null<Ui::VerticalLayout*> container,
  158. DividerWithLottieDescriptor &&descriptor) {
  159. const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
  160. container.get(),
  161. 0,
  162. st::boxDividerBg,
  163. descriptor.parts);
  164. const auto verticalLayout = container->add(
  165. object_ptr<Ui::VerticalLayout>(container.get()));
  166. const auto size = descriptor.lottieSize.value_or(
  167. st::settingsFilterIconSize);
  168. auto icon = CreateLottieIcon(
  169. verticalLayout,
  170. {
  171. .name = descriptor.lottie,
  172. .sizeOverride = { size, size },
  173. },
  174. descriptor.lottieMargins.value_or(st::settingsFilterIconPadding));
  175. if (descriptor.showFinished) {
  176. const auto repeat = descriptor.lottieRepeat.value_or(
  177. anim::repeat::once);
  178. std::move(
  179. descriptor.showFinished
  180. ) | rpl::start_with_next([animate = std::move(icon.animate), repeat] {
  181. animate(repeat);
  182. }, verticalLayout->lifetime());
  183. }
  184. verticalLayout->add(std::move(icon.widget));
  185. if (descriptor.about) {
  186. verticalLayout->add(
  187. object_ptr<Ui::CenterWrap<>>(
  188. verticalLayout,
  189. object_ptr<Ui::FlatLabel>(
  190. verticalLayout,
  191. std::move(descriptor.about),
  192. st::settingsFilterDividerLabel)),
  193. descriptor.aboutMargins.value_or(
  194. st::settingsFilterDividerLabelPadding));
  195. }
  196. verticalLayout->geometryValue(
  197. ) | rpl::start_with_next([=](const QRect &r) {
  198. divider->setGeometry(r);
  199. }, divider->lifetime());
  200. }
  201. LottieIcon CreateLottieIcon(
  202. not_null<QWidget*> parent,
  203. Lottie::IconDescriptor &&descriptor,
  204. style::margins padding) {
  205. Expects(!descriptor.frame); // I'm not sure it considers limitFps.
  206. descriptor.limitFps = true;
  207. auto object = object_ptr<Ui::RpWidget>(parent);
  208. const auto raw = object.data();
  209. const auto width = descriptor.sizeOverride.width();
  210. raw->resize(QRect(
  211. QPoint(),
  212. descriptor.sizeOverride).marginsAdded(padding).size());
  213. auto owned = Lottie::MakeIcon(std::move(descriptor));
  214. const auto icon = owned.get();
  215. raw->lifetime().add([kept = std::move(owned)]{});
  216. const auto looped = raw->lifetime().make_state<bool>(true);
  217. const auto start = [=] {
  218. icon->animate([=] { raw->update(); }, 0, icon->framesCount() - 1);
  219. };
  220. const auto animate = [=](anim::repeat repeat) {
  221. *looped = (repeat == anim::repeat::loop);
  222. start();
  223. };
  224. raw->paintRequest(
  225. ) | rpl::start_with_next([=] {
  226. auto p = QPainter(raw);
  227. const auto left = (raw->width() - width) / 2;
  228. icon->paint(p, left, padding.top());
  229. if (!icon->animating() && icon->frameIndex() > 0 && *looped) {
  230. start();
  231. }
  232. }, raw->lifetime());
  233. return { .widget = std::move(object), .animate = std::move(animate) };
  234. }
  235. SliderWithLabel MakeSliderWithLabel(
  236. QWidget *parent,
  237. const style::MediaSlider &sliderSt,
  238. const style::FlatLabel &labelSt,
  239. int skip,
  240. int minLabelWidth,
  241. bool ignoreWheel) {
  242. auto result = object_ptr<Ui::RpWidget>(parent);
  243. const auto raw = result.data();
  244. const auto height = std::max(
  245. sliderSt.seekSize.height(),
  246. labelSt.style.font->height);
  247. raw->resize(sliderSt.seekSize.width(), height);
  248. const auto slider = ignoreWheel
  249. ? Ui::CreateChild<Ui::MediaSliderWheelless>(raw, sliderSt)
  250. : Ui::CreateChild<Ui::MediaSlider>(raw, sliderSt);
  251. const auto label = Ui::CreateChild<Ui::FlatLabel>(raw, labelSt);
  252. slider->resize(slider->width(), sliderSt.seekSize.height());
  253. rpl::combine(
  254. raw->sizeValue(),
  255. label->sizeValue()
  256. ) | rpl::start_with_next([=](QSize outer, QSize size) {
  257. const auto right = std::max(size.width(), minLabelWidth) + skip;
  258. label->moveToRight(0, (outer.height() - size.height()) / 2);
  259. const auto width = std::max(
  260. sliderSt.seekSize.width(),
  261. outer.width() - right);
  262. slider->resizeToWidth(width);
  263. slider->moveToLeft(0, (outer.height() - slider->height()) / 2);
  264. }, label->lifetime());
  265. return {
  266. .widget = std::move(result),
  267. .slider = slider,
  268. .label = label,
  269. };
  270. }
  271. } // namespace Settings