"Fossies" - the Fresh Open Source Software Archive 
Member "tdesktop-4.8.3/Telegram/SourceFiles/window/section_widget.cpp" (1 Jun 2023, 13212 Bytes) of package /linux/misc/tdesktop-4.8.3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "section_widget.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
4.8.1_vs_4.8.3.
1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "window/section_widget.h"
9
10 #include "mainwidget.h"
11 #include "ui/ui_utility.h"
12 #include "ui/chat/chat_theme.h"
13 #include "ui/painter.h"
14 #include "boxes/premium_preview_box.h"
15 #include "data/data_peer.h"
16 #include "data/data_user.h"
17 #include "data/data_document.h"
18 #include "data/data_document_media.h"
19 #include "data/data_changes.h"
20 #include "data/data_session.h"
21 #include "data/data_cloud_themes.h"
22 #include "data/data_message_reactions.h"
23 #include "data/data_peer_values.h"
24 #include "history/history.h"
25 #include "history/history_item.h"
26 #include "settings/settings_premium.h"
27 #include "main/main_session.h"
28 #include "window/section_memento.h"
29 #include "window/window_slide_animation.h"
30 #include "window/window_session_controller.h"
31 #include "window/themes/window_theme.h"
32
33 #include <rpl/range.h>
34
35 namespace Window {
36 namespace {
37
38 [[nodiscard]] rpl::producer<QString> PeerThemeEmojiValue(
39 not_null<PeerData*> peer) {
40 return peer->session().changes().peerFlagsValue(
41 peer,
42 Data::PeerUpdate::Flag::ChatThemeEmoji
43 ) | rpl::map([=] {
44 return peer->themeEmoji();
45 });
46 }
47
48 struct ResolvedPaper {
49 Data::WallPaper paper;
50 std::shared_ptr<Data::DocumentMedia> media;
51 };
52
53 [[nodiscard]] rpl::producer<std::optional<ResolvedPaper>> PeerWallPaperValue(
54 not_null<PeerData*> peer) {
55 return peer->session().changes().peerFlagsValue(
56 peer,
57 Data::PeerUpdate::Flag::ChatWallPaper
58 ) | rpl::map([=]() -> rpl::producer<std::optional<ResolvedPaper>> {
59 const auto paper = peer->wallPaper();
60 const auto single = [](std::optional<ResolvedPaper> value) {
61 return rpl::single(std::move(value));
62 };
63 if (!paper) {
64 return single({});
65 }
66 const auto document = paper->document();
67 auto value = ResolvedPaper{
68 *paper,
69 document ? document->createMediaView() : nullptr,
70 };
71 if (!value.media || value.media->loaded(true)) {
72 return single(std::move(value));
73 }
74 paper->loadDocument();
75 return single(
76 value
77 ) | rpl::then(document->session().downloaderTaskFinished(
78 ) | rpl::filter([=] {
79 return value.media->loaded(true);
80 }) | rpl::take(1) | rpl::map_to(
81 std::optional<ResolvedPaper>(value)
82 ));
83 }) | rpl::flatten_latest();
84 }
85
86 [[nodiscard]] auto MaybeChatThemeDataValueFromPeer(
87 not_null<PeerData*> peer)
88 -> rpl::producer<std::optional<Data::CloudTheme>> {
89 return PeerThemeEmojiValue(
90 peer
91 ) | rpl::map([=](const QString &emoji)
92 -> rpl::producer<std::optional<Data::CloudTheme>> {
93 return peer->owner().cloudThemes().themeForEmojiValue(emoji);
94 }) | rpl::flatten_latest();
95 }
96
97 [[nodiscard]] rpl::producer<> DebouncedPaletteValue() {
98 return [=](auto consumer) {
99 auto lifetime = rpl::lifetime();
100
101 struct State {
102 base::has_weak_ptr guard;
103 bool scheduled = false;
104 };
105 const auto state = lifetime.make_state<State>();
106
107 consumer.put_next_copy(rpl::empty);
108 style::PaletteChanged(
109 ) | rpl::start_with_next([=] {
110 if (state->scheduled) {
111 return;
112 }
113 state->scheduled = true;
114 Ui::PostponeCall(&state->guard, [=] {
115 state->scheduled = false;
116 consumer.put_next_copy(rpl::empty);
117 });
118 }, lifetime);
119
120 return lifetime;
121 };
122 }
123
124 struct ResolvedTheme {
125 std::optional<Data::CloudTheme> theme;
126 std::optional<ResolvedPaper> paper;
127 bool dark = false;
128 };
129
130 [[nodiscard]] auto MaybeCloudThemeValueFromPeer(
131 not_null<PeerData*> peer)
132 -> rpl::producer<ResolvedTheme> {
133 return rpl::combine(
134 MaybeChatThemeDataValueFromPeer(peer),
135 PeerWallPaperValue(peer),
136 Theme::IsThemeDarkValue() | rpl::distinct_until_changed()
137 ) | rpl::map([](
138 std::optional<Data::CloudTheme> theme,
139 std::optional<ResolvedPaper> paper,
140 bool night) -> rpl::producer<ResolvedTheme> {
141 if (theme || !paper) {
142 return rpl::single<ResolvedTheme>({
143 std::move(theme),
144 std::move(paper),
145 night,
146 });
147 }
148 return DebouncedPaletteValue(
149 ) | rpl::map([=] {
150 return ResolvedTheme{
151 .paper = paper,
152 .dark = night,
153 };
154 });
155 }) | rpl::flatten_latest();
156 }
157
158 } // namespace
159
160 AbstractSectionWidget::AbstractSectionWidget(
161 QWidget *parent,
162 not_null<SessionController*> controller,
163 rpl::producer<PeerData*> peerForBackground)
164 : RpWidget(parent)
165 , _controller(controller) {
166 std::move(
167 peerForBackground
168 ) | rpl::map([=](PeerData *peer) -> rpl::producer<> {
169 if (!peer) {
170 return rpl::single(rpl::empty) | rpl::then(
171 controller->defaultChatTheme()->repaintBackgroundRequests()
172 );
173 }
174 return ChatThemeValueFromPeer(
175 controller,
176 peer
177 ) | rpl::map([](const std::shared_ptr<Ui::ChatTheme> &theme) {
178 return rpl::single(rpl::empty) | rpl::then(
179 theme->repaintBackgroundRequests()
180 );
181 }) | rpl::flatten_latest();
182 }) | rpl::flatten_latest() | rpl::start_with_next([=] {
183 update();
184 }, lifetime());
185 }
186
187 Main::Session &AbstractSectionWidget::session() const {
188 return _controller->session();
189 }
190
191 SectionWidget::SectionWidget(
192 QWidget *parent,
193 not_null<Window::SessionController*> controller,
194 rpl::producer<PeerData*> peerForBackground)
195 : AbstractSectionWidget(parent, controller, std::move(peerForBackground)) {
196 }
197
198 SectionWidget::SectionWidget(
199 QWidget *parent,
200 not_null<Window::SessionController*> controller,
201 not_null<PeerData*> peerForBackground)
202 : AbstractSectionWidget(
203 parent,
204 controller,
205 rpl::single(peerForBackground.get())) {
206 }
207
208 void SectionWidget::setGeometryWithTopMoved(
209 const QRect &newGeometry,
210 int topDelta) {
211 _topDelta = topDelta;
212 bool willBeResized = (size() != newGeometry.size());
213 if (geometry() != newGeometry) {
214 auto weak = Ui::MakeWeak(this);
215 setGeometry(newGeometry);
216 if (!weak) {
217 return;
218 }
219 }
220 if (!willBeResized) {
221 resizeEvent(nullptr);
222 }
223 _topDelta = 0;
224 }
225
226 void SectionWidget::showAnimated(
227 SlideDirection direction,
228 const SectionSlideParams ¶ms) {
229 if (_showAnimation) {
230 return;
231 }
232
233 showChildren();
234 auto myContentCache = grabForShowAnimation(params);
235 hideChildren();
236 showAnimatedHook(params);
237
238 _showAnimation = std::make_unique<SlideAnimation>();
239 _showAnimation->setDirection(direction);
240 _showAnimation->setRepaintCallback([this] { update(); });
241 _showAnimation->setFinishedCallback([this] { showFinished(); });
242 _showAnimation->setPixmaps(
243 params.oldContentCache,
244 myContentCache);
245 _showAnimation->setTopBarShadow(params.withTopBarShadow);
246 _showAnimation->setWithFade(params.withFade);
247 _showAnimation->setTopSkip(params.topSkip);
248 _showAnimation->setTopBarMask(params.topMask);
249 _showAnimation->start();
250
251 show();
252 }
253
254 std::shared_ptr<SectionMemento> SectionWidget::createMemento() {
255 return nullptr;
256 }
257
258 void SectionWidget::showFast() {
259 show();
260 showFinished();
261 }
262
263 QPixmap SectionWidget::grabForShowAnimation(
264 const SectionSlideParams ¶ms) {
265 return Ui::GrabWidget(this);
266 }
267
268 void SectionWidget::PaintBackground(
269 not_null<Window::SessionController*> controller,
270 not_null<Ui::ChatTheme*> theme,
271 not_null<QWidget*> widget,
272 QRect clip) {
273 PaintBackground(
274 theme,
275 widget,
276 controller->content()->height(),
277 controller->content()->backgroundFromY(),
278 clip);
279 }
280
281 void SectionWidget::PaintBackground(
282 not_null<Ui::ChatTheme*> theme,
283 not_null<QWidget*> widget,
284 int fillHeight,
285 int fromy,
286 QRect clip) {
287 auto p = QPainter(widget);
288 if (fromy) {
289 p.translate(0, fromy);
290 clip = clip.translated(0, -fromy);
291 }
292 PaintBackground(p, theme, QSize(widget->width(), fillHeight), clip);
293 }
294
295 void SectionWidget::PaintBackground(
296 QPainter &p,
297 not_null<Ui::ChatTheme*> theme,
298 QSize fill,
299 QRect clip) {
300 const auto &background = theme->background();
301 if (background.colorForFill) {
302 p.fillRect(clip, *background.colorForFill);
303 return;
304 }
305 const auto &gradient = background.gradientForFill;
306 auto state = theme->backgroundState(fill);
307 const auto paintCache = [&](const Ui::CachedBackground &cache) {
308 const auto to = QRect(
309 QPoint(cache.x, cache.y),
310 cache.pixmap.size() / cIntRetinaFactor());
311 if (cache.waitingForNegativePattern) {
312 // While we wait for pattern being loaded we paint just gradient.
313 // But in case of negative patter opacity we just fill-black.
314 p.fillRect(to, Qt::black);
315 } else if (cache.area == fill) {
316 p.drawPixmap(to, cache.pixmap);
317 } else {
318 const auto sx = fill.width() / float64(cache.area.width());
319 const auto sy = fill.height() / float64(cache.area.height());
320 const auto round = [](float64 value) -> int {
321 return (value >= 0.)
322 ? int(std::ceil(value))
323 : int(std::floor(value));
324 };
325 const auto sto = QPoint(round(to.x() * sx), round(to.y() * sy));
326 p.drawPixmap(
327 sto.x(),
328 sto.y(),
329 round((to.x() + to.width()) * sx) - sto.x(),
330 round((to.y() + to.height()) * sy) - sto.y(),
331 cache.pixmap);
332 }
333 };
334 const auto hasNow = !state.now.pixmap.isNull();
335 const auto goodNow = hasNow && (state.now.area == fill);
336 const auto useCache = goodNow || !gradient.isNull();
337 if (useCache) {
338 const auto fade = (state.shown < 1. && !gradient.isNull());
339 if (fade) {
340 paintCache(state.was);
341 p.setOpacity(state.shown);
342 }
343 paintCache(state.now);
344 if (fade) {
345 p.setOpacity(1.);
346 }
347 return;
348 }
349 const auto &prepared = background.prepared;
350 if (prepared.isNull()) {
351 return;
352 } else if (background.isPattern) {
353 const auto w = prepared.width() * fill.height() / prepared.height();
354 const auto cx = qCeil(fill.width() / float64(w));
355 const auto cols = (cx / 2) * 2 + 1;
356 const auto xshift = (fill.width() - w * cols) / 2;
357 for (auto i = 0; i != cols; ++i) {
358 p.drawImage(
359 QRect(xshift + i * w, 0, w, fill.height()),
360 prepared,
361 QRect(QPoint(), prepared.size()));
362 }
363 } else if (background.tile) {
364 const auto &tiled = background.preparedForTiled;
365 const auto left = clip.left();
366 const auto top = clip.top();
367 const auto right = clip.left() + clip.width();
368 const auto bottom = clip.top() + clip.height();
369 const auto w = tiled.width() / cRetinaFactor();
370 const auto h = tiled.height() / cRetinaFactor();
371 const auto sx = qFloor(left / w);
372 const auto sy = qFloor(top / h);
373 const auto cx = qCeil(right / w);
374 const auto cy = qCeil(bottom / h);
375 for (auto i = sx; i < cx; ++i) {
376 for (auto j = sy; j < cy; ++j) {
377 p.drawImage(QPointF(i * w, j * h), tiled);
378 }
379 }
380 } else {
381 const auto hq = PainterHighQualityEnabler(p);
382 const auto rects = Ui::ComputeChatBackgroundRects(
383 fill,
384 prepared.size());
385 p.drawImage(rects.to, prepared, rects.from);
386 }
387 }
388
389 void SectionWidget::paintEvent(QPaintEvent *e) {
390 if (_showAnimation) {
391 auto p = QPainter(this);
392 _showAnimation->paintContents(p);
393 }
394 }
395
396 bool SectionWidget::animatingShow() const {
397 return (_showAnimation != nullptr);
398 }
399
400 void SectionWidget::showFinished() {
401 _showAnimation.reset();
402 if (isHidden()) return;
403
404 showChildren();
405 showFinishedHook();
406
407 setInnerFocus();
408 }
409
410 rpl::producer<int> SectionWidget::desiredHeight() const {
411 return rpl::single(height());
412 }
413
414 SectionWidget::~SectionWidget() = default;
415
416 auto ChatThemeValueFromPeer(
417 not_null<SessionController*> controller,
418 not_null<PeerData*> peer)
419 -> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
420 auto cloud = MaybeCloudThemeValueFromPeer(
421 peer
422 ) | rpl::map([=](ResolvedTheme resolved)
423 -> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
424 if (!resolved.theme && !resolved.paper) {
425 return rpl::single(controller->defaultChatTheme());
426 }
427 const auto theme = resolved.theme.value_or(Data::CloudTheme());
428 const auto paper = resolved.paper
429 ? resolved.paper->paper
430 : Data::WallPaper(0);
431 const auto type = resolved.dark
432 ? Data::CloudThemeType::Dark
433 : Data::CloudThemeType::Light;
434 if (paper.document()
435 && resolved.paper->media
436 && !resolved.paper->media->loaded()
437 && !controller->chatThemeAlreadyCached(theme, paper, type)) {
438 return rpl::single(controller->defaultChatTheme());
439 }
440 return controller->cachedChatThemeValue(theme, paper, type);
441 }) | rpl::flatten_latest(
442 ) | rpl::distinct_until_changed();
443
444 return rpl::combine(
445 std::move(cloud),
446 controller->peerThemeOverrideValue()
447 ) | rpl::map([=](
448 std::shared_ptr<Ui::ChatTheme> &&cloud,
449 PeerThemeOverride &&overriden) {
450 return (overriden.peer == peer.get()
451 && Ui::Emoji::Find(peer->themeEmoji()) != overriden.emoji)
452 ? std::move(overriden.theme)
453 : std::move(cloud);
454 });
455 }
456
457 bool ShowSendPremiumError(
458 not_null<SessionController*> controller,
459 not_null<DocumentData*> document) {
460 if (!document->isPremiumSticker()
461 || document->session().premium()) {
462 return false;
463 }
464 ShowStickerPreviewBox(controller, document);
465 return true;
466 }
467
468 bool ShowReactPremiumError(
469 not_null<SessionController*> controller,
470 not_null<HistoryItem*> item,
471 const Data::ReactionId &id) {
472 if (controller->session().premium()
473 || ranges::contains(item->chosenReactions(), id)) {
474 return false;
475 }
476 const auto &list = controller->session().data().reactions().list(
477 Data::Reactions::Type::Active);
478 const auto i = ranges::find(list, id, &Data::Reaction::id);
479 if (i == end(list) || !i->premium) {
480 if (!id.custom()) {
481 return false;
482 }
483 }
484 ShowPremiumPreviewBox(controller, PremiumPreview::InfiniteReactions);
485 return true;
486 }
487
488 } // namespace Window