Move actions from Settings to Three-Dot-Menu.

This commit is contained in:
John Preston 2021-03-09 19:13:48 +04:00
parent b24e5ce809
commit f8783c3bfc
5 changed files with 358 additions and 201 deletions

View File

@ -428,6 +428,17 @@ groupCallPopupMenu: PopupMenu(defaultPopupMenu) {
animation: groupCallPanelAnimation;
}
groupCallInnerDropdown: InnerDropdown(defaultInnerDropdown) {
shadow: groupCallMenuShadow;
animation: groupCallPanelAnimation;
bg: groupCallMenuBg;
scroll: defaultSolidScroll;
scrollPadding: margins(0px, 8px, 0px, 8px);
}
groupCallDropdownMenu: DropdownMenu(defaultDropdownMenu) {
wrap: groupCallInnerDropdown;
menu: groupCallMenu;
}
groupCallMembersListItem: PeerListItem(defaultPeerListItem) {
button: OutlineButton(defaultPeerListButton) {
textBg: groupCallMembersBg;
@ -526,9 +537,9 @@ groupCallField: InputField(defaultInputField) {
menu: groupCallPopupMenu;
}
groupCallMembersTop: 62px;
groupCallTitleTop: 14px;
groupCallSubtitleTop: 33px;
groupCallMembersTop: 51px;
groupCallTitleTop: 8px;
groupCallSubtitleTop: 26px;
groupCallMembersMargin: margins(16px, 16px, 16px, 28px);
groupCallAddMember: SettingsButton(defaultSettingsButton) {
@ -562,7 +573,29 @@ groupCallAddButtonPosition: point(10px, 7px);
groupCallMembersWidthMax: 360px;
groupCallRecordingMark: 6px;
groupCallRecordingMarkSkip: 4px;
groupCallRecordingMarkTop: 6px;
groupCallRecordingMarkTop: 8px;
groupCallMenuTogglePosition: point(13px, 8px);
groupCallMenuToggle: IconButton {
width: 36px;
height: 36px;
icon: icon {{ "info/edit/dotsmini", groupCallMemberInactiveIcon }};
iconOver: icon {{ "info/edit/dotsmini", groupCallMemberInactiveIcon }};
iconPosition: point(6px, 6px);
rippleAreaPosition: point(3px, 3px);
rippleAreaSize: 30px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBg;
}
}
groupCallJoinAsToggle: UserpicButton(defaultUserpicButton) {
size: size(36px, 36px);
photoSize: 30px;
photoPosition: point(3px, 3px);
}
groupCallMenuPosition: point(-1px, 29px);
groupCallActiveButton: IconButton {
width: 36px;

View File

@ -16,10 +16,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/call_button.h"
#include "ui/widgets/call_mute_button.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/input_fields.h"
#include "ui/layers/layer_manager.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
#include "core/application.h"
#include "lang/lang_keys.h"
@ -31,6 +34,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "main/main_session.h"
#include "base/event_filter.h"
#include "base/unixtime.h"
#include "base/timer_rpl.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peer_lists_box.h"
@ -137,6 +142,81 @@ private:
return result;
}
void EditGroupCallTitleBox(
not_null<Ui::GenericBox*> box,
const QString &placeholder,
const QString &title,
Fn<void(QString)> done) {
box->setTitle(tr::lng_group_call_edit_title());
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
rpl::single(placeholder),
title));
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_settings_save(), [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StartGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
const QString &title,
Fn<void(QString)> done) {
box->setTitle(tr::lng_group_call_recording_start());
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_start_sure(),
st::groupCallBoxLabel));
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
tr::lng_group_call_recording_start_field(),
title));
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_group_call_recording_start_button(), [=] {
const auto result = input->getLastText().trimmed();
if (result.isEmpty()) {
input->showError();
return;
}
box->closeBox();
done(result);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StopGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
Fn<void(QString)> done) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_stop_sure(),
st::groupCallBoxLabel),
style::margins(
st::boxRowPadding.left(),
st::boxPadding.top(),
st::boxRowPadding.right(),
st::boxPadding.bottom()));
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();
done(QString());
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
InviteController::InviteController(
not_null<PeerData*> peer,
base::flat_set<not_null<UserData*>> alreadyIn)
@ -599,6 +679,7 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_recordingMark.destroy();
} else if (recording && !_recordingMark) {
_recordingMark.create(widget());
_recordingMark->show();
const auto size = st::groupCallRecordingMark;
const auto skip = st::groupCallRecordingMarkSkip;
_recordingMark->resize(size + 2 * skip, size + 2 * skip);
@ -631,8 +712,206 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
(recorded
? tr::lng_group_call_recording_started(tr::now)
: tr::lng_group_call_recording_stopped(tr::now)));
}, _callLifetime);
}, widget()->lifetime());
validateRecordingMark(real->recordStartDate() != 0);
const auto showMenu = _peer->canManageGroupCall();
const auto showUserpic = !showMenu
&& (_call->possibleJoinAs().size() > 1); // #TODO calls when to show
if (showMenu) {
_joinAsToggle.destroy();
if (!_menuToggle) {
_menuToggle.create(widget(), st::groupCallMenuToggle);
_menuToggle->show();
_menuToggle->setClickedCallback([=] { showMainMenu(); });
}
} else if (showUserpic) {
_menuToggle.destroy();
rpl::single(
_call->joinAs()
) | rpl::then(_call->rejoinEvents(
) | rpl::map([](const Group::RejoinEvent &event) {
return event.nowJoinAs;
})) | rpl::start_with_next([=](not_null<PeerData*> joinAs) {
auto joinAsToggle = object_ptr<Ui::UserpicButton>(
widget(),
joinAs,
Ui::UserpicButton::Role::Custom,
st::groupCallJoinAsToggle);
_joinAsToggle.destroy();
_joinAsToggle = std::move(joinAsToggle);
_joinAsToggle->show();
_joinAsToggle->setClickedCallback([=] {
chooseJoinAs();
});
}, widget()->lifetime());
} else {
_menuToggle.destroy();
_joinAsToggle.destroy();
}
updateControlsGeometry();
}
void GroupPanel::chooseJoinAs() {
const auto context = Group::ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](Group::JoinInfo info) {
if (_call) {
_call->rejoinAs(info);
}
};
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next));
};
const auto showToast = [=](QString text) {
Ui::Toast::Show(widget(), text);
};
_joinAsProcess.start(
_peer,
context,
showBox,
showToast,
callback,
_call->joinAs());
}
void GroupPanel::showMainMenu() {
const auto real = _peer->groupCall();
if (_menu || !_call || !real || real->id() != _call->id()) {
return;
}
_menu.create(widget(), st::groupCallDropdownMenu);
const auto raw = _menu.data();
raw->setHiddenCallback([=] {
raw->deleteLater();
if (_menu == raw) {
_menu = nullptr;
_menuToggle->setForceRippled(false);
}
});
raw->setShowStartCallback([=] {
if (_menu == raw) {
_menuToggle->setForceRippled(true);
}
});
raw->setHideStartCallback([=] {
if (_menu == raw) {
_menuToggle->setForceRippled(false);
}
});
_menuToggle->installEventFilter(_menu);
const auto addEditJoinAs = (_call->possibleJoinAs().size() > 1); // #TODO calls when to show
const auto addEditTitle = _peer->canManageGroupCall();
const auto addEditRecording = _peer->canManageGroupCall();
if (addEditJoinAs) {
_menu->addAction(tr::lng_group_call_display_as_header(tr::now), [=] {
chooseJoinAs();
});
}
if (addEditTitle) {
_menu->addAction(tr::lng_group_call_edit_title(tr::now), [=] {
const auto done = [=](const QString &title) {
if (_call) {
_call->changeTitle(title);
}
};
_layerBg->showBox(Box(
EditGroupCallTitleBox,
_peer->name,
real->title(),
done));
});
}
if (addEditRecording) {
const auto action = _menu->addAction((real->recordStartDate() != 0)
? tr::lng_group_call_recording_stop(tr::now)
: tr::lng_group_call_recording_start(tr::now), [=] {
const auto real = _peer->groupCall();
const auto id = _call ? _call->id() : 0;
if (!real || real->id() != id) {
return;
}
const auto recordStartDate = real->recordStartDate();
const auto done = [=](QString title) {
if (_call) {
_call->toggleRecording(!recordStartDate, title);
}
};
if (recordStartDate) {
_layerBg->showBox(Box(
StopGroupCallRecordingBox,
done));
} else {
_layerBg->showBox(Box(
StartGroupCallRecordingBox,
real->title(),
done));
}
});
static const auto ToDurationFrom = [](TimeId startDate) {
return [=] {
const auto now = base::unixtime::now();
const auto elapsed = std::max(now - startDate, 0);
const auto hours = elapsed / 3600;
const auto minutes = (elapsed % 3600) / 60;
const auto seconds = (elapsed % 60);
return hours
? QString("%1:%2:%3"
).arg(hours
).arg(minutes, 2, 10, QChar('0')
).arg(seconds, 2, 10, QChar('0'))
: QString("%1:%2"
).arg(minutes
).arg(seconds, 2, 10, QChar('0'));
};
};
static const auto ToRecordDuration = [](TimeId startDate) {
return !startDate
? (rpl::single(QString()) | rpl::type_erased())
: rpl::single(
rpl::empty_value()
) | rpl::then(base::timer_each(
crl::time(1000)
)) | rpl::map(ToDurationFrom(startDate));
};
rpl::combine(
real->recordStartDateValue(),
tr::lng_group_call_recording_stop(),
tr::lng_group_call_recording_start()
) | rpl::map([=](TimeId startDate, QString stop, QString start) {
using namespace rpl::mappers;
return startDate
? ToRecordDuration(
startDate
) | rpl::map(stop + '\t' + _1) : rpl::single(start);
}) | rpl::flatten_latest() | rpl::start_with_next([=](QString text) {
action->setText(text);
}, _menu->lifetime());
}
_menu->addAction(tr::lng_group_call_settings(tr::now), [=] {
if (_call) {
_layerBg->showBox(Box(GroupCallSettingsBox, _call));
}
});
_menu->addAction(tr::lng_group_call_end(tr::now), [=] {
if (_call) {
_layerBg->showBox(Box(
LeaveGroupCallBox,
_call,
true,
BoxContext::GroupCallPanel));
}
});
const auto x = st::groupCallMenuPosition.x();
const auto y = st::groupCallMenuPosition.y();
if (_menuToggle->x() > widget()->width() / 2) {
_menu->moveToRight(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
} else {
_menu->moveToLeft(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
}
}
void GroupPanel::addMembers() {
@ -847,6 +1126,11 @@ void GroupPanel::initGeometry() {
QRect GroupPanel::computeTitleRect() const {
const auto skip = st::groupCallTitleTop;
const auto remove = skip + (_menuToggle
? (_menuToggle->width() + st::groupCallMenuTogglePosition.x())
: 0) + (_joinAsToggle
? (_joinAsToggle->width() + st::groupCallMenuTogglePosition.x())
: 0);
const auto width = widget()->width();
#ifdef Q_OS_MAC
return QRect(70, 0, width - skip - 70, 28);
@ -854,8 +1138,8 @@ QRect GroupPanel::computeTitleRect() const {
const auto controls = _controls->geometry();
const auto right = controls.x() + controls.width() + skip;
return (controls.center().x() < width / 2)
? QRect(right, 0, width - right - skip, controls.height())
: QRect(skip, 0, controls.x() - 2 * skip, controls.height());
? QRect(right, 0, width - right - remove, controls.height())
: QRect(remove, 0, controls.x() - skip - remove, controls.height());
#endif // !Q_OS_MAC
}
@ -893,6 +1177,28 @@ void GroupPanel::updateControlsGeometry() {
_settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop);
_hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop);
refreshTitle();
#ifdef Q_OS_MAC
const auto controlsOnTheLeft = true;
#else // Q_OS_MAC
const auto controlsOnTheLeft = _controls->geometry().center().x()
< widget()->width() / 2;
#endif // Q_OS_MAC
const auto menux = st::groupCallMenuTogglePosition.x();
const auto menuy = st::groupCallMenuTogglePosition.y();
if (controlsOnTheLeft) {
if (_menuToggle) {
_menuToggle->moveToRight(menux, menuy);
} else if (_joinAsToggle) {
_joinAsToggle->moveToRight(menux, menuy);
}
} else {
if (_menuToggle) {
_menuToggle->moveToLeft(menux, menuy);
} else if (_joinAsToggle) {
_joinAsToggle->moveToLeft(menux, menuy);
}
}
}
void GroupPanel::refreshTitle() {

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/object_ptr.h"
#include "calls/calls_group_call.h"
#include "calls/calls_choose_join_as.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
@ -24,6 +25,7 @@ class GroupCall;
namespace Ui {
class AbstractButton;
class DropdownMenu;
class CallButton;
class CallMuteButton;
class IconButton;
@ -96,6 +98,8 @@ private:
void endCall();
void showMainMenu();
void chooseJoinAs();
void addMembers();
void kickMember(not_null<UserData*> user);
void kickMemberSure(not_null<UserData*> user);
@ -123,8 +127,12 @@ private:
object_ptr<Ui::FlatLabel> _title = { nullptr };
object_ptr<Ui::FlatLabel> _subtitle = { nullptr };
object_ptr<Ui::AbstractButton> _recordingMark = { nullptr };
object_ptr<Ui::IconButton> _menuToggle = { nullptr };
object_ptr<Ui::DropdownMenu> _menu = { nullptr };
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
object_ptr<GroupMembers> _members;
rpl::variable<QString> _titleText;
Group::ChooseJoinAsProcess _joinAsProcess;
object_ptr<Ui::CallButton> _settings;
std::unique_ptr<Ui::CallMuteButton> _mute;

View File

@ -86,81 +86,6 @@ void SaveCallJoinMuted(
QString::number(delay / 1000., 'f', 2));
}
void EditGroupCallTitleBox(
not_null<Ui::GenericBox*> box,
const QString &placeholder,
const QString &title,
Fn<void(QString)> done) {
box->setTitle(tr::lng_group_call_edit_title());
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
rpl::single(placeholder),
title));
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_settings_save(), [=] {
const auto result = input->getLastText().trimmed();
box->closeBox();
done(result);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StartGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
const QString &title,
Fn<void(QString)> done) {
box->setTitle(tr::lng_group_call_recording_start());
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_start_sure(),
st::groupCallBoxLabel));
const auto input = box->addRow(object_ptr<Ui::InputField>(
box,
st::groupCallField,
tr::lng_group_call_recording_start_field(),
title));
box->setFocusCallback([=] {
input->setFocusFast();
});
box->addButton(tr::lng_group_call_recording_start_button(), [=] {
const auto result = input->getLastText().trimmed();
if (result.isEmpty()) {
input->showError();
return;
}
box->closeBox();
done(result);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void StopGroupCallRecordingBox(
not_null<Ui::GenericBox*> box,
Fn<void(QString)> done) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_recording_stop_sure(),
st::groupCallBoxLabel),
style::margins(
st::boxRowPadding.left(),
st::boxPadding.top(),
st::boxRowPadding.right(),
st::boxPadding.bottom()));
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();
done(QString());
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace
void GroupCallSettingsBox(
@ -179,7 +104,6 @@ void GroupCallSettingsBox(
float micLevel = 0.;
Ui::Animations::Simple micLevelAnimation;
base::Timer levelUpdateTimer;
Group::ChooseJoinAsProcess joinAsProcess;
bool generatingLink = false;
};
const auto state = box->lifetime().make_state<State>();
@ -195,130 +119,16 @@ void GroupCallSettingsBox(
const auto joinMuted = goodReal ? real->joinMuted() : false;
const auto canChangeJoinMuted = (goodReal && real->canChangeJoinMuted());
const auto addCheck = (peer->canManageGroupCall() && canChangeJoinMuted);
const auto addEditJoinAs = (call->possibleJoinAs().size() > 1); // #TODO calls when to show
const auto addEditTitle = peer->canManageGroupCall() && goodReal;
const auto addEditRecording = peer->canManageGroupCall() && goodReal;
if (addCheck || addEditJoinAs) {
if (addCheck) {
AddSkip(layout);
}
const auto editJoinAs = addEditJoinAs
? AddButton(
layout,
tr::lng_group_call_display_as_header(),
st::groupCallSettingsButton).get()
: nullptr;
const auto editTitle = addEditTitle
? AddButton(
layout,
tr::lng_group_call_edit_title(),
st::groupCallSettingsButton).get()
: nullptr;
static const auto ToDurationFrom = [](TimeId startDate) {
return [=] {
const auto now = base::unixtime::now();
const auto elapsed = std::max(now - startDate, 0);
const auto hours = elapsed / 3600;
const auto minutes = (elapsed % 3600) / 60;
const auto seconds = (elapsed % 60);
return hours
? QString("%1:%2:%3"
).arg(hours
).arg(minutes, 2, 10, QChar('0')
).arg(seconds, 2, 10, QChar('0'))
: QString("%1:%2"
).arg(minutes
).arg(seconds, 2, 10, QChar('0'));
};
};
static const auto ToRecordDuration = [](TimeId startDate) {
return !startDate
? (rpl::single(QString()) | rpl::type_erased())
: rpl::single(
rpl::empty_value()
) | rpl::then(base::timer_each(
crl::time(1000)
)) | rpl::map(ToDurationFrom(startDate));
};
using namespace rpl::mappers;
const auto editRecording = !addEditRecording
? nullptr
: AddButtonWithLabel(
layout,
rpl::conditional(
real->recordStartDateValue() | rpl::map(!!_1),
tr::lng_group_call_recording_stop(),
tr::lng_group_call_recording_start()),
real->recordStartDateValue(
) | rpl::map(
ToRecordDuration
) | rpl::flatten_latest(),
st::groupCallSettingsButton).get();
if (editJoinAs) {
editJoinAs->setClickedCallback([=] {
const auto context = Group::ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](Group::JoinInfo info) {
call->rejoinAs(info);
};
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
box->getDelegate()->show(std::move(next));
};
const auto showToast = [=](QString text) {
const auto container = box->getDelegate()->outerContainer();
Ui::Toast::Show(container, text);
};
state->joinAsProcess.start(
peer,
context,
showBox,
showToast,
callback,
call->joinAs());
});
}
if (editTitle) {
editTitle->setClickedCallback([=] {
const auto done = [=](const QString &title) {
call->changeTitle(title);
box->closeBox();
};
box->getDelegate()->show(Box(
EditGroupCallTitleBox,
peer->name,
real->title(),
done));
});
}
if (editRecording) {
editRecording->setClickedCallback([=] {
const auto real = peer->groupCall();
const auto id = call->id();
if (!real || real->id() != id) {
return;
}
const auto recordStartDate = real->recordStartDate();
const auto done = [=](QString title) {
call->toggleRecording(!recordStartDate, title);
box->closeBox();
};
if (recordStartDate) {
box->getDelegate()->show(Box(
StopGroupCallRecordingBox,
done));
} else {
box->getDelegate()->show(Box(
StartGroupCallRecordingBox,
real->title(),
done));
}
});
}
const auto muteJoined = addCheck
? AddButton(
layout,
tr::lng_group_call_new_muted(),
st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted))
: nullptr;
if (addCheck || addEditJoinAs) {
if (addCheck) {
AddSkip(layout);
}

View File

@ -73,7 +73,7 @@ public:
const style::UserpicButton &st);
UserpicButton(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<::Window::SessionController*> controller,
not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st);
@ -132,7 +132,7 @@ private:
void uploadNewPeerPhoto();
const style::UserpicButton &_st;
Window::SessionController *_controller = nullptr;
::Window::SessionController *_controller = nullptr;
PeerData *_peer = nullptr;
std::shared_ptr<Data::CloudImageView> _userpicView;
QString _cropTitle;