mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Replaced old photo crop box with photo editor for profile photos.
This commit is contained in:
parent
a996b14291
commit
17465e1082
8 changed files with 162 additions and 113 deletions
|
@ -466,6 +466,7 @@ void GroupInfoBox::prepare() {
|
|||
|
||||
_photo.create(
|
||||
this,
|
||||
&_navigation->parentController()->window(),
|
||||
((_type == Type::Channel)
|
||||
? tr::lng_create_channel_crop
|
||||
: tr::lng_create_group_crop)(tr::now),
|
||||
|
|
|
@ -495,6 +495,7 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
|
|||
_wrap,
|
||||
object_ptr<Ui::UserpicButton>(
|
||||
_wrap,
|
||||
&_navigation->parentController()->window(),
|
||||
_peer,
|
||||
Ui::UserpicButton::Role::ChangePhoto,
|
||||
st::defaultUserpicButton),
|
||||
|
|
|
@ -7,12 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
|
||||
#include "app.h" // readImage
|
||||
#include "boxes/confirm_box.h" // InformBox
|
||||
#include "editor/photo_editor.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace Editor {
|
||||
namespace {
|
||||
|
||||
constexpr auto kProfilePhotoSize = 640;
|
||||
|
||||
} // namespace
|
||||
|
||||
void OpenWithPreparedFile(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
|
@ -48,6 +57,80 @@ void OpenWithPreparedFile(
|
|||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void PrepareProfilePhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback) {
|
||||
const auto resizeToMinSize = [=](
|
||||
QImage &&image,
|
||||
Qt::AspectRatioMode mode) {
|
||||
const auto &minSize = kProfilePhotoSize;
|
||||
if ((image.width() < minSize) || (image.height() < minSize)) {
|
||||
return image.scaled(
|
||||
minSize,
|
||||
minSize,
|
||||
mode,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
return std::move(image);
|
||||
};
|
||||
|
||||
const auto callback = [=, done = std::move(doneCallback)](
|
||||
const FileDialog::OpenResult &result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = result.remoteContent.isEmpty()
|
||||
? App::readImage(result.paths.front())
|
||||
: App::readImage(result.remoteContent);
|
||||
if (image.isNull()
|
||||
|| (image.width() > (10 * image.height()))
|
||||
|| (image.height() > (10 * image.width()))) {
|
||||
controller->show(Box<InformBox>(tr::lng_bad_photo(tr::now)));
|
||||
return;
|
||||
}
|
||||
image = resizeToMinSize(
|
||||
std::move(image),
|
||||
Qt::KeepAspectRatioByExpanding);
|
||||
const auto fileImage = std::make_shared<Image>(std::move(image));
|
||||
|
||||
auto applyModifications = [=, done = std::move(done)](
|
||||
const PhotoModifications &mods) {
|
||||
done(resizeToMinSize(
|
||||
ImageModified(fileImage->original(), mods),
|
||||
Qt::KeepAspectRatio));
|
||||
};
|
||||
|
||||
auto crop = [&] {
|
||||
const auto &i = fileImage;
|
||||
const auto minSide = std::min(i->width(), i->height());
|
||||
return QRect(
|
||||
(i->width() - minSide) / 2,
|
||||
(i->height() - minSide) / 2,
|
||||
minSide,
|
||||
minSide);
|
||||
}();
|
||||
|
||||
controller->showLayer(
|
||||
std::make_unique<LayerWidget>(
|
||||
parent,
|
||||
controller,
|
||||
fileImage,
|
||||
PhotoModifications{ .crop = std::move(crop) },
|
||||
std::move(applyModifications),
|
||||
EditorData{
|
||||
.cropType = EditorData::CropType::Ellipse,
|
||||
.keepAspectRatio = true, }),
|
||||
Ui::LayerOption::KeepOther);
|
||||
};
|
||||
FileDialog::GetOpenPath(
|
||||
parent.get(),
|
||||
tr::lng_choose_image(tr::now),
|
||||
FileDialog::ImagesOrAllFilter(),
|
||||
crl::guard(parent, callback));
|
||||
}
|
||||
|
||||
LayerWidget::LayerWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> window,
|
||||
|
|
|
@ -31,6 +31,11 @@ void OpenWithPreparedFile(
|
|||
int previewWidth,
|
||||
Fn<void()> &&doneCallback);
|
||||
|
||||
void PrepareProfilePhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback);
|
||||
|
||||
class PhotoEditor;
|
||||
|
||||
class LayerWidget : public Ui::LayerWidget {
|
||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
|
@ -269,6 +270,13 @@ Cover::Cover(
|
|||
|
||||
initViewers(std::move(title));
|
||||
setupChildGeometry();
|
||||
|
||||
_userpic->uploadPhotoRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
_peer->session().api().uploadPeerPhoto(
|
||||
_peer,
|
||||
_userpic->takeResultImage());
|
||||
}, _userpic->lifetime());
|
||||
}
|
||||
|
||||
void Cover::setupChildGeometry() {
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "settings/settings_information.h"
|
||||
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
|
@ -63,38 +64,13 @@ void SetupPhoto(
|
|||
st::settingsInfoPhotoSet);
|
||||
upload->setFullRadius(true);
|
||||
upload->addClickHandler([=] {
|
||||
const auto filter = FileDialog::ImagesOrAllFilter();
|
||||
const auto callback = [=](const FileDialog::OpenResult &result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto image = result.remoteContent.isEmpty()
|
||||
? App::readImage(result.paths.front())
|
||||
: App::readImage(result.remoteContent);
|
||||
if (image.isNull()
|
||||
|| image.width() > 10 * image.height()
|
||||
|| image.height() > 10 * image.width()) {
|
||||
controller->show(Box<InformBox>(tr::lng_bad_photo(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto box = Box<PhotoCropBox>(
|
||||
image,
|
||||
tr::lng_settings_crop_profile(tr::now));
|
||||
box->ready(
|
||||
) | rpl::start_with_next([=](QImage &&image) {
|
||||
self->session().api().uploadPeerPhoto(
|
||||
self,
|
||||
std::move(image));
|
||||
}, box->lifetime());
|
||||
controller->show(std::move(box));
|
||||
auto callback = [=](QImage &&image) {
|
||||
self->session().api().uploadPeerPhoto(self, std::move(image));
|
||||
};
|
||||
FileDialog::GetOpenPath(
|
||||
Editor::PrepareProfilePhoto(
|
||||
upload,
|
||||
tr::lng_choose_image(tr::now),
|
||||
filter,
|
||||
crl::guard(upload, callback));
|
||||
&controller->window(),
|
||||
std::move(callback));
|
||||
});
|
||||
rpl::combine(
|
||||
wrap->widthValue(),
|
||||
|
|
|
@ -29,9 +29,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/application.h"
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -68,74 +70,6 @@ QPixmap CreateSquarePixmap(int width, Callback &&paintCallback) {
|
|||
return App::pixmapFromImageInPlace(std::move(image));
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
void SuggestPhoto(
|
||||
const QImage &image,
|
||||
const QString &title,
|
||||
Callback &&callback) {
|
||||
auto badAspect = [](int a, int b) {
|
||||
return (a >= 10 * b);
|
||||
};
|
||||
if (image.isNull()
|
||||
|| badAspect(image.width(), image.height())
|
||||
|| badAspect(image.height(), image.width())) {
|
||||
Ui::show(
|
||||
Box<InformBox>(tr::lng_bad_photo(tr::now)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto box = Ui::show(
|
||||
Box<PhotoCropBox>(image, title),
|
||||
Ui::LayerOption::KeepOther);
|
||||
box->ready(
|
||||
) | rpl::start_with_next(
|
||||
std::forward<Callback>(callback),
|
||||
box->lifetime());
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void SuggestPhotoFile(
|
||||
const FileDialog::OpenResult &result,
|
||||
const QString &title,
|
||||
Callback &&callback) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = [&] {
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
return App::readImage(result.remoteContent);
|
||||
} else if (!result.paths.isEmpty()) {
|
||||
return App::readImage(result.paths.front());
|
||||
}
|
||||
return QImage();
|
||||
}();
|
||||
SuggestPhoto(
|
||||
image,
|
||||
title,
|
||||
std::forward<Callback>(callback));
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void ShowChoosePhotoBox(
|
||||
QPointer<QWidget> parent,
|
||||
const QString &title,
|
||||
Callback &&callback) {
|
||||
auto filter = FileDialog::ImagesOrAllFilter();
|
||||
auto handleChosenPhoto = [
|
||||
title,
|
||||
callback = std::forward<Callback>(callback)
|
||||
](auto &&result) mutable {
|
||||
SuggestPhotoFile(result, title, std::move(callback));
|
||||
};
|
||||
FileDialog::GetOpenPath(
|
||||
parent,
|
||||
tr::lng_choose_image(tr::now),
|
||||
filter,
|
||||
std::move(handleChosenPhoto));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
|
||||
|
@ -183,11 +117,33 @@ void HistoryDownButton::setUnreadCount(int unreadCount) {
|
|||
|
||||
UserpicButton::UserpicButton(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> window,
|
||||
not_null<PeerData*> peer,
|
||||
Role role,
|
||||
const style::UserpicButton &st)
|
||||
: RippleButton(parent, st.changeButton.ripple)
|
||||
, _st(st)
|
||||
, _controller(window->sessionController())
|
||||
, _window(window)
|
||||
, _peer(peer)
|
||||
, _cropTitle(CropTitle(peer))
|
||||
, _role(role) {
|
||||
Expects(_role == Role::ChangePhoto);
|
||||
|
||||
_waiting = false;
|
||||
prepare();
|
||||
}
|
||||
|
||||
UserpicButton::UserpicButton(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> window,
|
||||
const QString &cropTitle,
|
||||
Role role,
|
||||
const style::UserpicButton &st)
|
||||
: RippleButton(parent, st.changeButton.ripple)
|
||||
, _st(st)
|
||||
, _controller(window->sessionController())
|
||||
, _window(window)
|
||||
, _cropTitle(cropTitle)
|
||||
, _role(role) {
|
||||
Expects(_role == Role::ChangePhoto);
|
||||
|
@ -205,6 +161,7 @@ UserpicButton::UserpicButton(
|
|||
: RippleButton(parent, st.changeButton.ripple)
|
||||
, _st(st)
|
||||
, _controller(controller)
|
||||
, _window(&controller->window())
|
||||
, _peer(peer)
|
||||
, _cropTitle(CropTitle(_peer))
|
||||
, _role(role) {
|
||||
|
@ -246,7 +203,7 @@ void UserpicButton::setClickHandlerByRole() {
|
|||
addClickHandler(App::LambdaDelayed(
|
||||
_st.changeButton.ripple.hideDuration,
|
||||
this,
|
||||
[this] { changePhotoLazy(); }));
|
||||
[=] { changePhotoLocally(); }));
|
||||
break;
|
||||
|
||||
case Role::OpenPhoto:
|
||||
|
@ -265,18 +222,20 @@ void UserpicButton::setClickHandlerByRole() {
|
|||
}
|
||||
}
|
||||
|
||||
void UserpicButton::changePhotoLazy() {
|
||||
auto callback = crl::guard(
|
||||
void UserpicButton::changePhotoLocally(bool requestToUpload) {
|
||||
if (!_window) {
|
||||
return;
|
||||
}
|
||||
auto callback = [=](QImage &&image) {
|
||||
setImage(std::move(image));
|
||||
if (requestToUpload) {
|
||||
_uploadPhotoRequests.fire({});
|
||||
}
|
||||
};
|
||||
Editor::PrepareProfilePhoto(
|
||||
this,
|
||||
[this](QImage &&image) { setImage(std::move(image)); });
|
||||
ShowChoosePhotoBox(this, _cropTitle, std::move(callback));
|
||||
}
|
||||
|
||||
void UserpicButton::uploadNewPeerPhoto() {
|
||||
auto callback = crl::guard(this, [=](QImage &&image) {
|
||||
_peer->session().api().uploadPeerPhoto(_peer, std::move(image));
|
||||
});
|
||||
ShowChoosePhotoBox(this, _cropTitle, std::move(callback));
|
||||
_window,
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void UserpicButton::openPeerPhoto() {
|
||||
|
@ -284,7 +243,7 @@ void UserpicButton::openPeerPhoto() {
|
|||
Expects(_controller != nullptr);
|
||||
|
||||
if (_changeOverlayEnabled && _cursorInChangeOverlay) {
|
||||
uploadNewPeerPhoto();
|
||||
changePhotoLocally(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -774,6 +733,10 @@ void UserpicButton::prepareUserpicPixmap() {
|
|||
: InMemoryKey();
|
||||
}
|
||||
|
||||
rpl::producer<> UserpicButton::uploadPhotoRequests() const {
|
||||
return _uploadPhotoRequests.events();
|
||||
}
|
||||
|
||||
SilentToggle::SilentToggle(QWidget *parent, not_null<ChannelData*> channel)
|
||||
: RippleButton(parent, st::historySilentToggle.ripple)
|
||||
, _st(st::historySilentToggle)
|
||||
|
|
|
@ -21,6 +21,7 @@ class CloudImageView;
|
|||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
|
@ -68,6 +69,13 @@ public:
|
|||
|
||||
UserpicButton(
|
||||
QWidget *parent,
|
||||
not_null<::Window::Controller*> window,
|
||||
not_null<PeerData*> peer,
|
||||
Role role,
|
||||
const style::UserpicButton &st);
|
||||
UserpicButton(
|
||||
QWidget *parent,
|
||||
not_null<::Window::Controller*> window,
|
||||
const QString &cropTitle,
|
||||
Role role,
|
||||
const style::UserpicButton &st);
|
||||
|
@ -86,6 +94,8 @@ public:
|
|||
void switchChangePhotoOverlay(bool enabled);
|
||||
void showSavedMessagesOnSelf(bool enabled);
|
||||
|
||||
rpl::producer<> uploadPhotoRequests() const;
|
||||
|
||||
QImage takeResultImage() {
|
||||
return std::move(_result);
|
||||
}
|
||||
|
@ -128,11 +138,11 @@ private:
|
|||
void grabOldUserpic();
|
||||
void setClickHandlerByRole();
|
||||
void openPeerPhoto();
|
||||
void changePhotoLazy();
|
||||
void uploadNewPeerPhoto();
|
||||
void changePhotoLocally(bool requestToUpload = false);
|
||||
|
||||
const style::UserpicButton &_st;
|
||||
::Window::SessionController *_controller = nullptr;
|
||||
::Window::Controller *_window = nullptr;
|
||||
PeerData *_peer = nullptr;
|
||||
std::shared_ptr<Data::CloudImageView> _userpicView;
|
||||
QString _cropTitle;
|
||||
|
@ -154,6 +164,8 @@ private:
|
|||
bool _changeOverlayEnabled = false;
|
||||
Ui::Animations::Simple _changeOverlayShown;
|
||||
|
||||
rpl::event_stream<> _uploadPhotoRequests;
|
||||
|
||||
};
|
||||
|
||||
class SilentToggle
|
||||
|
|
Loading…
Add table
Reference in a new issue