feat: message shot

feat: send image as sticker
fix: don't query author pack if ID == 0
fix: use predefined confirmation boxes
This commit is contained in:
ZavaruKitsu 2024-01-26 18:25:03 +03:00
parent 0cf36db010
commit 9c71aba5d1
41 changed files with 1417 additions and 292 deletions

View file

@ -134,18 +134,20 @@ PRIVATE
ayu/ui/sections/edited/edited_log_item.h
ayu/ui/sections/edited/edited_log_section.cpp
ayu/ui/sections/edited/edited_log_section.h
ayu/ui/boxes/voice_confirmation_box.cpp
ayu/ui/boxes/voice_confirmation_box.h
ayu/ui/boxes/message_history_box.cpp
ayu/ui/boxes/message_history_box.h
ayu/ui/boxes/confirmation_box.cpp
ayu/ui/boxes/confirmation_box.h
ayu/ui/boxes/server_read_confirmation_box.cpp
ayu/ui/boxes/server_read_confirmation_box.h
ayu/ui/boxes/edit_deleted_mark.cpp
ayu/ui/boxes/edit_deleted_mark.h
ayu/ui/boxes/edit_edited_mark.cpp
ayu/ui/boxes/edit_edited_mark.h
ayu/ui/boxes/font_selector.cpp
ayu/ui/boxes/font_selector.h
ayu/ui/boxes/theme_selector_box.cpp
ayu/ui/boxes/theme_selector_box.h
ayu/ui/boxes/message_shot_box.cpp
ayu/ui/boxes/message_shot_box.h
ayu/ui/components/image_view.cpp
ayu/ui/components/image_view.h
ayu/sync/ayu_sync_controller.cpp
ayu/sync/ayu_sync_controller.h
ayu/sync/models.h
@ -164,6 +166,8 @@ PRIVATE
ayu/features/streamer_mode/streamer_mode_windows.cpp
ayu/features/streamer_mode/streamer_mode_linux.cpp
ayu/features/streamer_mode/streamer_mode.h
ayu/features/messageshot/message_shot.cpp
ayu/features/messageshot/message_shot.h
ayu/database/entities.h
ayu/database/ayu_database.cpp
ayu/database/ayu_database.h

View file

@ -4799,6 +4799,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ayu_MonospaceFont" = "Monospace font";
"ayu_FontDefault" = "Default";
"ayu_CustomizeFontTitle" = "Customize font";
"ayu_MessageShotTopBarText" = "Shot";
"ayu_MessageShotPreview" = "Preview";
"ayu_MessageShotPreferences" = "Preferences";
"ayu_MessageShotCopy" = "Copy";
"ayu_MessageShotSave" = "Save";
"ayu_MessageShotTheme" = "Theme";
"ayu_MessageShotThemeDefault" = "Default";
"ayu_MessageShotThemeSelectTitle" = "Select message theme";
"ayu_MessageShotThemeApply" = "Apply";
"ayu_MessageShotShowDate" = "Show date";
"ayu_MessageShotShowReactions" = "Show reactions";
"ayu_MessageShotShowColorfulReplies" = "Show colorful replies";
"ayu_SendAsSticker" = "Send as Sticker";
"ayu_AyuForwardStatusForwarding" = "Forwarding messages…";
"ayu_AyuForwardStatusLoadingMedia" = "Loading media…";
"ayu_AyuForwardForwardingDescription" = "Please keep this window open while AyuGram is forwarding your messages.";

View file

@ -0,0 +1,425 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "message_shot.h"
#include "styles/style_layers.h"
#include "data/data_cloud_themes.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "history/history_inner_widget.h"
#include "history/view/media/history_view_media.h"
#include "main/main_session.h"
#include "data/data_peer.h"
#include "history/history.h"
#include "qguiapplication.h"
#include "ayu/ui/boxes/message_shot_box.h"
#include "boxes/abstract_box.h"
#include "ui/chat/chat_theme.h"
#include "ui/painter.h"
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_message.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_ayu_styles.h"
#include "ui/layers/box_content.h"
#include "ui/effects/path_shift_gradient.h"
namespace AyuFeatures::MessageShot
{
ShotConfig *config;
Window::Theme::EmbeddedType defaultSelected = Window::Theme::EmbeddedType(-1);
std::optional<QColor> defaultSelectedColor;
std::optional<Data::CloudTheme> customSelected;
rpl::event_stream<> resetDefaultSelectedStream;
rpl::event_stream<> resetCustomSelectedStream;
bool takingShot = false;
bool choosingTheme = false;
rpl::event_stream<Data::CloudTheme> themeChosenStream;
rpl::event_stream<style::palette> paletteChosenStream;
void setShotConfig(ShotConfig &config)
{
MessageShot::config = &config;
}
void resetShotConfig()
{
config = nullptr;
}
ShotConfig getShotConfig()
{
return *config;
}
void setDefaultSelected(const Window::Theme::EmbeddedType type)
{
resetCustomSelected();
defaultSelected = type;
}
Window::Theme::EmbeddedType getSelectedFromDefault()
{
return defaultSelected;
}
void setDefaultSelectedColor(const QColor color)
{
resetCustomSelected();
defaultSelectedColor = color;
}
std::optional<QColor> getSelectedColorFromDefault()
{
return defaultSelectedColor;
}
void setCustomSelected(const Data::CloudTheme theme)
{
resetDefaultSelected();
customSelected = theme;
}
std::optional<Data::CloudTheme> getSelectedFromCustom()
{
return customSelected;
}
void resetDefaultSelected()
{
defaultSelected = Window::Theme::EmbeddedType(-1);
resetDefaultSelectedStream.fire({});
}
void resetCustomSelected()
{
customSelected = std::nullopt;
resetCustomSelectedStream.fire({});
}
rpl::producer<> resetDefaultSelectedEvents()
{
return resetDefaultSelectedStream.events();
}
rpl::producer<> resetCustomSelectedEvents()
{
return resetCustomSelectedStream.events();
}
bool ignoreRender(RenderPart part)
{
if (!config) {
return false;
}
const auto ignoreDate = !config->showDate;
const auto ignoreReactions = !config->showReactions;
return isTakingShot() &&
((part == RenderPart::Date && ignoreDate) ||
(part == RenderPart::Reactions && ignoreReactions));
}
bool isTakingShot()
{
return takingShot;
}
bool setChoosingTheme(bool val)
{
choosingTheme = val;
return choosingTheme;
}
bool isChoosingTheme()
{
return choosingTheme;
}
rpl::producer<Data::CloudTheme> themeChosen()
{
return themeChosenStream.events();
}
void setTheme(Data::CloudTheme theme)
{
themeChosenStream.fire(std::move(theme));
}
void setPalette(style::palette &palette)
{
paletteChosenStream.fire(std::move(palette));
}
rpl::producer<style::palette> paletteChosen()
{
return paletteChosenStream.events();
}
class MessageShotDelegate final : public HistoryView::DefaultElementDelegate
{
public:
MessageShotDelegate(
not_null<QWidget *> parent,
not_null<Ui::ChatStyle *> st,
Fn<void()> update);
bool elementAnimationsPaused() override;
not_null<Ui::PathShiftGradient *> elementPathShiftGradient() override;
HistoryView::Context elementContext() override;
bool elementIsChatWide() override;
private:
const not_null<QWidget *> _parent;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
};
MessageShotDelegate::MessageShotDelegate(
not_null<QWidget *> parent,
not_null<Ui::ChatStyle *> st,
Fn<void()> update)
: _parent(parent)
, _pathGradient(HistoryView::MakePathShiftGradient(st, update))
{
}
bool MessageShotDelegate::elementAnimationsPaused()
{
return _parent->window()->isActiveWindow();
}
auto MessageShotDelegate::elementPathShiftGradient()
-> not_null<Ui::PathShiftGradient *>
{
return _pathGradient.get();
}
HistoryView::Context MessageShotDelegate::elementContext()
{
return HistoryView::Context::AdminLog;
}
bool MessageShotDelegate::elementIsChatWide()
{
return true;
}
QImage removePadding(const QImage &original)
{
if (original.isNull()) {
return {};
}
int minX = original.width();
int minY = original.height();
int maxX = 0;
int maxY = 0;
for (int x = 0; x < original.width(); ++x) {
for (int y = 0; y < original.height(); ++y) {
if (qAlpha(original.pixel(x, y)) != 0) {
minX = std::min(minX, x);
minY = std::min(minY, y);
maxX = std::max(maxX, x);
maxY = std::max(maxY, y);
}
}
}
if (minX > maxX || minY > maxY) {
LOG(("Image is fully transparent ?"));
return {};
}
QRect bounds(minX, minY, maxX - minX + 1, maxY - minY + 1);
return original.copy(bounds);
}
QImage addPadding(const QImage &original, int padding)
{
if (original.isNull()) {
return {};
}
QImage paddedImage(
original.width() + padding * 2 * style::DevicePixelRatio(),
original.height() + padding * 2 * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied
);
paddedImage.fill(Qt::transparent);
Painter painter(&paddedImage);
painter.drawImage(padding, padding, original);
painter.end();
return paddedImage;
}
QImage Make(not_null<QWidget *> box, const ShotConfig &config)
{
const auto controller = config.controller;
const auto st = config.st;
const auto messages = config.messages;
if (messages.empty()) {
return {};
}
takingShot = true;
auto delegate = std::make_unique<MessageShotDelegate>(box,
st.get(),
[=]
{
box->update();
});
std::unordered_map<not_null<HistoryItem *>, std::shared_ptr<HistoryView::Element>> createdViews;
createdViews.reserve(messages.size());
for (const auto &message : messages) {
createdViews.emplace(message, message->createView(delegate.get()));
}
auto getView = [=](not_null<HistoryItem *> msg)
{
return createdViews.at(msg).get();
};
// recalculate blocks
if (messages.size() > 1) {
auto currentMsg = messages[0].get();
for (auto i = 1; i != messages.size(); ++i) {
const auto nextMsg = messages[i].get();
if (getView(nextMsg)->isHidden()) {
getView(nextMsg)->setDisplayDate(false);
}
else {
const auto viewDate = getView(currentMsg)->dateTime();
const auto nextDate = getView(nextMsg)->dateTime();
getView(nextMsg)->setDisplayDate(nextDate.date() != viewDate.date());
auto attached = getView(nextMsg)->computeIsAttachToPrevious(getView(currentMsg));
getView(nextMsg)->setAttachToPrevious(attached, getView(currentMsg));
getView(currentMsg)->setAttachToNext(attached, getView(nextMsg));
currentMsg = nextMsg;
}
}
getView(messages[messages.size() - 1])->setAttachToNext(false);
}
else {
getView(messages[0])->setAttachToPrevious(false);
getView(messages[0])->setAttachToNext(false);
}
// calculate the size of the image
int width = st::msgMaxWidth + (st::boxPadding.left() + st::boxPadding.right());
int height = 0;
for (int i = 0; i < messages.size(); i++) {
const auto &message = messages[i];
const auto view = getView(message);
view->itemDataChanged(); // refresh reactions
height += view->resizeGetHeight(width);
}
// width *= style::DevicePixelRatio();
height *= style::DevicePixelRatio();
// create the image
QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
const auto viewport = QRect(0, 0, width, height);
Painter p(&image);
// draw the messages
int y = 0;
for (int i = 0; i < messages.size(); i++) {
const auto &message = messages[i];
const auto view = getView(message);
const auto displayUserpic = view->displayFromPhoto() || message->isPost();
const auto rect = QRect(0, y, width, view->height());
auto context = controller->defaultChatTheme()->preparePaintContext(
st.get(),
viewport,
rect,
true);
// hides too much
// if (AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
// context.skipDrawingParts = Ui::ChatPaintContext::SkipDrawingParts::Surrounding;
// }
p.translate(0, y);
view->draw(p, context);
p.translate(0, -y);
if (displayUserpic) {
const auto picX = st::msgMargin.left();
const auto picY = y + view->height() - st::msgPhotoSize;
auto userpicView =
!message->displayFrom()->activeUserpicView().null()
? message->displayFrom()->activeUserpicView()
: message->displayFrom()->createUserpicView();
message->displayFrom()->paintUserpic(p, userpicView, picX, picY, st::msgPhotoSize);
}
y += view->height();
}
takingShot = false;
const auto overlay = addPadding(removePadding(image), 4);
return overlay;
}
void Wrapper(not_null<HistoryView::ListWidget *> widget)
{
const auto items = widget->getSelectedIds();
if (items.empty()) {
return;
}
const auto session = &widget->session();
const auto controller = widget->session().tryResolveWindow();
if (!controller) {
return;
}
const auto messages = ranges::views::all(items)
| ranges::views::transform([=](const auto item)
{
return gsl::not_null(session->data().message(item));
})
| ranges::to_vector;
const AyuFeatures::MessageShot::ShotConfig config = {
controller,
std::make_shared<Ui::ChatStyle>(controller->chatStyle()),
messages,
};
auto box = Box<MessageShotBox>(config);
Ui::show(std::move(box));
}
}

View file

@ -0,0 +1,69 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "window/window_session_controller.h"
#include "history/view/history_view_list_widget.h"
#include "ui/chat/chat_style.h"
#include "window/themes/window_themes_embedded.h"
namespace AyuFeatures::MessageShot {
struct ShotConfig {
not_null<Window::SessionController*> controller;
std::shared_ptr<Ui::ChatStyle> st;
std::vector<not_null<HistoryItem*>> messages;
bool showDate;
bool showReactions;
};
enum RenderPart
{
Date,
Reactions,
};
void setShotConfig(ShotConfig& config);
void resetShotConfig();
ShotConfig getShotConfig();
// for default themes
void setDefaultSelected(Window::Theme::EmbeddedType type);
Window::Theme::EmbeddedType getSelectedFromDefault();
void setDefaultSelectedColor(QColor color);
std::optional<QColor> getSelectedColorFromDefault();
// for custom themes
void setCustomSelected(Data::CloudTheme theme);
std::optional<Data::CloudTheme> getSelectedFromCustom();
// resets
void resetDefaultSelected();
void resetCustomSelected();
rpl::producer<> resetDefaultSelectedEvents();
rpl::producer<> resetCustomSelectedEvents();
bool ignoreRender(RenderPart part);
bool isTakingShot();
bool isChoosingTheme();
bool setChoosingTheme(bool val);
void setTheme(Data::CloudTheme theme);
rpl::producer<Data::CloudTheme> themeChosen();
void setPalette(style::palette& palette);
rpl::producer<style::palette> paletteChosen();
QImage Make(not_null<QWidget*> box, const ShotConfig &config);
void Wrapper(not_null<HistoryView::ListWidget*> widget);
}

View file

