[poll-create] Added document file preview overlay.

This commit is contained in:
23rd
2026-03-19 20:39:59 +03:00
parent 7e7af4118d
commit 66c450fc0d
5 changed files with 232 additions and 13 deletions
@@ -2251,6 +2251,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
},
remove);
return;
} else if (document) {
HistoryView::ShowPollDocumentPreview(
_controller,
document,
[=] { chooseDocument(media); },
remove);
return;
}
state->mediaMenu = base::make_unique_q<Ui::PopupMenu>(
button,
@@ -35,6 +35,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView {
namespace {
void SetupOverlayHideOnEscape(
not_null<Ui::AbstractButton*> clickable,
Fn<void()> hideAll) {
clickable->setClickedCallback(hideAll);
base::install_event_filter(QCoreApplication::instance(), [=](
not_null<QEvent*> e) {
if (e->type() == QEvent::KeyPress
&& clickable->window()->isActiveWindow()) {
const auto k = static_cast<QKeyEvent*>(e.get());
if (k->key() == Qt::Key_Escape) {
hideAll();
return base::EventFilterResult::Cancel;
}
}
return base::EventFilterResult::Continue;
}, clickable->lifetime());
}
struct PreviewOverlayState {
base::unique_qptr<Window::MediaPreviewWidget> mediaPreview;
base::unique_qptr<Ui::AbstractButton> clickable;
@@ -80,19 +98,7 @@ template <typename MediaData>
st::defaultToggle.duration,
[=] { state->clear(); });
};
state->clickable->setClickedCallback(hideAll);
base::install_event_filter(QCoreApplication::instance(), [=](
not_null<QEvent*> e) {
if (e->type() == QEvent::KeyPress
&& state->clickable->window()->isActiveWindow()) {
const auto k = static_cast<QKeyEvent*>(e.get());
if (k->key() == Qt::Key_Escape) {
hideAll();
return base::EventFilterResult::Cancel;
}
}
return base::EventFilterResult::Continue;
}, state->clickable->lifetime());
SetupOverlayHideOnEscape(state->clickable.get(), hideAll);
state->mediaPreview->showPreview(origin, media);
state->clickable->show();
const auto clickableRaw = state->clickable.get();
@@ -284,4 +290,74 @@ bool ShowReactionPreview(
return true;
}
void ShowWidgetPreview(
not_null<Window::SessionController*> controller,
Fn<void(not_null<Ui::RpWidget*>)> setupContent,
Fn<void(not_null<Ui::DropdownMenu*>)> fillMenu) {
struct State {
base::unique_qptr<Ui::RpWidget> preview;
base::unique_qptr<Ui::AbstractButton> clickable;
base::unique_qptr<Ui::DropdownMenu> menu;
};
const auto state = std::make_shared<State>();
const auto mainwidget = controller->widget()->bodyWidget();
state->preview = base::make_unique_q<Ui::RpWidget>(mainwidget);
const auto previewRaw = state->preview.get();
setupContent(previewRaw);
previewRaw->setAttribute(Qt::WA_TransparentForMouseEvents);
state->clickable = base::make_unique_q<Ui::AbstractButton>(mainwidget);
state->clickable->paintOn([=](QPainter &p) {
p.fillRect(state->clickable->rect(), st::stickerPreviewBg);
});
const auto hideAll = [=] {
state->clickable->setAttribute(Qt::WA_TransparentForMouseEvents);
if (state->menu) {
state->menu->hideAnimated();
}
base::call_delayed(
st::defaultToggle.duration,
[s = state] {
s->preview.reset();
s->menu.reset();
s->clickable.reset();
});
};
SetupOverlayHideOnEscape(state->clickable.get(), hideAll);
state->menu = base::make_unique_q<Ui::DropdownMenu>(
mainwidget,
st::dropdownMenuWithIcons);
state->menu->setAutoHiding(false);
state->menu->setHiddenCallback(
crl::guard(state->clickable.get(), hideAll));
fillMenu(state->menu.get());
const auto menuRaw = state->menu.get();
state->clickable->show();
previewRaw->show();
const auto fullW = previewRaw->width();
const auto fullH = previewRaw->height();
mainwidget->sizeValue() | rpl::on_next([=](QSize size) {
state->clickable->setGeometry(Rect(size));
state->clickable->raise();
const auto gap = st::defaultMenu.itemPadding.top();
const auto totalH = fullH + gap + menuRaw->height();
const auto previewY = (size.height() - totalH) / 2;
previewRaw->move((size.width() - fullW) / 2, previewY);
previewRaw->raise();
menuRaw->move(
(size.width() - menuRaw->width()) / 2,
previewY + fullH + gap);
menuRaw->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
menuRaw->raise();
}, previewRaw->lifetime());
}
} // namespace HistoryView
@@ -12,6 +12,7 @@ class PhotoData;
namespace Ui {
class DropdownMenu;
class RpWidget;
} // namespace Ui
namespace Data {
@@ -36,6 +37,11 @@ bool ShowPhotoPreview(
not_null<PhotoData*> photo,
Fn<void(not_null<Ui::DropdownMenu*>)> fillMenu = nullptr);
void ShowWidgetPreview(
not_null<Window::SessionController*> controller,
Fn<void(not_null<Ui::RpWidget*>)> setupContent,
Fn<void(not_null<Ui::DropdownMenu*>)> fillMenu);
bool ShowReactionPreview(
not_null<Window::SessionController*> controller,
FullMsgId origin,
@@ -23,17 +23,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_group_call_bar.h"
#include "history/view/history_view_reaction_preview.h"
#include "lang/lang_keys.h"
#include "layout/layout_document_generic_preview.h"
#include "main/main_session.h"
#include "mainwidget.h"
#include "storage/localimageloader.h"
#include "storage/storage_media_prepare.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/format_values.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/shadow.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include "styles/style_media_view.h"
#include "styles/style_menu_icons.h"
#include "styles/style_widgets.h"
@@ -287,6 +293,124 @@ void ShowPollPhotoPreview(
});
}
void ShowPollDocumentPreview(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document,
Fn<void()> replace,
Fn<void()> remove) {
const auto docGeneric = Layout::DocumentGenericPreview::Create(
document);
const auto docIcon = docGeneric.icon();
const auto docIconColor = docGeneric.color;
const auto docExt = [&] {
auto ext = docGeneric.ext;
const auto maxW = st::mediaviewFileIconSize
- st::mediaviewFileExtPadding * 2;
if (st::mediaviewFileExtFont->width(ext) > maxW) {
ext = st::mediaviewFileExtFont->elided(ext, maxW, Qt::ElideMiddle);
}
return ext;
}();
const auto maxTextW = st::mediaviewFileSize.width()
- st::mediaviewFileIconSize
- st::mediaviewFilePadding * 3;
const auto docName = [&] {
auto name = document->filename().isEmpty()
? tr::lng_mediaview_doc_image(tr::now)
: document->filename();
if (st::mediaviewFileNameFont->width(name) > maxTextW) {
name = st::mediaviewFileNameFont->elided(name, maxTextW, Qt::ElideMiddle);
}
return name;
}();
const auto docSize = [&] {
auto text = Ui::FormatSizeText(document->size);
if (st::mediaviewFont->width(text) > maxTextW) {
text = st::mediaviewFont->elided(text, maxTextW);
}
return text;
}();
ShowWidgetPreview(controller, [=](not_null<Ui::RpWidget*> preview) {
const auto shadowExtend = st::boxRoundShadow.extend;
const auto fullW = st::mediaviewFileSize.width()
+ rect::m::sum::h(shadowExtend);
const auto fullH = st::mediaviewFileSize.height()
+ rect::m::sum::v(shadowExtend);
preview->resize(fullW, fullH);
preview->paintRequest() | rpl::on_next([=] {
auto p = Painter(preview);
const auto outer = preview->rect() - shadowExtend;
Ui::Shadow::paint(
p,
outer,
preview->width(),
st::boxRoundShadow);
{
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::windowBg);
p.drawRoundedRect(outer, st::boxRadius, st::boxRadius);
}
const auto padding = st::mediaviewFilePadding;
const auto iconSize = st::mediaviewFileIconSize;
const auto iconRect = QRect(
outer.x() + padding,
outer.y() + padding,
iconSize,
iconSize);
p.fillRect(iconRect, docIconColor);
if (docIcon) {
docIcon->paint(
p,
iconRect.x() + (iconRect.width() - docIcon->width()),
iconRect.y(),
preview->width());
}
if (!docExt.isEmpty()) {
p.setPen(st::activeButtonFg);
p.setFont(st::mediaviewFileExtFont);
const auto extW = st::mediaviewFileExtFont->width(docExt);
p.drawText(
iconRect.x() + (iconRect.width() - extW) / 2,
iconRect.y()
+ st::mediaviewFileExtTop
+ st::mediaviewFileExtFont->ascent,
docExt);
}
const auto textX = outer.x() + 2 * padding + iconSize;
p.setPen(st::windowFg);
p.setFont(st::mediaviewFileNameFont);
p.drawText(
textX,
outer.y() + padding
+ st::mediaviewFileNameTop
+ st::mediaviewFileNameFont->ascent,
docName);
p.setPen(st::windowSubTextFg);
p.setFont(st::mediaviewFont);
p.drawText(
textX,
outer.y() + padding
+ st::mediaviewFileSizeTop
+ st::mediaviewFont->ascent,
docSize);
}, preview->lifetime());
}, [=](not_null<Ui::DropdownMenu*> menu) {
menu->addAction(
tr::lng_attach_replace(tr::now),
replace,
&st::menuIconReplace);
AddRemoveAction(menu, remove);
});
}
void EditPollPhoto(
not_null<Window::SessionController*> controller,
not_null<PhotoData*> photo,
@@ -43,6 +43,12 @@ void ShowPollPhotoPreview(
Fn<void()> edit,
Fn<void()> remove);
void ShowPollDocumentPreview(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document,
Fn<void()> replace,
Fn<void()> remove);
void EditPollPhoto(
not_null<Window::SessionController*> controller,
not_null<PhotoData*> photo,