auto_delete_settings.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 "ui/boxes/auto_delete_settings.h"
  8. #include "ui/widgets/checkbox.h"
  9. #include "ui/painter.h"
  10. #include "lang/lang_keys.h"
  11. #include "styles/style_chat.h"
  12. #include "styles/style_layers.h"
  13. namespace Ui {
  14. namespace {
  15. object_ptr<Ui::RpWidget> CreateSliderForTTL(
  16. not_null<QWidget*> parent,
  17. std::vector<QString> labels,
  18. int dashedAfterIndex,
  19. int selected,
  20. Fn<void(int)> callback) {
  21. Expects(labels.size() > 1);
  22. Expects(selected >= 0 && selected < labels.size());
  23. Expects(dashedAfterIndex >= 0 && dashedAfterIndex < labels.size());
  24. struct State {
  25. std::vector<int> points;
  26. std::vector<QString> labels;
  27. int selected = 0;
  28. };
  29. static const auto st = &st::defaultSliderForTTL;
  30. const auto height = st->font->height + st->skip + st->chosenSize;
  31. const auto count = int(labels.size());
  32. auto result = object_ptr<Ui::FixedHeightWidget>(parent.get(), height);
  33. const auto raw = result.data();
  34. const auto slider = Ui::CreateChild<Ui::FixedHeightWidget>(
  35. raw,
  36. st->chosenSize);
  37. slider->setCursor(style::cur_pointer);
  38. slider->move(0, height - slider->height());
  39. auto &lifetime = raw->lifetime();
  40. const auto state = lifetime.make_state<State>(State{
  41. .labels = std::move(labels),
  42. .selected = selected
  43. });
  44. state->points.resize(count, 0);
  45. raw->widthValue(
  46. ) | rpl::start_with_next([=](int width) {
  47. for (auto i = 0; i != count; ++i) {
  48. state->points[i] = (width * i) / (count - 1);
  49. }
  50. slider->resize(width, slider->height());
  51. }, lifetime);
  52. raw->paintRequest(
  53. ) | rpl::start_with_next([=] {
  54. auto p = QPainter(raw);
  55. p.setFont(st->font);
  56. for (auto i = 0; i != count; ++i) {
  57. // Label
  58. p.setPen(st->textFg);
  59. const auto &text = state->labels[i];
  60. const auto textWidth = st->font->width(text);
  61. const auto shift = (i == count - 1)
  62. ? textWidth
  63. : (i > 0)
  64. ? (textWidth / 2)
  65. : 0;
  66. const auto x = state->points[i] - shift;
  67. const auto y = st->font->ascent;
  68. p.drawText(x, y, text);
  69. }
  70. }, lifetime);
  71. slider->paintRequest(
  72. ) | rpl::start_with_next([=] {
  73. auto p = QPainter(slider);
  74. auto hq = PainterHighQualityEnabler(p);
  75. p.setFont(st->font);
  76. for (auto i = 0; i != count; ++i) {
  77. const auto middle = (st->chosenSize / 2.);
  78. // Point
  79. const auto size = (i == state->selected)
  80. ? st->chosenSize
  81. : st->pointSize;
  82. const auto pointfg = (i <= state->selected)
  83. ? st->activeFg
  84. : st->inactiveFg;
  85. const auto shift = (i == count - 1)
  86. ? float64(size)
  87. : (i > 0)
  88. ? (size / 2.)
  89. : 0.;
  90. const auto pointx = state->points[i] - shift;
  91. const auto pointy = middle - (size / 2.);
  92. p.setPen(Qt::NoPen);
  93. p.setBrush(pointfg);
  94. p.drawEllipse(QRectF{ pointx, pointy, size * 1., size * 1. });
  95. // Line
  96. if (i + 1 == count) {
  97. break;
  98. }
  99. const auto nextSize = (i + 1 == state->selected)
  100. ? st->chosenSize
  101. : st->pointSize;
  102. const auto nextShift = (i + 1 == count - 1)
  103. ? float64(nextSize)
  104. : (nextSize / 2.);
  105. const auto &linefg = (i + 1 <= state->selected)
  106. ? st->activeFg
  107. : st->inactiveFg;
  108. const auto from = pointx + size + st->stroke * 1.5;
  109. const auto till = state->points[i + 1] - nextShift - st->stroke * 1.5;
  110. auto pen = linefg->p;
  111. pen.setWidthF(st->stroke);
  112. if (i >= dashedAfterIndex) {
  113. // Try to fill the line with exact number of dash segments.
  114. // UPD Doesn't work so well because it changes when clicking.
  115. //const auto length = till - from;
  116. //const auto offSegmentsCount = int(base::SafeRound(
  117. // (length - st->dashOn) / (st->dashOn + st->dashOff)));
  118. //const auto onSegmentsCount = offSegmentsCount + 1;
  119. //const auto idealLength = offSegmentsCount * st->dashOff
  120. // + onSegmentsCount * st->dashOn;
  121. //const auto multiplier = length / float64(idealLength);
  122. const auto multiplier = 1.;
  123. auto dashPattern = QVector<qreal>{
  124. st->dashOn * multiplier / st->stroke,
  125. st->dashOff * multiplier / st->stroke
  126. };
  127. pen.setDashPattern(dashPattern);
  128. }
  129. pen.setCapStyle(Qt::RoundCap);
  130. p.setPen(pen);
  131. p.setBrush(Qt::NoBrush);
  132. p.drawLine(QPointF(from, middle), QPointF(till, middle));
  133. }
  134. }, lifetime);
  135. slider->events(
  136. ) | rpl::filter([=](not_null<QEvent*> e) {
  137. return (e->type() == QEvent::MouseButtonPress)
  138. && (static_cast<QMouseEvent*>(e.get())->button()
  139. == Qt::LeftButton)
  140. && (state->points[1] > 0);
  141. }) | rpl::map([=](not_null<QEvent*> e) {
  142. return rpl::single(
  143. static_cast<QMouseEvent*>(e.get())->pos()
  144. ) | rpl::then(slider->events(
  145. ) | rpl::take_while([=](not_null<QEvent*> e) {
  146. return (e->type() != QEvent::MouseButtonRelease)
  147. || (static_cast<QMouseEvent*>(e.get())->button()
  148. != Qt::LeftButton);
  149. }) | rpl::filter([=](not_null<QEvent*> e) {
  150. return (e->type() == QEvent::MouseMove);
  151. }) | rpl::map([=](not_null<QEvent*> e) {
  152. return static_cast<QMouseEvent*>(e.get())->pos();
  153. }));
  154. }) | rpl::flatten_latest(
  155. ) | rpl::start_with_next([=](QPoint position) {
  156. state->selected = std::clamp(
  157. (position.x() + (state->points[1] / 2)) / state->points[1],
  158. 0,
  159. count - 1);
  160. slider->update();
  161. callback(state->selected);
  162. }, lifetime);
  163. return result;
  164. }
  165. } // namespace
  166. void AutoDeleteSettingsBox(
  167. not_null<Ui::GenericBox*> box,
  168. TimeId ttlPeriod,
  169. rpl::producer<QString> about,
  170. Fn<void(TimeId)> callback) {
  171. box->setTitle(tr::lng_manage_messages_ttl_title());
  172. struct State {
  173. TimeId period = 0;
  174. };
  175. const auto state = box->lifetime().make_state<State>(State{
  176. .period = ttlPeriod,
  177. });
  178. const auto options = std::vector<QString>{
  179. tr::lng_manage_messages_ttl_disable(tr::now),
  180. //u"5 seconds"_q, AssertIsDebug()
  181. tr::lng_manage_messages_ttl_after1(tr::now),
  182. tr::lng_manage_messages_ttl_after2(tr::now),
  183. tr::lng_manage_messages_ttl_after3(tr::now),
  184. };
  185. const auto periodToIndex = [&](TimeId period) {
  186. return !period
  187. ? 0
  188. //: (period == 5) AssertIsDebug()
  189. //? 1 AssertIsDebug()
  190. : (period < 2 * 86400)
  191. ? 1
  192. : (period < 8 * 86400)
  193. ? 2
  194. : 3;
  195. };
  196. const auto indexToPeriod = [&](int index) {
  197. return !index
  198. ? 0
  199. //: (index == 1) AssertIsDebug()
  200. //? 5 AssertIsDebug()
  201. : (index == 1)
  202. ? 86400
  203. : (index == 2)
  204. ? 7 * 86400
  205. : 31 * 86400;
  206. };
  207. const auto sliderCallback = [=](int index) {
  208. state->period = indexToPeriod(index);
  209. };
  210. box->addRow(
  211. CreateSliderForTTL(
  212. box,
  213. options | ranges::to_vector,
  214. options.size() - 1,
  215. periodToIndex(ttlPeriod),
  216. sliderCallback),
  217. {
  218. st::boxRowPadding.left(),
  219. 0,
  220. st::boxRowPadding.right(),
  221. st::boxMediumSkip });
  222. box->addRow(
  223. object_ptr<Ui::DividerLabel>(
  224. box,
  225. object_ptr<Ui::FlatLabel>(
  226. box,
  227. std::move(about),
  228. st::boxDividerLabel),
  229. st::ttlDividerLabelPadding),
  230. style::margins());
  231. box->addButton(tr::lng_settings_save(), [=] {
  232. const auto period = state->period;
  233. box->closeBox();
  234. callback(period);
  235. });
  236. box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
  237. }
  238. } // namespace Ui