@ -23,3 +23,5 @@ cpSpacingY: 8px;
cpIconSize: 64px;
recentStickersLimitPadding: margins(22px, 4px, 22px, 8px);
imageViewPadding: margins(22px, 10px, 22px, 10px);
imageViewInnerPadding: margins(16px, 16px, 16px, 16px);

View file

@ -6,7 +6,6 @@
// Copyright @Radolyn, 2023
#pragma once
#include "lang/lang_cloud_manager.h"
#include "boxes/abstract_box.h"
#include "base/binary_guard.h"

View file

@ -1,77 +0,0 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "message_history_box.h"
#include "ayu/ayu_settings.h"
#include "ayu/database/ayu_database.h"
#include "ayu/messages/ayu_messages_controller.h"
#include "history/history.h"
#include "settings/settings_common.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "ui/effects/scroll_content_shadow.h"
#include "ui/vertical_list.h"
using namespace Settings;
namespace AyuUi
{
MessageHistoryBox::MessageHistoryBox(QWidget *, HistoryItem *item)
: _content(this), _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll))
{
setupControls();
addEditedMessagesToLayout(item);
}
void MessageHistoryBox::setupControls()
{
_content.create(this);
_content->resizeToWidth(st::boxWideWidth);
_content->moveToLeft(0, 0);
_content->heightValue(
) | start_to_stream(_contentHeight, _content->lifetime());
_scroll->setOwnedWidget(
object_ptr<RpWidget>::fromRaw(_content));
}
void MessageHistoryBox::resizeEvent(QResizeEvent *e)
{
_scroll->resize(width(), height() - st::boxPhotoPadding.top() - st::boxPadding.bottom());
_scroll->move(0, st::boxPadding.top());
if (_content) {
_content->resize(_scroll->width(), _content->height());
}
}
void MessageHistoryBox::prepare()
{
setTitle(tr::ayu_EditsHistoryTitle());
setDimensions(st::boxWideWidth, 900);
SetupShadowsToScrollContent(this, _scroll, _contentHeight.events());
}
void MessageHistoryBox::addEditedMessagesToLayout(HistoryItem *item)
{
auto messages = AyuMessages::getInstance().getEditedMessages(item);
if (messages.empty()) {
return;
}
for (const auto &message : messages) {
AddSkip(_content);
AddDividerText(_content, rpl::single(QString::fromStdString(message.text)));
AddSkip(_content);
}
}
}

View file

@ -1,38 +0,0 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "history/history_item.h"
#include "ui/layers/box_content.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/vertical_layout.h"
namespace AyuUi
{
class MessageHistoryBox : public Ui::BoxContent
{
public:
MessageHistoryBox(QWidget *, HistoryItem *item);
protected:
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void setupControls();
void addEditedMessagesToLayout(HistoryItem *item);
object_ptr<Ui::VerticalLayout> _content;
const base::unique_qptr<Ui::ScrollArea> _scroll;
rpl::event_stream<int> _contentHeight;
};
}

View file

@ -0,0 +1,202 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "message_shot_box.h"
#include <QFileDialog>
#include <QGuiApplication>
#include "styles/style_ayu_styles.h"
#include "boxes/abstract_box.h"
#include "ayu/ui/components/image_view.h"
#include "core/core_settings.h"
#include "data/data_session.h"
#include "lang_auto.h"
#include "main/main_session.h"
#include "settings/settings_common.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/vertical_list.h"
#include "theme_selector_box.h"
#include "ayu/ayu_settings.h"
MessageShotBox::MessageShotBox(
QWidget *parent,
AyuFeatures::MessageShot::ShotConfig config)
: _config(std::move(config))
{
}
void MessageShotBox::prepare()
{
setupContent();
}
void MessageShotBox::setupContent()
{
_selectedPalette = std::make_shared<style::palette>();
const auto settings = &AyuSettings::getInstance();
const auto savedShowColorfulReplies = !settings->simpleQuotesAndReplies;
using namespace Settings;
AyuFeatures::MessageShot::setShotConfig(_config);
setTitle(rpl::single(QString("Message Shot")));
auto wrap = object_ptr<Ui::VerticalLayout>(this);
const auto content = wrap.data();
setInnerWidget(object_ptr<Ui::OverrideMargins>(this, std::move(wrap)));
AddSubsectionTitle(content, tr::ayu_MessageShotPreview());
const auto imageView = content->add(object_ptr<ImageView>(content), st::imageViewPadding);
AddSkip(content);
AddDivider(content);
AddSkip(content);
AddSubsectionTitle(content, tr::ayu_MessageShotPreferences());
const auto updatePreview = [=]
{
const auto image = AyuFeatures::MessageShot::Make(this, _config);
imageView->setImage(image);
};
auto selectedTheme =
content->lifetime().make_state<rpl::variable<QString> >(tr::ayu_MessageShotThemeDefault(tr::now));
AddButtonWithLabel(
content,
tr::ayu_MessageShotTheme(),
selectedTheme->value(),
st::settingsButtonNoIcon
)->addClickHandler(
[=]
{
AyuFeatures::MessageShot::setChoosingTheme(true);
auto box = Box<ThemeSelectorBox>(_config.controller);
box->paletteSelected() | rpl::start_with_next(
[=](const style::palette &palette) mutable
{
_selectedPalette->reset();
_selectedPalette->load(palette.save());
_config.st = std::make_shared<Ui::ChatStyle>(
_selectedPalette.get());
updatePreview();
},
content->lifetime());
box->themeNameChanged() | rpl::start_with_next(
[=](const QString &name)
{
selectedTheme->force_assign(name);
},
content->lifetime());
box->boxClosing() | rpl::start_with_next(
[=]
{
AyuFeatures::MessageShot::setChoosingTheme(false);
},
content->lifetime());
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
});
AddButtonWithIcon(
content,
tr::ayu_MessageShotShowDate(),
st::settingsButtonNoIcon
)->toggleOn(rpl::single(false)
)->toggledValue(
) | start_with_next(
[=](bool enabled)
{
_config.showDate = enabled;
updatePreview();
},
content->lifetime());
AddButtonWithIcon(
content,
tr::ayu_MessageShotShowReactions(),
st::settingsButtonNoIcon
)->toggleOn(rpl::single(false)
)->toggledValue(
) | start_with_next(
[=](bool enabled)
{
_config.showReactions = enabled;
updatePreview();
},
content->lifetime());
AddButtonWithIcon(
content,
tr::ayu_MessageShotShowColorfulReplies(),
st::settingsButtonNoIcon
)->toggleOn(rpl::single(savedShowColorfulReplies)
)->toggledValue(
) | start_with_next(
[=](bool enabled)
{
const auto settings = &AyuSettings::getInstance();
settings->set_simpleQuotesAndReplies(!enabled);
_config.st = std::make_shared<Ui::ChatStyle>(_config.st.get());
updatePreview();
},
content->lifetime());
AddSkip(content);
addButton(tr::ayu_MessageShotSave(),
[=]
{
const auto image = imageView->getImage();
const auto path = QFileDialog::getSaveFileName(
this,
tr::lng_save_file(tr::now),
QString(),
"*.png");
if (!path.isEmpty()) {
image.save(path);
}
});
addButton(tr::ayu_MessageShotCopy(),
[=]
{
QGuiApplication::clipboard()->setImage(imageView->getImage());
});
updatePreview();
const auto boxWidth = imageView->getImage().width() + (st::boxPadding.left() + st::boxPadding.right()) * 4;
boxClosing() | rpl::start_with_next(
[=]
{
AyuFeatures::MessageShot::resetCustomSelected();
AyuFeatures::MessageShot::resetDefaultSelected();
AyuFeatures::MessageShot::resetShotConfig();
const auto settings = &AyuSettings::getInstance();
settings->set_simpleQuotesAndReplies(!savedShowColorfulReplies);
},
content->lifetime());
setDimensionsToContent(boxWidth, content);
}

View file

@ -0,0 +1,25 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ayu/features/messageshot/message_shot.h"
#include "ui/layers/box_content.h"
class MessageShotBox : public Ui::BoxContent {
public:
MessageShotBox(QWidget* parent, AyuFeatures::MessageShot::ShotConfig config);
protected:
void prepare() override;
private:
void setupContent();
AyuFeatures::MessageShot::ShotConfig _config;
std::shared_ptr<style::palette> _selectedPalette;
};

View file

@ -4,15 +4,14 @@
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "confirmation_box.h"
#include "server_read_confirmation_box.h"
#include "lang_auto.h"
#include "ayu/ayu_settings.h"
#include "ayu/utils/telegram_helpers.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "styles/style_layers.h"
#include "ui/text/text_utilities.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
namespace AyuUi

View file

@ -0,0 +1,190 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "theme_selector_box.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_origin.h"
#include "data/data_session.h"
#include "lang_auto.h"
#include "main/main_session.h"
#include "settings/settings_chat.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "window/themes/window_theme.h"
#include "window/themes/window_theme_preview.h"
#include "ui/vertical_list.h"
#include "window/themes/window_themes_cloud_list.h"
#include "ayu/features/messageshot/message_shot.h"
ThemeSelectorBox::ThemeSelectorBox(
QWidget *parent,
not_null<Window::SessionController *> controller)
: _controller(controller)
{
}
rpl::producer<style::palette> ThemeSelectorBox::paletteSelected()
{
return _palettes.events();
}
rpl::producer<QString> ThemeSelectorBox::themeNameChanged()
{
return _themeNames.events();
}
void ThemeSelectorBox::prepare()
{
setupContent();
}
void ThemeSelectorBox::setupContent()
{
using namespace Settings;
setTitle(tr::ayu_MessageShotThemeSelectTitle());
auto wrap2 = object_ptr<Ui::VerticalLayout>(this);
const auto container = wrap2.data();
setInnerWidget(object_ptr<Ui::OverrideMargins>(
this,
std::move(wrap2)));
AddSubsectionTitle(container, tr::lng_settings_themes());
AddSkip(container, st::settingsThemesTopSkip);
Settings::SetupDefaultThemes(&_controller->window(), container);
AddSkip(container);
using namespace Window::Theme;
using namespace rpl::mappers;
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout> >(
container,
object_ptr<Ui::VerticalLayout>(container))
)->setDuration(0);
const auto inner = wrap->entity();
AddDivider(inner);
AddSkip(inner);
const auto title = AddSubsectionTitle(
inner,
tr::lng_settings_bg_cloud_themes());
const auto showAll = Ui::CreateChild<Ui::LinkButton>(
inner,
tr::lng_settings_bg_show_all(tr::now));
rpl::combine(
title->topValue(),
inner->widthValue(),
showAll->widthValue()
) | rpl::start_with_next([=](int top, int outerWidth, int width)
{
showAll->moveToRight(
st::defaultSubsectionTitlePadding.left(),
top,
outerWidth);
},
showAll->lifetime());
Ui::AddSkip(inner, st::settingsThemesTopSkip);
const auto list = inner->lifetime().make_state<CloudList>(
inner,
_controller);
inner->add(
list->takeWidget(),
style::margins(
st::settingsButtonNoIcon.padding.left(),
0,
st::settingsButtonNoIcon.padding.right(),
0));
list->allShown(
) | rpl::start_with_next([=](bool shown)
{
showAll->setVisible(!shown);
},
showAll->lifetime());
showAll->addClickHandler([=]
{
list->showAll();
});
wrap->setDuration(0)->toggleOn(list->empty() | rpl::map(!_1));
_controller->session().data().cloudThemes().refresh();
AyuFeatures::MessageShot::themeChosen(
) | rpl::start_with_next(
[=](Data::CloudTheme theme)
{
const auto document = _controller->session().data().document(theme.documentId);
const auto documentView = document->createMediaView();
document->save(
Data::FileOriginTheme(theme.id, theme.accessHash),
QString());
const auto innerCallback = [=]
{
auto preview = Window::Theme::PreviewFromFile(
documentView->bytes(),
document->location().name(),
theme);
_selectedPalette = preview->instance.palette;
auto name = theme.title;
_themeNames.fire(std::move(name));
};
if (documentView->loaded()) {
innerCallback();
}
else {
_controller->session().downloaderTaskFinished(
) | rpl::filter(
[=]
{
return documentView->loaded();
}) | rpl::start_with_next(
[=]
{
innerCallback();
},
lifetime());
}
},
lifetime());
AyuFeatures::MessageShot::paletteChosen(
) | rpl::start_with_next([=](const auto &palette)
{
_themeNames.fire(tr::ayu_MessageShotThemeDefault(tr::now));
_selectedPalette = palette;
},
lifetime());
addButton(tr::ayu_MessageShotThemeApply(),
[=]
{
_palettes.fire(std::move(_selectedPalette));
closeBox();
});
setDimensionsToContent(st::boxWidth, container);
}

View file

@ -0,0 +1,35 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ui/style/style_core_palette.h"
#include "ui/layers/box_content.h"
#include "window/window_session_controller.h"
using Callback = Fn<void(style::palette&)>;
class ThemeSelectorBox : public Ui::BoxContent {
public:
ThemeSelectorBox(QWidget* parent, not_null<Window::SessionController*> controller);
rpl::producer<style::palette> paletteSelected();
rpl::producer<QString> themeNameChanged();
protected:
void prepare() override;
private:
void setupContent();
not_null<Window::SessionController*> _controller;
rpl::event_stream<style::palette> _palettes;
rpl::event_stream<QString> _themeNames;
style::palette _selectedPalette;
};

View file

