mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
Improve screencast source choosing design.
This commit is contained in:
parent
022c0a1327
commit
a48649987e
12 changed files with 304 additions and 74 deletions
|
@ -2000,6 +2000,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_call_share_button" = "Share";
|
"lng_group_call_share_button" = "Share";
|
||||||
"lng_group_call_video" = "Video";
|
"lng_group_call_video" = "Video";
|
||||||
"lng_group_call_screen_share" = "Share";
|
"lng_group_call_screen_share" = "Share";
|
||||||
|
"lng_group_call_screen_share_start" = "Share Screen";
|
||||||
|
"lng_group_call_screen_share_stop" = "Stop Sharing";
|
||||||
|
"lng_group_call_screen_title" = "Screen {index}";
|
||||||
"lng_group_call_unmute_small" = "Unmute";
|
"lng_group_call_unmute_small" = "Unmute";
|
||||||
"lng_group_call_you_are_live_small" = "Mute";
|
"lng_group_call_you_are_live_small" = "Mute";
|
||||||
"lng_group_call_force_muted_small" = "Muted";
|
"lng_group_call_force_muted_small" = "Muted";
|
||||||
|
|
|
@ -856,8 +856,7 @@ groupCallTopBarOpen: RoundButton(groupCallTopBarJoin) {
|
||||||
color: shadowFg;
|
color: shadowFg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupCallBox: Box(defaultBox) {
|
groupCallBoxButton: RoundButton(defaultBoxButton) {
|
||||||
button: RoundButton(defaultBoxButton) {
|
|
||||||
textFg: groupCallActiveFg;
|
textFg: groupCallActiveFg;
|
||||||
textFgOver: groupCallActiveFg;
|
textFgOver: groupCallActiveFg;
|
||||||
numbersTextFg: groupCallActiveFg;
|
numbersTextFg: groupCallActiveFg;
|
||||||
|
@ -867,6 +866,8 @@ groupCallBox: Box(defaultBox) {
|
||||||
|
|
||||||
ripple: groupCallRipple;
|
ripple: groupCallRipple;
|
||||||
}
|
}
|
||||||
|
groupCallBox: Box(defaultBox) {
|
||||||
|
button: groupCallBoxButton;
|
||||||
margin: margins(0px, 56px, 0px, 10px);
|
margin: margins(0px, 56px, 0px, 10px);
|
||||||
bg: groupCallMembersBg;
|
bg: groupCallMembersBg;
|
||||||
title: FlatLabel(boxTitle) {
|
title: FlatLabel(boxTitle) {
|
||||||
|
@ -1092,8 +1093,46 @@ groupCallStartsWhenTop: 160px;
|
||||||
groupCallCountdownFont: font(64px semibold);
|
groupCallCountdownFont: font(64px semibold);
|
||||||
groupCallCountdownTop: 52px;
|
groupCallCountdownTop: 52px;
|
||||||
|
|
||||||
desktopCaptureSourceSize: size(160px, 120px);
|
desktopCaptureMargins: margins(12px, 8px, 12px, 6px);
|
||||||
desktopCaptureSourceSkip: 12px;
|
desktopCaptureSourceSize: size(235px, 165px);
|
||||||
|
desktopCaptureSourceSkips: size(2px, 10px);
|
||||||
|
desktopCaptureSourceTitle: WindowTitle(groupCallTitle) {
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
desktopCapturePadding: margins(7px, 7px, 7px, 33px);
|
||||||
|
desktopCaptureLabelBottom: 7px;
|
||||||
|
desktopCaptureLabel: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 200px;
|
||||||
|
maxHeight: 20px;
|
||||||
|
textFg: groupCallMembersFg;
|
||||||
|
style: semiboldTextStyle;
|
||||||
|
}
|
||||||
|
desktopCaptureCancel: RoundButton(defaultBoxButton) {
|
||||||
|
textFg: groupCallActiveFg;
|
||||||
|
textFgOver: groupCallActiveFg;
|
||||||
|
numbersTextFg: groupCallActiveFg;
|
||||||
|
numbersTextFgOver: groupCallActiveFg;
|
||||||
|
textBg: groupCallBg;
|
||||||
|
textBgOver: groupCallMembersBg;
|
||||||
|
|
||||||
|
ripple: groupCallRipple;
|
||||||
|
}
|
||||||
|
desktopCaptureFinish: RoundButton(desktopCaptureCancel) {
|
||||||
|
textFg: groupCallMemberMutedIcon;
|
||||||
|
textFgOver: groupCallMemberMutedIcon;
|
||||||
|
}
|
||||||
|
desktopCaptureSubmit: RoundButton(desktopCaptureCancel) {
|
||||||
|
textFg: groupCallIconFg;
|
||||||
|
textFgOver: groupCallIconFg;
|
||||||
|
numbersTextFg: groupCallIconFg;
|
||||||
|
numbersTextFgOver: groupCallIconFg;
|
||||||
|
textBg: groupCallMuted1;
|
||||||
|
textBgOver: groupCallMuted1;
|
||||||
|
|
||||||
|
ripple: RippleAnimation(groupCallRipple) {
|
||||||
|
color: groupCallMuted2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
groupCallNarrowSkip: 9px;
|
groupCallNarrowSkip: 9px;
|
||||||
groupCallNarrowRowSkip: 8px;
|
groupCallNarrowRowSkip: 8px;
|
||||||
|
|
|
@ -322,7 +322,12 @@ GroupCall::~GroupCall() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GroupCall::isScreenSharing() const {
|
bool GroupCall::isScreenSharing() const {
|
||||||
return (_videoDeviceId != _videoInputId);
|
return (_videoDeviceId != _videoInputId)
|
||||||
|
&& (_videoOutgoing->state() == Webrtc::VideoState::Active);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GroupCall::screenSharingDeviceId() const {
|
||||||
|
return isScreenSharing() ? _videoDeviceId : QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::toggleVideo(bool active) {
|
void GroupCall::toggleVideo(bool active) {
|
||||||
|
|
|
@ -248,7 +248,8 @@ public:
|
||||||
|
|
||||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||||
void setCurrentVideoDevice(const QString &deviceId);
|
void setCurrentVideoDevice(const QString &deviceId);
|
||||||
bool isScreenSharing() const;
|
[[nodiscard]] bool isScreenSharing() const;
|
||||||
|
[[nodiscard]] QString screenSharingDeviceId() const;
|
||||||
void toggleVideo(bool active);
|
void toggleVideo(bool active);
|
||||||
void switchToScreenSharing(const QString &uniqueId);
|
void switchToScreenSharing(const QString &uniqueId);
|
||||||
|
|
||||||
|
|
|
@ -1460,9 +1460,9 @@ void MembersController::updateRow(
|
||||||
Assert(nowSsrc != 0);
|
Assert(nowSsrc != 0);
|
||||||
_soundingRowBySsrc.emplace(nowSsrc, row);
|
_soundingRowBySsrc.emplace(nowSsrc, row);
|
||||||
}
|
}
|
||||||
if (isMe(row->peer())) {
|
//if (isMe(row->peer())) {
|
||||||
row->setVideoTrack(_call->outgoingVideoTrack());
|
// row->setVideoTrack(_call->outgoingVideoTrack());
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
const auto nowNoSounding = _soundingRowBySsrc.empty();
|
const auto nowNoSounding = _soundingRowBySsrc.empty();
|
||||||
if (wasNoSounding && !nowNoSounding) {
|
if (wasNoSounding && !nowNoSounding) {
|
||||||
|
|
|
@ -484,6 +484,10 @@ QWidget *Panel::chooseSourceParent() {
|
||||||
return _window.get();
|
return _window.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Panel::chooseSourceActiveDeviceId() {
|
||||||
|
return _call->screenSharingDeviceId();
|
||||||
|
}
|
||||||
|
|
||||||
rpl::lifetime &Panel::chooseSourceInstanceLifetime() {
|
rpl::lifetime &Panel::chooseSourceInstanceLifetime() {
|
||||||
return _window->lifetime();
|
return _window->lifetime();
|
||||||
}
|
}
|
||||||
|
@ -492,6 +496,10 @@ void Panel::chooseSourceAccepted(const QString &deviceId) {
|
||||||
_call->switchToScreenSharing(deviceId);
|
_call->switchToScreenSharing(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::chooseSourceStop() {
|
||||||
|
_call->toggleVideo(false);
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::initWindow() {
|
void Panel::initWindow() {
|
||||||
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
_window->setAttribute(Qt::WA_NoSystemBackground);
|
_window->setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
|
|
@ -116,8 +116,10 @@ private:
|
||||||
void subscribeToPeerChanges();
|
void subscribeToPeerChanges();
|
||||||
|
|
||||||
QWidget *chooseSourceParent() override;
|
QWidget *chooseSourceParent() override;
|
||||||
|
QString chooseSourceActiveDeviceId() override;
|
||||||
rpl::lifetime &chooseSourceInstanceLifetime() override;
|
rpl::lifetime &chooseSourceInstanceLifetime() override;
|
||||||
void chooseSourceAccepted(const QString &deviceId) override;
|
void chooseSourceAccepted(const QString &deviceId) override;
|
||||||
|
void chooseSourceStop() override;
|
||||||
|
|
||||||
const not_null<GroupCall*> _call;
|
const not_null<GroupCall*> _call;
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
|
|
|
@ -11,8 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
#include "ui/image/image.h"
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "webrtc/webrtc_video_track.h"
|
#include "webrtc/webrtc_video_track.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_calls.h"
|
#include "styles/style_calls.h"
|
||||||
|
|
||||||
#include <tgcalls/desktop_capturer/DesktopCaptureSourceManager.h>
|
#include <tgcalls/desktop_capturer/DesktopCaptureSourceManager.h>
|
||||||
|
@ -33,6 +37,19 @@ struct Preview {
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SourceButton final : public RippleButton {
|
||||||
|
public:
|
||||||
|
using RippleButton::RippleButton;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage prepareRippleMask() const override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
QImage SourceButton::prepareRippleMask() const {
|
||||||
|
return RippleAnimation::roundRectMask(size(), st::roundRadiusLarge);
|
||||||
|
}
|
||||||
|
|
||||||
class Source final {
|
class Source final {
|
||||||
public:
|
public:
|
||||||
Source(
|
Source(
|
||||||
|
@ -43,19 +60,23 @@ public:
|
||||||
void setGeometry(QRect geometry);
|
void setGeometry(QRect geometry);
|
||||||
void clearHelper();
|
void clearHelper();
|
||||||
|
|
||||||
[[nodiscard]] bool ready() const;
|
[[nodiscard]] rpl::producer<> activations() const;
|
||||||
[[nodiscard]] rpl::producer<> clicks() const;
|
void setActive(bool active);
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void paint();
|
void paint();
|
||||||
void setupPreview();
|
void setupPreview();
|
||||||
|
|
||||||
AbstractButton _widget;
|
SourceButton _widget;
|
||||||
FlatLabel _label;
|
FlatLabel _label;
|
||||||
|
RoundRect _selectedRect;
|
||||||
|
RoundRect _activeRect;
|
||||||
tgcalls::DesktopCaptureSource _source;
|
tgcalls::DesktopCaptureSource _source;
|
||||||
std::unique_ptr<Preview> _preview;
|
std::unique_ptr<Preview> _preview;
|
||||||
|
rpl::event_stream<> _activations;
|
||||||
QImage _frame;
|
QImage _frame;
|
||||||
|
bool _active = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,11 +101,16 @@ private:
|
||||||
std::unique_ptr<ChooseSourceProcess>> &Map();
|
std::unique_ptr<ChooseSourceProcess>> &Map();
|
||||||
|
|
||||||
const not_null<ChooseSourceDelegate*> _delegate;
|
const not_null<ChooseSourceDelegate*> _delegate;
|
||||||
const std::unique_ptr<::Ui::Window> _window;
|
const std::unique_ptr<Ui::Window> _window;
|
||||||
const std::unique_ptr<ScrollArea> _scroll;
|
const std::unique_ptr<ScrollArea> _scroll;
|
||||||
const not_null<RpWidget*> _inner;
|
const not_null<RpWidget*> _inner;
|
||||||
|
const not_null<RpWidget*> _bottom;
|
||||||
|
const not_null<RoundButton*> _submit;
|
||||||
|
const not_null<RoundButton*> _finish;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Source>> _sources;
|
std::vector<std::unique_ptr<Source>> _sources;
|
||||||
|
Source *_selected = nullptr;
|
||||||
|
QString _selectedId;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,25 +135,45 @@ Source::Source(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
tgcalls::DesktopCaptureSource source,
|
tgcalls::DesktopCaptureSource source,
|
||||||
const QString &title)
|
const QString &title)
|
||||||
: _widget(parent)
|
: _widget(parent, st::groupCallRipple)
|
||||||
, _label(&_widget, title)
|
, _label(&_widget, title, st::desktopCaptureLabel)
|
||||||
|
, _selectedRect(ImageRoundRadius::Large, st::groupCallMembersBgOver)
|
||||||
|
, _activeRect(ImageRoundRadius::Large, st::groupCallMuted1)
|
||||||
, _source(source) {
|
, _source(source) {
|
||||||
_widget.paintRequest(
|
_widget.paintRequest(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
paint();
|
paint();
|
||||||
}, _widget.lifetime());
|
}, _widget.lifetime());
|
||||||
|
|
||||||
|
_label.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
_widget.sizeValue(
|
_widget.sizeValue(
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
_label.resizeToNaturalWidth(size.width());
|
const auto padding = st::desktopCapturePadding;
|
||||||
|
_label.resizeToNaturalWidth(
|
||||||
|
size.width() - padding.left() - padding.right());
|
||||||
_label.move(
|
_label.move(
|
||||||
(size.width() - _label.width()) / 2,
|
(size.width() - _label.width()) / 2,
|
||||||
size.height() - _label.height());
|
size.height() - _label.height() - st::desktopCaptureLabelBottom);
|
||||||
}, _label.lifetime());
|
}, _label.lifetime());
|
||||||
|
|
||||||
|
_widget.setClickedCallback([=] {
|
||||||
|
setActive(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> Source::clicks() const {
|
rpl::producer<> Source::activations() const {
|
||||||
return _widget.clicks() | rpl::to_empty;
|
return _activations.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Source::setActive(bool active) {
|
||||||
|
if (_active != active) {
|
||||||
|
_active = active;
|
||||||
|
_widget.update();
|
||||||
|
if (active) {
|
||||||
|
_activations.fire({});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Source::setGeometry(QRect geometry) {
|
void Source::setGeometry(QRect geometry) {
|
||||||
|
@ -144,14 +190,21 @@ void Source::paint() {
|
||||||
if (_frame.isNull() && !_preview) {
|
if (_frame.isNull() && !_preview) {
|
||||||
setupPreview();
|
setupPreview();
|
||||||
}
|
}
|
||||||
|
if (_active) {
|
||||||
|
_activeRect.paint(p, _widget.rect());
|
||||||
|
} else if (_widget.isOver() || _widget.isDown()) {
|
||||||
|
_selectedRect.paint(p, _widget.rect());
|
||||||
|
}
|
||||||
|
_widget.paintRipple(
|
||||||
|
p,
|
||||||
|
{ 0, 0 },
|
||||||
|
_active ? &st::groupCallMuted2->c : nullptr);
|
||||||
|
|
||||||
const auto size = _preview ? _preview->track.frameSize() : QSize();
|
const auto size = _preview ? _preview->track.frameSize() : QSize();
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
const auto padding = st::desktopCapturePadding;
|
||||||
const auto rect = _widget.rect();
|
const auto rect = _widget.rect();
|
||||||
const auto inner = QRect(
|
const auto inner = rect.marginsRemoved(padding);
|
||||||
rect.x(),
|
|
||||||
rect.y(),
|
|
||||||
rect.width(),
|
|
||||||
rect.height() - _label.height());
|
|
||||||
if (!size.isEmpty()) {
|
if (!size.isEmpty()) {
|
||||||
const auto scaled = size.scaled(inner.size(), Qt::KeepAspectRatio);
|
const auto scaled = size.scaled(inner.size(), Qt::KeepAspectRatio);
|
||||||
const auto request = Webrtc::FrameRequest{
|
const auto request = Webrtc::FrameRequest{
|
||||||
|
@ -190,9 +243,20 @@ rpl::lifetime &Source::lifetime() {
|
||||||
ChooseSourceProcess::ChooseSourceProcess(
|
ChooseSourceProcess::ChooseSourceProcess(
|
||||||
not_null<ChooseSourceDelegate*> delegate)
|
not_null<ChooseSourceDelegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
, _window(std::make_unique<::Ui::Window>())
|
, _window(std::make_unique<Ui::Window>())
|
||||||
, _scroll(std::make_unique<ScrollArea>(_window->body()))
|
, _scroll(std::make_unique<ScrollArea>(_window->body()))
|
||||||
, _inner(_scroll->setOwnedWidget(object_ptr<RpWidget>(_scroll.get()))) {
|
, _inner(_scroll->setOwnedWidget(object_ptr<RpWidget>(_scroll.get())))
|
||||||
|
, _bottom(CreateChild<RpWidget>(_window->body().get()))
|
||||||
|
, _submit(
|
||||||
|
CreateChild<RoundButton>(
|
||||||
|
_bottom.get(),
|
||||||
|
tr::lng_group_call_screen_share_start(),
|
||||||
|
st::desktopCaptureSubmit))
|
||||||
|
, _finish(
|
||||||
|
CreateChild<RoundButton>(
|
||||||
|
_bottom.get(),
|
||||||
|
tr::lng_group_call_screen_share_stop(),
|
||||||
|
st::desktopCaptureFinish)) {
|
||||||
setupPanel();
|
setupPanel();
|
||||||
setupSources();
|
setupSources();
|
||||||
activate();
|
activate();
|
||||||
|
@ -233,25 +297,102 @@ void ChooseSourceProcess::activate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChooseSourceProcess::setupPanel() {
|
void ChooseSourceProcess::setupPanel() {
|
||||||
const auto width = kColumns * st::desktopCaptureSourceSize.width()
|
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
+ (kColumns + 1) * st::desktopCaptureSourceSkip;
|
_window->setAttribute(Qt::WA_NoSystemBackground);
|
||||||
const auto height = kRows * st::desktopCaptureSourceSize.height()
|
_window->setWindowIcon(QIcon(
|
||||||
+ (kRows + 1) * st::desktopCaptureSourceSkip
|
QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
|
||||||
+ (st::desktopCaptureSourceSize.height() / 2);
|
_window->setTitleStyle(st::desktopCaptureSourceTitle);
|
||||||
|
|
||||||
|
const auto skips = st::desktopCaptureSourceSkips;
|
||||||
|
const auto margins = st::desktopCaptureMargins;
|
||||||
|
const auto padding = st::desktopCapturePadding;
|
||||||
|
const auto bottomSkip = margins.right() + padding.right();
|
||||||
|
const auto bottomHeight = 2 * bottomSkip
|
||||||
|
+ st::desktopCaptureCancel.height;
|
||||||
|
const auto width = margins.left()
|
||||||
|
+ kColumns * st::desktopCaptureSourceSize.width()
|
||||||
|
+ (kColumns - 1) * skips.width()
|
||||||
|
+ margins.right();
|
||||||
|
const auto height = margins.top()
|
||||||
|
+ kRows * st::desktopCaptureSourceSize.height()
|
||||||
|
+ (kRows - 1) * skips.height()
|
||||||
|
+ (st::desktopCaptureSourceSize.height() / 2)
|
||||||
|
+ bottomHeight;
|
||||||
_window->setFixedSize({ width, height });
|
_window->setFixedSize({ width, height });
|
||||||
_window->setWindowFlags(Qt::WindowStaysOnTopHint);
|
_window->setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
_window->body()->paintRequest(
|
||||||
|
) | rpl::start_with_next([=](QRect clip) {
|
||||||
|
QPainter(_window->body()).fillRect(clip, st::groupCallBg);
|
||||||
|
}, _window->lifetime());
|
||||||
|
|
||||||
|
_bottom->setGeometry(0, height - bottomHeight, width, bottomHeight);
|
||||||
|
|
||||||
|
_submit->setClickedCallback([=] {
|
||||||
|
if (_selectedId.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = MakeWeak(_window.get());
|
||||||
|
_delegate->chooseSourceAccepted(_selectedId);
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_finish->setClickedCallback([=] {
|
||||||
|
const auto weak = MakeWeak(_window.get());
|
||||||
|
_delegate->chooseSourceStop();
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const auto cancel = CreateChild<RoundButton>(
|
||||||
|
_bottom.get(),
|
||||||
|
tr::lng_cancel(),
|
||||||
|
st::desktopCaptureCancel);
|
||||||
|
cancel->setClickedCallback([=] {
|
||||||
|
_window->close();
|
||||||
|
});
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
_submit->widthValue(),
|
||||||
|
_submit->shownValue(),
|
||||||
|
_finish->widthValue(),
|
||||||
|
_finish->shownValue(),
|
||||||
|
cancel->widthValue()
|
||||||
|
) | rpl::start_with_next([=](
|
||||||
|
int submitWidth,
|
||||||
|
bool submitShown,
|
||||||
|
int finishWidth,
|
||||||
|
bool finishShown,
|
||||||
|
int cancelWidth) {
|
||||||
|
_finish->moveToRight(bottomSkip, bottomSkip);
|
||||||
|
_submit->moveToRight(bottomSkip, bottomSkip);
|
||||||
|
cancel->moveToRight(
|
||||||
|
bottomSkip * 2 + (submitShown ? submitWidth : finishWidth),
|
||||||
|
bottomSkip);
|
||||||
|
}, _bottom->lifetime());
|
||||||
|
|
||||||
|
const auto sharing = !_delegate->chooseSourceActiveDeviceId().isEmpty();
|
||||||
|
_finish->setVisible(sharing);
|
||||||
|
_submit->setVisible(!sharing);
|
||||||
|
|
||||||
_window->body()->sizeValue(
|
_window->body()->sizeValue(
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
_scroll->setGeometry({ QPoint(), size });
|
_scroll->setGeometry(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
size.width(),
|
||||||
|
size.height() - _bottom->height());
|
||||||
}, _scroll->lifetime());
|
}, _scroll->lifetime());
|
||||||
|
|
||||||
_scroll->widthValue(
|
_scroll->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
||||||
const auto height = rows * st::desktopCaptureSourceSize.height()
|
const auto innerHeight = margins.top()
|
||||||
+ (rows + 1) * st::desktopCaptureSourceSkip;
|
+ rows * st::desktopCaptureSourceSize.height()
|
||||||
_inner->resize(width, height);
|
+ (rows - 1) * skips.height()
|
||||||
|
+ margins.bottom();
|
||||||
|
_inner->resize(width, std::max(height, innerHeight));
|
||||||
}, _inner->lifetime());
|
}, _inner->lifetime());
|
||||||
|
|
||||||
if (const auto parent = _delegate->chooseSourceParent()) {
|
if (const auto parent = _delegate->chooseSourceParent()) {
|
||||||
|
@ -278,17 +419,42 @@ void ChooseSourceProcess::fillSources() {
|
||||||
|
|
||||||
auto screenIndex = 0;
|
auto screenIndex = 0;
|
||||||
auto windowIndex = 0;
|
auto windowIndex = 0;
|
||||||
|
const auto active = _delegate->chooseSourceActiveDeviceId();
|
||||||
const auto append = [&](const tgcalls::DesktopCaptureSource &source) {
|
const auto append = [&](const tgcalls::DesktopCaptureSource &source) {
|
||||||
const auto title = !source.title().empty()
|
const auto title = !source.isWindow()
|
||||||
|
? tr::lng_group_call_screen_title(
|
||||||
|
tr::now,
|
||||||
|
lt_index,
|
||||||
|
QString::number(++screenIndex))
|
||||||
|
: !source.title().empty()
|
||||||
? QString::fromStdString(source.title())
|
? QString::fromStdString(source.title())
|
||||||
: source.isWindow()
|
: "Window " + QString::number(++windowIndex);
|
||||||
? "Window " + QString::number(++windowIndex)
|
const auto id = source.deviceIdKey();
|
||||||
: "Screen " + QString::number(++screenIndex);
|
|
||||||
_sources.push_back(std::make_unique<Source>(_inner, source, title));
|
_sources.push_back(std::make_unique<Source>(_inner, source, title));
|
||||||
_sources.back()->clicks(
|
|
||||||
) | rpl::start_with_next([=, id = source.deviceIdKey()]{
|
const auto raw = _sources.back().get();
|
||||||
_delegate->chooseSourceAccepted(QString::fromStdString(id));
|
if (!active.isEmpty() && active.toStdString() == id) {
|
||||||
}, _sources.back()->lifetime());
|
_selected = raw;
|
||||||
|
raw->setActive(true);
|
||||||
|
}
|
||||||
|
_sources.back()->activations(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return (_selected != raw);
|
||||||
|
}) | rpl::start_with_next([=]{
|
||||||
|
if (_selected) {
|
||||||
|
_selected->setActive(false);
|
||||||
|
}
|
||||||
|
_selected = raw;
|
||||||
|
_selectedId = QString::fromStdString(id);
|
||||||
|
if (_selectedId == _delegate->chooseSourceActiveDeviceId()) {
|
||||||
|
_selectedId = QString();
|
||||||
|
_finish->setVisible(true);
|
||||||
|
_submit->setVisible(false);
|
||||||
|
} else {
|
||||||
|
_finish->setVisible(false);
|
||||||
|
_submit->setVisible(true);
|
||||||
|
}
|
||||||
|
}, raw->lifetime());
|
||||||
};
|
};
|
||||||
for (const auto &source : screensManager.sources()) {
|
for (const auto &source : screensManager.sources()) {
|
||||||
append(source);
|
append(source);
|
||||||
|
@ -300,31 +466,34 @@ void ChooseSourceProcess::fillSources() {
|
||||||
|
|
||||||
void ChooseSourceProcess::setupSourcesGeometry() {
|
void ChooseSourceProcess::setupSourcesGeometry() {
|
||||||
if (_sources.empty()) {
|
if (_sources.empty()) {
|
||||||
//LOG(());
|
|
||||||
destroy();
|
destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_inner->widthValue(
|
_inner->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
||||||
const auto skip = st::desktopCaptureSourceSkip;
|
const auto margins = st::desktopCaptureMargins;
|
||||||
const auto single = (width - (kColumns + 1) * skip) / kColumns;
|
const auto skips = st::desktopCaptureSourceSkips;
|
||||||
|
const auto single = (width
|
||||||
|
- margins.left()
|
||||||
|
- margins.right()
|
||||||
|
- (kColumns - 1) * skips.width()) / kColumns;
|
||||||
const auto height = st::desktopCaptureSourceSize.height();
|
const auto height = st::desktopCaptureSourceSize.height();
|
||||||
auto top = skip;
|
auto top = margins.top();
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (auto row = 0; row != rows; ++row) {
|
for (auto row = 0; row != rows; ++row) {
|
||||||
auto left = skip;
|
auto left = margins.left();
|
||||||
for (auto column = 0; column != kColumns; ++column) {
|
for (auto column = 0; column != kColumns; ++column) {
|
||||||
_sources[index]->setGeometry({ left, top, single, height });
|
_sources[index]->setGeometry({ left, top, single, height });
|
||||||
if (++index == _sources.size()) {
|
if (++index == _sources.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
left += single + skip;
|
left += single + skips.width();
|
||||||
}
|
}
|
||||||
if (index >= _sources.size()) {
|
if (index >= _sources.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
top += height + skip;
|
top += height + skips.height();
|
||||||
}
|
}
|
||||||
}, _inner->lifetime());
|
}, _inner->lifetime());
|
||||||
|
|
||||||
|
@ -333,9 +502,10 @@ void ChooseSourceProcess::setupSourcesGeometry() {
|
||||||
_scroll->heightValue()
|
_scroll->heightValue()
|
||||||
) | rpl::start_with_next([=](int scrollTop, int scrollHeight) {
|
) | rpl::start_with_next([=](int scrollTop, int scrollHeight) {
|
||||||
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
const auto rows = int(std::ceil(_sources.size() / float(kColumns)));
|
||||||
const auto skip = st::desktopCaptureSourceSkip;
|
const auto margins = st::desktopCaptureMargins;
|
||||||
|
const auto skips = st::desktopCaptureSourceSkips;
|
||||||
const auto height = st::desktopCaptureSourceSize.height();
|
const auto height = st::desktopCaptureSourceSize.height();
|
||||||
auto top = skip;
|
auto top = margins.top();
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (auto row = 0; row != rows; ++row) {
|
for (auto row = 0; row != rows; ++row) {
|
||||||
const auto hidden = (top + height <= scrollTop)
|
const auto hidden = (top + height <= scrollTop)
|
||||||
|
@ -353,7 +523,7 @@ void ChooseSourceProcess::setupSourcesGeometry() {
|
||||||
if (index >= _sources.size()) {
|
if (index >= _sources.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
top += height + skip;
|
top += height + skips.height();
|
||||||
}
|
}
|
||||||
}, _inner->lifetime());
|
}, _inner->lifetime());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@ namespace Calls::Group::Ui::DesktopCapture {
|
||||||
class ChooseSourceDelegate {
|
class ChooseSourceDelegate {
|
||||||
public:
|
public:
|
||||||
virtual QWidget *chooseSourceParent() = 0;
|
virtual QWidget *chooseSourceParent() = 0;
|
||||||
|
virtual QString chooseSourceActiveDeviceId() = 0;
|
||||||
virtual rpl::lifetime &chooseSourceInstanceLifetime() = 0;
|
virtual rpl::lifetime &chooseSourceInstanceLifetime() = 0;
|
||||||
virtual void chooseSourceAccepted(const QString &deviceId) = 0;
|
virtual void chooseSourceAccepted(const QString &deviceId) = 0;
|
||||||
|
virtual void chooseSourceStop() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ChooseSource(not_null<ChooseSourceDelegate*> delegate);
|
void ChooseSource(not_null<ChooseSourceDelegate*> delegate);
|
||||||
|
|
|
@ -93,16 +93,6 @@ QImage FromInlineBytes(const QByteArray &bytes) {
|
||||||
return App::readImage(ExpandInlineBytes(bytes));
|
return App::readImage(ExpandInlineBytes(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize GetSizeForDocument(const QVector<MTPDocumentAttribute> &attributes) {
|
|
||||||
for (const auto &attribute : attributes) {
|
|
||||||
if (attribute.type() == mtpc_documentAttributeImageSize) {
|
|
||||||
auto &size = attribute.c_documentAttributeImageSize();
|
|
||||||
return QSize(size.vw().v, size.vh().v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Images
|
} // namespace Images
|
||||||
|
|
||||||
Image::Image(const QString &path) : Image(ReadContent(path)) {
|
Image::Image(const QString &path) : Image(ReadContent(path)) {
|
||||||
|
|
|
@ -14,9 +14,6 @@ namespace Images {
|
||||||
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
||||||
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
||||||
|
|
||||||
[[nodiscard]] QSize GetSizeForDocument(
|
|
||||||
const QVector<MTPDocumentAttribute> &attributes);
|
|
||||||
|
|
||||||
} // namespace Images
|
} // namespace Images
|
||||||
|
|
||||||
class Image final {
|
class Image final {
|
||||||
|
|
|
@ -13,6 +13,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtCore/QBuffer>
|
#include <QtCore/QBuffer>
|
||||||
|
|
||||||
namespace Images {
|
namespace Images {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QSize GetSizeForDocument(const QVector<MTPDocumentAttribute> &attributes) {
|
||||||
|
for (const auto &attribute : attributes) {
|
||||||
|
if (attribute.type() == mtpc_documentAttributeImageSize) {
|
||||||
|
auto &size = attribute.c_documentAttributeImageSize();
|
||||||
|
return QSize(size.vw().v, size.vh().v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ImageWithLocation FromPhotoSize(
|
ImageWithLocation FromPhotoSize(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
|
|
Loading…
Add table
Reference in a new issue