mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Copied layout items mosaic drawing for overview layout.
This commit is contained in:
parent
abdd2fa99b
commit
039ffd3d34
4 changed files with 456 additions and 8 deletions
|
@ -819,6 +819,8 @@ 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
|
||||||
|
|
|
@ -9,6 +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 "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"
|
||||||
|
@ -101,6 +102,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addItem(not_null<BaseLayout*> item);
|
bool addItem(not_null<BaseLayout*> item);
|
||||||
|
void finishSection();
|
||||||
|
|
||||||
bool empty() const {
|
bool empty() const {
|
||||||
return _items.empty();
|
return _items.empty();
|
||||||
}
|
}
|
||||||
|
@ -168,7 +171,7 @@ private:
|
||||||
not_null<const BaseLayout*> item,
|
not_null<const BaseLayout*> item,
|
||||||
const Context &context) const;
|
const Context &context) const;
|
||||||
|
|
||||||
int recountHeight() const;
|
int recountHeight();
|
||||||
void refreshHeight();
|
void refreshHeight();
|
||||||
|
|
||||||
Type _type = Type::Photo;
|
Type _type = Type::Photo;
|
||||||
|
@ -183,6 +186,8 @@ private:
|
||||||
int _top = 0;
|
int _top = 0;
|
||||||
int _height = 0;
|
int _height = 0;
|
||||||
|
|
||||||
|
Overview::Layout::MosaicLayout _mosaic;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ListWidget::IsAfter(
|
bool ListWidget::IsAfter(
|
||||||
|
@ -231,6 +236,15 @@ bool ListWidget::Section::addItem(not_null<BaseLayout*> item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListWidget::Section::finishSection() {
|
||||||
|
if (_type == Type::GIF) {
|
||||||
|
_mosaic.setOffset(st::infoMediaSkip, headerHeight());
|
||||||
|
_mosaic.setRightSkip(st::infoMediaSkip);
|
||||||
|
const auto items = ranges::views::values(_items) | ranges::to_vector;
|
||||||
|
_mosaic.addItems(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::Section::setHeader(not_null<BaseLayout*> item) {
|
void ListWidget::Section::setHeader(not_null<BaseLayout*> item) {
|
||||||
auto text = [&] {
|
auto text = [&] {
|
||||||
auto date = item->dateTime().date();
|
auto date = item->dateTime().date();
|
||||||
|
@ -298,6 +312,9 @@ bool ListWidget::Section::removeItem(UniversalMsgId universalId) {
|
||||||
QRect ListWidget::Section::findItemRect(
|
QRect ListWidget::Section::findItemRect(
|
||||||
not_null<const BaseLayout*> item) const {
|
not_null<const BaseLayout*> item) const {
|
||||||
auto position = item->position();
|
auto position = item->position();
|
||||||
|
if (!_mosaic.empty()) {
|
||||||
|
return _mosaic.findRect(position);
|
||||||
|
}
|
||||||
auto top = position / _itemsInRow;
|
auto top = position / _itemsInRow;
|
||||||
auto indexInRow = position % _itemsInRow;
|
auto indexInRow = position % _itemsInRow;
|
||||||
auto left = _itemsLeft
|
auto left = _itemsLeft
|
||||||
|
@ -314,6 +331,17 @@ auto ListWidget::Section::completeResult(
|
||||||
auto ListWidget::Section::findItemByPoint(
|
auto ListWidget::Section::findItemByPoint(
|
||||||
QPoint point) const -> FoundItem {
|
QPoint point) const -> FoundItem {
|
||||||
Expects(!_items.empty());
|
Expects(!_items.empty());
|
||||||
|
if (!_mosaic.empty()) {
|
||||||
|
const auto found = _mosaic.findByPoint(point);
|
||||||
|
if (found.item) {
|
||||||
|
const auto rect = findItemRect(found.item);
|
||||||
|
return { found.item, rect, rect.contains(point) };
|
||||||
|
} else {
|
||||||
|
auto item = (_items.end() - 1)->second;
|
||||||
|
const auto rect = findItemRect(item);
|
||||||
|
return { item, rect, rect.contains(point) };
|
||||||
|
}
|
||||||
|
}
|
||||||
auto itemIt = findItemAfterTop(point.y());
|
auto itemIt = findItemAfterTop(point.y());
|
||||||
if (itemIt == _items.end()) {
|
if (itemIt == _items.end()) {
|
||||||
--itemIt;
|
--itemIt;
|
||||||
|
@ -362,6 +390,7 @@ auto ListWidget::Section::findItemDetails(not_null<BaseLayout*> item) const
|
||||||
|
|
||||||
auto ListWidget::Section::findItemAfterTop(
|
auto ListWidget::Section::findItemAfterTop(
|
||||||
int top) -> Items::iterator {
|
int top) -> Items::iterator {
|
||||||
|
Expects(_mosaic.empty());
|
||||||
return ranges::lower_bound(
|
return ranges::lower_bound(
|
||||||
_items,
|
_items,
|
||||||
top,
|
top,
|
||||||
|
@ -374,6 +403,7 @@ auto ListWidget::Section::findItemAfterTop(
|
||||||
|
|
||||||
auto ListWidget::Section::findItemAfterTop(
|
auto ListWidget::Section::findItemAfterTop(
|
||||||
int top) const -> Items::const_iterator {
|
int top) const -> Items::const_iterator {
|
||||||
|
Expects(_mosaic.empty());
|
||||||
return ranges::lower_bound(
|
return ranges::lower_bound(
|
||||||
_items,
|
_items,
|
||||||
top,
|
top,
|
||||||
|
@ -387,6 +417,7 @@ auto ListWidget::Section::findItemAfterTop(
|
||||||
auto ListWidget::Section::findItemAfterBottom(
|
auto ListWidget::Section::findItemAfterBottom(
|
||||||
Items::const_iterator from,
|
Items::const_iterator from,
|
||||||
int bottom) const -> Items::const_iterator {
|
int bottom) const -> Items::const_iterator {
|
||||||
|
Expects(_mosaic.empty());
|
||||||
return ranges::lower_bound(
|
return ranges::lower_bound(
|
||||||
from,
|
from,
|
||||||
_items.end(),
|
_items.end(),
|
||||||
|
@ -416,6 +447,20 @@ void ListWidget::Section::paint(
|
||||||
auto localContext = context.layoutContext;
|
auto localContext = context.layoutContext;
|
||||||
localContext.isAfterDate = (header > 0);
|
localContext.isAfterDate = (header > 0);
|
||||||
|
|
||||||
|
if (!_mosaic.empty()) {
|
||||||
|
auto paintItem = [&](const not_null<BaseLayout*> item, QPoint point) {
|
||||||
|
p.translate(point.x(), point.y());
|
||||||
|
item->paint(
|
||||||
|
p,
|
||||||
|
clip.translated(-point),
|
||||||
|
itemSelection(item, context),
|
||||||
|
&localContext);
|
||||||
|
p.translate(-point.x(), -point.y());
|
||||||
|
};
|
||||||
|
_mosaic.paint(std::move(paintItem), clip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto fromIt = findItemAfterTop(clip.y());
|
auto fromIt = findItemAfterTop(clip.y());
|
||||||
auto tillIt = findItemAfterBottom(
|
auto tillIt = findItemAfterBottom(
|
||||||
fromIt,
|
fromIt,
|
||||||
|
@ -508,7 +553,6 @@ void ListWidget::Section::resizeToWidth(int newWidth) {
|
||||||
};
|
};
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case Type::Photo:
|
case Type::Photo:
|
||||||
case Type::GIF:
|
|
||||||
case Type::Video:
|
case Type::Video:
|
||||||
case Type::RoundFile: {
|
case Type::RoundFile: {
|
||||||
_itemsLeft = st::infoMediaSkip;
|
_itemsLeft = st::infoMediaSkip;
|
||||||
|
@ -522,6 +566,10 @@ void ListWidget::Section::resizeToWidth(int newWidth) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Type::GIF: {
|
||||||
|
_mosaic.setFullWidth(newWidth - st::infoMediaSkip);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Type::RoundVoiceFile:
|
case Type::RoundVoiceFile:
|
||||||
case Type::MusicFile:
|
case Type::MusicFile:
|
||||||
resizeOneColumn(0, newWidth);
|
resizeOneColumn(0, newWidth);
|
||||||
|
@ -562,12 +610,11 @@ int ListWidget::Section::MinItemHeight(Type type, int width) {
|
||||||
Unexpected("Type in ListWidget::Section::MinItemHeight()");
|
Unexpected("Type in ListWidget::Section::MinItemHeight()");
|
||||||
}
|
}
|
||||||
|
|
||||||
int ListWidget::Section::recountHeight() const {
|
int ListWidget::Section::recountHeight() {
|
||||||
auto result = headerHeight();
|
auto result = headerHeight();
|
||||||
|
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case Type::Photo:
|
case Type::Photo:
|
||||||
case Type::GIF:
|
|
||||||
case Type::Video:
|
case Type::Video:
|
||||||
case Type::RoundFile: {
|
case Type::RoundFile: {
|
||||||
auto itemHeight = _itemWidth + st::infoMediaSkip;
|
auto itemHeight = _itemWidth + st::infoMediaSkip;
|
||||||
|
@ -588,6 +635,10 @@ int ListWidget::Section::recountHeight() const {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Type::GIF: {
|
||||||
|
return _mosaic.countDesiredHeight(0) + result;
|
||||||
|
} break;
|
||||||
|
|
||||||
case Type::RoundVoiceFile:
|
case Type::RoundVoiceFile:
|
||||||
case Type::File:
|
case Type::File:
|
||||||
case Type::MusicFile:
|
case Type::MusicFile:
|
||||||
|
@ -619,7 +670,9 @@ ListWidget::ListWidget(
|
||||||
, _dateBadge(DateBadge{
|
, _dateBadge(DateBadge{
|
||||||
.check = SingleQueuedInvokation([=] { scrollDateCheck(); }),
|
.check = SingleQueuedInvokation([=] { scrollDateCheck(); }),
|
||||||
.hideTimer = base::Timer([=] { scrollDateHide(); }),
|
.hideTimer = base::Timer([=] { scrollDateHide(); }),
|
||||||
.goodType = (_type == Type::Photo || _type == Type::Video),
|
.goodType = (_type == Type::Photo
|
||||||
|
|| _type == Type::Video
|
||||||
|
|| _type == Type::GIF),
|
||||||
}) {
|
}) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
start();
|
start();
|
||||||
|
@ -981,6 +1034,10 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case Type::GIF:
|
case Type::GIF:
|
||||||
|
if (const auto file = getFile()) {
|
||||||
|
return std::make_unique<Gif>(this, item, file);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
case Type::Video:
|
case Type::Video:
|
||||||
if (const auto file = getFile()) {
|
if (const auto file = getFile()) {
|
||||||
return std::make_unique<Video>(this, item, file);
|
return std::make_unique<Video>(this, item, file);
|
||||||
|
@ -1014,6 +1071,7 @@ void ListWidget::refreshRows() {
|
||||||
|
|
||||||
markLayoutsStale();
|
markLayoutsStale();
|
||||||
|
|
||||||
|
|
||||||
_sections.clear();
|
_sections.clear();
|
||||||
auto section = Section(_type);
|
auto section = Section(_type);
|
||||||
auto count = _slice.size();
|
auto count = _slice.size();
|
||||||
|
@ -1021,6 +1079,7 @@ void ListWidget::refreshRows() {
|
||||||
auto universalId = GetUniversalId(_slice[--i]);
|
auto universalId = GetUniversalId(_slice[--i]);
|
||||||
if (auto layout = getLayout(universalId)) {
|
if (auto layout = getLayout(universalId)) {
|
||||||
if (!section.addItem(layout)) {
|
if (!section.addItem(layout)) {
|
||||||
|
section.finishSection();
|
||||||
_sections.push_back(std::move(section));
|
_sections.push_back(std::move(section));
|
||||||
section = Section(_type);
|
section = Section(_type);
|
||||||
section.addItem(layout);
|
section.addItem(layout);
|
||||||
|
@ -1028,6 +1087,7 @@ void ListWidget::refreshRows() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!section.empty()) {
|
if (!section.empty()) {
|
||||||
|
section.finishSection();
|
||||||
_sections.push_back(std::move(section));
|
_sections.push_back(std::move(section));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1197,8 +1257,8 @@ void ListWidget::checkMoveToOtherViewer() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto topItem = findItemByPoint({ 0, _visibleTop });
|
auto topItem = findItemByPoint({ st::infoMediaSkip, _visibleTop });
|
||||||
auto bottomItem = findItemByPoint({ 0, _visibleBottom });
|
auto bottomItem = findItemByPoint({ st::infoMediaSkip, _visibleBottom });
|
||||||
|
|
||||||
auto preloadedHeight = kPreloadedScreensCountFull * visibleHeight;
|
auto preloadedHeight = kPreloadedScreensCountFull * visibleHeight;
|
||||||
auto minItemHeight = Section::MinItemHeight(_type, width());
|
auto minItemHeight = Section::MinItemHeight(_type, width());
|
||||||
|
@ -1276,7 +1336,7 @@ auto ListWidget::countScrollState() const -> ScrollTopState {
|
||||||
if (_sections.empty()) {
|
if (_sections.empty()) {
|
||||||
return { 0, 0 };
|
return { 0, 0 };
|
||||||
}
|
}
|
||||||
auto topItem = findItemByPoint({ 0, _visibleTop });
|
auto topItem = findItemByPoint({ st::infoMediaSkip, _visibleTop });
|
||||||
return {
|
return {
|
||||||
GetUniversalId(topItem.layout),
|
GetUniversalId(topItem.layout),
|
||||||
_visibleTop - topItem.geometry.y()
|
_visibleTop - topItem.geometry.y()
|
||||||
|
|
315
Telegram/SourceFiles/overview/overview_mosaic_layout.cpp
Normal file
315
Telegram/SourceFiles/overview/overview_mosaic_layout.cpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
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_utils.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;
|
||||||
|
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;
|
||||||
|
sx -= _rightSkip;
|
||||||
|
}
|
||||||
|
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 Overview::Layout
|
71
Telegram/SourceFiles/overview/overview_mosaic_layout.h
Normal file
71
Telegram/SourceFiles/overview/overview_mosaic_layout.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
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