@ -1,117 +0,0 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "ui/boxes/confirm_box.h"
#include "voice_confirmation_box.h"
#include "lang/lang_keys.h"
#include "styles/style_layers.h"
#include "ui/widgets/buttons.h"
namespace AyuUi
{
void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args)
{
const auto weak = MakeWeak(box);
const auto lifetime = box->lifetime().make_state<rpl::lifetime>();
v::match(args.text, [](v::null_t)
{
}, [&](auto &&)
{
const auto label = box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
v::text::take_marked(std::move(args.text)),
args.labelStyle ? *args.labelStyle : st::boxLabel),
st::boxPadding);
if (args.labelFilter) {
label->setClickHandlerFilter(std::move(args.labelFilter));
}
});
const auto prepareCallback = [&](Ui::ConfirmBoxArgs::Callback &callback)
{
return [=, confirmed = std::move(callback)]()
{
if (const auto callbackPtr = std::get_if<1>(&confirmed)) {
if (auto callback = (*callbackPtr)) {
callback();
}
}
else if (const auto callbackPtr = std::get_if<2>(&confirmed)) {
if (auto callback = (*callbackPtr)) {
callback(crl::guard(weak, [=]
{ weak->closeBox(); }));
}
}
else if (weak) {
weak->closeBox();
}
};
};
const auto &defaultButtonStyle = box->getDelegate()->style().button;
const auto confirmButton = box->addButton(
v::text::take_plain(std::move(args.confirmText), tr::lng_box_ok()),
[=, c = prepareCallback(args.confirmed)]()
{
lifetime->destroy();
c();
weak->closeBox();
},
args.confirmStyle ? *args.confirmStyle : defaultButtonStyle);
box->events(
) | start_with_next([=](not_null<QEvent *> e)
{
if ((e->type() != QEvent::KeyPress) || !confirmButton) {
return;
}
const auto k = static_cast<QKeyEvent *>(e.get());
if (k->key() == Qt::Key_Enter || k->key() == Qt::Key_Return) {
confirmButton->clicked(Qt::KeyboardModifiers(), Qt::LeftButton);
}
}, box->lifetime());
if (!args.inform) {
const auto cancelButton = box->addButton(
v::text::take_plain(std::move(args.cancelText), tr::lng_cancel()),
crl::guard(weak, [=, c = prepareCallback(args.cancelled)]()
{
lifetime->destroy();
c();
}),
args.cancelStyle ? *args.cancelStyle : defaultButtonStyle);
box->boxClosing(
) | start_with_next(crl::guard(cancelButton, [=]
{
cancelButton->clicked(Qt::KeyboardModifiers(), Qt::LeftButton);
}), *lifetime);
}
if (args.strictCancel) {
lifetime->destroy();
}
}
object_ptr<Ui::GenericBox> MakeConfirmBox(Ui::ConfirmBoxArgs &&args)
{
return Box(VoiceConfirmBox, std::move(args));
}
object_ptr<Ui::GenericBox> MakeInformBox(v::text::data text)
{
return MakeConfirmBox({
.text = std::move(text),
.inform = true,
});
}
} // namespace AyuUi

View file

@ -1,23 +0,0 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ui/boxes/confirm_box.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_variant.h"
namespace AyuUi
{
void VoiceConfirmBox(not_null<Ui::GenericBox *> box, Ui::ConfirmBoxArgs &&args);
[[nodiscard]] object_ptr<Ui::GenericBox> MakeConfirmBox(
Ui::ConfirmBoxArgs &&args);
[[nodiscard]] object_ptr<Ui::GenericBox> MakeInformBox(v::text::data text);
} // namespace Ui

View file

@ -0,0 +1,112 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "image_view.h"
#include "styles/style_ayu_styles.h"
#include "ayu/utils/telegram_helpers.h"
#include "styles/style_chat.h"
#include "window/themes/window_theme.h"
#include "ui/painter.h"
ImageView::ImageView(QWidget *parent)
: RpWidget(parent)
{
}
void ImageView::setImage(const QImage &image)
{
if (this->image == image) {
return;
}
const auto set = [=] {
this->prevImage = this->image;
this->image = image;
setMinimumSize(image.size().grownBy(st::imageViewInnerPadding));
if (this->animation.animating()) {
this->animation.stop();
}
if (this->prevImage.isNull()) {
update();
return;
}
this->animation.start(
[=]
{ update(); }, 0.0, 1.0, 300, anim::easeInCubic);
};
if (this->image.isNull()) {
set();
return;
}
dispatchToMainThread(set, 100);
}
QImage ImageView::getImage() const
{
return image;
}
void ImageView::paintEvent(QPaintEvent *e)
{
Painter p(this);
// PainterHighQualityEnabler hq(p);
auto brush = QBrush(st::boxBg); // copy
if (Window::Theme::IsNightMode()) {
brush.setColor(brush.color().lighter(120));
}
else {
brush.setColor(brush.color().darker(105));
}
QPainterPath path;
path.addRoundedRect(rect(), st::bubbleRadiusLarge, st::bubbleRadiusLarge);
p.fillPath(path, brush);
if (!prevImage.isNull()) {
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
const auto resizedRect = QRect(
(realRect.width() - prevImage.width()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - prevImage.height()) / 2 + st::imageViewInnerPadding.top(),
prevImage.width(),
prevImage.height());
const auto opacity = 1.0 - animation.value(1.0);
p.setOpacity(opacity);
p.drawImage(resizedRect, prevImage);
p.setOpacity(1.0);
}
if (!image.isNull()) {
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
const auto resizedRect = QRect(
(realRect.width() - image.width()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - image.height()) / 2 + st::imageViewInnerPadding.top(),
image.width(),
image.height());
const auto opacity = animation.value(1.0);
p.setOpacity(opacity);
p.drawImage(resizedRect, image);
p.setOpacity(1.0);
}
}
void ImageView::mousePressEvent(QMouseEvent *e)
{
}

View file

@ -0,0 +1,31 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ui/rp_widget.h"
#include "ui/effects/animations.h"
class ImageView : public Ui::RpWidget
{
public:
ImageView(QWidget *parent);
void setImage(const QImage& image);
QImage getImage() const;
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
private:
QImage image;
QImage prevImage;
Ui::Animations::Simple animation;
};

View file

@ -256,11 +256,14 @@ void AddMessageDetailsAction(not_null<Ui::PopupMenu *> menu, HistoryItem *item)
if (isSticker) {
const auto authorId = media->document()->sticker()->set.id >> 32;
menu2->addAction(Ui::ContextActionStickerAuthor(
menu2->menu(),
&item->history()->session(),
authorId
));
if (authorId != 0) {
menu2->addAction(Ui::ContextActionStickerAuthor(
menu2->menu(),
&item->history()->session(),
authorId
));
}
}
}
},

View file

@ -65,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "base/unixtime.h"
#include <QBuffer>
namespace {
@ -623,6 +624,36 @@ void SendFilesBox::addMenuButton() {
[=] { sendWhenOnline(); },
&_st.tabbed.icons);
}
using ImageInfo = Ui::PreparedFileInformation::Image;
if (_list.files.size() == 1 && std::get_if<ImageInfo>(&_list.files[0].information->media)) {
_menu->addAction(
tr::ayu_SendAsSticker(tr::now),
[=]() mutable
{
const auto file = std::move(_list.files[0]);
_list.files.clear();
const auto sourceImage = std::get_if<ImageInfo>(&file.information->media);
QByteArray targetArray;
QBuffer buffer(&targetArray);
buffer.open(QIODevice::WriteOnly);
sourceImage->data.save(&buffer, "WEBP");
QImage targetImage;
targetImage.loadFromData(targetArray, "WEBP");
addFiles(Storage::PrepareMediaFromImage(std::move(targetImage),
std::move(targetArray),
st::sendMediaPreviewSize));
_list.overrideSendImagesAsPhotos = false;
initSendWay();
send({}, false);
},
&st::menuIconStickers);
}
_menu->popup(QCursor::pos());
return true;
});
@ -1400,7 +1431,7 @@ void SendFilesBox::send(
{
DEBUG_LOG(("[AyuGram] Scheduling files"));
auto current = base::unixtime::now();
options.scheduled = current + 60; // well, files can be really big...
options.scheduled = current + 60; // well, files can be huge...
}
if ((_sendType == Api::SendType::Scheduled

View file

@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/features/messageshot/message_shot.h"
#include "ayu/utils/telegram_helpers.h"
@ -2458,7 +2459,7 @@ void HistoryItem::updateReactionsUnknown() {
const std::vector<Data::MessageReaction> &HistoryItem::reactions() const {
static const auto kEmpty = std::vector<Data::MessageReaction>();
return _reactions ? _reactions->list() : kEmpty;
return _reactions && !AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Reactions) ? _reactions->list() : kEmpty;
}
bool HistoryItem::reactionsAreTags() const {

View file

@ -171,6 +171,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/utils/telegram_helpers.h"
#include "ayu/features/messageshot/message_shot.h"
#include "ayu/ui/boxes/message_shot_box.h"
#include "boxes/abstract_box.h"
namespace {
@ -824,6 +827,10 @@ HistoryWidget::HistoryWidget(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->messageShotSelectionRequest(
) | rpl::start_with_next([=] {
messageShotSelected();
}, _topBar->lifetime());
_topBar->clearSelectionRequest(
) | rpl::start_with_next([=] {
clearSelected();
@ -7717,6 +7724,29 @@ void HistoryWidget::confirmDeleteSelected() {
controller()->show(std::move(box));
}
void HistoryWidget::messageShotSelected() {
if (!_list) return;
auto items = getSelectedItems();
if (items.empty()) {
return;
}
const auto messages = ranges::views::all(items)
| ranges::views::transform([this](const auto fullId) {
return gsl::not_null(session().data().message(fullId));
})
| ranges::to_vector;
const AyuFeatures::MessageShot::ShotConfig config = {
controller(),
std::make_shared<Ui::ChatStyle>(controller()->chatStyle()),
messages
};
auto box = Box<MessageShotBox>(config);
Ui::show(std::move(box));
}
void HistoryWidget::escape() {
if (_composeSearch) {
_composeSearch->hideAnimated();

View file

@ -261,6 +261,7 @@ public:
void forwardSelected();
void confirmDeleteSelected();
void messageShotSelected();
void clearSelected();
[[nodiscard]] SendMenu::Type sendMenuType() const;

View file

@ -42,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/ui/boxes/voice_confirmation_box.h"
#include "boxes/abstract_box.h"
@ -1725,7 +1724,7 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
});
if (settings->voiceConfirmation) {
Ui::show(AyuUi::MakeConfirmBox(
Ui::show(Ui::MakeConfirmBox(
{
.text = tr::ayu_ConfirmationVoice(),
.confirmed = sendVoiceCallback,
@ -1820,7 +1819,7 @@ void VoiceRecordBar::requestToSendWithOptions(Api::SendOptions options) {
});
if (settings->voiceConfirmation) {
Ui::show(AyuUi::MakeConfirmBox(
Ui::show(Ui::MakeConfirmBox(
{
.text = tr::ayu_ConfirmationVoice(),
.confirmed = sendVoiceCallback,

View file

@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
@ -132,7 +133,7 @@ TextState BottomInfo::textState(
}
const auto textWidth = _authorEditedDate.maxWidth();
auto withTicksWidth = textWidth;
if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
if (!AyuFeatures::MessageShot::isTakingShot() && _data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
withTicksWidth += st::historySendStateSpace;
}
if (!_views.isEmpty()) {
@ -263,7 +264,7 @@ void BottomInfo::paint(
auto right = position.x() + width();
const auto firstLineBottom = position.y() + st::msgDateFont->height;
if (_data.flags & Data::Flag::OutLayout) {
if (!AyuFeatures::MessageShot::isTakingShot() && _data.flags & Data::Flag::OutLayout) {
const auto &icon = (_data.flags & Data::Flag::Sending)
? (inverted
? st->historySendingInvertedIcon()
@ -332,7 +333,7 @@ void BottomInfo::paint(
firstLineBottom + st::historyViewsTop,
outerWidth);
}
if ((_data.flags & Data::Flag::Sending)
if (!AyuFeatures::MessageShot::isTakingShot() && (_data.flags & Data::Flag::Sending)
&& !(_data.flags & Data::Flag::OutLayout)) {
right -= st::historySendStateSpace;
const auto &icon = inverted
@ -558,7 +559,7 @@ void BottomInfo::layoutReactionsText() {
QSize BottomInfo::countOptimalSize() {
auto width = 0;
if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
if (!AyuFeatures::MessageShot::isTakingShot() && _data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
width += st::historySendStateSpace;
}
width += _authorEditedDate.maxWidth();

View file

@ -83,7 +83,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ayu/database/ayu_database.h"
#include "ayu/messages/ayu_messages_controller.h"
#include "ayu/ui/context_menu/context_menu.h"
#include "ayu/ui/boxes/message_history_box.h"
#include "ayu/ui/sections/edited/edited_log_section.h"

View file

@ -47,6 +47,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -306,6 +310,10 @@ void UnreadBar::paint(
int y,
int w,
bool chatWide) const {
if (AyuFeatures::MessageShot::isTakingShot()) {
return;
}
const auto st = context.st;
const auto bottom = y + height();
y += marginTop();
@ -668,6 +676,10 @@ bool Element::isTopicRootReply() const {
}
int Element::skipBlockWidth() const {
if (AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
return st::msgDateDelta.x();
}
return st::msgDateSpace + infoWidth() - st::msgDateDelta.x();
}
@ -1177,6 +1189,10 @@ void Element::destroyUnreadBar() {
}
int Element::displayedDateHeight() const {
if (AyuFeatures::MessageShot::isTakingShot()) {
return 0;
}
if (auto date = Get<DateBadge>()) {
return date->height();
}

View file

@ -43,6 +43,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_dialogs.h"
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -853,10 +857,14 @@ int Message::marginTop() const {
}
result += displayedDateHeight();
if (const auto bar = Get<UnreadBar>()) {
result += bar->height();
if (!AyuFeatures::MessageShot::isTakingShot()) {
result += bar->height();
}
}
if (const auto service = Get<ServicePreMessage>()) {
result += service->height;
if (!AyuFeatures::MessageShot::isTakingShot()) {
result += service->height;
}
}
return result;
}
@ -910,7 +918,7 @@ void Message::draw(Painter &p, const PaintContext &context) const {
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
const auto displayInfo = needInfoDisplay();
const auto displayInfo = needInfoDisplay() && !AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date);
const auto reactionsInBubble = _reactions && embedReactionsInBubble();
auto mediaSelectionIntervals = (!context.selected() && mediaDisplayed)
@ -3359,6 +3367,10 @@ bool Message::displayRightActionComments() const {
}
std::optional<QSize> Message::rightActionSize() const {
if (AyuFeatures::MessageShot::isTakingShot()) {
return {};
}
if (displayRightActionComments()) {
const auto views = data()->Get<HistoryMessageViews>();
Assert(views != nullptr);

View file

@ -53,6 +53,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QMimeData>
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -143,6 +147,10 @@ PinnedWidget::PinnedWidget(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->messageShotSelectionRequest(
) | rpl::start_with_next([=] {
AyuFeatures::MessageShot::Wrapper(_inner);
}, _topBar->lifetime());
_topBar->forwardSelectionRequest(
) | rpl::start_with_next([=] {
confirmForwardSelected();

View file

@ -96,6 +96,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QMimeData>
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -283,6 +287,10 @@ RepliesWidget::RepliesWidget(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->messageShotSelectionRequest(
) | rpl::start_with_next([=] {
AyuFeatures::MessageShot::Wrapper(_inner);
}, _topBar->lifetime());
_topBar->forwardSelectionRequest(
) | rpl::start_with_next([=] {
confirmForwardSelected();

View file

@ -66,6 +66,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QMimeData>
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
object_ptr<Window::SectionWidget> ScheduledMemento::createWidget(
@ -136,6 +140,10 @@ ScheduledWidget::ScheduledWidget(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->messageShotSelectionRequest(
) | rpl::start_with_next([=] {
AyuFeatures::MessageShot::Wrapper(_inner);
}, _topBar->lifetime());
_topBar->clearSelectionRequest(
) | rpl::start_with_next([=] {
clearSelected();

View file

@ -31,6 +31,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -110,6 +114,10 @@ SublistWidget::SublistWidget(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->messageShotSelectionRequest(
) | rpl::start_with_next([=] {
AyuFeatures::MessageShot::Wrapper(_inner);
}, _topBar->lifetime());
_topBar->forwardSelectionRequest(
) | rpl::start_with_next([=] {
confirmForwardSelected();

View file

@ -119,6 +119,7 @@ TopBarWidget::TopBarWidget(
, _forward(this, tr::lng_selected_forward(), st::defaultActiveButton)
, _sendNow(this, tr::lng_selected_send_now(), st::defaultActiveButton)
, _delete(this, tr::lng_selected_delete(), st::defaultActiveButton)
, _messageShot(this, tr::ayu_MessageShotTopBarText(), st::defaultActiveButton)
, _back(this, st::historyTopBarBack)
, _cancelChoose(this, st::topBarCloseChoose)
, _call(this, st::topBarCall)
@ -141,6 +142,8 @@ TopBarWidget::TopBarWidget(
_sendNow->setWidthChangedCallback([=] { updateControlsGeometry(); });
_delete->setClickedCallback([=] { _deleteSelection.fire({}); });
_delete->setWidthChangedCallback([=] { updateControlsGeometry(); });
_messageShot->setClickedCallback([=] { _messageShotSelection.fire({}); });
_messageShot->setWidthChangedCallback([=] { updateControlsGeometry(); });
_clear->setClickedCallback([=] { _clearSelection.fire({}); });
_call->setClickedCallback([=] { call(); });
_groupCall->setClickedCallback([=] { groupCall(); });
@ -970,6 +973,7 @@ void TopBarWidget::updateControlsGeometry() {
auto buttonsWidth = (_forward->isHidden() ? 0 : _forward->contentWidth())
+ (_sendNow->isHidden() ? 0 : _sendNow->contentWidth())
+ (_delete->isHidden() ? 0 : _delete->contentWidth())
+ (_messageShot->isHidden() ? 0 : _messageShot->contentWidth())
+ _clear->width();
buttonsWidth += buttonsLeft + st::topBarActionSkip * 3;
@ -978,6 +982,7 @@ void TopBarWidget::updateControlsGeometry() {
_forward->setFullWidth(buttonFullWidth);
_sendNow->setFullWidth(buttonFullWidth);
_delete->setFullWidth(buttonFullWidth);
_messageShot->setFullWidth(buttonFullWidth);
selectedButtonsTop += (height() - _forward->height()) / 2;
@ -992,6 +997,12 @@ void TopBarWidget::updateControlsGeometry() {
}
_delete->moveToLeft(buttonsLeft, selectedButtonsTop);
if (!_delete->isHidden()) {
buttonsLeft += _delete->width() + st::topBarActionSkip;
}
_messageShot->moveToLeft(buttonsLeft, selectedButtonsTop);
_clear->moveToRight(st::topBarActionSkip, selectedButtonsTop);
if (!_cancelChoose->isHidden()) {
@ -1091,6 +1102,7 @@ void TopBarWidget::updateControlsVisibility() {
}
_clear->show();
_delete->setVisible(_canDelete);
_messageShot->setVisible(true);
_forward->setVisible(_canForward);
_sendNow->setVisible(_canSendNow);
@ -1266,10 +1278,12 @@ void TopBarWidget::showSelected(SelectedState state) {
_forward->setNumbersText(_selectedCount);
_sendNow->setNumbersText(_selectedCount);
_delete->setNumbersText(_selectedCount);
_messageShot->setNumbersText(_selectedCount);
if (!wasSelectedState) {
_forward->finishNumbersAnimation();
_sendNow->finishNumbersAnimation();
_delete->finishNumbersAnimation();
_messageShot->finishNumbersAnimation();
}
}
if (visibilityChanged) {

View file

@ -100,6 +100,9 @@ public:
[[nodiscard]] rpl::producer<> deleteSelectionRequest() const {
return _deleteSelection.events();
}
[[nodiscard]] rpl::producer<> messageShotSelectionRequest() const {
return _messageShotSelection.events();
}
[[nodiscard]] rpl::producer<> clearSelectionRequest() const {
return _clearSelection.events();
}
@ -206,7 +209,7 @@ private:
Ui::Animations::Simple _searchShown;
object_ptr<Ui::RoundButton> _clear;
object_ptr<Ui::RoundButton> _forward, _sendNow, _delete;
object_ptr<Ui::RoundButton> _forward, _sendNow, _delete, _messageShot;
object_ptr<Ui::InputField> _searchField = { nullptr };
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _chooseFromUser
= { nullptr };
@ -252,6 +255,7 @@ private:
rpl::event_stream<> _forwardSelection;
rpl::event_stream<> _sendNowSelection;
rpl::event_stream<> _deleteSelection;
rpl::event_stream<> _messageShotSelection;
rpl::event_stream<> _clearSelection;
rpl::event_stream<> _cancelChooseForReport;

View file

@ -56,6 +56,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QSvgRenderer>
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -697,7 +701,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
}
p.setOpacity(1.);
}
if (displayMute) {
if (displayMute && !AyuFeatures::MessageShot::isTakingShot()) {
auto muteRect = style::rtlrect(rthumb.x() + (rthumb.width() - st::historyVideoMessageMuteSize) / 2, rthumb.y() + st::msgDateImgDelta, st::historyVideoMessageMuteSize, st::historyVideoMessageMuteSize, width());
p.setPen(Qt::NoPen);
p.setBrush(sti->msgDateImgBg);
@ -714,7 +718,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
drawCornerStatus(p, context, QPoint());
}
} else if (!skipDrawingSurrounding) {
if (isRound) {
if (isRound && !AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
const auto mediaUnread = item->hasUnreadMediaFlag();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
@ -820,7 +824,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
.selection = context.selection,
.highlight = highlightRequest ? &*highlightRequest : nullptr,
});
} else if (!inWebPage && !skipDrawingSurrounding) {
} else if (!inWebPage && !skipDrawingSurrounding && !AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
auto fullRight = paintx + usex + usew;
auto fullBottom = painty + painth;
auto maxRight = _parent->width() - st::msgMargin.left();
@ -1023,7 +1027,7 @@ void Gif::drawCornerStatus(
const auto padding = st::msgDateImgPadding;
const auto radial = _animation && _animation->radial.animating();
const auto cornerDownload = downloadInCorner() && !dataLoaded() && !_data->loadedInMediaCache();
const auto cornerMute = _streamed && _data->isVideoFile() && !cornerDownload;
const auto cornerMute = _streamed && _data->isVideoFile() && !cornerDownload && !AyuFeatures::MessageShot::isTakingShot();
const auto addLeft = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0;
const auto addRight = cornerMute ? st::historyVideoMuteSize : 0;
const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0;
@ -2056,11 +2060,20 @@ bool Gif::needInfoDisplay() const {
}
bool Gif::needCornerStatusDisplay() const {
if (AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
return false;
}
return _data->isVideoFile()
|| needInfoDisplay();
}
void Gif::ensureTranscribeButton() const {
if (AyuFeatures::MessageShot::isTakingShot()) {
_transcribe = nullptr;
return;
}
if (_data->isVideoMessage()
&& !_parent->data()->media()->ttlSeconds()
&& (_data->session().premium()

View file

@ -39,6 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "styles/style_chat.h"
// AyuGran includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView {
namespace {
@ -902,6 +906,10 @@ bool Photo::dataLoaded() const {
}
bool Photo::needInfoDisplay() const {
if (AyuFeatures::MessageShot::ignoreRender(AyuFeatures::MessageShot::RenderPart::Date)) {
return false;
}
if (_parent->data()->isFakeBotAbout()) {
return false;
}

View file

@ -28,6 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace HistoryView::Reactions {
namespace {
@ -484,7 +488,7 @@ void InlineList::paint(
p.setOpacity(1.);
}
}
if (!animations.empty()) {
if (!animations.empty() && !AyuFeatures::MessageShot::isTakingShot()) { // fix crash when taking shot
const auto now = context.now;
context.reactionInfo->effectPaint = [
now,

View file

@ -72,6 +72,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_menu_icons.h"
#include "styles/style_window.h"
// AyuGram includes
#include "ayu/ui/settings/settings_ayu.h"
#include "ayu/features/messageshot/message_shot.h"
#include "window/themes/window_theme_preview.h"
namespace Settings {
namespace {
@ -247,7 +253,7 @@ void ColorsPalette::show(Type type) {
return;
}
list.insert(list.begin(), scheme->accentColor);
const auto color = Core::App().settings().themesAccentColors().get(type);
const auto color = AyuFeatures::MessageShot::isChoosingTheme() ? AyuFeatures::MessageShot::getSelectedColorFromDefault() : Core::App().settings().themesAccentColors().get(type);
const auto current = color.value_or(scheme->accentColor);
const auto i = ranges::find(list, current);
if (i == end(list)) {
@ -1290,7 +1296,19 @@ void SetupDefaultThemes(
container.get(),
container.get());
const auto updateMessageShotPalette = [=](const QString &path)
{
const Data::CloudTheme theme;
if (const auto preview = PreviewFromFile(QByteArray(), path, theme)) {
AyuFeatures::MessageShot::setPalette(preview->instance.palette);
}
};
const auto chosen = [] {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
return AyuFeatures::MessageShot::getSelectedFromDefault();
}
const auto &object = Background()->themeObject();
if (object.cloud.id) {
return Type(-1);
@ -1328,6 +1346,12 @@ void SetupDefaultThemes(
const auto schemeClicked = [=](
const Scheme &scheme,
Qt::KeyboardModifiers modifiers) {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
AyuFeatures::MessageShot::setDefaultSelected(scheme.type);
updateMessageShotPalette(scheme.path);
return;
}
apply(scheme);
};
@ -1371,6 +1395,16 @@ void SetupDefaultThemes(
return;
}
if (i != end(checks)) {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
if (const auto color = AyuFeatures::MessageShot::getSelectedColorFromDefault()) {
const auto colorizer = ColorizerFrom(*scheme, color.value());
i->second->setColors(ColorsFromScheme(*scheme, colorizer));
} else {
i->second->setColors(ColorsFromScheme(*scheme));
}
return;
}
if (const auto color = colors.get(type)) {
const auto colorizer = ColorizerFrom(*scheme, *color);
i->second->setColors(ColorsFromScheme(*scheme, colorizer));
@ -1380,6 +1414,21 @@ void SetupDefaultThemes(
}
};
group->setChangedCallback([=](Type type) {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
palette->show(type);
refreshColorizer(type);
group->setValue(type);
AyuFeatures::MessageShot::setDefaultSelected(type);
const auto scheme = ranges::find(kSchemesList, type, &Scheme::type);
if (scheme == end(kSchemesList)) {
return;
}
updateMessageShotPalette(scheme->path);
return;
}
group->setValue(chosen());
});
for (const auto &scheme : kSchemesList) {
@ -1430,8 +1479,32 @@ void SetupDefaultThemes(
}
}, block->lifetime());
if (AyuFeatures::MessageShot::isChoosingTheme()) {
palette->selected() | rpl::start_with_next([=](QColor color) {
AyuFeatures::MessageShot::setDefaultSelectedColor(color);
refreshColorizer(AyuFeatures::MessageShot::getSelectedFromDefault());
const auto type = chosen();
const auto scheme = ranges::find(kSchemesList, type, &Scheme::type);
if (scheme == end(kSchemesList)) {
return;
}
updateMessageShotPalette(scheme->path);
}, container->lifetime());
AyuFeatures::MessageShot::resetDefaultSelectedEvents() | rpl::start_with_next([=] {
refreshColorizer(AyuFeatures::MessageShot::getSelectedFromDefault()); // hide colorizer
group->setValue(Type(-1));
}, container->lifetime());
}
palette->selected(
) | rpl::start_with_next([=](QColor color) {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
return;
}
if (Background()->editingTheme()) {
// We don't remember old accent color to revert it properly
// in Window::Theme::Revert which is called by Editor.

View file

@ -35,6 +35,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace Window {
namespace Theme {
namespace {
@ -332,11 +336,21 @@ rpl::producer<bool> CloudList::allShown() const {
void CloudList::setup() {
_group->setChangedCallback([=](int selected) {
if (AyuFeatures::MessageShot::isChoosingTheme()) {
return;
}
const auto &object = Background()->themeObject();
_group->setValue(groupValueForId(
object.cloud.id ? object.cloud.id : kFakeCloudThemeId));
});
if (AyuFeatures::MessageShot::isChoosingTheme()) {
AyuFeatures::MessageShot::resetCustomSelectedEvents() | rpl::start_with_next([=] {
_group->setValue(-1);
}, _outer->lifetime());
}
auto cloudListChanges = rpl::single(rpl::empty) | rpl::then(
_window->session().data().cloudThemes().updated()
);
@ -436,6 +450,13 @@ bool CloudList::applyChangesFrom(std::vector<Data::CloudTheme> &&list) {
changed = true;
}
_group->setValue(groupValueForId(id));
if (AyuFeatures::MessageShot::isChoosingTheme()) {
if (const auto selected = AyuFeatures::MessageShot::getSelectedFromCustom()) {
_group->setValue(groupValueForId(selected.value().id));
}
}
return changed;
}
@ -521,6 +542,14 @@ void CloudList::insert(int index, const Data::CloudTheme &theme) {
return;
}
const auto &cloud = i->theme;
if (AyuFeatures::MessageShot::isChoosingTheme()) {
AyuFeatures::MessageShot::setTheme(cloud);
AyuFeatures::MessageShot::setCustomSelected(cloud);
_group->setValue(groupValueForId(cloud.id));
return;
}
if (button == Qt::RightButton) {
showMenu(*i);
} else if (cloud.documentId) {

View file

@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/core_settings.h"
#include "ui/style/style_palette_colorizer.h"
// AyuGram includes
#include "ayu/features/messageshot/message_shot.h"
namespace Window {
namespace Theme {
namespace {
@ -188,7 +192,7 @@ style::colorizer ColorizerForTheme(const QString &absolutePath) {
return {};
}
const auto &colors = Core::App().settings().themesAccentColors();
if (const auto accent = colors.get(i->type)) {
if (const auto accent = AyuFeatures::MessageShot::isChoosingTheme() ? AyuFeatures::MessageShot::getSelectedColorFromDefault() : colors.get(i->type)) {
return ColorizerFrom(*i, *accent);
}
return {};

View file

@ -75,7 +75,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/utils/telegram_helpers.h"
#include "ayu/ui/boxes/confirmation_box.h"
#include "ayu/ui/boxes/server_read_confirmation_box.h"
#include "boxes/abstract_box.h"
#include "ayu/features/streamer_mode/streamer_mode.h"
#include "styles/style_ayu_icons.h"