mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Show nice tooltips about story privacy / silence.
This commit is contained in:
parent
320db83155
commit
2323aef899
7 changed files with 233 additions and 32 deletions
|
@ -3833,6 +3833,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_stories_no_views" = "No views";
|
||||
"lng_stories_unsupported" = "This story is not supported\nby your version of Telegram.";
|
||||
"lng_stories_cant_reply" = "You can't reply to this story.";
|
||||
"lng_stories_about_silent" = "This video has no sound.";
|
||||
"lng_stories_about_close_friends" = "You're seeing this story because {user} added you to their list of **Close Friends**.";
|
||||
"lng_stories_about_contacts" = "Only {user}'s contacts can view this story.";
|
||||
"lng_stories_about_selected_contacts" = "Only some contacts {user} selected can view this story.";
|
||||
"lng_stories_about_close_friends_my" = "Only your list of **Close Friends** can view this story.";
|
||||
"lng_stories_about_contacts_my" = "Only your contacts can view this story.";
|
||||
"lng_stories_about_selected_contacts_my" = "Only some contacts you selected can view this story.";
|
||||
"lng_stories_click_to_view" = "Click here to view updates from {users}.";
|
||||
"lng_stories_click_to_view_and_one" = "{accumulated}, {user}";
|
||||
"lng_stories_click_to_view_and_last" = "{accumulated} and {user}";
|
||||
|
||||
"lng_stories_my_title" = "Saved Stories";
|
||||
"lng_stories_archive_button" = "Stories Archive";
|
||||
|
|
|
@ -322,8 +322,18 @@ Controller::Controller(not_null<Delegate*> delegate)
|
|||
|
||||
_delegate->storiesLayerShown(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
_layerShown = shown;
|
||||
updatePlayingAllowed();
|
||||
if (_layerShown != shown) {
|
||||
_layerShown = shown;
|
||||
updatePlayingAllowed();
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
_header->tooltipShownValue(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
if (_tooltipShown != shown) {
|
||||
_tooltipShown = shown;
|
||||
updatePlayingAllowed();
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
const auto window = _wrap->window()->windowHandle();
|
||||
|
@ -961,7 +971,8 @@ void Controller::updatePlayingAllowed() {
|
|||
&& !_captionFullView
|
||||
&& !_captionExpanded
|
||||
&& !_layerShown
|
||||
&& !_menuShown);
|
||||
&& !_menuShown
|
||||
&& !_tooltipShown);
|
||||
}
|
||||
|
||||
void Controller::setPlayingAllowed(bool allowed) {
|
||||
|
|
|
@ -257,6 +257,7 @@ private:
|
|||
bool _hasSendText = false;
|
||||
bool _layerShown = false;
|
||||
bool _menuShown = false;
|
||||
bool _tooltipShown = false;
|
||||
bool _paused = false;
|
||||
|
||||
FullStoryId _shown;
|
||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
@ -50,10 +51,9 @@ struct PrivacyBadge {
|
|||
|
||||
class UserpicBadge final : public Ui::RpWidget {
|
||||
public:
|
||||
UserpicBadge(
|
||||
not_null<QWidget*> userpic,
|
||||
PrivacyBadge badge,
|
||||
Fn<void()> clicked);
|
||||
UserpicBadge(not_null<QWidget*> userpic, PrivacyBadge badge);
|
||||
|
||||
[[nodiscard]] QRect badgeGeometry() const;
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
@ -63,7 +63,6 @@ private:
|
|||
|
||||
const not_null<QWidget*> _userpic;
|
||||
const PrivacyBadge _badgeData;
|
||||
const std::unique_ptr<Ui::AbstractButton> _clickable;
|
||||
QRect _badge;
|
||||
QImage _layer;
|
||||
bool _grabbing = false;
|
||||
|
@ -95,15 +94,10 @@ private:
|
|||
return {};
|
||||
}
|
||||
|
||||
UserpicBadge::UserpicBadge(
|
||||
not_null<QWidget*> userpic,
|
||||
PrivacyBadge badge,
|
||||
Fn<void()> clicked)
|
||||
UserpicBadge::UserpicBadge(not_null<QWidget*> userpic, PrivacyBadge badge)
|
||||
: RpWidget(userpic->parentWidget())
|
||||
, _userpic(userpic)
|
||||
, _badgeData(badge)
|
||||
, _clickable(std::make_unique<Ui::AbstractButton>(parentWidget())) {
|
||||
_clickable->setClickedCallback(std::move(clicked));
|
||||
, _badgeData(badge) {
|
||||
userpic->installEventFilter(this);
|
||||
updateGeometry();
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
@ -113,6 +107,10 @@ UserpicBadge::UserpicBadge(
|
|||
show();
|
||||
}
|
||||
|
||||
QRect UserpicBadge::badgeGeometry() const {
|
||||
return _badge;
|
||||
}
|
||||
|
||||
bool UserpicBadge::eventFilter(QObject *o, QEvent *e) {
|
||||
if (o != _userpic) {
|
||||
return false;
|
||||
|
@ -173,22 +171,27 @@ void UserpicBadge::updateGeometry() {
|
|||
_badge = QRect(
|
||||
QPoint(width - badge.width(), height - badge.height()),
|
||||
badge);
|
||||
_clickable->setGeometry(_badge.translated(pos()));
|
||||
update();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> MakePrivacyBadge(
|
||||
struct MadePrivacyBadge {
|
||||
std::unique_ptr<Ui::RpWidget> widget;
|
||||
QRect geometry;
|
||||
};
|
||||
|
||||
[[nodiscard]] MadePrivacyBadge MakePrivacyBadge(
|
||||
not_null<QWidget*> userpic,
|
||||
Data::StoryPrivacy privacy,
|
||||
Fn<void()> clicked) {
|
||||
Data::StoryPrivacy privacy) {
|
||||
const auto badge = LookupPrivacyBadge(privacy);
|
||||
if (!badge.icon) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<UserpicBadge>(
|
||||
userpic,
|
||||
badge,
|
||||
std::move(clicked));
|
||||
auto widget = std::make_unique<UserpicBadge>(userpic, badge);
|
||||
const auto geometry = widget->badgeGeometry();
|
||||
return {
|
||||
.widget = std::move(widget),
|
||||
.geometry = geometry,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] Timestamp ComposeTimestamp(TimeId when, TimeId now) {
|
||||
|
@ -277,6 +280,8 @@ void Header::show(HeaderData data) {
|
|||
_info->setGeometry({ 0, 0, r, _widget->height() });
|
||||
}
|
||||
};
|
||||
_tooltip = nullptr;
|
||||
_tooltipShown = false;
|
||||
if (userChanged) {
|
||||
_volume = nullptr;
|
||||
_date = nullptr;
|
||||
|
@ -328,6 +333,8 @@ void Header::show(HeaderData data) {
|
|||
_controller->layoutValue(
|
||||
) | rpl::start_with_next([=](const Layout &layout) {
|
||||
raw->setGeometry(layout.header);
|
||||
_contentGeometry = layout.content;
|
||||
updateTooltipGeometry();
|
||||
}, raw->lifetime());
|
||||
}
|
||||
auto timestamp = ComposeDetails(data, base::unixtime::now());
|
||||
|
@ -357,8 +364,29 @@ void Header::show(HeaderData data) {
|
|||
_counter = nullptr;
|
||||
}
|
||||
|
||||
_privacy = MakePrivacyBadge(_userpic.get(), data.privacy, [=] {
|
||||
});
|
||||
auto made = MakePrivacyBadge(_userpic.get(), data.privacy);
|
||||
_privacy = std::move(made.widget);
|
||||
_privacyBadgeOver = false;
|
||||
_privacyBadgeGeometry = _privacy
|
||||
? Ui::MapFrom(_info.get(), _privacy.get(), made.geometry)
|
||||
: QRect();
|
||||
if (_privacy) {
|
||||
_info->setMouseTracking(true);
|
||||
_info->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
const auto type = e->type();
|
||||
if (type != QEvent::Leave && type != QEvent::MouseMove) {
|
||||
return false;
|
||||
}
|
||||
const auto over = (type == QEvent::MouseMove)
|
||||
&& _privacyBadgeGeometry.contains(
|
||||
static_cast<QMouseEvent*>(e.get())->pos());
|
||||
return (_privacyBadgeOver != over);
|
||||
}) | rpl::start_with_next([=] {
|
||||
_privacyBadgeOver = !_privacyBadgeOver;
|
||||
toggleTooltip(Tooltip::Privacy, _privacyBadgeOver);
|
||||
}, _privacy->lifetime());
|
||||
}
|
||||
|
||||
if (data.video) {
|
||||
createPlayPause();
|
||||
|
@ -369,6 +397,7 @@ void Header::show(HeaderData data) {
|
|||
_playPause->moveToRight(playPause.x(), playPause.y(), width);
|
||||
const auto volume = st::storiesVolumeButtonPosition;
|
||||
_volumeToggle->moveToRight(volume.x(), volume.y(), width);
|
||||
updateTooltipGeometry();
|
||||
}, _playPause->lifetime());
|
||||
|
||||
_pauseState = _controller->pauseState();
|
||||
|
@ -496,15 +525,14 @@ void Header::createVolumeToggle() {
|
|||
|
||||
_volumeToggle->events(
|
||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (state->silent) {
|
||||
return;
|
||||
}
|
||||
const auto type = e->type();
|
||||
if (type == QEvent::Enter || type == QEvent::Leave) {
|
||||
const auto over = (e->type() == QEvent::Enter);
|
||||
if (state->over != over) {
|
||||
state->over = over;
|
||||
if (over) {
|
||||
if (state->silent) {
|
||||
toggleTooltip(Tooltip::SilentVideo, over);
|
||||
} else if (over) {
|
||||
state->hideTimer.cancel();
|
||||
_volume->toggle(true, anim::type::normal);
|
||||
} else if (!state->dropdownOver) {
|
||||
|
@ -565,6 +593,123 @@ void Header::createVolumeToggle() {
|
|||
}
|
||||
}
|
||||
|
||||
void Header::toggleTooltip(Tooltip type, bool show) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
_tooltipShown = (_tooltip != nullptr);
|
||||
});
|
||||
if (const auto was = _tooltip.release()) {
|
||||
was->toggleAnimated(false);
|
||||
}
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
const auto text = [&]() -> TextWithEntities {
|
||||
using Privacy = Data::StoryPrivacy;
|
||||
const auto boldName = Ui::Text::Bold(_data->user->shortName());
|
||||
const auto self = _data->user->isSelf();
|
||||
switch (type) {
|
||||
case Tooltip::SilentVideo:
|
||||
return { tr::lng_stories_about_silent(tr::now) };
|
||||
case Tooltip::Privacy: switch (_data->privacy) {
|
||||
case Privacy::CloseFriends:
|
||||
return self
|
||||
? tr::lng_stories_about_close_friends_my(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_stories_about_close_friends(
|
||||
tr::now,
|
||||
lt_user,
|
||||
boldName,
|
||||
Ui::Text::RichLangValue);
|
||||
case Privacy::Contacts:
|
||||
return self
|
||||
? tr::lng_stories_about_contacts_my(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_stories_about_contacts(
|
||||
tr::now,
|
||||
lt_user,
|
||||
boldName,
|
||||
Ui::Text::RichLangValue);
|
||||
case Privacy::SelectedContacts:
|
||||
return self
|
||||
? tr::lng_stories_about_selected_contacts_my(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_stories_about_selected_contacts(
|
||||
tr::now,
|
||||
lt_user,
|
||||
boldName,
|
||||
Ui::Text::RichLangValue);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}();
|
||||
if (text.empty()) {
|
||||
return;
|
||||
}
|
||||
_tooltipType = type;
|
||||
_tooltip = std::make_unique<Ui::ImportantTooltip>(
|
||||
_widget->parentWidget(),
|
||||
Ui::MakeNiceTooltipLabel(
|
||||
_widget.get(),
|
||||
rpl::single(text),
|
||||
st::storiesInfoTooltipMaxWidth,
|
||||
st::storiesInfoTooltipLabel),
|
||||
st::storiesInfoTooltip);
|
||||
const auto tooltip = _tooltip.get();
|
||||
const auto weak = QPointer<QWidget>(tooltip);
|
||||
const auto destroy = [=] {
|
||||
delete weak.data();
|
||||
};
|
||||
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
tooltip->setHiddenCallback(destroy);
|
||||
updateTooltipGeometry();
|
||||
tooltip->toggleAnimated(true);
|
||||
}
|
||||
|
||||
void Header::updateTooltipGeometry() {
|
||||
if (!_tooltip) {
|
||||
return;
|
||||
}
|
||||
const auto geometry = [&] {
|
||||
switch (_tooltipType) {
|
||||
case Tooltip::SilentVideo:
|
||||
return Ui::MapFrom(
|
||||
_widget->parentWidget(),
|
||||
_volumeToggle.get(),
|
||||
_volumeToggle->rect());
|
||||
case Tooltip::Privacy:
|
||||
return Ui::MapFrom(
|
||||
_widget->parentWidget(),
|
||||
_info.get(),
|
||||
_privacyBadgeGeometry.marginsAdded(
|
||||
st::storiesInfoTooltip.padding));
|
||||
}
|
||||
return QRect();
|
||||
}();
|
||||
if (geometry.isEmpty()) {
|
||||
toggleTooltip(Tooltip::None, false);
|
||||
return;
|
||||
}
|
||||
const auto weak = QPointer<QWidget>(_tooltip.get());
|
||||
const auto countPosition = [=](QSize size) {
|
||||
const auto result = geometry.bottomLeft()
|
||||
- QPoint(size.width() / 2, 0);
|
||||
const auto inner = _contentGeometry.marginsRemoved(
|
||||
st::storiesInfoTooltip.padding);
|
||||
if (size.width() > inner.width()) {
|
||||
return QPoint(
|
||||
inner.x() + (inner.width() - size.width()) / 2,
|
||||
result.y());
|
||||
} else if (result.x() < inner.x()) {
|
||||
return QPoint(inner.x(), result.y());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
_tooltip->pointAt(geometry, RectPart::Bottom, countPosition);
|
||||
}
|
||||
|
||||
void Header::rebuildVolumeControls(
|
||||
not_null<Ui::RpWidget*> dropdown,
|
||||
bool horizontal) {
|
||||
|
@ -682,11 +827,14 @@ void Header::raise() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool Header::ignoreWindowMove(QPoint position) const {
|
||||
return _ignoreWindowMove;
|
||||
}
|
||||
|
||||
rpl::producer<bool> Header::tooltipShownValue() const {
|
||||
return _tooltipShown.value();
|
||||
}
|
||||
|
||||
void Header::updateDateText() {
|
||||
if (!_date || !_data || !_data->date) {
|
||||
return;
|
||||
|
|
|
@ -20,6 +20,7 @@ class FlatLabel;
|
|||
class IconButton;
|
||||
class AbstractButton;
|
||||
class UserpicButton;
|
||||
class ImportantTooltip;
|
||||
template <typename Widget>
|
||||
class FadeWrap;
|
||||
} // namespace Ui
|
||||
|
@ -55,8 +56,15 @@ public:
|
|||
void raise();
|
||||
|
||||
[[nodiscard]] bool ignoreWindowMove(QPoint position) const;
|
||||
[[nodiscard]] rpl::producer<bool> tooltipShownValue() const;
|
||||
|
||||
private:
|
||||
enum class Tooltip {
|
||||
None,
|
||||
SilentVideo,
|
||||
Privacy,
|
||||
};
|
||||
|
||||
void updateDateText();
|
||||
void applyPauseState();
|
||||
void createPlayPause();
|
||||
|
@ -64,6 +72,8 @@ private:
|
|||
void rebuildVolumeControls(
|
||||
not_null<Ui::RpWidget*> dropdown,
|
||||
bool horizontal);
|
||||
void toggleTooltip(Tooltip type, bool show);
|
||||
void updateTooltipGeometry();
|
||||
|
||||
const not_null<Controller*> _controller;
|
||||
|
||||
|
@ -81,9 +91,15 @@ private:
|
|||
std::unique_ptr<Ui::FadeWrap<Ui::RpWidget>> _volume;
|
||||
rpl::variable<const style::icon*> _volumeIcon;
|
||||
std::unique_ptr<Ui::RpWidget> _privacy;
|
||||
QRect _privacyBadgeGeometry;
|
||||
std::optional<HeaderData> _data;
|
||||
std::unique_ptr<Ui::ImportantTooltip> _tooltip = { nullptr };
|
||||
rpl::variable<bool> _tooltipShown = false;
|
||||
QRect _contentGeometry;
|
||||
Tooltip _tooltipType = {};
|
||||
base::Timer _dateUpdateTimer;
|
||||
bool _ignoreWindowMove = false;
|
||||
bool _privacyBadgeOver = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -891,3 +891,18 @@ storiesVolumeSlider: MediaSlider {
|
|||
seekSize: size(12px, 12px);
|
||||
duration: mediaviewOverDuration;
|
||||
}
|
||||
storiesInfoTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: font(11px);
|
||||
linkFont: font(11px);
|
||||
linkFontOver: font(11px underline);
|
||||
}
|
||||
minWidth: 36px;
|
||||
}
|
||||
storiesInfoTooltip: ImportantTooltip(defaultImportantTooltip) {
|
||||
bg: importantTooltipBg;
|
||||
padding: margins(10px, 3px, 10px, 5px);
|
||||
radius: 4px;
|
||||
arrow: 4px;
|
||||
}
|
||||
storiesInfoTooltipMaxWidth: 360px;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ad852f0f4ab271de4db799d01fa8b7032eb33b11
|
||||
Subproject commit bd1e8f7c47c3e99493adf9653d684c86a0a51941
|
Loading…
Add table
Reference in a new issue