mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
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:
parent
0cf36db010
commit
9c71aba5d1
41 changed files with 1417 additions and 292 deletions
|
@ -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
|
||||
|
|
|
@ -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.";
|
||||
|
|
425
Telegram/SourceFiles/ayu/features/messageshot/message_shot.cpp
Normal file
425
Telegram/SourceFiles/ayu/features/messageshot/message_shot.cpp
Normal 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));
|
||||
}
|
||||
}
|
69
Telegram/SourceFiles/ayu/features/messageshot/message_shot.h
Normal file
69
Telegram/SourceFiles/ayu/features/messageshot/message_shot.h
Normal 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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
202
Telegram/SourceFiles/ayu/ui/boxes/message_shot_box.cpp
Normal file
202
Telegram/SourceFiles/ayu/ui/boxes/message_shot_box.cpp
Normal 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);
|
||||
}
|
25
Telegram/SourceFiles/ayu/ui/boxes/message_shot_box.h
Normal file
25
Telegram/SourceFiles/ayu/ui/boxes/message_shot_box.h
Normal 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;
|
||||
|
||||
};
|
|
@ -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
|
190
Telegram/SourceFiles/ayu/ui/boxes/theme_selector_box.cpp
Normal file
190
Telegram/SourceFiles/ayu/ui/boxes/theme_selector_box.cpp
Normal 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);
|
||||
}
|
35
Telegram/SourceFiles/ayu/ui/boxes/theme_selector_box.h
Normal file
35
Telegram/SourceFiles/ayu/ui/boxes/theme_selector_box.h
Normal 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;
|
||||
|
||||
};
|
|
@ -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
|
|
@ -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
|
112
Telegram/SourceFiles/ayu/ui/components/image_view.cpp
Normal file
112
Telegram/SourceFiles/ayu/ui/components/image_view.cpp
Normal 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)
|
||||
{
|
||||
}
|
31
Telegram/SourceFiles/ayu/ui/components/image_view.h
Normal file
31
Telegram/SourceFiles/ayu/ui/components/image_view.h
Normal 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;
|
||||
|
||||
};
|
|
@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -261,6 +261,7 @@ public:
|
|||
|
||||
void forwardSelected();
|
||||
void confirmDeleteSelected();
|
||||
void messageShotSelected();
|
||||
void clearSelected();
|
||||
|
||||
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue