mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Replaced overview and inline mosaic layouts with new mosaic layout.
This commit is contained in:
parent
bb404a38d3
commit
793f748d2e
10 changed files with 85 additions and 871 deletions
|
@ -676,8 +676,6 @@ PRIVATE
|
||||||
inline_bots/inline_bot_send_data.h
|
inline_bots/inline_bot_send_data.h
|
||||||
inline_bots/inline_results_inner.cpp
|
inline_bots/inline_results_inner.cpp
|
||||||
inline_bots/inline_results_inner.h
|
inline_bots/inline_results_inner.h
|
||||||
inline_bots/inline_results_mosaic_layout.cpp
|
|
||||||
inline_bots/inline_results_mosaic_layout.h
|
|
||||||
inline_bots/inline_results_widget.cpp
|
inline_bots/inline_results_widget.cpp
|
||||||
inline_bots/inline_results_widget.h
|
inline_bots/inline_results_widget.h
|
||||||
intro/intro_code.cpp
|
intro/intro_code.cpp
|
||||||
|
@ -817,8 +815,6 @@ PRIVATE
|
||||||
overview/overview_layout.cpp
|
overview/overview_layout.cpp
|
||||||
overview/overview_layout.h
|
overview/overview_layout.h
|
||||||
overview/overview_layout_delegate.h
|
overview/overview_layout_delegate.h
|
||||||
overview/overview_mosaic_layout.cpp
|
|
||||||
overview/overview_mosaic_layout.h
|
|
||||||
passport/passport_encryption.cpp
|
passport/passport_encryption.cpp
|
||||||
passport/passport_encryption.h
|
passport/passport_encryption.h
|
||||||
passport/passport_form_controller.cpp
|
passport/passport_form_controller.cpp
|
||||||
|
|
|
@ -202,6 +202,11 @@ GifsListWidget::GifsListWidget(
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_mosaic.setFullWidth(s.width());
|
_mosaic.setFullWidth(s.width());
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_mosaic.setOffset(
|
||||||
|
st::inlineResultsLeft - st::roundRadiusSmall,
|
||||||
|
st::stickerPanPadding);
|
||||||
|
_mosaic.setRightSkip(st::inlineResultsSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<TabbedSelector::FileChosen> GifsListWidget::fileChosen() const {
|
rpl::producer<TabbedSelector::FileChosen> GifsListWidget::fileChosen() const {
|
||||||
|
@ -337,12 +342,15 @@ void GifsListWidget::paintInlineItems(Painter &p, QRect clip) {
|
||||||
using namespace InlineBots::Layout;
|
using namespace InlineBots::Layout;
|
||||||
PaintContext context(crl::now(), false, gifPaused, false);
|
PaintContext context(crl::now(), false, gifPaused, false);
|
||||||
|
|
||||||
_mosaic.paint(
|
auto paintItem = [&](const not_null<ItemBase*> item, QPoint point) {
|
||||||
p,
|
p.translate(point.x(), point.y());
|
||||||
st::stickerPanPadding,
|
item->paint(
|
||||||
st::inlineResultsLeft - st::roundRadiusSmall,
|
p,
|
||||||
clip,
|
clip.translated(-point),
|
||||||
context);
|
&context);
|
||||||
|
p.translate(-point.x(), -point.y());
|
||||||
|
};
|
||||||
|
_mosaic.paint(std::move(paintItem), clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GifsListWidget::mousePressEvent(QMouseEvent *e) {
|
void GifsListWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
|
@ -520,7 +528,7 @@ void GifsListWidget::refreshSavedGifs() {
|
||||||
return layoutPrepareSavedGif(gif);
|
return layoutPrepareSavedGif(gif);
|
||||||
}) | ranges::views::filter([](const LayoutItem *item) {
|
}) | ranges::views::filter([](const LayoutItem *item) {
|
||||||
return item != nullptr;
|
return item != nullptr;
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to<std::vector<not_null<LayoutItem*>>>;
|
||||||
|
|
||||||
_mosaic.addItems(layouts);
|
_mosaic.addItems(layouts);
|
||||||
}
|
}
|
||||||
|
@ -610,7 +618,9 @@ void GifsListWidget::deleteUnusedInlineLayouts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GifsListWidget::preloadImages() {
|
void GifsListWidget::preloadImages() {
|
||||||
_mosaic.preloadImages();
|
_mosaic.forEach([](const not_null<LayoutItem*> item) {
|
||||||
|
item->preload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GifsListWidget::switchToSavedGifs() {
|
void GifsListWidget::switchToSavedGifs() {
|
||||||
|
@ -645,10 +655,11 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
|
||||||
return layoutPrepareInlineResult(r.get());
|
return layoutPrepareInlineResult(r.get());
|
||||||
}) | ranges::views::filter([](const LayoutItem *item) {
|
}) | ranges::views::filter([](const LayoutItem *item) {
|
||||||
return item != nullptr;
|
return item != nullptr;
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to<std::vector<not_null<LayoutItem*>>>;
|
||||||
|
|
||||||
_mosaic.addItems(resultLayouts);
|
_mosaic.addItems(resultLayouts);
|
||||||
added = resultLayouts.size();
|
added = resultLayouts.size();
|
||||||
|
preloadImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeToWidth(width());
|
resizeToWidth(width());
|
||||||
|
@ -661,7 +672,11 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
|
||||||
}
|
}
|
||||||
|
|
||||||
int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
|
int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
|
||||||
const auto until = _mosaic.validateExistingRows(results);
|
const auto until = _mosaic.validateExistingRows([&](
|
||||||
|
const not_null<LayoutItem*> item,
|
||||||
|
int untilIndex) {
|
||||||
|
return item->getResult() != results[untilIndex].get();
|
||||||
|
}, results.size());
|
||||||
|
|
||||||
if (_mosaic.empty()) {
|
if (_mosaic.empty()) {
|
||||||
_inlineWithThumb = false;
|
_inlineWithThumb = false;
|
||||||
|
@ -853,10 +868,12 @@ void GifsListWidget::updateSelected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto p = mapFromGlobal(_lastMousePos);
|
const auto p = mapFromGlobal(_lastMousePos);
|
||||||
const auto sx = (rtl() ? width() - p.x() : p.x())
|
const auto sx = rtl() ? (width() - p.x()) : p.x();
|
||||||
- (st::inlineResultsLeft - st::roundRadiusSmall);
|
const auto sy = p.y();
|
||||||
const auto sy = p.y() - st::stickerPanPadding;
|
const auto &[index, exact, relative] = _mosaic.findByPoint({ sx, sy });
|
||||||
const auto &[link, item, selected] = _mosaic.findByPoint({ sx, sy });
|
const auto selected = exact ? index : -1;
|
||||||
|
const auto item = exact ? _mosaic.itemAt(selected) : nullptr;
|
||||||
|
const auto link = exact ? item->getState(relative, {}).link : nullptr;
|
||||||
|
|
||||||
if (_selected != selected) {
|
if (_selected != selected) {
|
||||||
if (const auto s = _mosaic.maybeItemAt(_selected)) {
|
if (const auto s = _mosaic.maybeItemAt(_selected)) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "inline_bots/inline_bot_layout_item.h"
|
#include "inline_bots/inline_bot_layout_item.h"
|
||||||
#include "inline_bots/inline_results_mosaic_layout.h"
|
#include "layout/layout_mosaic.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
@ -167,7 +167,7 @@ private:
|
||||||
|
|
||||||
Footer *_footer = nullptr;
|
Footer *_footer = nullptr;
|
||||||
|
|
||||||
InlineBots::Layout::MosaicLayout _mosaic;
|
Mosaic::Layout::MosaicLayout<LayoutItem> _mosaic;
|
||||||
|
|
||||||
int _selected = -1;
|
int _selected = -1;
|
||||||
int _pressed = -1;
|
int _pressed = -1;
|
||||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
#include "overview/overview_layout.h"
|
#include "overview/overview_layout.h"
|
||||||
#include "overview/overview_mosaic_layout.h"
|
#include "layout/layout_mosaic.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -42,8 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
|
||||||
namespace Layout = Overview::Layout;
|
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace Media {
|
namespace Media {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -88,7 +86,7 @@ bool HasFloatingHeader(Type type) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct ListWidget::Context {
|
struct ListWidget::Context {
|
||||||
Layout::PaintContext layoutContext;
|
Overview::Layout::PaintContext layoutContext;
|
||||||
not_null<SelectedMap*> selected;
|
not_null<SelectedMap*> selected;
|
||||||
not_null<SelectedMap*> dragSelected;
|
not_null<SelectedMap*> dragSelected;
|
||||||
DragSelectAction dragSelectAction;
|
DragSelectAction dragSelectAction;
|
||||||
|
@ -98,7 +96,8 @@ class ListWidget::Section {
|
||||||
public:
|
public:
|
||||||
Section(Type type)
|
Section(Type type)
|
||||||
: _type(type)
|
: _type(type)
|
||||||
, _hasFloatingHeader(HasFloatingHeader(type)) {
|
, _hasFloatingHeader(HasFloatingHeader(type))
|
||||||
|
, _mosaic(st::emojiPanWidth - st::inlineResultsLeft) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addItem(not_null<BaseLayout*> item);
|
bool addItem(not_null<BaseLayout*> item);
|
||||||
|
@ -186,7 +185,7 @@ private:
|
||||||
int _top = 0;
|
int _top = 0;
|
||||||
int _height = 0;
|
int _height = 0;
|
||||||
|
|
||||||
Overview::Layout::MosaicLayout _mosaic;
|
Mosaic::Layout::MosaicLayout<BaseLayout> _mosaic;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,9 +332,10 @@ auto ListWidget::Section::findItemByPoint(
|
||||||
Expects(!_items.empty());
|
Expects(!_items.empty());
|
||||||
if (!_mosaic.empty()) {
|
if (!_mosaic.empty()) {
|
||||||
const auto found = _mosaic.findByPoint(point);
|
const auto found = _mosaic.findByPoint(point);
|
||||||
Assert(found.item != nullptr);
|
Assert(found.index != -1);
|
||||||
const auto rect = findItemRect(found.item);
|
const auto item = _mosaic.itemAt(found.index);
|
||||||
return { found.item, rect, found.exact };
|
const auto rect = findItemRect(item);
|
||||||
|
return { item, rect, found.exact };
|
||||||
}
|
}
|
||||||
auto itemIt = findItemAfterTop(point.y());
|
auto itemIt = findItemAfterTop(point.y());
|
||||||
if (itemIt == _items.end()) {
|
if (itemIt == _items.end()) {
|
||||||
|
@ -1030,7 +1030,7 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
||||||
};
|
};
|
||||||
|
|
||||||
auto &songSt = st::overviewFileLayout;
|
auto &songSt = st::overviewFileLayout;
|
||||||
using namespace Layout;
|
using namespace Overview::Layout;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Photo:
|
case Type::Photo:
|
||||||
if (const auto photo = getPhoto()) {
|
if (const auto photo = getPhoto()) {
|
||||||
|
@ -1386,7 +1386,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
fromSectionIt,
|
fromSectionIt,
|
||||||
clip.y() + clip.height());
|
clip.y() + clip.height());
|
||||||
auto context = Context {
|
auto context = Context {
|
||||||
Layout::PaintContext(ms, hasSelectedItems()),
|
Overview::Layout::PaintContext(ms, hasSelectedItems()),
|
||||||
&_selected,
|
&_selected,
|
||||||
&_dragSelected,
|
&_dragSelected,
|
||||||
_dragSelectAction
|
_dragSelectAction
|
||||||
|
|
|
@ -79,6 +79,8 @@ Inner::Inner(
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_mosaic.setFullWidth(s.width());
|
_mosaic.setFullWidth(s.width());
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_mosaic.setRightSkip(st::inlineResultsSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inner::visibleTopBottomUpdated(
|
void Inner::visibleTopBottomUpdated(
|
||||||
|
@ -189,16 +191,15 @@ void Inner::paintInlineItems(Painter &p, const QRect &r) {
|
||||||
context.pathGradient = _pathGradient.get();
|
context.pathGradient = _pathGradient.get();
|
||||||
context.pathGradient->startFrame(0, width(), width() / 2);
|
context.pathGradient->startFrame(0, width(), width() / 2);
|
||||||
|
|
||||||
const auto top = st::stickerPanPadding
|
auto paintItem = [&](const not_null<ItemBase*> item, QPoint point) {
|
||||||
+ (_switchPmButton
|
p.translate(point.x(), point.y());
|
||||||
? _switchPmButton->height() + st::inlineResultsSkip
|
item->paint(
|
||||||
: 0);
|
p,
|
||||||
_mosaic.paint(
|
r.translated(-point),
|
||||||
p,
|
&context);
|
||||||
top,
|
p.translate(-point.x(), -point.y());
|
||||||
st::inlineResultsLeft - st::roundRadiusSmall,
|
};
|
||||||
r,
|
_mosaic.paint(std::move(paintItem), r);
|
||||||
context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inner::mousePressEvent(QMouseEvent *e) {
|
void Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
|
@ -377,7 +378,9 @@ void Inner::deleteUnusedInlineLayouts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inner::preloadImages() {
|
void Inner::preloadImages() {
|
||||||
_mosaic.preloadImages();
|
_mosaic.forEach([](const not_null<ItemBase*> item) {
|
||||||
|
item->preload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inner::hideInlineRowsPanel() {
|
void Inner::hideInlineRowsPanel() {
|
||||||
|
@ -388,6 +391,16 @@ void Inner::clearInlineRowsPanel() {
|
||||||
clearInlineRows(false);
|
clearInlineRows(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inner::refreshMosaicOffset() {
|
||||||
|
const auto top = st::stickerPanPadding
|
||||||
|
+ (_switchPmButton
|
||||||
|
? _switchPmButton->height() + st::inlineResultsSkip
|
||||||
|
: 0);
|
||||||
|
_mosaic.setOffset(
|
||||||
|
st::inlineResultsLeft - st::roundRadiusSmall,
|
||||||
|
top);
|
||||||
|
}
|
||||||
|
|
||||||
void Inner::refreshSwitchPmButton(const CacheEntry *entry) {
|
void Inner::refreshSwitchPmButton(const CacheEntry *entry) {
|
||||||
if (!entry || entry->switchPmText.isEmpty()) {
|
if (!entry || entry->switchPmText.isEmpty()) {
|
||||||
_switchPmButton.destroy();
|
_switchPmButton.destroy();
|
||||||
|
@ -414,6 +427,7 @@ int Inner::refreshInlineRows(PeerData *queryPeer, UserData *bot, const CacheEntr
|
||||||
_inlineBot = bot;
|
_inlineBot = bot;
|
||||||
_inlineQueryPeer = queryPeer;
|
_inlineQueryPeer = queryPeer;
|
||||||
refreshSwitchPmButton(entry);
|
refreshSwitchPmButton(entry);
|
||||||
|
refreshMosaicOffset();
|
||||||
auto clearResults = [&] {
|
auto clearResults = [&] {
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -449,10 +463,11 @@ int Inner::refreshInlineRows(PeerData *queryPeer, UserData *bot, const CacheEntr
|
||||||
return layoutPrepareInlineResult(r.get());
|
return layoutPrepareInlineResult(r.get());
|
||||||
}) | ranges::views::filter([](const ItemBase *item) {
|
}) | ranges::views::filter([](const ItemBase *item) {
|
||||||
return item != nullptr;
|
return item != nullptr;
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to<std::vector<not_null<ItemBase*>>>;
|
||||||
|
|
||||||
_mosaic.addItems(resultItems);
|
_mosaic.addItems(resultItems);
|
||||||
added = resultItems.size();
|
added = resultItems.size();
|
||||||
|
preloadImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto h = countHeight();
|
auto h = countHeight();
|
||||||
|
@ -466,7 +481,11 @@ int Inner::refreshInlineRows(PeerData *queryPeer, UserData *bot, const CacheEntr
|
||||||
}
|
}
|
||||||
|
|
||||||
int Inner::validateExistingInlineRows(const Results &results) {
|
int Inner::validateExistingInlineRows(const Results &results) {
|
||||||
const auto until = _mosaic.validateExistingRows(results);
|
const auto until = _mosaic.validateExistingRows([&](
|
||||||
|
const not_null<ItemBase*> item,
|
||||||
|
int untilIndex) {
|
||||||
|
return item->getResult() != results[untilIndex].get();
|
||||||
|
}, results.size());
|
||||||
|
|
||||||
if (_mosaic.empty()) {
|
if (_mosaic.empty()) {
|
||||||
_inlineWithThumb = false;
|
_inlineWithThumb = false;
|
||||||
|
@ -528,15 +547,12 @@ void Inner::updateSelected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto p = mapFromGlobal(_lastMousePos);
|
const auto p = mapFromGlobal(_lastMousePos);
|
||||||
|
const auto sx = rtl() ? (width() - p.x()) : p.x();
|
||||||
const auto sx = (rtl() ? width() - p.x() : p.x())
|
const auto sy = p.y();
|
||||||
- (st::inlineResultsLeft - st::roundRadiusSmall);
|
const auto &[index, exact, relative] = _mosaic.findByPoint({ sx, sy });
|
||||||
const auto sy = p.y()
|
const auto selected = exact ? index : -1;
|
||||||
- st::stickerPanPadding
|
const auto item = exact ? _mosaic.itemAt(selected) : nullptr;
|
||||||
- (_switchPmButton
|
const auto link = exact ? item->getState(relative, {}).link : nullptr;
|
||||||
? _switchPmButton->height() + st::inlineResultsSkip
|
|
||||||
: 0);
|
|
||||||
const auto &[link, item, selected] = _mosaic.findByPoint({ sx, sy });
|
|
||||||
|
|
||||||
if (_selected != selected) {
|
if (_selected != selected) {
|
||||||
if (const auto s = _mosaic.maybeItemAt(_selected)) {
|
if (const auto s = _mosaic.maybeItemAt(_selected)) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
#include "inline_bots/inline_bot_layout_item.h"
|
#include "inline_bots/inline_bot_layout_item.h"
|
||||||
#include "inline_bots/inline_results_mosaic_layout.h"
|
#include "layout/layout_mosaic.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
struct SendOptions;
|
struct SendOptions;
|
||||||
|
@ -131,6 +131,7 @@ private:
|
||||||
void paintInlineItems(Painter &p, const QRect &r);
|
void paintInlineItems(Painter &p, const QRect &r);
|
||||||
|
|
||||||
void refreshSwitchPmButton(const CacheEntry *entry);
|
void refreshSwitchPmButton(const CacheEntry *entry);
|
||||||
|
void refreshMosaicOffset();
|
||||||
|
|
||||||
void showPreview();
|
void showPreview();
|
||||||
void updateInlineItems();
|
void updateInlineItems();
|
||||||
|
@ -165,7 +166,7 @@ private:
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
|
||||||
InlineBots::Layout::MosaicLayout _mosaic;
|
Mosaic::Layout::MosaicLayout<InlineBots::Layout::ItemBase> _mosaic;
|
||||||
|
|
||||||
std::map<Result*, std::unique_ptr<ItemBase>> _inlineLayouts;
|
std::map<Result*, std::unique_ptr<ItemBase>> _inlineLayouts;
|
||||||
|
|
||||||
|
|
|
@ -1,341 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#include "inline_bots/inline_results_mosaic_layout.h"
|
|
||||||
|
|
||||||
#include "history/view/history_view_cursor_state.h"
|
|
||||||
#include "layout/layout_position.h"
|
|
||||||
#include "styles/style_chat_helpers.h"
|
|
||||||
|
|
||||||
namespace InlineBots::Layout {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr auto kInlineItemsMaxPerRow = 5;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
MosaicLayout::MosaicLayout(int bigWidth)
|
|
||||||
: _bigWidth(bigWidth) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::setFullWidth(int w) {
|
|
||||||
_width = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosaicLayout::empty() const {
|
|
||||||
return _rows.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::rowsCount() const {
|
|
||||||
return _rows.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::countDesiredHeight(int newWidth) {
|
|
||||||
auto result = 0;
|
|
||||||
for (auto &row : _rows) {
|
|
||||||
layoutRow(row, newWidth);
|
|
||||||
result += row.height;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<ItemBase*> MosaicLayout::itemAt(int row, int column) const {
|
|
||||||
Expects((row >= 0)
|
|
||||||
&& (row < _rows.size())
|
|
||||||
&& (column >= 0)
|
|
||||||
&& (column < _rows[row].items.size()));
|
|
||||||
return _rows[row].items[column];
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<ItemBase*> MosaicLayout::itemAt(int index) const {
|
|
||||||
const auto &[row, column] = ::Layout::IndexToPosition(index);
|
|
||||||
return itemAt(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemBase *MosaicLayout::maybeItemAt(int row, int column) const {
|
|
||||||
if ((row >= 0)
|
|
||||||
&& (row < _rows.size())
|
|
||||||
&& (column >= 0)
|
|
||||||
&& (column < _rows[row].items.size())) {
|
|
||||||
return _rows[row].items[column];
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemBase *MosaicLayout::maybeItemAt(int index) const {
|
|
||||||
const auto &[row, column] = ::Layout::IndexToPosition(index);
|
|
||||||
return maybeItemAt(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::addItems(const std::vector<ItemBase*> &items) {
|
|
||||||
_rows.reserve(items.size());
|
|
||||||
auto row = Row();
|
|
||||||
row.items.reserve(kInlineItemsMaxPerRow);
|
|
||||||
auto sumWidth = 0;
|
|
||||||
for (const auto &item : items) {
|
|
||||||
addItem(item, row, sumWidth);
|
|
||||||
}
|
|
||||||
rowFinalize(row, sumWidth, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::addItem(
|
|
||||||
not_null<ItemBase*> item,
|
|
||||||
Row &row,
|
|
||||||
int &sumWidth) {
|
|
||||||
item->preload();
|
|
||||||
|
|
||||||
using namespace ::Layout;
|
|
||||||
item->setPosition(PositionToIndex(_rows.size(), row.items.size()));
|
|
||||||
if (rowFinalize(row, sumWidth, item->isFullLine())) {
|
|
||||||
item->setPosition(PositionToIndex(_rows.size(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
sumWidth += item->maxWidth();
|
|
||||||
if (!row.items.empty() && row.items.back()->hasRightSkip()) {
|
|
||||||
sumWidth += st::inlineResultsSkip;
|
|
||||||
}
|
|
||||||
|
|
||||||
row.items.push_back(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosaicLayout::rowFinalize(Row &row, int &sumWidth, bool force) {
|
|
||||||
if (row.items.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto full = (row.items.size() >= kInlineItemsMaxPerRow);
|
|
||||||
// Currently use the same GIFs layout for all widget sizes.
|
|
||||||
const auto big = (sumWidth >= _bigWidth);
|
|
||||||
if (full || big || force) {
|
|
||||||
row.maxWidth = (full || big) ? sumWidth : 0;
|
|
||||||
layoutRow(row, _width);
|
|
||||||
_rows.push_back(std::move(row));
|
|
||||||
row = Row();
|
|
||||||
row.items.reserve(kInlineItemsMaxPerRow);
|
|
||||||
sumWidth = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::layoutRow(Row &row, int fullWidth) {
|
|
||||||
const auto count = int(row.items.size());
|
|
||||||
Assert(count <= kInlineItemsMaxPerRow);
|
|
||||||
|
|
||||||
// Enumerate items in the order of growing maxWidth()
|
|
||||||
// for that sort item indices by maxWidth().
|
|
||||||
int indices[kInlineItemsMaxPerRow];
|
|
||||||
for (auto i = 0; i != count; ++i) {
|
|
||||||
indices[i] = i;
|
|
||||||
}
|
|
||||||
std::sort(indices, indices + count, [&](int a, int b) {
|
|
||||||
return row.items[a]->maxWidth() < row.items[b]->maxWidth();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto desiredWidth = row.maxWidth;
|
|
||||||
row.height = 0;
|
|
||||||
auto availableWidth = fullWidth
|
|
||||||
- (st::inlineResultsLeft - st::roundRadiusSmall);
|
|
||||||
for (auto i = 0; i < count; ++i) {
|
|
||||||
const auto index = indices[i];
|
|
||||||
const auto &item = row.items[index];
|
|
||||||
const auto w = desiredWidth
|
|
||||||
? (item->maxWidth() * availableWidth / desiredWidth)
|
|
||||||
: item->maxWidth();
|
|
||||||
const auto actualWidth = std::max(w, st::inlineResultsMinWidth);
|
|
||||||
row.height = std::max(row.height, item->resizeGetHeight(actualWidth));
|
|
||||||
if (desiredWidth) {
|
|
||||||
availableWidth -= actualWidth;
|
|
||||||
desiredWidth -= row.items[index]->maxWidth();
|
|
||||||
if (index > 0 && row.items[index - 1]->hasRightSkip()) {
|
|
||||||
availableWidth -= st::inlineResultsSkip;
|
|
||||||
desiredWidth -= st::inlineResultsSkip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::paint(
|
|
||||||
Painter &p,
|
|
||||||
int top,
|
|
||||||
int startLeft,
|
|
||||||
const QRect &clip,
|
|
||||||
PaintContext &context) {
|
|
||||||
const auto fromX = rtl() ? (_width - clip.x() - clip.width()) : clip.x();
|
|
||||||
const auto toX = rtl() ? (_width - clip.x()) : (clip.x() + clip.width());
|
|
||||||
const auto rows = _rows.size();
|
|
||||||
for (auto row = 0; row != rows; ++row) {
|
|
||||||
if (top >= clip.top() + clip.height()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto &inlineRow = _rows[row];
|
|
||||||
if ((top + inlineRow.height) > clip.top()) {
|
|
||||||
auto left = startLeft;
|
|
||||||
if (row == (rows - 1)) {
|
|
||||||
context.lastRow = true;
|
|
||||||
}
|
|
||||||
for (const auto &item : inlineRow.items) {
|
|
||||||
if (left >= toX) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto w = item->width();
|
|
||||||
if ((left + w) > fromX) {
|
|
||||||
p.translate(left, top);
|
|
||||||
item->paint(p, clip.translated(-left, -top), &context);
|
|
||||||
p.translate(-left, -top);
|
|
||||||
}
|
|
||||||
left += w;
|
|
||||||
if (item->hasRightSkip()) {
|
|
||||||
left += st::inlineResultsSkip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
top += inlineRow.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::clearRows(bool resultsDeleted) {
|
|
||||||
if (!resultsDeleted) {
|
|
||||||
for (const auto &row : _rows) {
|
|
||||||
for (const auto &item : row.items) {
|
|
||||||
item->setPosition(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_rows.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::preloadImages() {
|
|
||||||
for (const auto &row : _rows) {
|
|
||||||
for (const auto &item : row.items) {
|
|
||||||
item->preload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::validateExistingRows(const Results &results) {
|
|
||||||
const auto count = results.size();
|
|
||||||
auto until = 0;
|
|
||||||
auto untilRow = 0;
|
|
||||||
auto untilCol = 0;
|
|
||||||
while (until < count) {
|
|
||||||
auto &rowItems = _rows[untilRow].items;
|
|
||||||
if ((untilRow >= _rows.size())
|
|
||||||
|| (rowItems[untilCol]->getResult() != results[until].get())) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++until;
|
|
||||||
if (++untilCol == rowItems.size()) {
|
|
||||||
++untilRow;
|
|
||||||
untilCol = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (until == count) { // All items are layed out.
|
|
||||||
if (untilRow == _rows.size()) { // Nothing changed.
|
|
||||||
return until;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const auto rows = _rows.size();
|
|
||||||
auto skip = untilCol;
|
|
||||||
for (auto i = untilRow; i < rows; ++i) {
|
|
||||||
for (const auto &item : _rows[i].items) {
|
|
||||||
if (skip) {
|
|
||||||
--skip;
|
|
||||||
} else {
|
|
||||||
item->setPosition(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!untilCol) { // All good rows are filled.
|
|
||||||
_rows.resize(untilRow);
|
|
||||||
return until;
|
|
||||||
}
|
|
||||||
_rows.resize(untilRow + 1);
|
|
||||||
_rows[untilRow].items.resize(untilCol);
|
|
||||||
_rows[untilRow].maxWidth = ranges::accumulate(
|
|
||||||
_rows[untilRow].items,
|
|
||||||
0,
|
|
||||||
[](int w, auto &row) { return w + row->maxWidth(); });
|
|
||||||
layoutRow(_rows[untilRow], _width);
|
|
||||||
return until;
|
|
||||||
}
|
|
||||||
if (untilRow && !untilCol) { // Remove last row, maybe it is not full.
|
|
||||||
--untilRow;
|
|
||||||
untilCol = _rows[untilRow].items.size();
|
|
||||||
}
|
|
||||||
until -= untilCol;
|
|
||||||
|
|
||||||
for (auto i = untilRow; i < _rows.size(); ++i) {
|
|
||||||
for (const auto &item : _rows[i].items) {
|
|
||||||
item->setPosition(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_rows.resize(untilRow);
|
|
||||||
|
|
||||||
return until;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::columnsCountAt(int row) const {
|
|
||||||
Expects(row >= 0 && row < _rows.size());
|
|
||||||
return _rows[row].items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::rowHeightAt(int row) {
|
|
||||||
Expects(row >= 0 && row < _rows.size());
|
|
||||||
return _rows[row].height;
|
|
||||||
}
|
|
||||||
|
|
||||||
MosaicLayout::FoundItem MosaicLayout::findByPoint(const QPoint &globalPoint) {
|
|
||||||
auto sx = globalPoint.x();
|
|
||||||
auto sy = globalPoint.y();
|
|
||||||
auto row = -1;
|
|
||||||
auto col = -1;
|
|
||||||
auto sel = -1;
|
|
||||||
ClickHandlerPtr link;
|
|
||||||
ItemBase *item = nullptr;
|
|
||||||
if (sy >= 0) {
|
|
||||||
row = 0;
|
|
||||||
for (auto rows = rowsCount(); row < rows; ++row) {
|
|
||||||
const auto rowHeight = _rows[row].height;
|
|
||||||
if (sy < rowHeight) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sy -= rowHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sx >= 0 && row >= 0 && row < rowsCount()) {
|
|
||||||
const auto columnsCount = _rows[row].items.size();
|
|
||||||
col = 0;
|
|
||||||
for (int cols = columnsCount; col < cols; ++col) {
|
|
||||||
const auto item = itemAt(row, col);
|
|
||||||
const auto width = item->width();
|
|
||||||
if (sx < width) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sx -= width;
|
|
||||||
if (item->hasRightSkip()) {
|
|
||||||
sx -= st::inlineResultsSkip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (col < columnsCount) {
|
|
||||||
item = itemAt(row, col);
|
|
||||||
sel = ::Layout::PositionToIndex(row, + col);
|
|
||||||
const auto result = item->getState(QPoint(sx, sy), {});
|
|
||||||
link = result.link;
|
|
||||||
} else {
|
|
||||||
row = col = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = col = -1;
|
|
||||||
}
|
|
||||||
return { link, item, sel };
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace InlineBots::Layout
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "inline_bots/inline_bot_layout_item.h"
|
|
||||||
|
|
||||||
namespace InlineBots {
|
|
||||||
class Result;
|
|
||||||
} // namespace InlineBots
|
|
||||||
|
|
||||||
namespace InlineBots::Layout {
|
|
||||||
|
|
||||||
using Results = std::vector<std::unique_ptr<Result>>;
|
|
||||||
|
|
||||||
class MosaicLayout final {
|
|
||||||
public:
|
|
||||||
struct FoundItem {
|
|
||||||
ClickHandlerPtr link;
|
|
||||||
ItemBase *item = nullptr;
|
|
||||||
int index = -1;
|
|
||||||
};
|
|
||||||
MosaicLayout(int bigWidth);
|
|
||||||
|
|
||||||
[[nodiscard]] int rowHeightAt(int row);
|
|
||||||
[[nodiscard]] int countDesiredHeight(int newWidth);
|
|
||||||
|
|
||||||
[[nodiscard]] FoundItem findByPoint(const QPoint &globalPoint);
|
|
||||||
|
|
||||||
void addItems(const std::vector<ItemBase*> &items);
|
|
||||||
|
|
||||||
void setFullWidth(int w);
|
|
||||||
[[nodiscard]] bool empty() const;
|
|
||||||
|
|
||||||
[[nodiscard]] int rowsCount() const;
|
|
||||||
[[nodiscard]] int columnsCountAt(int row) const;
|
|
||||||
|
|
||||||
[[nodiscard]] not_null<ItemBase*> itemAt(int row, int column) const;
|
|
||||||
[[nodiscard]] not_null<ItemBase*> itemAt(int index) const;
|
|
||||||
[[nodiscard]] ItemBase *maybeItemAt(int row, int column) const;
|
|
||||||
[[nodiscard]] ItemBase *maybeItemAt(int index) const;
|
|
||||||
|
|
||||||
void clearRows(bool resultsDeleted);
|
|
||||||
[[nodiscard]]int validateExistingRows(const Results &results);
|
|
||||||
|
|
||||||
void preloadImages();
|
|
||||||
|
|
||||||
void paint(
|
|
||||||
Painter &p,
|
|
||||||
int top,
|
|
||||||
int startLeft,
|
|
||||||
const QRect &clip,
|
|
||||||
PaintContext &context);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Row {
|
|
||||||
int maxWidth = 0;
|
|
||||||
int height = 0;
|
|
||||||
std::vector<ItemBase*> items;
|
|
||||||
};
|
|
||||||
|
|
||||||
void addItem(not_null<ItemBase*> item, Row &row, int &sumWidth);
|
|
||||||
|
|
||||||
bool rowFinalize(Row &row, int &sumWidth, bool force);
|
|
||||||
void layoutRow(Row &row, int fullWidth);
|
|
||||||
|
|
||||||
const int _bigWidth;
|
|
||||||
int _width = 0;
|
|
||||||
std::vector<Row> _rows;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace InlineBots::Layout
|
|
|
@ -1,327 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#include "overview/overview_mosaic_layout.h"
|
|
||||||
|
|
||||||
#include "history/view/history_view_cursor_state.h"
|
|
||||||
#include "layout/layout_position.h"
|
|
||||||
#include "styles/style_chat_helpers.h"
|
|
||||||
|
|
||||||
namespace Overview::Layout {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr auto kInlineItemsMaxPerRow = 5;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
MosaicLayout::MosaicLayout()
|
|
||||||
: _bigWidth(st::emojiPanWidth - st::inlineResultsLeft) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::setRightSkip(int rightSkip) {
|
|
||||||
_rightSkip = rightSkip;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::setOffset(int left, int top) {
|
|
||||||
_offset = { left, top };
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::setFullWidth(int w) {
|
|
||||||
_width = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosaicLayout::empty() const {
|
|
||||||
return _rows.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::rowsCount() const {
|
|
||||||
return _rows.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::countDesiredHeight(int newWidth) {
|
|
||||||
auto result = 0;
|
|
||||||
for (auto &row : _rows) {
|
|
||||||
layoutRow(row, newWidth ? newWidth : _width);
|
|
||||||
result += row.height;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<ItemBase*> MosaicLayout::itemAt(int row, int column) const {
|
|
||||||
Expects((row >= 0)
|
|
||||||
&& (row < _rows.size())
|
|
||||||
&& (column >= 0)
|
|
||||||
&& (column < _rows[row].items.size()));
|
|
||||||
return _rows[row].items[column];
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<ItemBase*> MosaicLayout::itemAt(int index) const {
|
|
||||||
const auto &[row, column] = ::Layout::IndexToPosition(index);
|
|
||||||
return itemAt(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemBase *MosaicLayout::maybeItemAt(int row, int column) const {
|
|
||||||
if ((row >= 0)
|
|
||||||
&& (row < _rows.size())
|
|
||||||
&& (column >= 0)
|
|
||||||
&& (column < _rows[row].items.size())) {
|
|
||||||
return _rows[row].items[column];
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemBase *MosaicLayout::maybeItemAt(int index) const {
|
|
||||||
const auto &[row, column] = ::Layout::IndexToPosition(index);
|
|
||||||
return maybeItemAt(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::addItems(const std::vector<not_null<ItemBase*>> &items) {
|
|
||||||
_rows.reserve(items.size());
|
|
||||||
auto row = Row();
|
|
||||||
row.items.reserve(kInlineItemsMaxPerRow);
|
|
||||||
auto sumWidth = 0;
|
|
||||||
for (const auto &item : items) {
|
|
||||||
addItem(item, row, sumWidth);
|
|
||||||
}
|
|
||||||
rowFinalize(row, sumWidth, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::addItem(
|
|
||||||
not_null<ItemBase*> item,
|
|
||||||
Row &row,
|
|
||||||
int &sumWidth) {
|
|
||||||
// item->preload();
|
|
||||||
|
|
||||||
using namespace ::Layout;
|
|
||||||
item->setPosition(PositionToIndex(_rows.size(), row.items.size()));
|
|
||||||
if (rowFinalize(row, sumWidth, false)) {
|
|
||||||
item->setPosition(PositionToIndex(_rows.size(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
sumWidth += item->maxWidth();
|
|
||||||
if (!row.items.empty() && _rightSkip) {
|
|
||||||
sumWidth += _rightSkip;
|
|
||||||
}
|
|
||||||
|
|
||||||
row.items.push_back(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosaicLayout::rowFinalize(Row &row, int &sumWidth, bool force) {
|
|
||||||
if (row.items.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto full = (row.items.size() >= kInlineItemsMaxPerRow);
|
|
||||||
// Currently use the same GIFs layout for all widget sizes.
|
|
||||||
const auto big = (sumWidth >= _bigWidth);
|
|
||||||
if (full || big || force) {
|
|
||||||
row.maxWidth = (full || big) ? sumWidth : 0;
|
|
||||||
layoutRow(row, _width);
|
|
||||||
_rows.push_back(std::move(row));
|
|
||||||
row = Row();
|
|
||||||
row.items.reserve(kInlineItemsMaxPerRow);
|
|
||||||
sumWidth = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::layoutRow(Row &row, int fullWidth) {
|
|
||||||
const auto count = int(row.items.size());
|
|
||||||
Assert(count <= kInlineItemsMaxPerRow);
|
|
||||||
|
|
||||||
// Enumerate items in the order of growing maxWidth()
|
|
||||||
// for that sort item indices by maxWidth().
|
|
||||||
int indices[kInlineItemsMaxPerRow];
|
|
||||||
for (auto i = 0; i != count; ++i) {
|
|
||||||
indices[i] = i;
|
|
||||||
}
|
|
||||||
std::sort(indices, indices + count, [&](int a, int b) {
|
|
||||||
return row.items[a]->maxWidth() < row.items[b]->maxWidth();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto desiredWidth = row.maxWidth;
|
|
||||||
row.height = 0;
|
|
||||||
auto availableWidth = fullWidth
|
|
||||||
- (st::inlineResultsLeft - st::roundRadiusSmall);
|
|
||||||
for (auto i = 0; i < count; ++i) {
|
|
||||||
const auto index = indices[i];
|
|
||||||
const auto &item = row.items[index];
|
|
||||||
const auto w = desiredWidth
|
|
||||||
? (item->maxWidth() * availableWidth / desiredWidth)
|
|
||||||
: item->maxWidth();
|
|
||||||
const auto actualWidth = std::max(w, st::inlineResultsMinWidth);
|
|
||||||
row.height = std::max(row.height, item->resizeGetHeight(actualWidth));
|
|
||||||
if (desiredWidth) {
|
|
||||||
availableWidth -= actualWidth;
|
|
||||||
desiredWidth -= row.items[index]->maxWidth();
|
|
||||||
if (index > 0 && _rightSkip) {
|
|
||||||
availableWidth -= _rightSkip;
|
|
||||||
desiredWidth -= _rightSkip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect MosaicLayout::findRect(int index) const {
|
|
||||||
const auto clip = QRect(0, 0, _width, 100);
|
|
||||||
const auto fromX = rtl() ? (_width - clip.x() - clip.width()) : clip.x();
|
|
||||||
const auto toX = rtl() ? (_width - clip.x()) : (clip.x() + clip.width());
|
|
||||||
const auto rows = _rows.size();
|
|
||||||
auto top = 0;
|
|
||||||
for (auto row = 0; row != rows; ++row) {
|
|
||||||
auto &inlineRow = _rows[row];
|
|
||||||
// if ((top + inlineRow.height) > clip.top()) {
|
|
||||||
auto left = 0;
|
|
||||||
if (row == (rows - 1)) {
|
|
||||||
// context.lastRow = true;
|
|
||||||
}
|
|
||||||
for (const auto &item : inlineRow.items) {
|
|
||||||
if (left >= toX) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto w = item->width();
|
|
||||||
if ((left + w) > fromX) {
|
|
||||||
if (item->position() == index) {
|
|
||||||
return QRect(
|
|
||||||
left + _offset.x(),
|
|
||||||
top + _offset.y(),
|
|
||||||
item->width(),
|
|
||||||
item->height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
left += w;
|
|
||||||
left += _rightSkip;
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
top += inlineRow.height;
|
|
||||||
}
|
|
||||||
return QRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::paint(
|
|
||||||
Fn<void(const not_null<ItemBase*>, QPoint)> paintItemCallback,
|
|
||||||
const QRect &clip) const {
|
|
||||||
auto top = _offset.y();
|
|
||||||
const auto fromX = rtl() ? (_width - clip.x() - clip.width()) : clip.x();
|
|
||||||
const auto toX = rtl() ? (_width - clip.x()) : (clip.x() + clip.width());
|
|
||||||
const auto rows = _rows.size();
|
|
||||||
for (auto row = 0; row != rows; ++row) {
|
|
||||||
if (top >= clip.top() + clip.height()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto &inlineRow = _rows[row];
|
|
||||||
if ((top + inlineRow.height) > clip.top()) {
|
|
||||||
auto left = _offset.x();
|
|
||||||
if (row == (rows - 1)) {
|
|
||||||
// context.lastRow = true;
|
|
||||||
}
|
|
||||||
for (const auto &item : inlineRow.items) {
|
|
||||||
if (left >= toX) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto w = item->width();
|
|
||||||
if ((left + w) > fromX) {
|
|
||||||
paintItemCallback(item, QPoint(left, top));
|
|
||||||
}
|
|
||||||
left += w;
|
|
||||||
left += _rightSkip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
top += inlineRow.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::clearRows(bool resultsDeleted) {
|
|
||||||
if (!resultsDeleted) {
|
|
||||||
for (const auto &row : _rows) {
|
|
||||||
for (const auto &item : row.items) {
|
|
||||||
item->setPosition(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_rows.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosaicLayout::preloadImages() {
|
|
||||||
// for (const auto &row : _rows) {
|
|
||||||
// for (const auto &item : row.items) {
|
|
||||||
// item->preload();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::columnsCountAt(int row) const {
|
|
||||||
Expects(row >= 0 && row < _rows.size());
|
|
||||||
return _rows[row].items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MosaicLayout::rowHeightAt(int row) const {
|
|
||||||
Expects(row >= 0 && row < _rows.size());
|
|
||||||
return _rows[row].height;
|
|
||||||
}
|
|
||||||
|
|
||||||
MosaicLayout::FoundItem MosaicLayout::findByPoint(
|
|
||||||
const QPoint &globalPoint) const {
|
|
||||||
auto sx = globalPoint.x() - _offset.x();
|
|
||||||
auto sy = globalPoint.y() - _offset.y();
|
|
||||||
auto row = -1;
|
|
||||||
auto col = -1;
|
|
||||||
auto sel = -1;
|
|
||||||
bool exact = true;
|
|
||||||
ClickHandlerPtr link;
|
|
||||||
ItemBase *item = nullptr;
|
|
||||||
if (sy >= 0) {
|
|
||||||
row = 0;
|
|
||||||
for (auto rows = rowsCount(); row < rows; ++row) {
|
|
||||||
const auto rowHeight = _rows[row].height;
|
|
||||||
if (sy < rowHeight) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sy -= rowHeight;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = 0;
|
|
||||||
exact = false;
|
|
||||||
}
|
|
||||||
if (row >= rowsCount()) {
|
|
||||||
row = rowsCount() - 1;
|
|
||||||
exact = false;
|
|
||||||
}
|
|
||||||
if (sx < 0) {
|
|
||||||
sx = 0;
|
|
||||||
exact = false;
|
|
||||||
}
|
|
||||||
if (sx >= 0 && row >= 0 && row < rowsCount()) {
|
|
||||||
const auto columnsCount = _rows[row].items.size();
|
|
||||||
col = 0;
|
|
||||||
for (int cols = columnsCount; col < cols; ++col) {
|
|
||||||
const auto item = itemAt(row, col);
|
|
||||||
const auto width = item->width();
|
|
||||||
if (sx < width) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sx -= width;
|
|
||||||
sx -= _rightSkip;
|
|
||||||
}
|
|
||||||
if (col >= columnsCount) {
|
|
||||||
col = columnsCount - 1;
|
|
||||||
exact = false;
|
|
||||||
}
|
|
||||||
item = itemAt(row, col);
|
|
||||||
sel = ::Layout::PositionToIndex(row, + col);
|
|
||||||
const auto result = item->getState(QPoint(sx, sy), {});
|
|
||||||
link = result.link;
|
|
||||||
} else {
|
|
||||||
row = col = -1;
|
|
||||||
}
|
|
||||||
return { link, item, sel, exact };
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Overview::Layout
|
|
|
@ -1,72 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "overview/overview_layout.h"
|
|
||||||
|
|
||||||
namespace Overview::Layout {
|
|
||||||
|
|
||||||
class MosaicLayout final {
|
|
||||||
public:
|
|
||||||
struct FoundItem {
|
|
||||||
ClickHandlerPtr link;
|
|
||||||
ItemBase *item = nullptr;
|
|
||||||
int index = -1;
|
|
||||||
bool exact = false;
|
|
||||||
};
|
|
||||||
MosaicLayout();
|
|
||||||
|
|
||||||
[[nodiscard]] int rowHeightAt(int row) const;
|
|
||||||
[[nodiscard]] int countDesiredHeight(int newWidth);
|
|
||||||
|
|
||||||
[[nodiscard]] FoundItem findByPoint(const QPoint &globalPoint) const;
|
|
||||||
[[nodiscard]] QRect findRect(int index) const;
|
|
||||||
|
|
||||||
void addItems(const std::vector<not_null<ItemBase*>> &items);
|
|
||||||
|
|
||||||
void setRightSkip(int rightSkip);
|
|
||||||
void setFullWidth(int w);
|
|
||||||
void setOffset(int left, int top);
|
|
||||||
[[nodiscard]] bool empty() const;
|
|
||||||
|
|
||||||
[[nodiscard]] int rowsCount() const;
|
|
||||||
[[nodiscard]] int columnsCountAt(int row) const;
|
|
||||||
|
|
||||||
[[nodiscard]] not_null<ItemBase*> itemAt(int row, int column) const;
|
|
||||||
[[nodiscard]] not_null<ItemBase*> itemAt(int index) const;
|
|
||||||
[[nodiscard]] ItemBase *maybeItemAt(int row, int column) const;
|
|
||||||
[[nodiscard]] ItemBase *maybeItemAt(int index) const;
|
|
||||||
|
|
||||||
void clearRows(bool resultsDeleted);
|
|
||||||
|
|
||||||
void preloadImages();
|
|
||||||
|
|
||||||
void paint(
|
|
||||||
Fn<void(const not_null<ItemBase*>, QPoint)> paintItemCallback,
|
|
||||||
const QRect &clip) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Row {
|
|
||||||
int maxWidth = 0;
|
|
||||||
int height = 0;
|
|
||||||
std::vector<ItemBase*> items;
|
|
||||||
};
|
|
||||||
|
|
||||||
void addItem(not_null<ItemBase*> item, Row &row, int &sumWidth);
|
|
||||||
|
|
||||||
bool rowFinalize(Row &row, int &sumWidth, bool force);
|
|
||||||
void layoutRow(Row &row, int fullWidth);
|
|
||||||
|
|
||||||
int _bigWidth;
|
|
||||||
int _width = 0;
|
|
||||||
int _rightSkip = 0;
|
|
||||||
QPoint _offset;
|
|
||||||
std::vector<Row> _rows;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Overview::Layout
|
|
Loading…
Add table
Reference in a new issue