mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Apply geometry constraints in stories viewer.
This commit is contained in:
parent
89ca38ed29
commit
027bd89e5b
15 changed files with 607 additions and 91 deletions
|
@ -962,6 +962,8 @@ PRIVATE
|
||||||
media/player/media_player_volume_controller.h
|
media/player/media_player_volume_controller.h
|
||||||
media/player/media_player_widget.cpp
|
media/player/media_player_widget.cpp
|
||||||
media/player/media_player_widget.h
|
media/player/media_player_widget.h
|
||||||
|
media/stories/media_stories_controller.cpp
|
||||||
|
media/stories/media_stories_controller.h
|
||||||
media/stories/media_stories_delegate.cpp
|
media/stories/media_stories_delegate.cpp
|
||||||
media/stories/media_stories_delegate.h
|
media/stories/media_stories_delegate.h
|
||||||
media/stories/media_stories_header.cpp
|
media/stories/media_stories_header.cpp
|
||||||
|
|
|
@ -355,6 +355,7 @@ public:
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
rpl::producer<QString> description,
|
rpl::producer<QString> description,
|
||||||
rpl::producer<WebPageData*> page);
|
rpl::producer<WebPageData*> page);
|
||||||
|
void previewUnregister();
|
||||||
|
|
||||||
[[nodiscard]] bool isDisplayed() const;
|
[[nodiscard]] bool isDisplayed() const;
|
||||||
[[nodiscard]] bool isEditingMessage() const;
|
[[nodiscard]] bool isEditingMessage() const;
|
||||||
|
@ -413,6 +414,7 @@ private:
|
||||||
rpl::event_stream<> _replyCancelled;
|
rpl::event_stream<> _replyCancelled;
|
||||||
rpl::event_stream<> _forwardCancelled;
|
rpl::event_stream<> _forwardCancelled;
|
||||||
rpl::event_stream<> _previewCancelled;
|
rpl::event_stream<> _previewCancelled;
|
||||||
|
rpl::lifetime _previewLifetime;
|
||||||
|
|
||||||
rpl::variable<FullMsgId> _editMsgId;
|
rpl::variable<FullMsgId> _editMsgId;
|
||||||
rpl::variable<FullMsgId> _replyToId;
|
rpl::variable<FullMsgId> _replyToId;
|
||||||
|
@ -677,17 +679,18 @@ void FieldHeader::resolveMessageData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldHeader::previewRequested(
|
void FieldHeader::previewRequested(
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
rpl::producer<QString> description,
|
rpl::producer<QString> description,
|
||||||
rpl::producer<WebPageData*> page) {
|
rpl::producer<WebPageData*> page) {
|
||||||
|
_previewLifetime.destroy();
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
title
|
title
|
||||||
) | rpl::filter([=] {
|
) | rpl::filter([=] {
|
||||||
return !_preview.cancelled;
|
return !_preview.cancelled;
|
||||||
}) | start_with_next([=](const QString &t) {
|
}) | rpl::start_with_next([=](const QString &t) {
|
||||||
_title = t;
|
_title = t;
|
||||||
}, lifetime());
|
}, _previewLifetime);
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
description
|
description
|
||||||
|
@ -695,7 +698,7 @@ void FieldHeader::previewRequested(
|
||||||
return !_preview.cancelled;
|
return !_preview.cancelled;
|
||||||
}) | rpl::start_with_next([=](const QString &d) {
|
}) | rpl::start_with_next([=](const QString &d) {
|
||||||
_description = d;
|
_description = d;
|
||||||
}, lifetime());
|
}, _previewLifetime);
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
page
|
page
|
||||||
|
@ -704,8 +707,11 @@ void FieldHeader::previewRequested(
|
||||||
}) | rpl::start_with_next([=](WebPageData *p) {
|
}) | rpl::start_with_next([=](WebPageData *p) {
|
||||||
_preview.data = p;
|
_preview.data = p;
|
||||||
updateVisible();
|
updateVisible();
|
||||||
}, lifetime());
|
}, _previewLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldHeader::previewUnregister() {
|
||||||
|
_previewLifetime.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldHeader::paintWebPage(Painter &p, not_null<PeerData*> context) {
|
void FieldHeader::paintWebPage(Painter &p, not_null<PeerData*> context) {
|
||||||
|
@ -996,10 +1002,6 @@ Main::Session &ComposeControls::session() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
||||||
// Right now only single non-null set of history is supported.
|
|
||||||
// Otherwise initWebpageProcess should be updated / rewritten.
|
|
||||||
Expects(!_history && (*args.history));
|
|
||||||
|
|
||||||
_showSlowmodeError = std::move(args.showSlowmodeError);
|
_showSlowmodeError = std::move(args.showSlowmodeError);
|
||||||
_sendActionFactory = std::move(args.sendActionFactory);
|
_sendActionFactory = std::move(args.sendActionFactory);
|
||||||
_slowmodeSecondsLeft = rpl::single(0)
|
_slowmodeSecondsLeft = rpl::single(0)
|
||||||
|
@ -1014,6 +1016,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
||||||
}
|
}
|
||||||
unregisterDraftSources();
|
unregisterDraftSources();
|
||||||
_history = history;
|
_history = history;
|
||||||
|
_historyLifetime.destroy();
|
||||||
_header->setHistory(args);
|
_header->setHistory(args);
|
||||||
registerDraftSource();
|
registerDraftSource();
|
||||||
_selector->setCurrentPeer(history ? history->peer.get() : nullptr);
|
_selector->setCurrentPeer(history ? history->peer.get() : nullptr);
|
||||||
|
@ -1025,9 +1028,12 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
updateFieldPlaceholder();
|
updateFieldPlaceholder();
|
||||||
updateAttachBotsMenu();
|
updateAttachBotsMenu();
|
||||||
//if (!_history) {
|
|
||||||
// return;
|
_sendAs = nullptr;
|
||||||
//}
|
_silent = nullptr;
|
||||||
|
if (!_history) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto peer = _history->peer;
|
const auto peer = _history->peer;
|
||||||
initSendAsButton(peer);
|
initSendAsButton(peer);
|
||||||
if (peer->isChat() && peer->asChat()->noParticipantInfo()) {
|
if (peer->isChat() && peer->asChat()->noParticipantInfo()) {
|
||||||
|
@ -2134,7 +2140,7 @@ void ComposeControls::initSendAsButton(not_null<PeerData*> peer) {
|
||||||
updateControlsGeometry(_wrap->size());
|
updateControlsGeometry(_wrap->size());
|
||||||
orderControls();
|
orderControls();
|
||||||
}
|
}
|
||||||
}, _wrap->lifetime());
|
}, _historyLifetime);
|
||||||
|
|
||||||
updateSendAsButton();
|
updateSendAsButton();
|
||||||
}
|
}
|
||||||
|
@ -2218,7 +2224,7 @@ void ComposeControls::initVoiceRecordBar() {
|
||||||
_voiceRecordBar->setStartRecordingFilter([=] {
|
_voiceRecordBar->setStartRecordingFilter([=] {
|
||||||
const auto error = [&]() -> std::optional<QString> {
|
const auto error = [&]() -> std::optional<QString> {
|
||||||
const auto peer = _history ? _history->peer.get() : nullptr;
|
const auto peer = _history ? _history->peer.get() : nullptr;
|
||||||
if (!peer) {
|
if (peer) {
|
||||||
if (const auto error = Data::RestrictionError(
|
if (const auto error = Data::RestrictionError(
|
||||||
peer,
|
peer,
|
||||||
ChatRestriction::SendVoiceMessages)) {
|
ChatRestriction::SendVoiceMessages)) {
|
||||||
|
@ -2448,10 +2454,9 @@ void ComposeControls::updateMessagesTTLShown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComposeControls::updateSendAsButton() {
|
bool ComposeControls::updateSendAsButton() {
|
||||||
Expects(_history != nullptr);
|
const auto peer = _history ? _history->peer.get() : nullptr;
|
||||||
|
if (!peer
|
||||||
const auto peer = _history->peer;
|
|| !_regularWindow
|
||||||
if (!_regularWindow
|
|
||||||
|| isEditingMessage()
|
|| isEditingMessage()
|
||||||
|| !session().sendAsPeers().shouldChoose(peer)) {
|
|| !session().sendAsPeers().shouldChoose(peer)) {
|
||||||
if (!_sendAs) {
|
if (!_sendAs) {
|
||||||
|
@ -2467,7 +2472,7 @@ bool ComposeControls::updateSendAsButton() {
|
||||||
st::sendAsButton);
|
st::sendAsButton);
|
||||||
Ui::SetupSendAsButton(
|
Ui::SetupSendAsButton(
|
||||||
_sendAs.get(),
|
_sendAs.get(),
|
||||||
rpl::single(peer.get()),
|
rpl::single(peer),
|
||||||
_regularWindow);
|
_regularWindow);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2781,15 +2786,18 @@ bool ComposeControls::handleCancelRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::initWebpageProcess() {
|
void ComposeControls::initWebpageProcess() {
|
||||||
Expects(_history);
|
if (!_history) {
|
||||||
|
_preview = nullptr;
|
||||||
|
_header->previewUnregister();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto &lifetime = _wrap->lifetime();
|
|
||||||
_preview = std::make_unique<WebpageProcessor>(_history, _field);
|
_preview = std::make_unique<WebpageProcessor>(_history, _field);
|
||||||
|
|
||||||
_preview->paintRequests(
|
_preview->paintRequests(
|
||||||
) | rpl::start_with_next(crl::guard(_header.get(), [=] {
|
) | rpl::start_with_next(crl::guard(_header.get(), [=] {
|
||||||
_header->update();
|
_header->update();
|
||||||
}), lifetime);
|
}), _historyLifetime);
|
||||||
|
|
||||||
session().changes().peerUpdates(
|
session().changes().peerUpdates(
|
||||||
Data::PeerUpdate::Flag::Rights
|
Data::PeerUpdate::Flag::Rights
|
||||||
|
@ -2818,7 +2826,7 @@ void ComposeControls::initWebpageProcess() {
|
||||||
updateControlsGeometry(_wrap->size());
|
updateControlsGeometry(_wrap->size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, lifetime);
|
}, _historyLifetime);
|
||||||
|
|
||||||
_header->previewRequested(
|
_header->previewRequested(
|
||||||
_preview->titleChanges(),
|
_preview->titleChanges(),
|
||||||
|
|
|
@ -389,10 +389,11 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<WebpageProcessor> _preview;
|
std::unique_ptr<WebpageProcessor> _preview;
|
||||||
|
|
||||||
rpl::lifetime _uploaderSubscriptions;
|
|
||||||
|
|
||||||
Fn<void()> _raiseEmojiSuggestions;
|
Fn<void()> _raiseEmojiSuggestions;
|
||||||
|
|
||||||
|
rpl::lifetime _historyLifetime;
|
||||||
|
rpl::lifetime _uploaderSubscriptions;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
155
Telegram/SourceFiles/media/stories/media_stories_controller.cpp
Normal file
155
Telegram/SourceFiles/media/stories/media_stories_controller.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
|
|
||||||
|
#include "data/data_stories.h"
|
||||||
|
#include "media/stories/media_stories_delegate.h"
|
||||||
|
#include "media/stories/media_stories_header.h"
|
||||||
|
#include "media/stories/media_stories_slider.h"
|
||||||
|
#include "media/stories/media_stories_reply.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "styles/style_media_view.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
#include "styles/style_boxes.h" // UserpicButton
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
|
||||||
|
Controller::Controller(not_null<Delegate*> delegate)
|
||||||
|
: _delegate(delegate)
|
||||||
|
, _wrap(_delegate->storiesWrap())
|
||||||
|
, _header(std::make_unique<Header>(this))
|
||||||
|
, _slider(std::make_unique<Slider>(this))
|
||||||
|
, _replyArea(std::make_unique<ReplyArea>(this)) {
|
||||||
|
initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::initLayout() {
|
||||||
|
const auto headerHeight = st::storiesHeaderMargin.top()
|
||||||
|
+ st::storiesHeaderPhoto.photoSize
|
||||||
|
+ st::storiesHeaderMargin.bottom();
|
||||||
|
const auto sliderHeight = st::storiesSliderMargin.top()
|
||||||
|
+ st::storiesSlider.width
|
||||||
|
+ st::storiesSliderMargin.bottom();
|
||||||
|
const auto outsideHeaderHeight = headerHeight + sliderHeight;
|
||||||
|
const auto fieldMinHeight = st::storiesFieldMargin.top()
|
||||||
|
+ st::storiesAttach.height
|
||||||
|
+ st::storiesFieldMargin.bottom();
|
||||||
|
const auto minHeightForOutsideHeader = st::storiesMaxSize.height()
|
||||||
|
+ outsideHeaderHeight
|
||||||
|
+ fieldMinHeight;
|
||||||
|
_layout = _wrap->sizeValue(
|
||||||
|
) | rpl::map([=](QSize size) {
|
||||||
|
size = QSize(
|
||||||
|
std::max(size.width(), st::mediaviewMinWidth),
|
||||||
|
std::max(size.height(), st::mediaviewMinHeight));
|
||||||
|
|
||||||
|
auto layout = Layout();
|
||||||
|
layout.headerLayout = (size.height() >= minHeightForOutsideHeader)
|
||||||
|
? HeaderLayout::Outside
|
||||||
|
: HeaderLayout::Normal;
|
||||||
|
|
||||||
|
const auto topSkip = (layout.headerLayout == HeaderLayout::Outside)
|
||||||
|
? outsideHeaderHeight
|
||||||
|
: st::storiesFieldMargin.bottom();
|
||||||
|
const auto bottomSkip = fieldMinHeight;
|
||||||
|
const auto maxWidth = size.width() - 2 * st::storiesSideSkip;
|
||||||
|
const auto availableHeight = size.height() - topSkip - bottomSkip;
|
||||||
|
const auto maxContentHeight = std::min(
|
||||||
|
availableHeight,
|
||||||
|
st::storiesMaxSize.height());
|
||||||
|
const auto nowWidth = maxContentHeight * st::storiesMaxSize.width()
|
||||||
|
/ st::storiesMaxSize.height();
|
||||||
|
const auto contentWidth = std::min(nowWidth, maxWidth);
|
||||||
|
const auto contentHeight = (contentWidth < nowWidth)
|
||||||
|
? (contentWidth * st::storiesMaxSize.height()
|
||||||
|
/ st::storiesMaxSize.width())
|
||||||
|
: maxContentHeight;
|
||||||
|
const auto addedTopSkip = (availableHeight - contentHeight) / 2;
|
||||||
|
layout.content = QRect(
|
||||||
|
(size.width() - contentWidth) / 2,
|
||||||
|
addedTopSkip + topSkip,
|
||||||
|
contentWidth,
|
||||||
|
contentHeight);
|
||||||
|
|
||||||
|
if (layout.headerLayout == HeaderLayout::Outside) {
|
||||||
|
layout.header = QRect(
|
||||||
|
layout.content.topLeft() - QPoint(0, outsideHeaderHeight),
|
||||||
|
QSize(contentWidth, outsideHeaderHeight));
|
||||||
|
layout.slider = QRect(
|
||||||
|
layout.header.topLeft() + QPoint(0, headerHeight),
|
||||||
|
QSize(contentWidth, sliderHeight));
|
||||||
|
} else {
|
||||||
|
layout.slider = QRect(
|
||||||
|
layout.content.topLeft(),
|
||||||
|
QSize(contentWidth, sliderHeight));
|
||||||
|
layout.header = QRect(
|
||||||
|
layout.slider.topLeft() + QPoint(0, sliderHeight),
|
||||||
|
QSize(contentWidth, headerHeight));
|
||||||
|
}
|
||||||
|
layout.controlsWidth = std::max(
|
||||||
|
layout.content.width(),
|
||||||
|
st::storiesControlsMinWidth);
|
||||||
|
layout.controlsBottomPosition = QPoint(
|
||||||
|
(size.width() - layout.controlsWidth) / 2,
|
||||||
|
(layout.content.y()
|
||||||
|
+ layout.content.height()
|
||||||
|
+ fieldMinHeight
|
||||||
|
- st::storiesFieldMargin.bottom()));
|
||||||
|
layout.autocompleteRect = QRect(
|
||||||
|
layout.controlsBottomPosition.x(),
|
||||||
|
0,
|
||||||
|
layout.controlsWidth,
|
||||||
|
layout.controlsBottomPosition.y());
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<Ui::RpWidget*> Controller::wrap() const {
|
||||||
|
return _wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout Controller::layout() const {
|
||||||
|
Expects(_layout.current().has_value());
|
||||||
|
|
||||||
|
return *_layout.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Layout> Controller::layoutValue() const {
|
||||||
|
return _layout.value() | rpl::filter_optional();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
|
||||||
|
return _delegate->storiesShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Controller::stickerOrEmojiChosen() const
|
||||||
|
->rpl::producer<ChatHelpers::FileChosen> {
|
||||||
|
return _delegate->storiesStickerOrEmojiChosen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::show(const Data::StoriesList &list, int index) {
|
||||||
|
Expects(index < list.items.size());
|
||||||
|
|
||||||
|
const auto &item = list.items[index];
|
||||||
|
|
||||||
|
const auto id = ShownId{
|
||||||
|
.user = list.user,
|
||||||
|
.id = item.id,
|
||||||
|
};
|
||||||
|
if (_shown == id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_shown = id;
|
||||||
|
|
||||||
|
_header->show({ .user = list.user, .date = item.date });
|
||||||
|
_slider->show({ .index = index, .total = int(list.items.size()) });
|
||||||
|
_replyArea->show({ .user = list.user });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ChatHelpers {
|
||||||
|
class Show;
|
||||||
|
struct FileChosen;
|
||||||
|
} // namespace ChatHelpers
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct StoriesList;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
|
||||||
|
class Header;
|
||||||
|
class Slider;
|
||||||
|
class ReplyArea;
|
||||||
|
class Delegate;
|
||||||
|
|
||||||
|
struct ShownId {
|
||||||
|
UserData *user = nullptr;
|
||||||
|
StoryId id = 0;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return user != nullptr && id != 0;
|
||||||
|
}
|
||||||
|
friend inline auto operator<=>(ShownId, ShownId) = default;
|
||||||
|
friend inline bool operator==(ShownId, ShownId) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HeaderLayout {
|
||||||
|
Normal,
|
||||||
|
Outside,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Layout {
|
||||||
|
QRect content;
|
||||||
|
QRect header;
|
||||||
|
QRect slider;
|
||||||
|
int controlsWidth = 0;
|
||||||
|
QPoint controlsBottomPosition;
|
||||||
|
QRect autocompleteRect;
|
||||||
|
HeaderLayout headerLayout = HeaderLayout::Normal;
|
||||||
|
|
||||||
|
friend inline auto operator<=>(Layout, Layout) = default;
|
||||||
|
friend inline bool operator==(Layout, Layout) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Controller final {
|
||||||
|
public:
|
||||||
|
explicit Controller(not_null<Delegate*> delegate);
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::RpWidget*> wrap() const;
|
||||||
|
[[nodiscard]] Layout layout() const;
|
||||||
|
[[nodiscard]] rpl::producer<Layout> layoutValue() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
||||||
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
|
-> rpl::producer<ChatHelpers::FileChosen>;
|
||||||
|
|
||||||
|
void show(const Data::StoriesList &list, int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initLayout();
|
||||||
|
|
||||||
|
const not_null<Delegate*> _delegate;
|
||||||
|
|
||||||
|
rpl::variable<std::optional<Layout>> _layout;
|
||||||
|
|
||||||
|
const not_null<Ui::RpWidget*> _wrap;
|
||||||
|
const std::unique_ptr<Header> _header;
|
||||||
|
const std::unique_ptr<Slider> _slider;
|
||||||
|
const std::unique_ptr<ReplyArea> _replyArea;
|
||||||
|
|
||||||
|
ShownId _shown;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
|
@ -9,18 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "media/stories/media_stories_delegate.h"
|
#include "media/stories/media_stories_controller.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "styles/style_boxes.h" // defaultUserpicButton.
|
#include "styles/style_media_view.h"
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
Header::Header(not_null<Delegate*> delegate)
|
Header::Header(not_null<Controller*> controller)
|
||||||
: _delegate(delegate) {
|
: _controller(controller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Header::~Header() {
|
Header::~Header() {
|
||||||
|
@ -34,32 +34,37 @@ void Header::show(HeaderData data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
if (userChanged) {
|
if (userChanged) {
|
||||||
_date = nullptr;
|
_date = nullptr;
|
||||||
const auto parent = _delegate->storiesWrap();
|
const auto parent = _controller->wrap();
|
||||||
auto widget = std::make_unique<Ui::RpWidget>(parent);
|
auto widget = std::make_unique<Ui::RpWidget>(parent);
|
||||||
const auto raw = widget.get();
|
const auto raw = widget.get();
|
||||||
parent->sizeValue() | rpl::start_with_next([=](QSize size) {
|
|
||||||
raw->setGeometry(50, 50, 600, 100);
|
|
||||||
}, raw->lifetime());
|
|
||||||
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||||
raw,
|
raw,
|
||||||
data.user,
|
data.user,
|
||||||
st::defaultUserpicButton);
|
st::storiesHeaderPhoto);
|
||||||
userpic->move(0, 0);
|
userpic->show();
|
||||||
|
userpic->move(
|
||||||
|
st::storiesHeaderMargin.left(),
|
||||||
|
st::storiesHeaderMargin.top());
|
||||||
const auto name = Ui::CreateChild<Ui::FlatLabel>(
|
const auto name = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
raw,
|
raw,
|
||||||
data.user->firstName,
|
data.user->firstName,
|
||||||
st::defaultFlatLabel);
|
st::storiesHeaderName);
|
||||||
name->move(100, 0);
|
name->move(st::storiesHeaderNamePosition);
|
||||||
raw->show();
|
raw->show();
|
||||||
_widget = std::move(widget);
|
_widget = std::move(widget);
|
||||||
|
|
||||||
|
_controller->layoutValue(
|
||||||
|
) | rpl::start_with_next([=](const Layout &layout) {
|
||||||
|
raw->setGeometry(layout.header);
|
||||||
|
}, raw->lifetime());
|
||||||
}
|
}
|
||||||
_date = std::make_unique<Ui::FlatLabel>(
|
_date = std::make_unique<Ui::FlatLabel>(
|
||||||
_widget.get(),
|
_widget.get(),
|
||||||
Ui::FormatDateTime(base::unixtime::parse(data.date)),
|
Ui::FormatDateTime(base::unixtime::parse(data.date)),
|
||||||
st::defaultFlatLabel);
|
st::storiesHeaderDate);
|
||||||
_date->move(100, 50);
|
|
||||||
_date->show();
|
_date->show();
|
||||||
|
_date->move(st::storiesHeaderDatePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -16,24 +16,26 @@ class FlatLabel;
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
class Delegate;
|
class Controller;
|
||||||
|
|
||||||
struct HeaderData {
|
struct HeaderData {
|
||||||
not_null<UserData*> user;
|
not_null<UserData*> user;
|
||||||
TimeId date = 0;
|
TimeId date = 0;
|
||||||
|
|
||||||
friend inline auto operator<=>(HeaderData, HeaderData) = default;
|
friend inline auto operator<=>(HeaderData, HeaderData) = default;
|
||||||
|
friend inline bool operator==(HeaderData, HeaderData) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Header final {
|
class Header final {
|
||||||
public:
|
public:
|
||||||
explicit Header(not_null<Delegate*> delegate);
|
explicit Header(not_null<Controller*> controller);
|
||||||
~Header();
|
~Header();
|
||||||
|
|
||||||
void show(HeaderData data);
|
void show(HeaderData data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<Delegate*> _delegate;
|
const not_null<Controller*> _controller;
|
||||||
|
|
||||||
std::unique_ptr<Ui::RpWidget> _widget;
|
std::unique_ptr<Ui::RpWidget> _widget;
|
||||||
std::unique_ptr<Ui::FlatLabel> _date;
|
std::unique_ptr<Ui::FlatLabel> _date;
|
||||||
std::optional<HeaderData> _data;
|
std::optional<HeaderData> _data;
|
||||||
|
|
|
@ -7,42 +7,156 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "media/stories/media_stories_reply.h"
|
#include "media/stories/media_stories_reply.h"
|
||||||
|
|
||||||
|
#include "base/call_delayed.h"
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "history/view/controls/compose_controls_common.h"
|
||||||
#include "history/view/controls/history_view_compose_controls.h"
|
#include "history/view/controls/history_view_compose_controls.h"
|
||||||
#include "media/stories/media_stories_delegate.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
#include "menu/menu_send.h"
|
#include "menu/menu_send.h"
|
||||||
|
#include "styles/style_media_view.h"
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
ReplyArea::ReplyArea(not_null<Delegate*> delegate)
|
ReplyArea::ReplyArea(not_null<Controller*> controller)
|
||||||
: _delegate(delegate)
|
: _controller(controller)
|
||||||
, _controls(std::make_unique<HistoryView::ComposeControls>(
|
, _controls(std::make_unique<HistoryView::ComposeControls>(
|
||||||
_delegate->storiesWrap(),
|
_controller->wrap(),
|
||||||
HistoryView::ComposeControlsDescriptor{
|
HistoryView::ComposeControlsDescriptor{
|
||||||
.show = _delegate->storiesShow(),
|
.show = _controller->uiShow(),
|
||||||
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
|
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
|
||||||
showPremiumToast(emoji);
|
showPremiumToast(emoji);
|
||||||
},
|
},
|
||||||
.mode = HistoryView::ComposeControlsMode::Normal,
|
.mode = HistoryView::ComposeControlsMode::Normal,
|
||||||
.sendMenuType = SendMenu::Type::SilentOnly,
|
.sendMenuType = SendMenu::Type::SilentOnly,
|
||||||
.stickerOrEmojiChosen = _delegate->storiesStickerOrEmojiChosen(),
|
.stickerOrEmojiChosen = _controller->stickerOrEmojiChosen(),
|
||||||
}
|
}
|
||||||
)) {
|
)) {
|
||||||
_delegate->storiesWrap()->sizeValue(
|
initGeometry();
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
initActions();
|
||||||
_controls->resizeToWidth(size.width() - 200);
|
|
||||||
_controls->move(100, size.height() - _controls->heightCurrent() - 20);
|
|
||||||
_controls->setAutocompleteBoundingRect({ QPoint() ,size });
|
|
||||||
}, _lifetime);
|
|
||||||
|
|
||||||
_controls->show();
|
|
||||||
_controls->showFinished();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplyArea::~ReplyArea() {
|
ReplyArea::~ReplyArea() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReplyArea::initGeometry() {
|
||||||
|
_controller->layoutValue(
|
||||||
|
) | rpl::start_with_next([=](const Layout &layout) {
|
||||||
|
_controls->resizeToWidth(layout.content.width());
|
||||||
|
const auto position = layout.controlsBottomPosition
|
||||||
|
- QPoint(0, _controls->heightCurrent());
|
||||||
|
_controls->move(position.x(), position.y());
|
||||||
|
_controls->setAutocompleteBoundingRect(layout.autocompleteRect);
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplyArea::send(Api::SendOptions options) {
|
||||||
|
// #TODO stories
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplyArea::sendVoice(VoiceToSend &&data) {
|
||||||
|
// #TODO stories
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplyArea::chooseAttach(std::optional<bool> overrideCompress) {
|
||||||
|
// #TODO stories
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplyArea::initActions() {
|
||||||
|
_controls->cancelRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
// #TODO stories
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->sendRequests(
|
||||||
|
) | rpl::start_with_next([=](Api::SendOptions options) {
|
||||||
|
send(options);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->sendVoiceRequests(
|
||||||
|
) | rpl::start_with_next([=](VoiceToSend &&data) {
|
||||||
|
sendVoice(std::move(data));
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->attachRequests(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return !_choosingAttach;
|
||||||
|
}) | rpl::start_with_next([=](std::optional<bool> overrideCompress) {
|
||||||
|
_choosingAttach = true;
|
||||||
|
base::call_delayed(
|
||||||
|
st::storiesAttach.ripple.hideDuration,
|
||||||
|
this,
|
||||||
|
[=] { chooseAttach(overrideCompress); });
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->fileChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||||
|
_controller->uiShow()->hideLayer();
|
||||||
|
//controller()->sendingAnimation().appendSending(
|
||||||
|
// data.messageSendingFrom);
|
||||||
|
//const auto localId = data.messageSendingFrom.localId;
|
||||||
|
//sendExistingDocument(data.document, data.options, localId);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->photoChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
|
||||||
|
//sendExistingPhoto(chosen.photo, chosen.options);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->inlineResultChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
|
||||||
|
//controller()->sendingAnimation().appendSending(
|
||||||
|
// chosen.messageSendingFrom);
|
||||||
|
//const auto localId = chosen.messageSendingFrom.localId;
|
||||||
|
//sendInlineResult(chosen.result, chosen.bot, chosen.options, localId);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->setMimeDataHook([=](
|
||||||
|
not_null<const QMimeData*> data,
|
||||||
|
Ui::InputField::MimeAction action) {
|
||||||
|
if (action == Ui::InputField::MimeAction::Check) {
|
||||||
|
return false;// checkSendingFiles(data);
|
||||||
|
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||||
|
return false;/* confirmSendingFiles(
|
||||||
|
data,
|
||||||
|
std::nullopt,
|
||||||
|
Core::ReadMimeText(data));*/
|
||||||
|
}
|
||||||
|
Unexpected("action in MimeData hook.");
|
||||||
|
});
|
||||||
|
|
||||||
|
_controls->lockShowStarts(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_controls->show();
|
||||||
|
_controls->finishAnimating();
|
||||||
|
_controls->showFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplyArea::show(ReplyAreaData data) {
|
||||||
|
if (_data == data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto userChanged = (_data.user != data.user);
|
||||||
|
_data = data;
|
||||||
|
if (!userChanged) {
|
||||||
|
if (_data.user) {
|
||||||
|
_controls->clear();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto user = data.user;
|
||||||
|
const auto history = user ? user->owner().history(user).get() : nullptr;
|
||||||
|
_controls->setHistory({
|
||||||
|
.history = history,
|
||||||
|
});
|
||||||
|
_controls->clear();
|
||||||
|
}
|
||||||
|
|
||||||
void ReplyArea::showPremiumToast(not_null<DocumentData*> emoji) {
|
void ReplyArea::showPremiumToast(not_null<DocumentData*> emoji) {
|
||||||
// #TODO stories
|
// #TODO stories
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,31 +7,57 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
struct SendOptions;
|
||||||
|
} // namespace Api
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
class ComposeControls;
|
class ComposeControls;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
|
namespace HistoryView::Controls {
|
||||||
|
struct VoiceToSend;
|
||||||
|
} // namespace HistoryView::Controls
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
class Delegate;
|
class Controller;
|
||||||
|
|
||||||
struct ReplyAreaData {
|
struct ReplyAreaData {
|
||||||
not_null<UserData*> user;
|
UserData *user = nullptr;
|
||||||
|
StoryId id = 0;
|
||||||
|
|
||||||
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
|
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
|
||||||
|
friend inline bool operator==(ReplyAreaData, ReplyAreaData) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReplyArea final {
|
class ReplyArea final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
explicit ReplyArea(not_null<Delegate*> delegate);
|
explicit ReplyArea(not_null<Controller*> controller);
|
||||||
~ReplyArea();
|
~ReplyArea();
|
||||||
|
|
||||||
|
void show(ReplyAreaData data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using VoiceToSend = HistoryView::Controls::VoiceToSend;
|
||||||
|
|
||||||
|
void initGeometry();
|
||||||
|
void initActions();
|
||||||
|
|
||||||
|
void send(Api::SendOptions options);
|
||||||
|
void sendVoice(VoiceToSend &&data);
|
||||||
|
void chooseAttach(std::optional<bool> overrideSendImagesAsPhotos);
|
||||||
|
|
||||||
void showPremiumToast(not_null<DocumentData*> emoji);
|
void showPremiumToast(not_null<DocumentData*> emoji);
|
||||||
|
|
||||||
const not_null<Delegate*> _delegate;
|
const not_null<Controller*> _controller;
|
||||||
const std::unique_ptr<HistoryView::ComposeControls> _controls;
|
const std::unique_ptr<HistoryView::ComposeControls> _controls;
|
||||||
|
|
||||||
|
ReplyAreaData _data;
|
||||||
|
bool _choosingAttach = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,12 +7,68 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "media/stories/media_stories_slider.h"
|
#include "media/stories/media_stories_slider.h"
|
||||||
|
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
#include "styles/style_media_view.h"
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
Slider::Slider() {
|
Slider::Slider(not_null<Controller*> controller)
|
||||||
|
: _controller(controller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Slider::~Slider() {
|
Slider::~Slider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Slider::show(SliderData data) {
|
||||||
|
if (_data == data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_data = data;
|
||||||
|
|
||||||
|
const auto parent = _controller->wrap();
|
||||||
|
auto widget = std::make_unique<Ui::RpWidget>(parent);
|
||||||
|
const auto raw = widget.get();
|
||||||
|
|
||||||
|
raw->paintRequest(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return (raw->width() >= st::storiesSlider.width);
|
||||||
|
}) | rpl::start_with_next([=](QRect clip) {
|
||||||
|
auto clipf = QRectF(clip);
|
||||||
|
auto p = QPainter(raw);
|
||||||
|
const auto single = st::storiesSlider.width;
|
||||||
|
const auto skip = st::storiesSliderSkip;
|
||||||
|
// width() == single * max + skip * (max - 1);
|
||||||
|
// max == (width() + skip) / (single + skip);
|
||||||
|
const auto max = (raw->width() + skip) / (single + skip);
|
||||||
|
Assert(max > 0);
|
||||||
|
const auto count = std::clamp(_data.total, 1, max);
|
||||||
|
const auto index = std::clamp(data.index, 0, count - 1);
|
||||||
|
const auto radius = st::storiesSlider.width / 2.;
|
||||||
|
const auto width = (raw->width() - (count - 1) * skip)
|
||||||
|
/ float64(count);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
auto left = 0.;
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
const auto rect = QRectF(left, 0, width, single);
|
||||||
|
p.setBrush((i == index) // #TODO stories
|
||||||
|
? st::mediaviewPipControlsFgOver
|
||||||
|
: st::mediaviewPipPlaybackInactive);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawRoundedRect(rect, radius, radius);
|
||||||
|
left += width + skip;
|
||||||
|
}
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
raw->show();
|
||||||
|
_widget = std::move(widget);
|
||||||
|
|
||||||
|
_controller->layoutValue(
|
||||||
|
) | rpl::start_with_next([=](const Layout &layout) {
|
||||||
|
raw->setGeometry(layout.slider - st::storiesSliderMargin);
|
||||||
|
}, raw->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -7,13 +7,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
|
class Controller;
|
||||||
|
|
||||||
|
struct SliderData {
|
||||||
|
int index = 0;
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
friend inline auto operator<=>(SliderData, SliderData) = default;
|
||||||
|
friend inline bool operator==(SliderData, SliderData) = default;
|
||||||
|
};
|
||||||
|
|
||||||
class Slider final {
|
class Slider final {
|
||||||
public:
|
public:
|
||||||
Slider();
|
explicit Slider(not_null<Controller*> controller);
|
||||||
~Slider();
|
~Slider();
|
||||||
|
|
||||||
|
void show(SliderData data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<Controller*> _controller;
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::RpWidget> _widget;
|
||||||
|
|
||||||
|
SliderData _data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "media/stories/media_stories_view.h"
|
#include "media/stories/media_stories_view.h"
|
||||||
|
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
#include "media/stories/media_stories_delegate.h"
|
#include "media/stories/media_stories_delegate.h"
|
||||||
#include "media/stories/media_stories_header.h"
|
#include "media/stories/media_stories_header.h"
|
||||||
#include "media/stories/media_stories_slider.h"
|
#include "media/stories/media_stories_slider.h"
|
||||||
|
@ -15,23 +16,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
View::View(not_null<Delegate*> delegate)
|
View::View(not_null<Delegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _controller(std::make_unique<Controller>(delegate)) {
|
||||||
, _wrap(_delegate->storiesWrap())
|
|
||||||
, _header(std::make_unique<Header>(_delegate))
|
|
||||||
, _slider(std::make_unique<Slider>())
|
|
||||||
, _replyArea(std::make_unique<ReplyArea>(_delegate)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
View::~View() = default;
|
View::~View() = default;
|
||||||
|
|
||||||
void View::show(const Data::StoriesList &list, int index) {
|
void View::show(const Data::StoriesList &list, int index) {
|
||||||
Expects(index < list.items.size());
|
_controller->show(list, index);
|
||||||
|
}
|
||||||
|
|
||||||
const auto &item = list.items[index];
|
QRect View::contentGeometry() const {
|
||||||
_header->show({
|
return _controller->layout().content;
|
||||||
.user = list.user,
|
|
||||||
.date = item.date,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -7,18 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "data/data_stories.h"
|
namespace Data {
|
||||||
|
struct StoriesList;
|
||||||
namespace Ui {
|
} // namespace Data
|
||||||
class RpWidget;
|
|
||||||
} // namespace Ui
|
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
class Header;
|
|
||||||
class Slider;
|
|
||||||
class ReplyArea;
|
|
||||||
class Delegate;
|
class Delegate;
|
||||||
|
class Controller;
|
||||||
|
|
||||||
class View final {
|
class View final {
|
||||||
public:
|
public:
|
||||||
|
@ -26,14 +22,10 @@ public:
|
||||||
~View();
|
~View();
|
||||||
|
|
||||||
void show(const Data::StoriesList &list, int index);
|
void show(const Data::StoriesList &list, int index);
|
||||||
|
[[nodiscard]] QRect contentGeometry() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<Delegate*> _delegate;
|
const std::unique_ptr<Controller> _controller;
|
||||||
const not_null<Ui::RpWidget*> _wrap;
|
|
||||||
|
|
||||||
std::unique_ptr<Header> _header;
|
|
||||||
std::unique_ptr<Slider> _slider;
|
|
||||||
std::unique_ptr<ReplyArea> _replyArea;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ using "ui/basic.style";
|
||||||
using "ui/widgets/widgets.style";
|
using "ui/widgets/widgets.style";
|
||||||
using "ui/menu_icons.style";
|
using "ui/menu_icons.style";
|
||||||
using "media/player/media_player.style";
|
using "media/player/media_player.style";
|
||||||
|
using "boxes/boxes.style";
|
||||||
|
|
||||||
mediaviewOverDuration: 150;
|
mediaviewOverDuration: 150;
|
||||||
|
|
||||||
|
@ -403,3 +404,41 @@ pipVolumeIcon2: icon {{ "player/player_volume_on", mediaviewPipControlsFg }};
|
||||||
pipVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPipControlsFgOver }};
|
pipVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPipControlsFgOver }};
|
||||||
|
|
||||||
speedSliderDividerSize: size(2px, 8px);
|
speedSliderDividerSize: size(2px, 8px);
|
||||||
|
|
||||||
|
storiesMaxSize: size(405px, 720px);
|
||||||
|
storiesSlider: MediaSlider(mediaviewPlayback) {
|
||||||
|
width: 2px;
|
||||||
|
seekSize: size(2px, 2px);
|
||||||
|
}
|
||||||
|
storiesSliderMargin: margins(8px, 7px, 8px, 10px);
|
||||||
|
storiesSliderSkip: 4px;
|
||||||
|
storiesHeaderMargin: margins(12px, 3px, 12px, 8px);
|
||||||
|
storiesHeaderPhoto: UserpicButton(defaultUserpicButton) {
|
||||||
|
size: size(28px, 28px);
|
||||||
|
photoSize: 28px;
|
||||||
|
}
|
||||||
|
storiesHeaderName: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: mediaviewPipControlsFgOver; // #TODO stories
|
||||||
|
style: semiboldTextStyle;
|
||||||
|
}
|
||||||
|
storiesHeaderNamePosition: point(50px, 2px);
|
||||||
|
storiesHeaderDate: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: mediaviewPipControlsFg; // #TODO stories
|
||||||
|
}
|
||||||
|
storiesHeaderDatePosition: point(50px, 19px);
|
||||||
|
storiesControlsMinWidth: 200px;
|
||||||
|
storiesFieldMargin: margins(0px, 14px, 0px, 16px);
|
||||||
|
storiesAttach: IconButton(defaultIconButton) {
|
||||||
|
width: 44px;
|
||||||
|
height: 46px;
|
||||||
|
|
||||||
|
icon: icon {{ "chat/input_attach", historyComposeIconFg }};
|
||||||
|
iconOver: icon {{ "chat/input_attach", historyComposeIconFgOver }};
|
||||||
|
|
||||||
|
rippleAreaPosition: point(2px, 3px);
|
||||||
|
rippleAreaSize: 40px;
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: windowBgOver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storiesSideSkip: 145px;
|
||||||
|
|
|
@ -1543,6 +1543,14 @@ void OverlayWidget::recountSkipTop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::resizeContentByScreenSize() {
|
void OverlayWidget::resizeContentByScreenSize() {
|
||||||
|
if (_stories) {
|
||||||
|
const auto content = _stories->contentGeometry();
|
||||||
|
_x = content.x();
|
||||||
|
_y = content.y();
|
||||||
|
_w = content.width();
|
||||||
|
_h = content.height();
|
||||||
|
return;
|
||||||
|
}
|
||||||
recountSkipTop();
|
recountSkipTop();
|
||||||
const auto availableWidth = width();
|
const auto availableWidth = width();
|
||||||
const auto countZoomFor = [&](int outerw, int outerh) {
|
const auto countZoomFor = [&](int outerw, int outerh) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue