mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Added ability to set profile photo from camera.
This commit is contained in:
parent
3b9ac19482
commit
3cb595c3c9
8 changed files with 297 additions and 189 deletions
|
@ -1308,6 +1308,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_profile_migrate_learn_more" = "Learn more »";
|
||||
"lng_profile_migrate_button" = "Upgrade to supergroup";
|
||||
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
|
||||
"lng_profile_camera_title" = "Capture yourself";
|
||||
|
||||
"lng_channel_not_accessible" = "Sorry, this channel is not accessible.";
|
||||
"lng_group_not_accessible" = "Sorry, this group is not accessible.";
|
||||
|
@ -1459,6 +1460,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_attach_failed" = "Failed";
|
||||
"lng_attach_file" = "File";
|
||||
"lng_attach_photo" = "Photo";
|
||||
"lng_attach_camera" = "Camera";
|
||||
|
||||
"lng_media_open_with" = "Open With";
|
||||
"lng_media_download" = "Download";
|
||||
|
|
|
@ -67,7 +67,8 @@ void OpenWithPreparedFile(
|
|||
void PrepareProfilePhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback) {
|
||||
Fn<void(QImage &&image)> &&doneCallback,
|
||||
QImage &&image) {
|
||||
const auto resizeToMinSize = [=](
|
||||
QImage &&image,
|
||||
Qt::AspectRatioMode mode) {
|
||||
|
@ -82,8 +83,53 @@ void PrepareProfilePhoto(
|
|||
return std::move(image);
|
||||
};
|
||||
|
||||
if (image.isNull()
|
||||
|| (image.width() > (10 * image.height()))
|
||||
|| (image.height() > (10 * image.width()))) {
|
||||
controller->show(Ui::MakeInformBox(tr::lng_bad_photo()));
|
||||
return;
|
||||
}
|
||||
image = resizeToMinSize(
|
||||
std::move(image),
|
||||
Qt::KeepAspectRatioByExpanding);
|
||||
const auto fileImage = std::make_shared<Image>(std::move(image));
|
||||
|
||||
auto applyModifications = [=, done = std::move(doneCallback)](
|
||||
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);
|
||||
}
|
||||
|
||||
void PrepareProfilePhotoFromFile(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback) {
|
||||
const auto callback = [=, done = std::move(doneCallback)](
|
||||
const FileDialog::OpenResult &result) {
|
||||
const FileDialog::OpenResult &result) mutable {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -93,45 +139,11 @@ void PrepareProfilePhoto(
|
|||
.content = result.remoteContent,
|
||||
.forceOpaque = true,
|
||||
}).image;
|
||||
if (image.isNull()
|
||||
|| (image.width() > (10 * image.height()))
|
||||
|| (image.height() > (10 * image.width()))) {
|
||||
controller->show(Ui::MakeInformBox(tr::lng_bad_photo()));
|
||||
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);
|
||||
PrepareProfilePhoto(
|
||||
parent,
|
||||
controller,
|
||||
std::move(done),
|
||||
std::move(image));
|
||||
};
|
||||
FileDialog::GetOpenPath(
|
||||
parent.get(),
|
||||
|
|
|
@ -32,6 +32,12 @@ void OpenWithPreparedFile(
|
|||
Fn<void()> &&doneCallback);
|
||||
|
||||
void PrepareProfilePhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback,
|
||||
QImage &&image);
|
||||
|
||||
void PrepareProfilePhotoFromFile(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback);
|
||||
|
|
|
@ -56,6 +56,155 @@ Calls::Calls(
|
|||
|
||||
Calls::~Calls() = default;
|
||||
|
||||
Webrtc::VideoTrack *Calls::AddCameraSubsection(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> content,
|
||||
bool saveToSettings) {
|
||||
auto &lifetime = content->lifetime();
|
||||
|
||||
const auto hasCall = (Core::App().calls().currentCall() != nullptr);
|
||||
|
||||
const auto cameraNameStream = lifetime.make_state<
|
||||
rpl::event_stream<QString>
|
||||
>();
|
||||
|
||||
auto capturerOwner = lifetime.make_state<
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface>
|
||||
>();
|
||||
|
||||
const auto track = lifetime.make_state<VideoTrack>(
|
||||
(hasCall
|
||||
? VideoState::Inactive
|
||||
: VideoState::Active));
|
||||
|
||||
const auto currentCameraName = [&] {
|
||||
const auto cameras = GetVideoInputList();
|
||||
const auto i = ranges::find(
|
||||
cameras,
|
||||
Core::App().settings().callVideoInputDeviceId(),
|
||||
&VideoInput::id);
|
||||
return (i != end(cameras))
|
||||
? i->name
|
||||
: tr::lng_settings_call_device_default(tr::now);
|
||||
}();
|
||||
|
||||
AddButtonWithLabel(
|
||||
content,
|
||||
tr::lng_settings_call_input_device(),
|
||||
rpl::single(
|
||||
currentCameraName
|
||||
) | rpl::then(
|
||||
cameraNameStream->events()
|
||||
),
|
||||
st::settingsButtonNoIcon
|
||||
)->addClickHandler([=] {
|
||||
const auto &devices = GetVideoInputList();
|
||||
const auto options = ranges::views::concat(
|
||||
ranges::views::single(
|
||||
tr::lng_settings_call_device_default(tr::now)),
|
||||
devices | ranges::views::transform(&VideoInput::name)
|
||||
) | ranges::to_vector;
|
||||
const auto i = ranges::find(
|
||||
devices,
|
||||
Core::App().settings().callVideoInputDeviceId(),
|
||||
&VideoInput::id);
|
||||
const auto currentOption = (i != end(devices))
|
||||
? int(i - begin(devices) + 1)
|
||||
: 0;
|
||||
const auto save = crl::guard(content, [=](int option) {
|
||||
cameraNameStream->fire_copy(options[option]);
|
||||
const auto deviceId = option
|
||||
? devices[option - 1].id
|
||||
: "default";
|
||||
if (saveToSettings) {
|
||||
Core::App().settings().setCallVideoInputDeviceId(deviceId);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
if (const auto call = Core::App().calls().currentCall()) {
|
||||
call->setCurrentCameraDevice(deviceId);
|
||||
}
|
||||
if (*capturerOwner) {
|
||||
(*capturerOwner)->switchToDevice(
|
||||
deviceId.toStdString(),
|
||||
false);
|
||||
}
|
||||
});
|
||||
show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
SingleChoiceBox(box, {
|
||||
.title = tr::lng_settings_call_camera(),
|
||||
.options = options,
|
||||
.initialSelection = currentOption,
|
||||
.callback = save,
|
||||
});
|
||||
}));
|
||||
});
|
||||
const auto bubbleWrap = content->add(object_ptr<Ui::RpWidget>(content));
|
||||
const auto bubble = lifetime.make_state<::Calls::VideoBubble>(
|
||||
bubbleWrap,
|
||||
track);
|
||||
const auto padding = st::settingsButtonNoIcon.padding.left();
|
||||
const auto top = st::boxRoundShadow.extend.top();
|
||||
const auto bottom = st::boxRoundShadow.extend.bottom();
|
||||
|
||||
auto frameSize = track->renderNextFrame(
|
||||
) | rpl::map([=] {
|
||||
return track->frameSize();
|
||||
}) | rpl::filter([=](QSize size) {
|
||||
return !size.isEmpty()
|
||||
&& !Core::App().calls().currentCall()
|
||||
&& !Core::App().calls().currentGroupCall();
|
||||
});
|
||||
auto bubbleWidth = bubbleWrap->widthValue(
|
||||
) | rpl::filter([=](int width) {
|
||||
return width > 2 * padding + 1;
|
||||
});
|
||||
rpl::combine(
|
||||
std::move(bubbleWidth),
|
||||
std::move(frameSize)
|
||||
) | rpl::start_with_next([=](int width, QSize frame) {
|
||||
const auto useWidth = (width - 2 * padding);
|
||||
const auto useHeight = std::min(
|
||||
((useWidth * frame.height()) / frame.width()),
|
||||
(useWidth * 480) / 640);
|
||||
bubbleWrap->resize(width, top + useHeight + bottom);
|
||||
bubble->updateGeometry(
|
||||
::Calls::VideoBubble::DragMode::None,
|
||||
QRect(padding, top, useWidth, useHeight));
|
||||
bubbleWrap->update();
|
||||
}, bubbleWrap->lifetime());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
const auto checkCapturer = [=] {
|
||||
if (*capturerOwner
|
||||
|| Core::App().calls().currentCall()
|
||||
|| Core::App().calls().currentGroupCall()) {
|
||||
return;
|
||||
}
|
||||
*capturerOwner = Core::App().calls().getVideoCapture(
|
||||
Core::App().settings().callVideoInputDeviceId(),
|
||||
false);
|
||||
(*capturerOwner)->setPreferredAspectRatio(0.);
|
||||
track->setState(VideoState::Active);
|
||||
(*capturerOwner)->setState(tgcalls::VideoState::Active);
|
||||
(*capturerOwner)->setOutput(track->sink());
|
||||
};
|
||||
rpl::combine(
|
||||
Core::App().calls().currentCallValue(),
|
||||
Core::App().calls().currentGroupCallValue(),
|
||||
_1 || _2
|
||||
) | rpl::start_with_next([=](bool has) {
|
||||
if (has) {
|
||||
track->setState(VideoState::Inactive);
|
||||
bubbleWrap->resize(bubbleWrap->width(), 0);
|
||||
*capturerOwner = nullptr;
|
||||
} else {
|
||||
crl::on_main(content, checkCapturer);
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void Calls::sectionSaveChanges(FnMut<void()> done) {
|
||||
if (_micTester) {
|
||||
_micTester.reset();
|
||||
|
@ -66,144 +215,13 @@ void Calls::sectionSaveChanges(FnMut<void()> done) {
|
|||
void Calls::setupContent() {
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
const auto &settings = Core::App().settings();
|
||||
const auto cameras = GetVideoInputList();
|
||||
if (!cameras.empty()) {
|
||||
const auto hasCall = (Core::App().calls().currentCall() != nullptr);
|
||||
|
||||
auto capturerOwner = content->lifetime().make_state<
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface>
|
||||
>();
|
||||
|
||||
const auto track = content->lifetime().make_state<VideoTrack>(
|
||||
(hasCall
|
||||
? VideoState::Inactive
|
||||
: VideoState::Active));
|
||||
|
||||
const auto currentCameraName = [&] {
|
||||
const auto i = ranges::find(
|
||||
cameras,
|
||||
settings.callVideoInputDeviceId(),
|
||||
&VideoInput::id);
|
||||
return (i != end(cameras))
|
||||
? i->name
|
||||
: tr::lng_settings_call_device_default(tr::now);
|
||||
}();
|
||||
|
||||
if (!GetVideoInputList().empty()) {
|
||||
AddSkip(content);
|
||||
AddSubsectionTitle(content, tr::lng_settings_call_camera());
|
||||
AddButtonWithLabel(
|
||||
AddCameraSubsection(
|
||||
std::make_shared<Window::Show>(_controller),
|
||||
content,
|
||||
tr::lng_settings_call_input_device(),
|
||||
rpl::single(
|
||||
currentCameraName
|
||||
) | rpl::then(
|
||||
_cameraNameStream.events()
|
||||
),
|
||||
st::settingsButtonNoIcon
|
||||
)->addClickHandler([=] {
|
||||
const auto &devices = GetVideoInputList();
|
||||
const auto options = ranges::views::concat(
|
||||
ranges::views::single(
|
||||
tr::lng_settings_call_device_default(tr::now)),
|
||||
devices | ranges::views::transform(&VideoInput::name)
|
||||
) | ranges::to_vector;
|
||||
const auto i = ranges::find(
|
||||
devices,
|
||||
Core::App().settings().callVideoInputDeviceId(),
|
||||
&VideoInput::id);
|
||||
const auto currentOption = (i != end(devices))
|
||||
? int(i - begin(devices) + 1)
|
||||
: 0;
|
||||
const auto save = crl::guard(this, [=](int option) {
|
||||
_cameraNameStream.fire_copy(options[option]);
|
||||
const auto deviceId = option
|
||||
? devices[option - 1].id
|
||||
: "default";
|
||||
Core::App().settings().setCallVideoInputDeviceId(deviceId);
|
||||
Core::App().saveSettingsDelayed();
|
||||
if (const auto call = Core::App().calls().currentCall()) {
|
||||
call->setCurrentCameraDevice(deviceId);
|
||||
}
|
||||
if (*capturerOwner) {
|
||||
(*capturerOwner)->switchToDevice(
|
||||
deviceId.toStdString(),
|
||||
false);
|
||||
}
|
||||
});
|
||||
_controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
SingleChoiceBox(box, {
|
||||
.title = tr::lng_settings_call_camera(),
|
||||
.options = options,
|
||||
.initialSelection = currentOption,
|
||||
.callback = save,
|
||||
});
|
||||
}));
|
||||
});
|
||||
const auto bubbleWrap = content->add(object_ptr<Ui::RpWidget>(content));
|
||||
const auto bubble = content->lifetime().make_state<::Calls::VideoBubble>(
|
||||
bubbleWrap,
|
||||
track);
|
||||
const auto padding = st::settingsButtonNoIcon.padding.left();
|
||||
const auto top = st::boxRoundShadow.extend.top();
|
||||
const auto bottom = st::boxRoundShadow.extend.bottom();
|
||||
|
||||
auto frameSize = track->renderNextFrame(
|
||||
) | rpl::map([=] {
|
||||
return track->frameSize();
|
||||
}) | rpl::filter([=](QSize size) {
|
||||
return !size.isEmpty()
|
||||
&& !Core::App().calls().currentCall()
|
||||
&& !Core::App().calls().currentGroupCall();
|
||||
});
|
||||
auto bubbleWidth = bubbleWrap->widthValue(
|
||||
) | rpl::filter([=](int width) {
|
||||
return width > 2 * padding + 1;
|
||||
});
|
||||
rpl::combine(
|
||||
std::move(bubbleWidth),
|
||||
std::move(frameSize)
|
||||
) | rpl::start_with_next([=](int width, QSize frame) {
|
||||
const auto useWidth = (width - 2 * padding);
|
||||
const auto useHeight = std::min(
|
||||
((useWidth * frame.height()) / frame.width()),
|
||||
(useWidth * 480) / 640);
|
||||
bubbleWrap->resize(width, top + useHeight + bottom);
|
||||
bubble->updateGeometry(
|
||||
::Calls::VideoBubble::DragMode::None,
|
||||
QRect(padding, top, useWidth, useHeight));
|
||||
bubbleWrap->update();
|
||||
}, bubbleWrap->lifetime());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
const auto checkCapturer = [=] {
|
||||
if (*capturerOwner
|
||||
|| Core::App().calls().currentCall()
|
||||
|| Core::App().calls().currentGroupCall()) {
|
||||
return;
|
||||
}
|
||||
*capturerOwner = Core::App().calls().getVideoCapture(
|
||||
Core::App().settings().callVideoInputDeviceId(),
|
||||
false);
|
||||
(*capturerOwner)->setPreferredAspectRatio(0.);
|
||||
track->setState(VideoState::Active);
|
||||
(*capturerOwner)->setState(tgcalls::VideoState::Active);
|
||||
(*capturerOwner)->setOutput(track->sink());
|
||||
};
|
||||
rpl::combine(
|
||||
Core::App().calls().currentCallValue(),
|
||||
Core::App().calls().currentGroupCallValue(),
|
||||
_1 || _2
|
||||
) | rpl::start_with_next([=](bool has) {
|
||||
if (has) {
|
||||
track->setState(VideoState::Inactive);
|
||||
bubbleWrap->resize(bubbleWrap->width(), 0);
|
||||
*capturerOwner = nullptr;
|
||||
} else {
|
||||
crl::on_main(content, checkCapturer);
|
||||
}
|
||||
}, content->lifetime());
|
||||
|
||||
true);
|
||||
AddSkip(content);
|
||||
AddDivider(content);
|
||||
}
|
||||
|
|
|
@ -23,10 +23,12 @@ class Call;
|
|||
namespace Ui {
|
||||
class LevelMeter;
|
||||
class GenericBox;
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Webrtc {
|
||||
class AudioInputTester;
|
||||
class VideoTrack;
|
||||
} // namespace Webrtc
|
||||
|
||||
namespace Settings {
|
||||
|
@ -38,6 +40,11 @@ public:
|
|||
|
||||
void sectionSaveChanges(FnMut<void()> done) override;
|
||||
|
||||
static Webrtc::VideoTrack *AddCameraSubsection(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> content,
|
||||
bool saveToSettings);
|
||||
|
||||
private:
|
||||
void setupContent();
|
||||
void requestPermissionAndStartTestingMicrophone();
|
||||
|
|
|
@ -66,7 +66,7 @@ void SetupPhoto(
|
|||
auto callback = [=](QImage &&image) {
|
||||
self->session().api().peerPhoto().upload(self, std::move(image));
|
||||
};
|
||||
Editor::PrepareProfilePhoto(
|
||||
Editor::PrepareProfilePhotoFromFile(
|
||||
upload,
|
||||
&controller->window(),
|
||||
std::move(callback));
|
||||
|
|
|
@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "ui/special_buttons.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
|
@ -32,19 +31,66 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
#include "settings/settings_calls.h" // Calls::AddCameraSubsection.
|
||||
#include "calls/calls_instance.h"
|
||||
#include "webrtc/webrtc_media_devices.h" // Webrtc::GetVideoInputList.
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "facades.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kAnimationDuration = crl::time(120);
|
||||
|
||||
bool IsCameraAvailable() {
|
||||
return (Core::App().calls().currentCall() == nullptr)
|
||||
&& !Webrtc::GetVideoInputList().empty();
|
||||
}
|
||||
|
||||
void CameraBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::Controller*> controller,
|
||||
Fn<void(QImage &&image)> &&doneCallback) {
|
||||
using namespace Webrtc;
|
||||
|
||||
const auto track = Settings::Calls::AddCameraSubsection(
|
||||
std::make_shared<Ui::BoxShow>(box),
|
||||
box->verticalLayout(),
|
||||
false);
|
||||
if (!track) {
|
||||
box->closeBox();
|
||||
return;
|
||||
}
|
||||
track->stateValue(
|
||||
) | rpl::start_with_next([=](const VideoState &state) {
|
||||
if (state == VideoState::Inactive) {
|
||||
box->closeBox();
|
||||
}
|
||||
}, box->lifetime());
|
||||
|
||||
auto done = [=, done = std::move(doneCallback)](QImage &&image) {
|
||||
box->closeBox();
|
||||
done(std::move(image));
|
||||
};
|
||||
|
||||
box->setTitle(tr::lng_profile_camera_title());
|
||||
box->addButton(tr::lng_continue(), [=, done = std::move(done)]() mutable {
|
||||
Editor::PrepareProfilePhoto(
|
||||
box,
|
||||
controller,
|
||||
std::move(done),
|
||||
track->frame(FrameRequest()).mirrored(true, false));
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
QString CropTitle(not_null<PeerData*> peer) {
|
||||
if (peer->isChat() || peer->isMegagroup()) {
|
||||
return tr::lng_create_group_crop(tr::now);
|
||||
|
@ -198,10 +244,7 @@ void UserpicButton::prepare() {
|
|||
void UserpicButton::setClickHandlerByRole() {
|
||||
switch (_role) {
|
||||
case Role::ChangePhoto:
|
||||
addClickHandler(App::LambdaDelayed(
|
||||
_st.changeButton.ripple.hideDuration,
|
||||
this,
|
||||
[=] { changePhotoLocally(); }));
|
||||
addClickHandler([=] { changePhotoLocally(); });
|
||||
break;
|
||||
|
||||
case Role::OpenPhoto:
|
||||
|
@ -230,10 +273,26 @@ void UserpicButton::changePhotoLocally(bool requestToUpload) {
|
|||
_uploadPhotoRequests.fire({});
|
||||
}
|
||||
};
|
||||
Editor::PrepareProfilePhoto(
|
||||
this,
|
||||
_window,
|
||||
std::move(callback));
|
||||
const auto chooseFile = [=] {
|
||||
base::call_delayed(
|
||||
_st.changeButton.ripple.hideDuration,
|
||||
crl::guard(this, [=] {
|
||||
Editor::PrepareProfilePhotoFromFile(
|
||||
this,
|
||||
_window,
|
||||
callback);
|
||||
}));
|
||||
};
|
||||
if (!IsCameraAvailable()) {
|
||||
chooseFile();
|
||||
} else {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(this);
|
||||
_menu->addAction(tr::lng_attach_file(tr::now), chooseFile);
|
||||
_menu->addAction(tr::lng_attach_camera(tr::now), [=] {
|
||||
_window->show(Box(CameraBox, _window, callback));
|
||||
});
|
||||
_menu->popup(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void UserpicButton::openPeerPhoto() {
|
||||
|
|
|
@ -36,6 +36,8 @@ struct Information;
|
|||
|
||||
namespace Ui {
|
||||
|
||||
class PopupMenu;
|
||||
|
||||
class HistoryDownButton : public RippleButton {
|
||||
public:
|
||||
HistoryDownButton(QWidget *parent, const style::TwoIconButton &st);
|
||||
|
@ -158,6 +160,8 @@ private:
|
|||
std::unique_ptr<Media::Streaming::Instance> _streamed;
|
||||
PhotoData *_streamedPhoto = nullptr;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
bool _showSavedMessagesOnSelf = false;
|
||||
bool _canOpenPhoto = false;
|
||||
bool _cursorInChangeOverlay = false;
|
||||
|
|
Loading…
Add table
Reference in a new issue