Fix / improve support for album items selection.

This commit is contained in:
John Preston 2018-01-27 16:59:24 +03:00
parent 722264f634
commit 2fdc3169ce
34 changed files with 983 additions and 598 deletions

View File

@ -280,7 +280,7 @@ void BotKeyboard::updateSelected() {
auto p = mapFromGlobal(_lastMousePos);
auto x = rtl() ? st::botKbScroll.width : _st->margin;
auto link = _impl->getState(p - QPoint(x, _st->margin));
auto link = _impl->getLink(p - QPoint(x, _st->margin));
if (ClickHandler::setActive(link, this)) {
Ui::Tooltip::Hide();
setCursor(link ? style::cur_pointer : style::cur_default);

View File

@ -888,7 +888,7 @@ void GifsListWidget::updateSelected() {
int row = -1, col = -1, sel = -1;
ClickHandlerPtr lnk;
ClickHandlerHost *lnkhost = nullptr;
HistoryCursorState cursor = HistoryDefaultCursorState;
HistoryView::CursorState cursor = HistoryView::CursorState::None;
if (sy >= 0) {
row = 0;
for (int rows = _rows.size(); row < rows; ++row) {
@ -913,10 +913,12 @@ void GifsListWidget::updateSelected() {
}
if (col < inlineItems.size()) {
sel = row * MatrixRowShift + col;
auto result = inlineItems[col]->getState(QPoint(sx, sy), HistoryStateRequest());
auto result = inlineItems[col]->getState(
QPoint(sx, sy),
HistoryView::StateRequest());
lnk = result.link;
cursor = result.cursor;
lnkhost = inlineItems.at(col);
lnkhost = inlineItems[col];
} else {
row = col = -1;
}

View File

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/admin_log/history_admin_log_filter.h"
#include "history/view/history_view_message.h"
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_cursor_state.h"
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
#include "mainwidget.h"
@ -451,13 +452,15 @@ void InnerWidget::updateEmptyText() {
}
QString InnerWidget::tooltipText() const {
if (_mouseCursorState == HistoryInDateCursorState && _mouseAction == MouseAction::None) {
if (_mouseCursorState == CursorState::Date
&& _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) {
auto dateText = view->data()->date.toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat));
return dateText;
}
} else if (_mouseCursorState == HistoryInForwardedCursorState && _mouseAction == MouseAction::None) {
} else if (_mouseCursorState == CursorState::Forwarded
&& _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) {
if (const auto forwarded = view->data()->Get<HistoryMessageForwarded>()) {
return forwarded->text.originalText(AllTextSelection, ExpandLinksNone);
@ -872,10 +875,10 @@ void InnerWidget::keyPressEvent(QKeyEvent *e) {
void InnerWidget::mouseDoubleClickEvent(QMouseEvent *e) {
mouseActionStart(e->globalPos(), e->button());
if (((_mouseAction == MouseAction::Selecting && _selectedItem != nullptr) || (_mouseAction == MouseAction::None)) && _mouseSelectType == TextSelectType::Letters && _mouseActionItem) {
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = _mouseActionItem->getState(_dragStartPosition, request);
if (dragState.cursor == HistoryInTextCursorState) {
auto dragState = _mouseActionItem->textState(_dragStartPosition, request);
if (dragState.cursor == CursorState::Text) {
_mouseTextSymbol = dragState.symbol;
_mouseSelectType = TextSelectType::Words;
if (_mouseAction == MouseAction::None) {
@ -914,10 +917,10 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
auto mousePos = mapPointToItem(
mapFromGlobal(_mousePosition),
App::mousedItem());
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = App::mousedItem()->getState(mousePos, request);
if (dragState.cursor == HistoryInTextCursorState
auto dragState = App::mousedItem()->textState(mousePos, request);
if (dragState.cursor == CursorState::Text
&& base::in_range(dragState.symbol, selFrom, selTo)) {
isUponSelected = 1;
}
@ -1275,12 +1278,12 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt
_mouseAction = MouseAction::PrepareDrag;
}
if (_mouseAction == MouseAction::None && _mouseActionItem) {
HistoryTextState dragState;
TextState dragState;
if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = _mouseActionItem->getState(_dragStartPosition, request);
if (dragState.cursor == HistoryInTextCursorState) {
dragState = _mouseActionItem->textState(_dragStartPosition, request);
if (dragState.cursor == CursorState::Text) {
auto selection = TextSelection { dragState.symbol, dragState.symbol };
repaintItem(std::exchange(_selectedItem, _mouseActionItem));
_selectedText = selection;
@ -1291,14 +1294,14 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt
_trippleClickTimer.callOnce(QApplication::doubleClickInterval());
}
} else if (App::pressedItem()) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = _mouseActionItem->getState(_dragStartPosition, request);
dragState = _mouseActionItem->textState(_dragStartPosition, request);
}
if (_mouseSelectType != TextSelectType::Paragraphs) {
if (App::pressedItem()) {
_mouseTextSymbol = dragState.symbol;
auto uponSelected = (dragState.cursor == HistoryInTextCursorState);
auto uponSelected = (dragState.cursor == CursorState::Text);
if (uponSelected) {
if (!_selectedItem || _selectedItem != _mouseActionItem) {
uponSelected = false;
@ -1399,7 +1402,7 @@ void InnerWidget::updateSelected() {
if (item) {
App::mousedItem(view);
itemPoint = mapPointToItem(point, view);
if (view->hasPoint(itemPoint)) {
if (view->pointState(itemPoint) != PointState::Outside) {
if (App::hoveredItem() != view) {
repaintItem(App::hoveredItem());
App::hoveredItem(view);
@ -1411,7 +1414,7 @@ void InnerWidget::updateSelected() {
}
}
HistoryTextState dragState;
TextState dragState;
ClickHandlerHost *lnkhost = nullptr;
auto selectingText = _selectedItem
&& (view == _mouseActionItem)
@ -1423,13 +1426,13 @@ void InnerWidget::updateSelected() {
InvokeQueued(this, [this] { performDrag(); });
}
}
HistoryStateRequest request;
StateRequest request;
if (_mouseAction == MouseAction::Selecting) {
request.flags |= Text::StateRequest::Flag::LookupSymbol;
} else {
selectingText = false;
}
dragState = view->getState(itemPoint, request);
dragState = view->textState(itemPoint, request);
lnkhost = view;
if (!dragState.link && itemPoint.x() >= st::historyPhotoLeft && itemPoint.x() < st::historyPhotoLeft + st::msgPhotoSize) {
if (auto message = item->toHistoryMessage()) {
@ -1459,7 +1462,9 @@ void InnerWidget::updateSelected() {
if (lnkChanged || dragState.cursor != _mouseCursorState) {
Ui::Tooltip::Hide();
}
if (dragState.link || dragState.cursor == HistoryInDateCursorState || dragState.cursor == HistoryInForwardedCursorState) {
if (dragState.link
|| dragState.cursor == CursorState::Date
|| dragState.cursor == CursorState::Forwarded) {
Ui::Tooltip::Show(1000, this);
}
@ -1468,9 +1473,9 @@ void InnerWidget::updateSelected() {
_mouseCursorState = dragState.cursor;
if (dragState.link) {
cursor = style::cur_pointer;
} else if (_mouseCursorState == HistoryInTextCursorState) {
} else if (_mouseCursorState == CursorState::Text) {
cursor = style::cur_text;
} else if (_mouseCursorState == HistoryInDateCursorState) {
} else if (_mouseCursorState == CursorState::Date) {
// cursor = style::cur_cross;
}
} else if (item) {
@ -1530,10 +1535,10 @@ void InnerWidget::performDrag() {
// if (!_selected.isEmpty() && _selected.cbegin().value() == FullSelection) {
// uponSelected = _selected.contains(_mouseActionItem);
// } else {
// HistoryStateRequest request;
// StateRequest request;
// request.flags |= Text::StateRequest::Flag::LookupSymbol;
// auto dragState = _mouseActionItem->getState(_dragStartPosition.x(), _dragStartPosition.y(), request);
// uponSelected = (dragState.cursor == HistoryInTextCursorState);
// auto dragState = _mouseActionItem->textState(_dragStartPosition.x(), _dragStartPosition.y(), request);
// uponSelected = (dragState.cursor == CursorState::Text);
// if (uponSelected) {
// if (_selected.isEmpty() ||
// _selected.cbegin().value() == FullSelection ||
@ -1584,7 +1589,7 @@ void InnerWidget::performDrag() {
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
// if (auto pressedItem = App::pressedItem()) {
// pressedMedia = pressedItem->media();
// if (_mouseCursorState == HistoryInDateCursorState
// if (_mouseCursorState == CursorState::Date
// || (pressedMedia && pressedMedia->dragItem())) {
// forwardMimeType = qsl("application/x-td-forward");
// Auth().data().setMimeForwardIds(

View File

@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_element.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_section.h"
@ -16,6 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "base/timer.h"
namespace HistoryView {
class Element;
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
} // namespace HistoryView
namespace Ui {
class PopupMenu;
} // namespace Ui
@ -115,6 +122,10 @@ private:
TopToBottom,
BottomToTop,
};
using TextState = HistoryView::TextState;
using CursorState = HistoryView::CursorState;
using PointState = HistoryView::PointState;
using StateRequest = HistoryView::StateRequest;
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionUpdate(const QPoint &screenPos);
@ -228,7 +239,7 @@ private:
QPoint _dragStartPosition;
QPoint _mousePosition;
Element *_mouseActionItem = nullptr;
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;

View File

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_text.h"
#include "history/view/history_view_message.h"
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_cursor_state.h"
#include "ui/text_options.h"
#include "ui/widgets/popup_menu.h"
#include "window/window_controller.h"
@ -131,7 +132,8 @@ HistoryInner::HistoryInner(
, _migrated(history->migrateFrom())
, _widget(historyWidget)
, _scroll(scroll)
, _scrollDateCheck([this] { onScrollDateCheck(); }) {
, _scrollDateCheck([this] { scrollDateCheck(); })
, _scrollDateHideTimer([this] { scrollDateHideByTimer(); }) {
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
@ -140,8 +142,6 @@ HistoryInner::HistoryInner(
_trippleClickTimer.setSingleShot(true);
connect(&_scrollDateHideTimer, SIGNAL(timeout()), this, SLOT(onScrollDateHideByTimer()));
notifyIsBotChanged();
setMouseTracking(true);
@ -166,9 +166,15 @@ HistoryInner::HistoryInner(
mouseActionCancel();
}, lifetime());
Auth().data().viewRepaintRequest(
) | rpl::start_with_next(
[this](auto view) { repaintItem(view); },
lifetime());
) | rpl::start_with_next([this](not_null<const Element*> view) {
repaintItem(view);
}, lifetime());
Auth().data().viewLayoutChanged(
) | rpl::filter([](not_null<const Element*> view) {
return (view == view->data()->mainView()) && view->isUnderCursor();
}) | rpl::start_with_next([this](not_null<const Element*> view) {
mouseActionUpdate();
}, lifetime());
}
void HistoryInner::messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages) {
@ -423,6 +429,10 @@ void HistoryInner::enumerateDates(Method method) {
TextSelection HistoryInner::computeRenderSelection(
not_null<const SelectedItems*> selected,
not_null<Element*> view) const {
if (view->isHiddenByGroup()) {
return TextSelection();
}
const auto item = view->data();
const auto itemSelection = [&](not_null<HistoryItem*> item) {
auto i = selected->find(item);
if (i != selected->end()) {
@ -430,32 +440,30 @@ TextSelection HistoryInner::computeRenderSelection(
}
return TextSelection();
};
// #TODO group selection
//if (const auto group = view->Get<HistoryView::Group>()) {
// if (group->leader != view) {
// return TextSelection();
// }
// auto result = TextSelection();
// auto allFullSelected = true;
// const auto count = int(group->others.size());
// for (auto i = 0; i != count; ++i) {
// if (itemSelection(group->others[i]->data()) == FullSelection) {
// result = AddGroupItemSelection(result, i);
// } else {
// allFullSelected = false;
// }
// }
// const auto leaderSelection = itemSelection(view->data());
// if (leaderSelection == FullSelection) {
// return allFullSelected
// ? FullSelection
// : AddGroupItemSelection(result, count);
// } else if (leaderSelection != TextSelection()) {
// return leaderSelection;
// }
// return result;
//}
return itemSelection(view->data());
const auto result = itemSelection(item);
if (result != TextSelection() && result != FullSelection) {
return result;
}
if (const auto group = Auth().data().groups().find(item)) {
auto parts = TextSelection();
auto allFullSelected = true;
const auto count = int(group->items.size());
for (auto i = 0; i != count; ++i) {
const auto part = group->items[i];
const auto selection = itemSelection(part);
if (part == item
&& selection != FullSelection
&& selection != TextSelection()) {
return selection;
} else if (selection == FullSelection) {
parts = AddGroupItemSelection(parts, i);
} else {
allFullSelected = false;
}
}
return allFullSelected ? FullSelection : parts;
}
return itemSelection(item);
}
TextSelection HistoryInner::itemRenderSelection(
@ -894,7 +902,7 @@ void HistoryInner::mouseMoveEvent(QMouseEvent *e) {
void HistoryInner::mouseActionUpdate(const QPoint &screenPos) {
_mousePosition = screenPos;
onUpdateSelected();
mouseActionUpdate();
}
void HistoryInner::touchScrollUpdated(const QPoint &screenPos) {
@ -958,12 +966,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
}
}
if (_mouseAction == MouseAction::None && mouseActionView) {
HistoryTextState dragState;
TextState dragState;
if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = mouseActionView->getState(_dragStartPosition, request);
if (dragState.cursor == HistoryInTextCursorState) {
dragState = mouseActionView->textState(_dragStartPosition, request);
if (dragState.cursor == CursorState::Text) {
TextSelection selStatus = { dragState.symbol, dragState.symbol };
if (selStatus != FullSelection && (_selected.empty() || _selected.cbegin()->second != FullSelection)) {
if (!_selected.empty()) {
@ -979,14 +987,14 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
}
}
} else if (App::pressedItem()) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = mouseActionView->getState(_dragStartPosition, request);
dragState = mouseActionView->textState(_dragStartPosition, request);
}
if (_mouseSelectType != TextSelectType::Paragraphs) {
if (App::pressedItem()) {
_mouseTextSymbol = dragState.symbol;
bool uponSelected = (dragState.cursor == HistoryInTextCursorState);
bool uponSelected = (dragState.cursor == CursorState::Text);
if (uponSelected) {
if (_selected.empty()
|| _selected.cbegin()->second == FullSelection
@ -1002,7 +1010,8 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
if (uponSelected) {
_mouseAction = MouseAction::PrepareDrag; // start text drag
} else if (!_pressWasInactive) {
if (dynamic_cast<HistorySticker*>(App::pressedItem()->media()) || _mouseCursorState == HistoryInDateCursorState) {
if (dynamic_cast<HistorySticker*>(App::pressedItem()->media())
|| _mouseCursorState == CursorState::Date) {
_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
} else {
if (dragState.afterSymbol) ++_mouseTextSymbol;
@ -1055,10 +1064,10 @@ void HistoryInner::performDrag() {
uponSelected = _dragStateItem
&& (_selected.find(_dragStateItem) != _selected.cend());
} else {
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = mouseActionView->getState(_dragStartPosition, request);
uponSelected = (dragState.cursor == HistoryInTextCursorState);
auto dragState = mouseActionView->textState(_dragStartPosition, request);
uponSelected = (dragState.cursor == CursorState::Text);
if (uponSelected) {
if (_selected.empty()
|| _selected.cbegin()->second == FullSelection
@ -1108,7 +1117,7 @@ void HistoryInner::performDrag() {
auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
if (auto pressedItem = App::pressedItem()) {
pressedMedia = pressedItem->media();
if (_mouseCursorState == HistoryInDateCursorState
if (_mouseCursorState == CursorState::Date
|| (pressedMedia && pressedMedia->dragItem())) {
Auth().data().setMimeForwardIds(
Auth().data().itemOrItsGroup(pressedItem->data()));
@ -1173,10 +1182,12 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
_dragSelTo = nullptr;
update();
}
onUpdateSelected();
mouseActionUpdate();
}
void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
void HistoryInner::mouseActionFinish(
const QPoint &screenPos,
Qt::MouseButton button) {
mouseActionUpdate(screenPos);
auto activated = ClickHandler::unpressed();
@ -1290,10 +1301,10 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
|| (_mouseAction == MouseAction::None
&& (_selected.empty()
|| _selected.cbegin()->second != FullSelection)))) {
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = mouseActionView->getState(_dragStartPosition, request);
if (dragState.cursor == HistoryInTextCursorState) {
auto dragState = mouseActionView->textState(_dragStartPosition, request);
if (dragState.cursor == CursorState::Text) {
_mouseTextSymbol = dragState.symbol;
_mouseSelectType = TextSelectType::Words;
if (_mouseAction == MouseAction::None) {
@ -1342,10 +1353,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
hasSelected = (selTo > selFrom) ? 1 : 0;
if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) {
auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), App::mousedItem());
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = App::mousedItem()->getState(mousePos, request);
if (dragState.cursor == HistoryInTextCursorState && dragState.symbol >= selFrom && dragState.symbol < selTo) {
auto dragState = App::mousedItem()->textState(mousePos, request);
if (dragState.cursor == CursorState::Text
&& dragState.symbol >= selFrom
&& dragState.symbol < selTo) {
isUponSelected = 1;
}
}
@ -1397,7 +1410,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto item = _dragStateItem;
const auto itemId = item ? item->fullId() : FullMsgId();
if (isUponSelected > 0) {
_menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText()));
_menu->addAction(
lang((isUponSelected > 1)
? lng_context_copy_selected_items
: lng_context_copy_selected),
[=] { copySelectedText(); });
}
addItemActions(item);
if (lnkPhoto) {
@ -1503,7 +1520,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto msg = dynamic_cast<HistoryMessage*>(item);
if (isUponSelected > 0) {
_menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText()));
_menu->addAction(
lang((isUponSelected > 1)
? lng_context_copy_selected_items
: lng_context_copy_selected),
[=] { copySelectedText(); });
addItemActions(item);
} else {
addItemActions(item);
@ -1724,7 +1745,7 @@ void HistoryInner::copyContextText(FullMsgId itemId) {
}
void HistoryInner::resizeEvent(QResizeEvent *e) {
onUpdateSelected();
mouseActionUpdate();
}
TextWithEntities HistoryInner::getSelectedText() const {
@ -1986,7 +2007,7 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) {
if (scrolledUp) {
_scrollDateCheck.call();
} else {
onScrollDateHideByTimer();
scrollDateHideByTimer();
}
}
@ -1994,7 +2015,7 @@ bool HistoryInner::displayScrollDate() const {
return (_visibleAreaTop <= height() - 2 * (_visibleAreaBottom - _visibleAreaTop));
}
void HistoryInner::onScrollDateCheck() {
void HistoryInner::scrollDateCheck() {
if (!_history) return;
auto newScrollDateItem = _history->scrollTopItem ? _history->scrollTopItem : (_migrated ? _migrated->scrollTopItem : nullptr);
@ -2015,12 +2036,12 @@ void HistoryInner::onScrollDateCheck() {
}
_scrollDateLastItem = newScrollDateItem;
_scrollDateLastItemTop = newScrollDateItemTop;
_scrollDateHideTimer.start(kScrollDateHideTimeout);
_scrollDateHideTimer.callOnce(kScrollDateHideTimeout);
}
}
void HistoryInner::onScrollDateHideByTimer() {
_scrollDateHideTimer.stop();
void HistoryInner::scrollDateHideByTimer() {
_scrollDateHideTimer.cancel();
if (!_scrollDateLink || ClickHandler::getPressed() != _scrollDateLink) {
scrollDateHide();
}
@ -2036,7 +2057,7 @@ void HistoryInner::keepScrollDateForNow() {
if (!_scrollDateShown && _scrollDateLastItem && _scrollDateOpacity.animating()) {
toggleScrollDateShown();
}
_scrollDateHideTimer.start(kScrollDateHideTimeout);
_scrollDateHideTimer.callOnce(kScrollDateHideTimeout);
}
void HistoryInner::toggleScrollDateShown() {
@ -2261,7 +2282,7 @@ void HistoryInner::onTouchSelect() {
mouseActionStart(_touchPos, Qt::LeftButton);
}
void HistoryInner::onUpdateSelected() {
void HistoryInner::mouseActionUpdate() {
if (!_history || hasPendingResizedItems()) {
return;
}
@ -2282,7 +2303,7 @@ void HistoryInner::onUpdateSelected() {
App::mousedItem(view);
m = mapPointToItem(point, view);
if (view->hasPoint(m)) {
if (view->pointState(m) != PointState::Outside) {
if (App::hoveredItem() != view) {
repaintItem(App::hoveredItem());
App::hoveredItem(view);
@ -2297,7 +2318,7 @@ void HistoryInner::onUpdateSelected() {
mouseActionCancel();
}
HistoryTextState dragState;
TextState dragState;
ClickHandlerHost *lnkhost = nullptr;
auto selectingText = (item == _mouseActionItem)
&& (view == App::hoveredItem())
@ -2305,7 +2326,7 @@ void HistoryInner::onUpdateSelected() {
&& (_selected.cbegin()->second != FullSelection);
if (point.y() < _historyPaddingTop) {
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
dragState = HistoryTextState(nullptr, _botAbout->info->text.getState(
dragState = TextState(nullptr, _botAbout->info->text.getState(
point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height),
_botAbout->width));
_dragStateItem = App::histItemById(dragState.itemId);
@ -2363,7 +2384,7 @@ void HistoryInner::onUpdateSelected() {
} else {
static_cast<DateClickHandler*>(_scrollDateLink.get())->setDate(item->date.date());
}
dragState = HistoryTextState(
dragState = TextState(
nullptr,
_scrollDateLink);
_dragStateItem = App::histItemById(dragState.itemId);
@ -2375,13 +2396,13 @@ void HistoryInner::onUpdateSelected() {
return true;
});
if (!dragState.link) {
HistoryStateRequest request;
StateRequest request;
if (_mouseAction == MouseAction::Selecting) {
request.flags |= Text::StateRequest::Flag::LookupSymbol;
} else {
selectingText = false;
}
dragState = view->getState(m, request);
dragState = view->textState(m, request);
_dragStateItem = App::histItemById(dragState.itemId);
lnkhost = view;
if (!dragState.link && m.x() >= st::historyPhotoLeft && m.x() < st::historyPhotoLeft + st::msgPhotoSize) {
@ -2398,7 +2419,7 @@ void HistoryInner::onUpdateSelected() {
const auto message = view->data()->toHistoryMessage();
Assert(message != nullptr);
dragState = HistoryTextState(
dragState = TextState(
nullptr,
message->displayFrom()->openLink());
_dragStateItem = App::histItemById(dragState.itemId);
@ -2416,7 +2437,9 @@ void HistoryInner::onUpdateSelected() {
if (lnkChanged || dragState.cursor != _mouseCursorState) {
Ui::Tooltip::Hide();
}
if (dragState.link || dragState.cursor == HistoryInDateCursorState || dragState.cursor == HistoryInForwardedCursorState) {
if (dragState.link
|| dragState.cursor == CursorState::Date
|| dragState.cursor == CursorState::Forwarded) {
Ui::Tooltip::Show(1000, this);
}
@ -2425,9 +2448,9 @@ void HistoryInner::onUpdateSelected() {
_mouseCursorState = dragState.cursor;
if (dragState.link) {
cur = style::cur_pointer;
} else if (_mouseCursorState == HistoryInTextCursorState && (_selected.empty() || _selected.cbegin()->second != FullSelection)) {
} else if (_mouseCursorState == CursorState::Text && (_selected.empty() || _selected.cbegin()->second != FullSelection)) {
cur = style::cur_text;
} else if (_mouseCursorState == HistoryInDateCursorState) {
} else if (_mouseCursorState == CursorState::Date) {
//cur = style::cur_cross;
}
} else if (item) {
@ -2457,7 +2480,8 @@ void HistoryInner::onUpdateSelected() {
auto selectingDown = (itemTop(_mouseActionItem) < itemTop(item)) || (_mouseActionItem == item && _dragStartPosition.y() < m.y());
auto dragSelFrom = _mouseActionItem->mainView();
auto dragSelTo = view;
if (!dragSelFrom->hasPoint(_dragStartPosition)) { // maybe exclude dragSelFrom
// Maybe exclude dragSelFrom.
if (dragSelFrom->pointState(_dragStartPosition) == PointState::Outside) {
if (selectingDown) {
if (_dragStartPosition.y() >= dragSelFrom->height() - dragSelFrom->marginBottom() || ((view == dragSelFrom) && (m.y() < _dragStartPosition.y() + QApplication::startDragDistance() || m.y() < dragSelFrom->marginTop()))) {
dragSelFrom = (dragSelFrom != dragSelTo)
@ -2743,9 +2767,7 @@ void HistoryInner::changeSelectionAsGroup(
: SelectAction::Select;
}
auto total = int(toItems->size());
const auto add = (action == SelectAction::Select);
const auto adding = [&] {
const auto canSelect = [&] {
for (const auto other : group->items) {
if (!goodForSelection(toItems, other, total)) {
return false;
@ -2753,7 +2775,7 @@ void HistoryInner::changeSelectionAsGroup(
}
return (total <= MaxSelectedItems);
}();
if (adding) {
if (action == SelectAction::Select && canSelect) {
for (const auto other : group->items) {
addToSelection(toItems, other);
}
@ -2880,7 +2902,8 @@ void HistoryInner::applyDragSelection(
}
QString HistoryInner::tooltipText() const {
if (_mouseCursorState == HistoryInDateCursorState && _mouseAction == MouseAction::None) {
if (_mouseCursorState == CursorState::Date
&& _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) {
auto dateText = view->data()->date.toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat));
@ -2893,7 +2916,8 @@ QString HistoryInner::tooltipText() const {
}
return dateText;
}
} else if (_mouseCursorState == HistoryInForwardedCursorState && _mouseAction == MouseAction::None) {
} else if (_mouseCursorState == CursorState::Forwarded
&& _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) {
if (const auto forwarded = view->data()->Get<HistoryMessageForwarded>()) {
return forwarded->text.originalText(AllTextSelection, ExpandLinksNone);

View File

@ -7,14 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/timer.h"
#include "ui/rp_widget.h"
#include "ui/widgets/tooltip.h"
#include "ui/widgets/scroll_area.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_top_bar_widget.h"
namespace HistoryView {
class ElementDelegate;
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
} // namespace HistoryView
namespace Window {
@ -113,18 +117,11 @@ protected:
void contextMenuEvent(QContextMenuEvent *e) override;
public slots:
void onUpdateSelected();
void onParentGeometryChanged();
void copySelectedText();
void onTouchSelect();
void onTouchScrollTimer();
private slots:
void onScrollDateCheck();
void onScrollDateHideByTimer();
private:
class BotAbout;
using SelectedItems = std::map<HistoryItem*, TextSelection, std::less<>>;
@ -144,6 +141,11 @@ private:
TopToBottom,
BottomToTop,
};
using CursorState = HistoryView::CursorState;
using PointState = HistoryView::PointState;
using TextState = HistoryView::TextState;
using StateRequest = HistoryView::StateRequest;
// This function finds all history items that are displayed and calls template method
// for each found message (in given direction) in the passed history with passed top offset.
//
@ -180,8 +182,11 @@ private:
template <typename Method>
void enumerateDates(Method method);
void scrollDateCheck();
void scrollDateHideByTimer();
bool canHaveFromUserpics() const;
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionUpdate();
void mouseActionUpdate(const QPoint &screenPos);
void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionCancel();
@ -265,6 +270,7 @@ private:
void deleteItem(not_null<HistoryItem*> item);
void deleteItem(FullMsgId itemId);
void deleteAsGroup(FullMsgId itemId);
void copySelectedText();
// Does any of the shown histories has this flag set.
bool hasPendingResizedItems() const;
@ -301,7 +307,7 @@ private:
QPoint _mousePosition;
HistoryItem *_mouseActionItem = nullptr;
HistoryItem *_dragStateItem = nullptr;
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;
@ -338,7 +344,7 @@ private:
bool _scrollDateShown = false;
Animation _scrollDateOpacity;
SingleQueuedInvokation _scrollDateCheck;
SingleTimer _scrollDateHideTimer;
base::Timer _scrollDateHideTimer;
Element *_scrollDateLastItem = nullptr;
int _scrollDateLastItemTop = 0;
ClickHandlerPtr _scrollDateLink;

View File

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/runtime_composer.h"
#include "base/flags.h"
#include "base/value_ordering.h"
#include "history/view/history_view_cursor_state.h"
enum class UnreadMentionType;
struct HistoryMessageReplyMarkup;
@ -47,6 +46,10 @@ class Controller;
} // namespace Window
namespace HistoryView {
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
enum class Context : char;
class ElementDelegate;
} // namespace HistoryView

View File

@ -521,7 +521,7 @@ void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip, TimeMs
}
}
ClickHandlerPtr ReplyKeyboard::getState(QPoint point) const {
ClickHandlerPtr ReplyKeyboard::getLink(QPoint point) const {
Assert(_width > 0);
for_const (auto &row, _rows) {

View File

@ -274,7 +274,7 @@ public:
int naturalHeight() const;
void paint(Painter &p, int outerWidth, const QRect &clip, TimeMs ms) const;
ClickHandlerPtr getState(QPoint point) const;
ClickHandlerPtr getLink(QPoint point) const;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active);
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed);

View File

@ -9,9 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "storage/storage_shared_media.h"
#include "ui/text_options.h"
namespace {
using PointState = HistoryView::PointState;
using TextState = HistoryView::TextState;
} // namespace
Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
return {};
}
@ -54,9 +62,15 @@ TextSelection HistoryMedia::unskipSelection(TextSelection selection) const {
fullSelectionLength());
}
HistoryTextState HistoryMedia::getStateGrouped(
PointState HistoryMedia::pointState(QPoint point) const {
return QRect(0, 0, width(), height()).contains(point)
? PointState::Inside
: PointState::Outside;
}
TextState HistoryMedia::getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
Unexpected("Grouping method call.");
}

View File

@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_object.h"
struct HistoryMessageEdited;
struct HistoryTextState;
struct HistoryStateRequest;
struct TextSelection;
namespace base {
@ -24,6 +22,14 @@ enum class SharedMediaType : char;
using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
} // namespace Storage
namespace HistoryView {
enum class PointState : char;
enum class CursorState : char;
enum class InfoDisplayType : char;
struct TextState;
struct StateRequest;
} // namespace HistoryView
enum class MediaInBubbleState {
None,
Top,
@ -53,6 +59,9 @@ enum HistoryMediaType : char {
class HistoryMedia : public HistoryView::Object {
public:
using Element = HistoryView::Element;
using PointState = HistoryView::PointState;
using TextState = HistoryView::TextState;
using StateRequest = HistoryView::StateRequest;
HistoryMedia(not_null<Element*> parent) : _parent(parent) {
}
@ -63,16 +72,9 @@ public:
return TextWithEntities();
}
bool hasPoint(QPoint point) const {
return QRect(0, 0, width(), height()).contains(point);
}
virtual bool isDisplayed() const;
virtual void updateNeedBubbleState() {
}
virtual bool isAboveMessage() const {
return false;
}
virtual bool hasTextForCopy() const {
return false;
}
@ -85,7 +87,8 @@ public:
virtual void refreshParentId(not_null<HistoryItem*> realParent) {
}
virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0;
virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0;
virtual PointState pointState(QPoint point) const;
virtual TextState textState(QPoint point, StateRequest request) const = 0;
virtual void updatePressed(QPoint point) {
}
@ -156,10 +159,10 @@ public:
not_null<QPixmap*> cache) const {
Unexpected("Grouping method call.");
}
virtual HistoryTextState getStateGrouped(
virtual TextState getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const;
StateRequest request) const;
virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() {
return nullptr;
}
@ -236,6 +239,9 @@ public:
virtual ~HistoryMedia() = default;
protected:
using CursorState = HistoryView::CursorState;
using InfoDisplayType = HistoryView::InfoDisplayType;
QSize countCurrentSize(int newWidth) override;
Text createCaption(not_null<HistoryItem*> item) const;

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "data/data_media_types.h"
#include "storage/storage_shared_media.h"
#include "lang/lang_keys.h"
@ -19,6 +20,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_history.h"
#include "layout.h"
namespace {
using TextState = HistoryView::TextState;
using PointState = HistoryView::PointState;
} // namespace
HistoryGroupedMedia::Part::Part(not_null<HistoryItem*> item)
: item(item) {
}
@ -176,7 +182,7 @@ void HistoryGroupedMedia::draw(
auto fullRight = width();
auto fullBottom = height();
if (needInfoDisplay()) {
_parent->drawInfo(p, fullRight, fullBottom, width(), selected, InfoDisplayOverImage);
_parent->drawInfo(p, fullRight, fullBottom, width(), selected, InfoDisplayType::Image);
}
if (!_parent->hasBubble() && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -186,9 +192,9 @@ void HistoryGroupedMedia::draw(
}
}
HistoryTextState HistoryGroupedMedia::getPartState(
TextState HistoryGroupedMedia::getPartState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
for (const auto &part : _parts) {
if (part.geometry.contains(point)) {
auto result = part.content->getStateGrouped(
@ -199,12 +205,24 @@ HistoryTextState HistoryGroupedMedia::getPartState(
return result;
}
}
return HistoryTextState(_parent->data());
return TextState(_parent->data());
}
HistoryTextState HistoryGroupedMedia::getState(
PointState HistoryGroupedMedia::pointState(QPoint point) const {
if (!QRect(0, 0, width(), height()).contains(point)) {
return PointState::Outside;
}
for (const auto &part : _parts) {
if (part.geometry.contains(point)) {
return PointState::GroupPart;
}
}
return PointState::Inside;
}
HistoryView::TextState HistoryGroupedMedia::textState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
auto result = getPartState(point, request);
if (!result.link && !_caption.isEmpty()) {
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
@ -212,7 +230,7 @@ HistoryTextState HistoryGroupedMedia::getState(
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw);
if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) {
return HistoryTextState(_parent->data(), _caption.getState(
return TextState(_parent->data(), _caption.getState(
point - QPoint(st::msgPadding.left(), captiony),
captionw,
request.forText()));
@ -220,8 +238,8 @@ HistoryTextState HistoryGroupedMedia::getState(
} else if (_parent->media() == this) {
auto fullRight = width();
auto fullBottom = height();
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (!_parent->hasBubble() && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);

View File

@ -28,9 +28,10 @@ public:
const QRect &clip,
TextSelection selection,
TimeMs ms) const override;
HistoryTextState getState(
PointState pointState(QPoint point) const override;
TextState textState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
bool toggleSelectionByHandlerClick(
const ClickHandlerPtr &p) const override;
@ -107,9 +108,9 @@ private:
not_null<HistoryMedia*> main() const;
bool validateGroupParts(
const std::vector<not_null<HistoryItem*>> &items) const;
HistoryTextState getPartState(
TextState getPartState(
QPoint point,
HistoryStateRequest request) const;
StateRequest request) const;
Text _caption;
std::vector<Part> _parts;

View File

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_location_manager.h"
#include "history/history_message.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "window/main_window.h"
#include "window/window_controller.h"
#include "styles/style_history.h"
@ -41,6 +42,8 @@ namespace {
constexpr auto kMaxGifForwardedBarLines = 4;
constexpr auto kMaxOriginalEntryLines = 8192;
using TextState = HistoryView::TextState;
int documentMaxStatusWidth(DocumentData *document) {
auto result = st::normalFont->width(formatDownloadText(document->size, document->size));
if (const auto song = document->song()) {
@ -419,7 +422,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
if (needInfoDisplay()) {
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayOverImage);
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayType::Image);
}
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -429,8 +432,8 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
}
}
HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryPhoto::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -447,7 +450,7 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
painth -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
result = HistoryTextState(_parent, _caption.getState(
result = TextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), painth),
captionw,
request.forText()));
@ -472,8 +475,8 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
if (_caption.isEmpty() && _parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -586,15 +589,15 @@ void HistoryPhoto::drawGrouped(
}
}
HistoryTextState HistoryPhoto::getStateGrouped(
TextState HistoryPhoto::getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (!geometry.contains(point)) {
return {};
}
const auto delayed = _data->full->toDelayedStorageImage();
return HistoryTextState(_parent, _data->uploading()
return TextState(_parent, _data->uploading()
? _cancell
: _data->loaded()
? _openl
@ -870,7 +873,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim
_caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
} else if (_parent->media() == this) {
auto fullRight = paintx + paintw, fullBottom = painty + painth;
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayOverImage);
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayType::Image);
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
@ -879,12 +882,12 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim
}
}
HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest request) const {
TextState HistoryVideo::textState(QPoint point, StateRequest request) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return {};
}
auto result = HistoryTextState(_parent);
auto result = TextState(_parent);
bool loaded = _data->loaded();
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@ -899,7 +902,7 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
painth -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
result = HistoryTextState(_parent, _caption.getState(
result = TextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), painth),
captionw,
request.forText()));
@ -916,8 +919,8 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
if (_caption.isEmpty() && _parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -1026,14 +1029,14 @@ void HistoryVideo::drawGrouped(
}
}
HistoryTextState HistoryVideo::getStateGrouped(
TextState HistoryVideo::getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (!geometry.contains(point)) {
return {};
}
return HistoryTextState(_parent, _data->uploading()
return TextState(_parent, _data->uploading()
? _cancell
: _data->loaded()
? _openl
@ -1564,8 +1567,8 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
}
}
HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryDocument::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -1631,7 +1634,7 @@ HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest req
auto painth = height();
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
if (point.y() >= bottom) {
result = HistoryTextState(_parent, captioned->_caption.getState(
result = TextState(_parent, captioned->_caption.getState(
point - QPoint(st::msgPadding.left(), bottom),
width() - st::msgPadding.left() - st::msgPadding.right(),
request.forText()));
@ -2306,7 +2309,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
}
}
if (isRound || needInfoDisplay()) {
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage);
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, isRound ? InfoDisplayType::Background : InfoDisplayType::Image);
}
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -2320,8 +2323,8 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
}
}
HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryGif::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -2336,7 +2339,7 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
painth -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
result = HistoryTextState(_parent, _caption.getState(
result = TextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), painth),
captionw,
request.forText()));
@ -2386,16 +2389,16 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
result = HistoryTextState(_parent, forwarded->text.getState(
result = TextState(_parent, forwarded->text.getState(
point - QPoint(rectx + st::msgReplyPadding.left(), recty + st::msgReplyPadding.top()),
innerw,
textRequest));
result.symbol = 0;
result.afterSymbol = false;
if (breakEverywhere) {
result.cursor = HistoryInForwardedCursorState;
result.cursor = CursorState::Forwarded;
} else {
result.cursor = HistoryDefaultCursorState;
result.cursor = CursorState::None;
}
return result;
}
@ -2447,8 +2450,8 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
}
}
if (!inWebPage) {
if (_parent->pointInTime(fullRight, fullBottom, point, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, isRound ? InfoDisplayType::Background : InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
}
if (!bubble && _parent->displayRightAction()) {
@ -2810,7 +2813,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
if (!inWebPage) {
auto fullRight = usex + usew;
auto fullBottom = height();
_parent->drawInfo(p, fullRight, fullBottom, usex * 2 + usew, selected, InfoDisplayOverBackground);
_parent->drawInfo(p, fullRight, fullBottom, usex * 2 + usew, selected, InfoDisplayType::Background);
if (via || reply) {
int rectw = width() - usew - st::msgReplyPadding.left();
int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom();
@ -2850,8 +2853,8 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
}
}
HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistorySticker::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
@ -2904,8 +2907,8 @@ HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest requ
if (_parent->media() == this) {
auto fullRight = usex + usew;
auto fullBottom = height();
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (_parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
@ -3112,8 +3115,8 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T
p.drawTextLeft(nameleft, statustop, paintw, _phone);
}
HistoryTextState HistoryContact::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryContact::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
@ -3210,8 +3213,8 @@ void HistoryCall::draw(Painter &p, const QRect &r, TextSelection selection, Time
icon.paint(p, paintw - st::historyCallIconPosition.x() - icon.width(), st::historyCallIconPosition.y() - topMinus, paintw);
}
HistoryTextState HistoryCall::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryCall::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (QRect(0, 0, width(), height()).contains(point)) {
result.link = _link;
return result;
@ -3626,8 +3629,8 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T
}
}
HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryWebPage::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -3660,7 +3663,7 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = HistoryTextState(_parent, _title.getStateElidedLeft(
result = TextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -3676,13 +3679,13 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
if (_descriptionLines > 0) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = HistoryTextState(_parent, _description.getStateElidedLeft(
result = TextState(_parent, _description.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
descriptionRequest));
} else {
result = HistoryTextState(_parent, _description.getStateLeft(
result = TextState(_parent, _description.getStateLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -3703,7 +3706,7 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
auto attachLeft = padding.left() - bubble.left();
auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = width() - attachLeft - _attach->width();
result = _attach->getState(point - QPoint(attachLeft, attachTop), request);
result = _attach->textState(point - QPoint(attachLeft, attachTop), request);
if (result.link && !_data->document && _data->photo && _attach->isReadyForOpen()) {
if (_data->type == WebPageProfile || _data->type == WebPageVideo) {
@ -4059,8 +4062,8 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, Time
}
}
HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryGame::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -4083,7 +4086,7 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = HistoryTextState(_parent, _title.getStateElidedLeft(
result = TextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -4097,7 +4100,7 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
if (point.y() >= tshift && point.y() < tshift + _descriptionLines * lineHeight) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = HistoryTextState(_parent, _description.getStateElidedLeft(
result = TextState(_parent, _description.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -4125,7 +4128,7 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
result.link = _openl;
}
} else {
result = _attach->getState(point - QPoint(attachLeft, attachTop), request);
result = _attach->textState(point - QPoint(attachLeft, attachTop), request);
}
}
}
@ -4473,8 +4476,8 @@ void HistoryInvoice::draw(Painter &p, const QRect &r, TextSelection selection, T
}
}
HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryInvoice::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
@ -4496,7 +4499,7 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
if (point.y() >= tshift && point.y() < tshift + _titleHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleHeight / lineHeight;
result = HistoryTextState(_parent, _title.getStateElidedLeft(
result = TextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -4508,7 +4511,7 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
}
if (_descriptionHeight) {
if (point.y() >= tshift && point.y() < tshift + _descriptionHeight) {
result = HistoryTextState(_parent, _description.getStateLeft(
result = TextState(_parent, _description.getStateLeft(
point - QPoint(padding.left(), tshift),
paintw,
width(),
@ -4527,7 +4530,7 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
if (rtl()) attachLeft = width() - attachLeft - _attach->width();
if (QRect(attachLeft, tshift, _attach->width(), height() - tshift - bshift).contains(point)) {
result = _attach->getState(point - QPoint(attachLeft, attachTop), request);
result = _attach->textState(point - QPoint(attachLeft, attachTop), request);
}
}
@ -4753,7 +4756,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
_parent->drawInfo(p, fullRight, fullBottom, paintx * 2 + paintw, selected, InfoDisplayOverImage);
_parent->drawInfo(p, fullRight, fullBottom, paintx * 2 + paintw, selected, InfoDisplayType::Image);
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
@ -4762,8 +4765,8 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
}
}
HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest request) const {
auto result = HistoryTextState(_parent);
TextState HistoryLocation::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
auto symbolAdd = 0;
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
@ -4784,7 +4787,7 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
if (!_title.isEmpty()) {
auto titleh = qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
if (point.y() >= painty && point.y() < painty + titleh) {
result = HistoryTextState(_parent, _title.getStateLeft(
result = TextState(_parent, _title.getStateLeft(
point - QPoint(paintx + st::msgPadding.left(), painty),
textw,
width(),
@ -4798,7 +4801,7 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
if (!_description.isEmpty()) {
auto descriptionh = qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
if (point.y() >= painty && point.y() < painty + descriptionh) {
result = HistoryTextState(_parent, _description.getStateLeft(
result = TextState(_parent, _description.getStateLeft(
point - QPoint(paintx + st::msgPadding.left(), painty),
textw,
width(),
@ -4819,8 +4822,8 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);

View File

@ -141,7 +141,7 @@ public:
}
void draw(Painter &p, const QRect &clip, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
@ -171,10 +171,10 @@ public:
RectParts corners,
not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override;
HistoryTextState getStateGrouped(
TextState getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
@ -233,7 +233,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
@ -263,10 +263,10 @@ public:
RectParts corners,
not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override;
HistoryTextState getStateGrouped(
TextState getStateGrouped(
const QRect &geometry,
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
bool uploading() const override {
return _data->uploading();
@ -328,7 +328,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
void updatePressed(QPoint point) override;
[[nodiscard]] TextSelection adjustSelection(
@ -400,7 +400,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
@ -501,7 +501,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
@ -562,7 +562,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
@ -619,7 +619,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
@ -665,7 +665,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override;
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
bool hideMessageText() const override {
return false;
@ -772,7 +772,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override;
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
@ -780,9 +780,6 @@ public:
uint16 fullSelectionLength() const override {
return _title.length() + _description.length();
}
bool isAboveMessage() const override {
return true;
}
bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy.
}
@ -881,7 +878,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,
@ -962,7 +959,7 @@ public:
}
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection(
TextSelection selection,

View File

@ -666,14 +666,6 @@ HistoryWidget::HistoryWidget(
}
}
});
Auth().data().viewLayoutChanged(
) | rpl::start_with_next([this](auto view) {
if (view == view->data()->mainView()) {
if (view->isUnderCursor() && _list) {
_list->onUpdateSelected();
}
}
}, lifetime());
_topBar->membersShowAreaActive(
) | rpl::start_with_next([=](bool active) {
setMembersShowAreaActive(active);

View File

@ -10,43 +10,61 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/view/history_view_element.h"
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
namespace HistoryView {
TextState::TextState(not_null<const HistoryItem*> item)
: itemId(item->fullId()) {
}
HistoryTextState::HistoryTextState(
TextState::TextState(
not_null<const HistoryItem*> item,
const Text::StateResult &state)
: itemId(item->fullId())
, cursor(state.uponSymbol
? HistoryInTextCursorState
: HistoryDefaultCursorState)
? CursorState::Text
: CursorState::None)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
}
HistoryTextState::HistoryTextState(
TextState::TextState(
not_null<const HistoryItem*> item,
ClickHandlerPtr link)
: itemId(item->fullId())
, link(link) {
}
HistoryTextState::HistoryTextState(
TextState::TextState(
not_null<const HistoryView::Element*> view)
: HistoryTextState(view->data()) {
: TextState(view->data()) {
}
HistoryTextState::HistoryTextState(
TextState::TextState(
not_null<const HistoryView::Element*> view,
const Text::StateResult &state)
: HistoryTextState(view->data(), state) {
: TextState(view->data(), state) {
}
HistoryTextState::HistoryTextState(
TextState::TextState(
not_null<const HistoryView::Element*> view,
ClickHandlerPtr link)
: HistoryTextState(view->data(), link) {
: TextState(view->data(), link) {
}
TextState::TextState(
std::nullptr_t,
const Text::StateResult &state)
: cursor(state.uponSymbol
? CursorState::Text
: CursorState::None)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
}
TextState::TextState(std::nullptr_t, ClickHandlerPtr link)
: link(link) {
}
} // namespace HistoryView

View File

@ -7,58 +7,54 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace HistoryView {
class Element;
} // namespace HistoryView
class HistoryItem;
enum HistoryCursorState {
HistoryDefaultCursorState,
HistoryInTextCursorState,
HistoryInDateCursorState,
HistoryInForwardedCursorState,
namespace HistoryView {
class Element;
enum class PointState : char {
Outside,
Inside,
GroupPart,
};
enum class CursorState : char {
None,
Text,
Date,
Forwarded,
};
struct HistoryTextState {
HistoryTextState() = default;
HistoryTextState(not_null<const HistoryItem*> item);
HistoryTextState(
struct TextState {
TextState() = default;
TextState(not_null<const HistoryItem*> item);
TextState(
not_null<const HistoryItem*> item,
const Text::StateResult &state);
HistoryTextState(
TextState(
not_null<const HistoryItem*> item,
ClickHandlerPtr link);
HistoryTextState(not_null<const HistoryView::Element*> view);
HistoryTextState(
TextState(not_null<const HistoryView::Element*> view);
TextState(
not_null<const HistoryView::Element*> view,
const Text::StateResult &state);
HistoryTextState(
TextState(
not_null<const HistoryView::Element*> view,
ClickHandlerPtr link);
HistoryTextState(
TextState(
std::nullptr_t,
const Text::StateResult &state)
: cursor(state.uponSymbol
? HistoryInTextCursorState
: HistoryDefaultCursorState)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
}
HistoryTextState(std::nullptr_t, ClickHandlerPtr link)
: link(link) {
}
const Text::StateResult &state);
TextState(std::nullptr_t, ClickHandlerPtr link);
FullMsgId itemId;
HistoryCursorState cursor = HistoryDefaultCursorState;
CursorState cursor = CursorState::None;
ClickHandlerPtr link;
bool afterSymbol = false;
uint16 symbol = 0;
};
struct HistoryStateRequest {
struct StateRequest {
Text::StateRequest::Flags flags = Text::StateRequest::Flag::LookupLink;
Text::StateRequest forText() const {
Text::StateRequest result;
@ -67,8 +63,10 @@ struct HistoryStateRequest {
}
};
enum InfoDisplayType : char {
InfoDisplayDefault,
InfoDisplayOverImage,
InfoDisplayOverBackground,
enum class InfoDisplayType : char {
Default,
Image,
Background,
};
} // namespace HistoryView

View File

@ -17,12 +17,14 @@ class HistoryMessage;
class HistoryService;
class HistoryMedia;
class HistoryWebPage;
struct HistoryTextState;
struct HistoryStateRequest;
enum InfoDisplayType : char;
namespace HistoryView {
enum class PointState : char;
enum class InfoDisplayType : char;
struct StateRequest;
struct TextState;
enum class Context : char {
History,
Feed,
@ -166,10 +168,10 @@ public:
QRect clip,
TextSelection selection,
TimeMs ms) const = 0;
[[nodiscard]] virtual bool hasPoint(QPoint point) const = 0;
[[nodiscard]] virtual HistoryTextState getState(
[[nodiscard]] virtual PointState pointState(QPoint point) const = 0;
[[nodiscard]] virtual TextState textState(
QPoint point,
HistoryStateRequest request) const = 0;
StateRequest request) const = 0;
virtual void updatePressed(QPoint point) = 0;
virtual void drawInfo(
Painter &p,

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
#include "history/view/history_view_message.h"
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_cursor_state.h"
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
#include "mainwidget.h"
@ -44,6 +45,20 @@ constexpr auto kPreloadedScreensCountFull
} // namespace
ListWidget::MouseState::MouseState() : pointState(PointState::Outside) {
}
ListWidget::MouseState::MouseState(
FullMsgId itemId,
int height,
QPoint point,
PointState pointState)
: itemId(itemId)
, height(height)
, point(point)
, pointState(pointState) {
}
template <ListWidget::EnumItemsDirection direction, typename Method>
void ListWidget::enumerateItems(Method method) {
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
@ -501,40 +516,151 @@ bool ListWidget::hasSelectedItems() const {
return !_selected.empty();
}
bool ListWidget::applyItemSelection(
SelectedMap &applyTo,
FullMsgId itemId) const {
if (applyTo.size() >= MaxSelectedItems) {
return false;
bool ListWidget::overSelectedItems() const {
if (_overState.pointState == PointState::GroupPart) {
return _overItemExact
&& _selected.contains(_overItemExact->fullId());
} else if (_overState.pointState == PointState::Inside) {
return _overElement
&& isSelectedAsGroup(_selected, _overElement->data());
}
return false;
}
//bool ListWidget::applyItemSelection(
// SelectedMap &applyTo,
// FullMsgId itemId) const {
// if (applyTo.size() >= MaxSelectedItems) {
// return false;
// }
// auto [iterator, ok] = applyTo.try_emplace(
// itemId,
// SelectionData());
// if (!ok) {
// return false;
// }
// const auto item = App::histItemById(itemId);
// if (!item) {
// applyTo.erase(iterator);
// return false;
// }
// iterator->second.canDelete = item->canDelete();
// iterator->second.canForward = item->allowsForward();
// return true;
//}
//
//void ListWidget::toggleItemSelection(FullMsgId itemId) {
// auto it = _selected.find(itemId);
// if (it == _selected.cend()) {
// if (_selectedTextItem) {
// clearTextSelection();
// }
// if (applyItemSelection(_selected, itemId)) {
// repaintItem(itemId);
// pushSelectedItems();
// }
// } else {
// removeItemSelection(it);
// }
//}
bool ListWidget::isSelectedAsGroup(
const SelectedMap &applyTo,
not_null<HistoryItem*> item) const {
if (const auto group = Auth().data().groups().find(item)) {
for (const auto other : group->items) {
if (!applyTo.contains(other->fullId())) {
return false;
}
}
return true;
}
return applyTo.contains(item->fullId());
}
bool ListWidget::isGoodForSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
int &totalCount) const {
if (!IsServerMsgId(item->id) || item->serviceMsg()) {
return false;
} else if (!applyTo.contains(item->fullId())) {
++totalCount;
}
return (totalCount <= MaxSelectedItems);
}
bool ListWidget::addToSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item) const {
const auto itemId = item->fullId();
auto [iterator, ok] = applyTo.try_emplace(
itemId,
SelectionData());
if (!ok) {
return false;
}
const auto item = App::histItemById(itemId);
if (!item) {
applyTo.erase(iterator);
return false;
}
iterator->second.canDelete = item->canDelete();
iterator->second.canForward = item->allowsForward();
return true;
}
void ListWidget::toggleItemSelection(FullMsgId itemId) {
auto it = _selected.find(itemId);
if (it == _selected.cend()) {
if (_selectedTextItem) {
clearTextSelection();
}
if (applyItemSelection(_selected, itemId)) {
repaintItem(itemId);
pushSelectedItems();
bool ListWidget::removeFromSelection(
SelectedMap &applyTo,
FullMsgId itemId) const {
return applyTo.remove(itemId);
}
void ListWidget::changeSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
SelectAction action) const {
const auto itemId = item->fullId();
if (action == SelectAction::Invert) {
action = applyTo.contains(itemId)
? SelectAction::Deselect
: SelectAction::Select;
}
if (action == SelectAction::Select) {
auto already = int(applyTo.size());
if (isGoodForSelection(applyTo, item, already)) {
addToSelection(applyTo, item);
}
} else {
removeItemSelection(it);
removeFromSelection(applyTo, itemId);
}
}
void ListWidget::changeSelectionAsGroup(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
SelectAction action) const {
const auto group = Auth().data().groups().find(item);
if (!group) {
return changeSelection(applyTo, item, action);
}
if (action == SelectAction::Invert) {
action = isSelectedAsGroup(applyTo, item)
? SelectAction::Deselect
: SelectAction::Select;
}
auto already = int(applyTo.size());
const auto canSelect = [&] {
for (const auto other : group->items) {
if (!isGoodForSelection(applyTo, other, already)) {
return false;
}
}
return true;
}();
if (action == SelectAction::Select && canSelect) {
for (const auto other : group->items) {
addToSelection(applyTo, other);
}
} else {
for (const auto other : group->items) {
removeFromSelection(applyTo, other->fullId());
}
}
}
@ -543,21 +669,23 @@ bool ListWidget::isItemUnderPressSelected() const {
}
auto ListWidget::itemUnderPressSelection() -> SelectedMap::iterator {
return (_pressState.itemId && _pressState.inside)
return (_pressState.itemId
&& _pressState.pointState != PointState::Outside)
? _selected.find(_pressState.itemId)
: _selected.end();
}
auto ListWidget::itemUnderPressSelection() const
-> SelectedMap::const_iterator {
return (_pressState.itemId && _pressState.inside)
return (_pressState.itemId
&& _pressState.pointState != PointState::Outside)
? _selected.find(_pressState.itemId)
: _selected.end();
}
bool ListWidget::requiredToStartDragging(
not_null<Element*> view) const {
if (_mouseCursorState == HistoryInDateCursorState) {
if (_mouseCursorState == CursorState::Date) {
return true;
} else if (const auto media = view->media()) {
return media->type() == MediaTypeSticker;
@ -565,8 +693,8 @@ bool ListWidget::requiredToStartDragging(
return false;
}
bool ListWidget::isPressInSelectedText(HistoryTextState state) const {
if (state.cursor != HistoryInTextCursorState) {
bool ListWidget::isPressInSelectedText(TextState state) const {
if (state.cursor != CursorState::Text) {
return false;
}
if (!hasSelectedText()
@ -692,13 +820,13 @@ void ListWidget::checkMoveToOtherViewer() {
}
QString ListWidget::tooltipText() const {
const auto item = (_overItem && _mouseAction == MouseAction::None)
? _overItem->data().get()
const auto item = (_overElement && _mouseAction == MouseAction::None)
? _overElement->data().get()
: nullptr;
if (_mouseCursorState == HistoryInDateCursorState && item) {
if (_mouseCursorState == CursorState::Date && item) {
return item->date.toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat));
} else if (_mouseCursorState == HistoryInForwardedCursorState && item) {
} else if (_mouseCursorState == CursorState::Forwarded && item) {
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
return forwarded->text.originalText(
AllTextSelection,
@ -730,7 +858,7 @@ std::unique_ptr<Element> ListWidget::elementCreate(
bool ListWidget::elementUnderCursor(
not_null<const HistoryView::Element*> view) {
return (_overItem == view);
return (_overElement == view);
}
void ListWidget::elementAnimationAutoplayAsync(
@ -974,11 +1102,17 @@ void ListWidget::applyDragSelection() {
void ListWidget::applyDragSelection(SelectedMap &applyTo) const {
if (_dragSelectAction == DragSelectAction::Selecting) {
for (const auto itemId : _dragSelected) {
applyItemSelection(applyTo, itemId);
if (applyTo.size() >= MaxSelectedItems) {
break;
} else if (!applyTo.contains(itemId)) {
if (const auto item = App::histItemById(itemId)) {
addToSelection(applyTo, item);
}
}
}
} else if (_dragSelectAction == DragSelectAction::Deselecting) {
for (const auto itemId : _dragSelected) {
applyTo.remove(itemId);
removeFromSelection(applyTo, itemId);
}
}
}
@ -1132,7 +1266,7 @@ void ListWidget::trySwitchToWordSelection() {
&& hasSelectedText();
auto willSelectSome = (_mouseAction == MouseAction::None)
&& !hasSelectedItems();
auto checkSwitchToWordSelection = _overItem
auto checkSwitchToWordSelection = _overElement
&& (_mouseSelectType == TextSelectType::Letters)
&& (selectingSome || willSelectSome);
if (checkSwitchToWordSelection) {
@ -1141,19 +1275,19 @@ void ListWidget::trySwitchToWordSelection() {
}
void ListWidget::switchToWordSelection() {
Expects(_overItem != nullptr);
Expects(_overElement != nullptr);
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = _overItem->getState(_pressState.cursor, request);
if (dragState.cursor != HistoryInTextCursorState) {
auto dragState = _overElement->textState(_pressState.point, request);
if (dragState.cursor != CursorState::Text) {
return;
}
_mouseTextSymbol = dragState.symbol;
_mouseSelectType = TextSelectType::Words;
if (_mouseAction == MouseAction::None) {
_mouseAction = MouseAction::Selecting;
setTextSelection(_overItem, TextSelection(
setTextSelection(_overElement, TextSelection(
dragState.symbol,
dragState.symbol
));
@ -1184,8 +1318,10 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
ContextMenuRequest request;
request.link = ClickHandler::getActive();
request.view = _overItem;
request.overView = _overItem && _overState.inside;
request.view = _overElement;
// #TODO group part context menu using _overItemExact
request.overView = _overElement
&& (_overState.pointState != PointState::Outside);
request.selectedText = _selectedText;
const auto itemId = request.view
? request.view->data()->fullId()
@ -1202,12 +1338,12 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto pointInItem = mapPointToItem(
mapFromGlobal(_mousePosition),
request.view);
HistoryStateRequest stateRequest;
StateRequest stateRequest;
stateRequest.flags |= Text::StateRequest::Flag::LookupSymbol;
const auto dragState = request.view->getState(
const auto dragState = request.view->textState(
pointInItem,
stateRequest);
if (dragState.cursor == HistoryInTextCursorState
if (dragState.cursor == CursorState::Text
&& base::in_range(
dragState.symbol,
_selectedTextRange.from,
@ -1257,10 +1393,10 @@ void ListWidget::enterEventHook(QEvent *e) {
}
void ListWidget::leaveEventHook(QEvent *e) {
if (const auto view = _overItem) {
if (_overState.inside) {
if (const auto view = _overElement) {
if (_overState.pointState != PointState::Outside) {
repaintItem(view);
_overState.inside = false;
_overState.pointState = PointState::Outside;
}
}
ClickHandler::clearActive();
@ -1276,7 +1412,7 @@ void ListWidget::updateDragSelection() {
if (!_overState.itemId || !_pressState.itemId) {
clearDragSelection();
return;
} else if (_items.empty() || !_overItem || !_selectEnabled) {
} else if (_items.empty() || !_overElement || !_selectEnabled) {
return;
}
const auto pressItem = App::histItemById(_pressState.itemId);
@ -1284,7 +1420,7 @@ void ListWidget::updateDragSelection() {
return;
}
const auto overView = _overItem;
const auto overView = _overElement;
const auto pressView = viewForItem(pressItem);
const auto selectingUp = _delegate->listIsLessInOrder(
overView->data(),
@ -1309,14 +1445,37 @@ void ListWidget::updateDragSelection() {
[](auto view) { return view.get(); })
: end(_items);
Assert(from <= till);
const auto &groups = Auth().data().groups();
const auto changeItem = [&](not_null<HistoryItem*> item, bool add) {
const auto itemId = item->fullId();
if (add) {
_dragSelected.emplace(itemId);
} else {
_dragSelected.remove(itemId);
}
};
const auto changeGroup = [&](not_null<HistoryItem*> item, bool add) {
if (const auto group = groups.find(item)) {
for (const auto item : group->items) {
changeItem(item, add);
}
} else {
changeItem(item, add);
}
};
const auto changeView = [&](not_null<Element*> view, bool add) {
if (!view->isHiddenByGroup()) {
changeGroup(view->data(), add);
}
};
for (auto i = begin(_items); i != from; ++i) {
_dragSelected.remove((*i)->data()->fullId());
changeView(*i, false);
}
for (auto i = from; i != till; ++i) {
_dragSelected.emplace((*i)->data()->fullId());
changeView(*i, true);
}
for (auto i = till; i != end(_items); ++i) {
_dragSelected.remove((*i)->data()->fullId());
changeView(*i, false);
}
_dragSelectAction = [&] {
if (_dragSelected.empty()) {
@ -1363,7 +1522,7 @@ void ListWidget::mouseActionStart(
_pressState = _overState;
repaintItem(_overState.itemId);
}
const auto pressedItem = _overItem;
const auto pressedView = _overElement;
_mouseAction = MouseAction::None;
_pressWasInactive = _controller->window()->wasInactivePress();
@ -1371,18 +1530,24 @@ void ListWidget::mouseActionStart(
if (ClickHandler::getPressed()) {
_mouseAction = MouseAction::PrepareDrag;
} else if (hasSelectedItems()) {
if (overSelectedItems()) {
_mouseAction = MouseAction::PrepareDrag;
} else if (!_pressWasInactive) {
_mouseAction = MouseAction::PrepareSelect;
}
}
if (_mouseAction == MouseAction::None && pressedItem) {
if (_mouseAction == MouseAction::None && pressedView) {
validateTrippleClickStartTime();
HistoryTextState dragState;
TextState dragState;
auto startDistance = (globalPosition - _trippleClickPoint).manhattanLength();
auto validStartPoint = startDistance < QApplication::startDragDistance();
if (_trippleClickStartTime != 0 && validStartPoint) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = pressedItem->getState(_pressState.cursor, request);
if (dragState.cursor == HistoryInTextCursorState) {
setTextSelection(pressedItem, TextSelection(
dragState = pressedView->textState(_pressState.point, request);
if (dragState.cursor == CursorState::Text) {
setTextSelection(pressedView, TextSelection(
dragState.symbol,
dragState.symbol
));
@ -1392,23 +1557,22 @@ void ListWidget::mouseActionStart(
mouseActionUpdate();
_trippleClickStartTime = getms();
}
} else if (pressedItem) {
HistoryStateRequest request;
} else if (pressedView) {
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = pressedItem->getState(_pressState.cursor, request);
dragState = pressedView->textState(_pressState.point, request);
}
if (_mouseSelectType != TextSelectType::Paragraphs) {
_mouseTextSymbol = dragState.symbol;
if (isPressInSelectedText(dragState)) {
_mouseAction = MouseAction::PrepareDrag; // start text drag
} else if (!_pressWasInactive) {
if (requiredToStartDragging(pressedItem)) {
if (requiredToStartDragging(pressedView)) {
_mouseAction = MouseAction::PrepareDrag;
} else {
if (dragState.afterSymbol) ++_mouseTextSymbol;
;
if (!hasSelectedItems()) {
setTextSelection(pressedItem, TextSelection(
setTextSelection(pressedView, TextSelection(
_mouseTextSymbol,
_mouseTextSymbol));
_mouseAction = MouseAction::Selecting;
@ -1419,7 +1583,7 @@ void ListWidget::mouseActionStart(
}
}
}
if (!pressedItem) {
if (!pressedView) {
_mouseAction = MouseAction::None;
} else if (_mouseAction == MouseAction::None) {
mouseActionCancel();
@ -1432,7 +1596,7 @@ void ListWidget::mouseActionUpdate(const QPoint &globalPosition) {
}
void ListWidget::mouseActionCancel() {
_pressState = CursorState();
_pressState = MouseState();
_mouseAction = MouseAction::None;
clearDragSelection();
_wasSelectedText = false;
@ -1444,33 +1608,63 @@ void ListWidget::mouseActionFinish(
Qt::MouseButton button) {
mouseActionUpdate(globalPosition);
auto activated = ClickHandler::unpressed();
if (_mouseAction == MouseAction::Dragging) {
activated = nullptr;
}
auto pressState = base::take(_pressState);
repaintItem(pressState.itemId);
const auto toggleByHandler = [&](const ClickHandlerPtr &handler) {
if (_overElement) {
// If we are in selecting items mode perhaps we want to
// toggle selection instead of activating the pressed link.
if (const auto media = _overElement->media()) {
if (media->toggleSelectionByHandlerClick(handler)) {
return true;
}
}
}
return false;
};
auto activated = ClickHandler::unpressed();
auto simpleSelectionChange = pressState.itemId
&& pressState.inside
&& (pressState.pointState != PointState::Outside)
&& !_pressWasInactive
&& (button != Qt::RightButton)
&& (_mouseAction == MouseAction::PrepareDrag
|| _mouseAction == MouseAction::PrepareSelect);
&& (_mouseAction == MouseAction::PrepareSelect
|| _mouseAction == MouseAction::PrepareDrag);
auto needItemSelectionToggle = simpleSelectionChange
&& (!activated || toggleByHandler(activated))
&& hasSelectedItems();
auto needTextSelectionClear = simpleSelectionChange
&& hasSelectedText();
_wasSelectedText = false;
if (activated) {
if (_mouseAction == MouseAction::Dragging
|| _mouseAction == MouseAction::Selecting
|| needItemSelectionToggle) {
activated = nullptr;
} else if (activated) {
mouseActionCancel();
App::activateClickHandler(activated, button);
return;
}
if (needItemSelectionToggle) {
toggleItemSelection(pressState.itemId);
if (const auto item = App::histItemById(pressState.itemId)) {
clearTextSelection();
if (pressState.pointState == PointState::GroupPart) {
changeSelection(
_selected,
_overItemExact ? _overItemExact : item,
SelectAction::Invert);
} else {
changeSelectionAsGroup(
_selected,
item,
SelectAction::Invert);
}
pushSelectedItems();
}
} else if (needTextSelectionClear) {
clearTextSelection();
} else if (_mouseAction == MouseAction::Selecting) {
@ -1506,26 +1700,26 @@ void ListWidget::mouseActionUpdate() {
const auto view = strictFindItemByY(point.y());
const auto item = view ? view->data().get() : nullptr;
const auto itemPoint = mapPointToItem(point, view);
_overState = CursorState{
_overState = MouseState{
item ? item->fullId() : FullMsgId(),
view ? view->height() : 0,
itemPoint,
view ? view->hasPoint(itemPoint) : false
view ? view->pointState(itemPoint) : PointState::Outside
};
if (_overItem != view) {
repaintItem(_overItem);
_overItem = view;
repaintItem(_overItem);
if (_overElement != view) {
repaintItem(_overElement);
_overElement = view;
repaintItem(_overElement);
}
HistoryTextState dragState;
TextState dragState;
ClickHandlerHost *lnkhost = nullptr;
auto inTextSelection = _overState.inside
auto inTextSelection = (_overState.pointState != PointState::Outside)
&& (_overState.itemId == _pressState.itemId)
&& hasSelectedText();
if (view) {
auto cursorDeltaLength = [&] {
auto cursorDelta = (_overState.cursor - _pressState.cursor);
auto cursorDelta = (_overState.point - _pressState.point);
return cursorDelta.manhattanLength();
};
auto dragStartLength = [] {
@ -1540,14 +1734,15 @@ void ListWidget::mouseActionUpdate() {
_mouseAction = MouseAction::Selecting;
}
}
HistoryStateRequest request;
StateRequest request;
if (_mouseAction == MouseAction::Selecting) {
request.flags |= Text::StateRequest::Flag::LookupSymbol;
} else {
inTextSelection = false;
}
// #TODO enumerate dates like HistoryInner
dragState = view->getState(itemPoint, request);
dragState = view->textState(itemPoint, request);
_overItemExact = App::histItemById(dragState.itemId);
lnkhost = view;
if (!dragState.link
&& itemPoint.x() >= st::historyPhotoLeft
@ -1564,7 +1759,10 @@ void ListWidget::mouseActionUpdate() {
const auto message = view->data()->toHistoryMessage();
Assert(message != nullptr);
dragState.link = message->from()->openLink();
dragState = TextState(
nullptr,
message->displayFrom()->openLink());
_overItemExact = App::histItemById(dragState.itemId);
lnkhost = view;
return false;
}
@ -1578,8 +1776,8 @@ void ListWidget::mouseActionUpdate() {
Ui::Tooltip::Hide();
}
if (dragState.link
|| dragState.cursor == HistoryInDateCursorState
|| dragState.cursor == HistoryInForwardedCursorState) {
|| dragState.cursor == CursorState::Date
|| dragState.cursor == CursorState::Forwarded) {
Ui::Tooltip::Show(1000, this);
}
@ -1617,7 +1815,8 @@ void ListWidget::mouseActionUpdate() {
}
// Voice message seek support.
if (_pressState.inside && ClickHandler::getPressed()) {
if (_pressState.pointState != PointState::Outside
&& ClickHandler::getPressed()) {
if (const auto item = App::histItemById(_pressState.itemId)) {
if (const auto view = viewForItem(item)) {
auto adjustedPoint = mapPointToItem(point, view);
@ -1637,7 +1836,7 @@ style::cursor ListWidget::computeMouseCursor() const {
if (ClickHandler::getPressed() || ClickHandler::getActive()) {
return style::cur_pointer;
} else if (!hasSelectedItems()
&& (_mouseCursorState == HistoryInTextCursorState)) {
&& (_mouseCursorState == CursorState::Text)) {
return style::cur_text;
}
return style::cur_default;
@ -1651,10 +1850,10 @@ void ListWidget::performDrag() {
// if (!_selected.isEmpty() && _selected.cbegin().value() == FullSelection) {
// uponSelected = _selected.contains(_mouseActionItem);
// } else {
// HistoryStateRequest request;
// StateRequest request;
// request.flags |= Text::StateRequest::Flag::LookupSymbol;
// auto dragState = _mouseActionItem->getState(_dragStartPosition.x(), _dragStartPosition.y(), request);
// uponSelected = (dragState.cursor == HistoryInTextCursorState);
// auto dragState = _mouseActionItem->textState(_dragStartPosition.x(), _dragStartPosition.y(), request);
// uponSelected = (dragState.cursor == CursorState::Text);
// if (uponSelected) {
// if (_selected.isEmpty() ||
// _selected.cbegin().value() == FullSelection ||
@ -1705,7 +1904,7 @@ void ListWidget::performDrag() {
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
// if (auto pressedItem = App::pressedItem()) { // #TODO no App::
// pressedMedia = pressedItem->media();
// if (_mouseCursorState == HistoryInDateCursorState
// if (_mouseCursorState == CursorState::Date
// || (pressedMedia && pressedMedia->dragItem())) {
// Auth().data().setMimeForwardIds(
// Auth().data().itemOrItsGroup(pressedItem->data()));
@ -1779,7 +1978,8 @@ void ListWidget::refreshAttachmentsAtIndex(int index) {
return index;
}();
const auto till = [&] {
for (auto i = index + 1, count = int(_items.size()); i != count; ) {
const auto count = int(_items.size());
for (auto i = index + 1; i != count; ++i) {
if (!_items[i]->isHiddenByGroup()) {
return i + 1;
}
@ -1835,13 +2035,16 @@ void ListWidget::refreshItem(not_null<const Element*> view) {
void ListWidget::viewReplaced(not_null<const Element*> was, Element *now) {
if (_visibleTopItem == was) _visibleTopItem = now;
if (_scrollDateLastItem == was) _scrollDateLastItem = now;
if (_overItem == was) _overItem = now;
if (_overElement == was) _overElement = now;
}
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
if (_selectedTextItem == item) {
clearTextSelection();
}
if (_overItemExact == item) {
_overItemExact = nullptr;
}
const auto i = _views.find(item);
if (i == end(_views)) {
return;

View File

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "base/timer.h"
#include "data/data_messages.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_element.h"
namespace Ui {
@ -25,6 +24,10 @@ class Controller;
namespace HistoryView {
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
enum class Context : char;
struct SelectedItem {
@ -166,17 +169,24 @@ protected:
int resizeGetHeight(int newWidth) override;
private:
struct CursorState {
struct MouseState {
MouseState();
MouseState(
FullMsgId itemId,
int height,
QPoint point,
PointState pointState);
FullMsgId itemId;
int height = 0;
QPoint cursor;
bool inside = false;
QPoint point;
PointState pointState;
inline bool operator==(const CursorState &other) const {
inline bool operator==(const MouseState &other) const {
return (itemId == other.itemId)
&& (cursor == other.cursor);
&& (point == other.point);
}
inline bool operator!=(const CursorState &other) const {
inline bool operator!=(const MouseState &other) const {
return !(*this == other);
}
@ -192,6 +202,11 @@ private:
PrepareSelect,
Selecting,
};
enum class SelectAction {
Select,
Deselect,
Invert,
};
enum class EnumItemsDirection {
TopToBottom,
BottomToTop,
@ -202,6 +217,8 @@ private:
Deselecting,
};
using ScrollTopState = ListMemento::ScrollTopState;
using PointState = HistoryView::PointState;
using CursorState = HistoryView::CursorState;
void refreshViewer();
void updateAroundPositionFromRows();
@ -264,18 +281,42 @@ private:
const SelectedMap::const_iterator &i);
bool hasSelectedText() const;
bool hasSelectedItems() const;
bool overSelectedItems() const;
void clearTextSelection();
void clearSelected();
void setTextSelection(
not_null<Element*> view,
TextSelection selection);
bool applyItemSelection(SelectedMap &applyTo, FullMsgId itemId) const;
void toggleItemSelection(FullMsgId itemId);
//bool applyItemSelection(SelectedMap &applyTo, FullMsgId itemId) const;
//void toggleItemSelection(FullMsgId itemId);
bool isGoodForSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
int &totalCount) const;
bool addToSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item) const;
bool removeFromSelection(
SelectedMap &applyTo,
FullMsgId itemId) const;
void changeSelection(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
SelectAction action) const;
bool isSelectedAsGroup(
const SelectedMap &applyTo,
not_null<HistoryItem*> item) const;
void changeSelectionAsGroup(
SelectedMap &applyTo,
not_null<HistoryItem*> item,
SelectAction action) const;
SelectedMap::iterator itemUnderPressSelection();
SelectedMap::const_iterator itemUnderPressSelection() const;
bool isItemUnderPressSelected() const;
bool requiredToStartDragging(not_null<Element*> view) const;
bool isPressInSelectedText(HistoryTextState state) const;
bool isPressInSelectedText(TextState state) const;
void updateDragSelection();
void clearDragSelection();
void applyDragSelection();
@ -345,10 +386,11 @@ private:
MouseAction _mouseAction = MouseAction::None;
TextSelectType _mouseSelectType = TextSelectType::Letters;
QPoint _mousePosition;
CursorState _overState;
CursorState _pressState;
Element *_overItem = nullptr;
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
MouseState _overState;
MouseState _pressState;
Element *_overElement = nullptr;
HistoryItem *_overItemExact = nullptr;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;

View File

@ -435,31 +435,15 @@ void Message::draw(
if (entry) {
trect.setHeight(trect.height() - entry->height());
}
auto needDrawInfo = mediaOnBottom
? !(entry
? entry->customInfoLayout()
: media->customInfoLayout())
: true;
paintText(p, trect, selection);
if (mediaDisplayed) {
auto mediaAboveText = media->isAboveMessage();
auto mediaHeight = media->height();
auto mediaLeft = g.left();
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
if (!mediaAboveText) {
paintText(p, trect, selection);
}
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
p.translate(mediaLeft, mediaTop);
media->draw(p, clip.translated(-mediaLeft, -mediaTop), skipTextSelection(selection), ms);
p.translate(-mediaLeft, -mediaTop);
if (mediaAboveText) {
trect.setY(trect.y() + mediaHeight);
paintText(p, trect, selection);
} else {
needDrawInfo = !media->customInfoLayout();
}
} else {
paintText(p, trect, selection);
}
if (entry) {
auto entryLeft = g.left();
@ -472,8 +456,13 @@ void Message::draw(
entry->draw(p, clip.translated(-entryLeft, -entryTop), entrySelection, ms);
p.translate(-entryLeft, -entryTop);
}
const auto needDrawInfo = entry
? !entry->customInfoLayout()
: (mediaDisplayed
? !media->customInfoLayout()
: true);
if (needDrawInfo) {
drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayDefault);
drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayType::Default);
}
if (displayRightAction()) {
const auto fastShareSkip = snap(
@ -618,20 +607,56 @@ void Message::paintText(Painter &p, QRect &trect, TextSelection selection) const
item->_text.draw(p, trect.x(), trect.y(), trect.width(), style::al_left, 0, -1, selection);
}
bool Message::hasPoint(QPoint point) const {
PointState Message::pointState(QPoint point) const {
const auto g = countGeometry();
if (g.width() < 1) {
return false;
return PointState::Outside;
}
const auto media = this->media();
const auto item = message();
if (drawBubble()) {
return g.contains(point);
} else if (const auto media = this->media()) {
return media->hasPoint(point - g.topLeft());
} else {
return false;
if (!g.contains(point)) {
return PointState::Outside;
}
if (const auto mediaDisplayed = media && media->isDisplayed()) {
// Hack for grouped media point state.
auto entry = logEntryOriginal();
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
auto trect = g.marginsRemoved(st::msgPadding);
if (mediaOnBottom) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
}
//if (mediaOnTop) {
// trect.setY(trect.y() - st::msgPadding.top());
//} else {
// if (getStateFromName(point, trect, &result)) return result;
// if (getStateForwardedInfo(point, trect, &result, request)) return result;
// if (getStateReplyInfo(point, trect, &result)) return result;
// if (getStateViaBotIdInfo(point, trect, &result)) return result;
//}
if (entry) {
auto entryHeight = entry->height();
trect.setHeight(trect.height() - entryHeight);
}
auto mediaHeight = media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
return media->pointState(point - QPoint(mediaLeft, mediaTop));
}
}
return PointState::Inside;
} else if (media) {
return media->pointState(point - g.topLeft());
}
return PointState::Outside;
}
bool Message::displayFromPhoto() const {
@ -661,13 +686,13 @@ bool Message::hasFromPhoto() const {
Unexpected("Context in Message::hasFromPhoto.");
}
HistoryTextState Message::getState(
TextState Message::textState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
const auto item = message();
const auto media = this->media();
auto result = HistoryTextState(item);
auto result = TextState(item);
auto g = countGeometry();
if (g.width() < 1) {
@ -707,7 +732,7 @@ HistoryTextState Message::getState(
auto entryLeft = g.left();
auto entryTop = trect.y() + trect.height();
if (point.y() >= entryTop && point.y() < entryTop + entryHeight) {
result = entry->getState(
result = entry->textState(
point - QPoint(entryLeft, entryTop),
request);
result.symbol += item->_text.length() + (mediaDisplayed ? media->fullSelectionLength() : 0);
@ -720,28 +745,22 @@ HistoryTextState Message::getState(
: media->customInfoLayout())
: true;
if (mediaDisplayed) {
auto mediaAboveText = media->isAboveMessage();
auto mediaHeight = media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
result = media->getState(point - QPoint(mediaLeft, mediaTop), request);
result = media->textState(point - QPoint(mediaLeft, mediaTop), request);
result.symbol += item->_text.length();
} else {
if (mediaAboveText) {
trect.setY(trect.y() + mediaHeight);
}
if (trect.contains(point)) {
getStateText(point, trect, &result, request);
}
} else if (trect.contains(point)) {
getStateText(point, trect, &result, request);
}
} else if (trect.contains(point)) {
getStateText(point, trect, &result, request);
}
if (needDateCheck) {
if (pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayDefault)) {
result.cursor = HistoryInDateCursorState;
if (pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayType::Default)) {
result.cursor = CursorState::Date;
}
}
if (displayRightAction()) {
@ -761,14 +780,14 @@ HistoryTextState Message::getState(
}
}
} else if (media && media->isDisplayed()) {
result = media->getState(point - g.topLeft(), request);
result = media->textState(point - g.topLeft(), request);
result.symbol += item->_text.length();
}
if (keyboard && !item->isLogEntry()) {
auto keyboardTop = g.top() + g.height() + st::msgBotKbButton.margin;
if (QRect(g.left(), keyboardTop, g.width(), keyboardHeight).contains(point)) {
result.link = keyboard->getState(point - QPoint(g.left(), keyboardTop));
result.link = keyboard->getLink(point - QPoint(g.left(), keyboardTop));
return result;
}
}
@ -779,7 +798,7 @@ HistoryTextState Message::getState(
bool Message::getStateFromName(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
not_null<TextState*> outResult) const {
const auto item = message();
if (displayFromName()) {
const auto replyWidth = [&] {
@ -828,8 +847,8 @@ bool Message::getStateFromName(
bool Message::getStateForwardedInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
HistoryStateRequest request) const {
not_null<TextState*> outResult,
StateRequest request) const {
if (displayForwardedFrom()) {
const auto item = message();
auto forwarded = item->Get<HistoryMessageForwarded>();
@ -840,16 +859,16 @@ bool Message::getStateForwardedInfo(
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
*outResult = HistoryTextState(item, forwarded->text.getState(
*outResult = TextState(item, forwarded->text.getState(
point - trect.topLeft(),
trect.width(),
textRequest));
outResult->symbol = 0;
outResult->afterSymbol = false;
if (breakEverywhere) {
outResult->cursor = HistoryInForwardedCursorState;
outResult->cursor = CursorState::Forwarded;
} else {
outResult->cursor = HistoryDefaultCursorState;
outResult->cursor = CursorState::None;
}
return true;
}
@ -861,7 +880,7 @@ bool Message::getStateForwardedInfo(
bool Message::getStateReplyInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
not_null<TextState*> outResult) const {
const auto item = message();
if (auto reply = item->Get<HistoryMessageReply>()) {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
@ -879,7 +898,7 @@ bool Message::getStateReplyInfo(
bool Message::getStateViaBotIdInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
not_null<TextState*> outResult) const {
const auto item = message();
if (!displayFromName() && !item->Has<HistoryMessageForwarded>()) {
if (auto via = item->Get<HistoryMessageVia>()) {
@ -896,14 +915,14 @@ bool Message::getStateViaBotIdInfo(
bool Message::getStateText(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
HistoryStateRequest request) const {
not_null<TextState*> outResult,
StateRequest request) const {
if (!hasVisibleText()) {
return false;
}
const auto item = message();
if (trect.contains(point)) {
*outResult = HistoryTextState(item, item->_text.getState(
*outResult = TextState(item, item->_text.getState(
point - trect.topLeft(),
trect.width(),
request.forText()));
@ -954,10 +973,9 @@ void Message::updatePressed(QPoint point) {
auto needDateCheck = true;
if (mediaDisplayed) {
auto mediaAboveText = media->isAboveMessage();
auto mediaHeight = media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
media->updatePressed(point - QPoint(mediaLeft, mediaTop));
}
} else {
@ -1054,23 +1072,23 @@ void Message::drawInfo(
p.setFont(st::msgDateFont);
bool outbg = hasOutLayout();
bool invertedsprites = (type == InfoDisplayOverImage)
|| (type == InfoDisplayOverBackground);
bool invertedsprites = (type == InfoDisplayType::Image)
|| (type == InfoDisplayType::Background);
int32 infoRight = right, infoBottom = bottom;
switch (type) {
case InfoDisplayDefault:
case InfoDisplayType::Default:
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
p.setPen(selected
? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected)
: (outbg ? st::msgOutDateFg : st::msgInDateFg));
break;
case InfoDisplayOverImage:
case InfoDisplayType::Image:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgDateImgFg);
break;
case InfoDisplayOverBackground:
case InfoDisplayType::Background:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgServiceFg);
@ -1083,10 +1101,10 @@ void Message::drawInfo(
auto dateX = infoRight - infoW;
auto dateY = infoBottom - st::msgDateFont->height;
if (type == InfoDisplayOverImage) {
if (type == InfoDisplayType::Image) {
auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
} else if (type == InfoDisplayOverBackground) {
} else if (type == InfoDisplayType::Background) {
auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners);
}
@ -1143,11 +1161,11 @@ bool Message::pointInTime(
auto infoRight = right;
auto infoBottom = bottom;
switch (type) {
case InfoDisplayDefault:
case InfoDisplayType::Default:
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
break;
case InfoDisplayOverImage:
case InfoDisplayType::Image:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
break;
@ -1414,11 +1432,7 @@ void Message::updateMediaInBubbleState() {
mediaHasSomethingAbove = getMediaHasSomethingAbove();
}
if (hasVisibleText()) {
if (media->isAboveMessage()) {
mediaHasSomethingBelow = true;
} else {
mediaHasSomethingAbove = true;
}
mediaHasSomethingAbove = true;
}
const auto state = [&] {
if (mediaHasSomethingAbove) {

View File

@ -37,10 +37,10 @@ public:
QRect clip,
TextSelection selection,
TimeMs ms) const override;
bool hasPoint(QPoint point) const override;
HistoryTextState getState(
PointState pointState(QPoint point) const override;
TextState textState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
void updatePressed(QPoint point) override;
void drawInfo(
Painter &p,
@ -104,25 +104,25 @@ private:
bool getStateFromName(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
not_null<TextState*> outResult) const;
bool getStateForwardedInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
HistoryStateRequest request) const;
not_null<TextState*> outResult,
StateRequest request) const;
bool getStateReplyInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
not_null<TextState*> outResult) const;
bool getStateViaBotIdInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
not_null<TextState*> outResult) const;
bool getStateText(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
HistoryStateRequest request) const;
not_null<TextState*> outResult,
StateRequest request) const;
void updateMediaInBubbleState();
QRect countGeometry() const;

View File

@ -436,13 +436,13 @@ void Service::draw(
}
}
bool Service::hasPoint(QPoint point) const {
PointState Service::pointState(QPoint point) const {
const auto item = message();
const auto media = this->media();
auto g = countGeometry();
if (g.width() < 1) {
return false;
return PointState::Outside;
}
if (const auto dateh = displayedDateHeight()) {
@ -454,14 +454,14 @@ bool Service::hasPoint(QPoint point) const {
if (media) {
g.setHeight(g.height() - (st::msgServiceMargin.top() + media->height()));
}
return g.contains(point);
return g.contains(point) ? PointState::Inside : PointState::Outside;
}
HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const {
TextState Service::textState(QPoint point, StateRequest request) const {
const auto item = message();
const auto media = this->media();
auto result = HistoryTextState(item);
auto result = TextState(item);
auto g = countGeometry();
if (g.width() < 1) {
@ -485,21 +485,25 @@ HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) co
if (trect.contains(point)) {
auto textRequest = request.forText();
textRequest.align = style::al_center;
result = HistoryTextState(item, item->_text.getState(
result = TextState(item, item->_text.getState(
point - trect.topLeft(),
trect.width(),
textRequest));
if (auto gamescore = item->Get<HistoryServiceGameScore>()) {
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
if (!result.link
&& result.cursor == CursorState::Text
&& g.contains(point)) {
result.link = gamescore->lnk;
}
} else if (auto payment = item->Get<HistoryServicePayment>()) {
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
if (!result.link
&& result.cursor == CursorState::Text
&& g.contains(point)) {
result.link = payment->lnk;
}
}
} else if (media) {
result = media->getState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, st::msgServiceMargin.top() + g.height() + st::msgServiceMargin.top()), request);
result = media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, st::msgServiceMargin.top() + g.height() + st::msgServiceMargin.top()), request);
}
return result;
}

View File

@ -24,10 +24,10 @@ public:
QRect clip,
TextSelection selection,
TimeMs ms) const override;
bool hasPoint(QPoint point) const override;
HistoryTextState getState(
PointState pointState(QPoint point) const override;
TextState textState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
void updatePressed(QPoint point) override;
TextWithEntities selectedText(TextSelection selection) const override;
TextSelection adjustSelection(

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "history/history_item.h"
#include "history/history.h"
#include "history/view/history_view_cursor_state.h"
#include "window/themes/window_theme.h"
#include "window/window_controller.h"
#include "window/window_peer_menu.h"
@ -151,8 +152,8 @@ private:
};
bool ListWidget::IsAfter(
const CursorState &a,
const CursorState &b) {
const MouseState &a,
const MouseState &b) {
if (a.itemId != b.itemId) {
return (a.itemId < b.itemId);
}
@ -161,7 +162,7 @@ bool ListWidget::IsAfter(
return (xAfter + yAfter >= 0);
}
bool ListWidget::SkipSelectFromItem(const CursorState &state) {
bool ListWidget::SkipSelectFromItem(const MouseState &state) {
if (state.cursor.y() >= state.size.height()
|| state.cursor.x() >= state.size.width()) {
return true;
@ -169,7 +170,7 @@ bool ListWidget::SkipSelectFromItem(const CursorState &state) {
return false;
}
bool ListWidget::SkipSelectTillItem(const CursorState &state) {
bool ListWidget::SkipSelectTillItem(const MouseState &state) {
if (state.cursor.x() < 0 || state.cursor.y() < 0) {
return true;
}
@ -1440,10 +1441,10 @@ void ListWidget::trySwitchToWordSelection() {
void ListWidget::switchToWordSelection() {
Expects(_overLayout != nullptr);
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = _overLayout->getState(_pressState.cursor, request);
if (dragState.cursor != HistoryInTextCursorState) {
if (dragState.cursor != CursorState::Text) {
return;
}
_mouseTextSymbol = dragState.symbol;
@ -1535,15 +1536,15 @@ auto ListWidget::itemUnderPressSelection() const
bool ListWidget::requiredToStartDragging(
not_null<BaseLayout*> layout) const {
if (_mouseCursorState == HistoryInDateCursorState) {
if (_mouseCursorState == CursorState::Date) {
return true;
}
// return dynamic_cast<HistorySticker*>(layout->getMedia());
return false;
}
bool ListWidget::isPressInSelectedText(HistoryTextState state) const {
if (state.cursor != HistoryInTextCursorState) {
bool ListWidget::isPressInSelectedText(TextState state) const {
if (state.cursor != CursorState::Text) {
return false;
}
if (!hasSelectedText()
@ -1616,7 +1617,7 @@ void ListWidget::mouseActionUpdate(const QPoint &globalPosition) {
auto local = mapFromGlobal(_mousePosition);
auto point = clampMousePosition(local);
auto [layout, geometry, inside] = findItemByPoint(point);
auto state = CursorState {
auto state = MouseState{
GetUniversalId(layout),
geometry.size(),
point - geometry.topLeft(),
@ -1630,7 +1631,7 @@ void ListWidget::mouseActionUpdate(const QPoint &globalPosition) {
}
_overState = state;
HistoryTextState dragState;
TextState dragState;
ClickHandlerHost *lnkhost = nullptr;
auto inTextSelection = _overState.inside
&& (_overState.itemId == _pressState.itemId)
@ -1652,7 +1653,7 @@ void ListWidget::mouseActionUpdate(const QPoint &globalPosition) {
_mouseAction = MouseAction::Selecting;
}
}
HistoryStateRequest request;
StateRequest request;
if (_mouseAction == MouseAction::Selecting) {
request.flags |= Text::StateRequest::Flag::LookupSymbol;
} else {
@ -1709,7 +1710,7 @@ style::cursor ListWidget::computeMouseCursor() const {
if (ClickHandler::getPressed() || ClickHandler::getActive()) {
return style::cur_pointer;
} else if (!hasSelectedItems()
&& (_mouseCursorState == HistoryInTextCursorState)) {
&& (_mouseCursorState == CursorState::Text)) {
return style::cur_text;
}
return style::cur_default;
@ -1814,14 +1815,14 @@ void ListWidget::mouseActionStart(
}
if (_mouseAction == MouseAction::None && pressLayout) {
validateTrippleClickStartTime();
HistoryTextState dragState;
TextState dragState;
auto startDistance = (globalPosition - _trippleClickPoint).manhattanLength();
auto validStartPoint = startDistance < QApplication::startDragDistance();
if (_trippleClickStartTime != 0 && validStartPoint) {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = pressLayout->getState(_pressState.cursor, request);
if (dragState.cursor == HistoryInTextCursorState) {
if (dragState.cursor == CursorState::Text) {
TextSelection selStatus = { dragState.symbol, dragState.symbol };
if (selStatus != FullSelection && !hasSelectedItems()) {
clearSelected();
@ -1834,7 +1835,7 @@ void ListWidget::mouseActionStart(
}
}
} else {
HistoryStateRequest request;
StateRequest request;
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = pressLayout->getState(_pressState.cursor, request);
}
@ -1873,7 +1874,7 @@ void ListWidget::mouseActionStart(
}
void ListWidget::mouseActionCancel() {
_pressState = CursorState();
_pressState = MouseState();
_mouseAction = MouseAction::None;
clearDragSelection();
_wasSelectedText = false;
@ -1889,7 +1890,7 @@ void ListWidget::performDrag() {
uponSelected = isItemUnderPressSelected();
} else if (auto pressLayout = getExistingLayout(
_pressState.itemId)) {
HistoryStateRequest request;
StateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = pressLayout->getState(
_pressState.cursor,
@ -1932,7 +1933,7 @@ void ListWidget::performDrag() {
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
// if (auto pressedItem = _pressState.layout) {
// pressedMedia = pressedItem->getMedia();
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
// if (_mouseCursorState == CursorState::Date || (pressedMedia && pressedMedia->dragItem())) {
// Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem));
// forwardMimeType = qsl("application/x-td-forward");
// }

View File

@ -10,10 +10,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "info/media/info_media_widget.h"
#include "data/data_shared_media.h"
#include "history/view/history_view_cursor_state.h"
class DeleteMessagesBox;
namespace HistoryView {
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
} // namespace HistoryView
namespace Ui {
class PopupMenu;
} // namespace Ui
@ -80,6 +86,11 @@ protected:
void leaveEventHook(QEvent *e) override;
private:
struct Context;
class Section;
using CursorState = HistoryView::CursorState;
using TextState = HistoryView::TextState;
using StateRequest = HistoryView::StateRequest;
enum class MouseAction {
None,
PrepareDrag,
@ -94,8 +105,6 @@ private:
std::unique_ptr<BaseLayout> item;
bool stale = false;
};
struct Context;
class Section;
struct FoundItem {
not_null<BaseLayout*> layout;
QRect geometry;
@ -118,17 +127,17 @@ private:
Selecting,
Deselecting,
};
struct CursorState {
struct MouseState {
UniversalMsgId itemId = 0;
QSize size;
QPoint cursor;
bool inside = false;
inline bool operator==(const CursorState &other) const {
inline bool operator==(const MouseState &other) const {
return (itemId == other.itemId)
&& (cursor == other.cursor);
}
inline bool operator!=(const CursorState &other) const {
inline bool operator!=(const MouseState &other) const {
return !(*this == other);
}
@ -198,7 +207,7 @@ private:
SelectedMap::const_iterator itemUnderPressSelection() const;
bool isItemUnderPressSelected() const;
bool requiredToStartDragging(not_null<BaseLayout*> layout) const;
bool isPressInSelectedText(HistoryTextState state) const;
bool isPressInSelectedText(TextState state) const;
void applyDragSelection();
void applyDragSelection(SelectedMap &applyTo) const;
bool changeItemSelection(
@ -207,10 +216,10 @@ private:
TextSelection selection) const;
static bool IsAfter(
const CursorState &a,
const CursorState &b);
static bool SkipSelectFromItem(const CursorState &state);
static bool SkipSelectTillItem(const CursorState &state);
const MouseState &a,
const MouseState &b);
static bool SkipSelectFromItem(const MouseState &state);
static bool SkipSelectTillItem(const MouseState &state);
void markLayoutsStale();
void clearStaleLayouts();
@ -281,11 +290,11 @@ private:
MouseAction _mouseAction = MouseAction::None;
TextSelectType _mouseSelectType = TextSelectType::Letters;
QPoint _mousePosition;
CursorState _overState;
CursorState _pressState;
MouseState _overState;
MouseState _pressState;
BaseLayout *_overLayout = nullptr;
UniversalMsgId _contextUniversalId = 0;
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;
SelectedMap _selected;

View File

@ -28,6 +28,8 @@ namespace InlineBots {
namespace Layout {
namespace internal {
using TextState = HistoryView::TextState;
FileBase::FileBase(not_null<Context*> context, Result *result) : ItemBase(context, result) {
}
@ -208,9 +210,9 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
}
}
HistoryTextState Gif::getState(
TextState Gif::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
if (_delete && rtlpoint(point, _width).x() >= _width - st::stickerPanDeleteIconBg.width() && point.y() < st::stickerPanDeleteIconBg.height()) {
return { nullptr, _delete };
@ -402,9 +404,9 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context)
}
}
HistoryTextState Sticker::getState(
TextState Sticker::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
return { nullptr, _send };
}
@ -492,9 +494,9 @@ void Photo::paint(Painter &p, const QRect &clip, const PaintContext *context) co
}
}
HistoryTextState Photo::getState(
TextState Photo::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
return { nullptr, _send };
}
@ -637,9 +639,9 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co
}
}
HistoryTextState Video::getState(
TextState Video::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return { nullptr, _link };
}
@ -778,9 +780,9 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con
}
}
HistoryTextState File::getState(
TextState File::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (QRect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize).contains(point)) {
return { nullptr, getShownDocument()->loading() ? _cancel : _open };
} else {
@ -938,9 +940,9 @@ void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context)
}
}
HistoryTextState Contact::getState(
TextState Contact::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (!QRect(0, st::inlineRowMargin, st::msgFileSize, st::inlineThumbSize).contains(point)) {
auto left = (st::msgFileSize + st::inlineThumbSkip);
if (QRect(left, 0, _width - left, _height).contains(point)) {
@ -1075,9 +1077,9 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context)
}
}
HistoryTextState Article::getState(
TextState Article::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (_withThumb && QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return { nullptr, _link };
}
@ -1259,9 +1261,9 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
}
}
HistoryTextState Game::getState(
TextState Game::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
int left = st::inlineThumbSize + st::inlineThumbSkip;
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return { nullptr, _send };

View File

@ -60,9 +60,9 @@ public:
}
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -121,9 +121,9 @@ public:
}
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
PhotoData *getShownPhoto() const;
@ -153,9 +153,9 @@ public:
void preload() const override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -179,9 +179,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
ClickHandlerPtr _link;
@ -228,9 +228,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -292,9 +292,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
mutable QPixmap _thumb;
@ -312,9 +312,9 @@ public:
int resizeGetHeight(int width) override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
ClickHandlerPtr _url, _link;
@ -337,9 +337,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
void countFrameSize();

View File

@ -607,14 +607,14 @@ void Inner::updateSelected() {
int row = -1, col = -1, sel = -1;
ClickHandlerPtr lnk;
ClickHandlerHost *lnkhost = nullptr;
HistoryCursorState cursor = HistoryDefaultCursorState;
HistoryView::CursorState cursor = HistoryView::CursorState::None;
if (sy >= 0) {
row = 0;
for (int rows = _rows.size(); row < rows; ++row) {
if (sy < _rows.at(row).height) {
if (sy < _rows[row].height) {
break;
}
sy -= _rows.at(row).height;
sy -= _rows[row].height;
}
}
if (sx >= 0 && row >= 0 && row < _rows.size()) {
@ -632,7 +632,9 @@ void Inner::updateSelected() {
}
if (col < inlineItems.size()) {
sel = row * MatrixRowShift + col;
auto result = inlineItems[col]->getState(QPoint(sx, sy), HistoryStateRequest());
auto result = inlineItems[col]->getState(
QPoint(sx, sy),
HistoryView::StateRequest());
lnk = result.link;
cursor = result.cursor;
lnkhost = inlineItems[col];

View File

@ -223,9 +223,9 @@ msp mst paf pif ps1 reg rgs sct shb shs u3p vb vbe vbs vbscript ws wsf\
return (lastDotIndex >= 0) && (executableTypes->indexOf(filename.mid(lastDotIndex + 1).toLower()) >= 0);
}
[[nodiscard]] HistoryTextState LayoutItemBase::getState(
[[nodiscard]] HistoryView::TextState LayoutItemBase::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
return {};
}

View File

@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/runtime_composer.h"
struct HistoryTextState;
struct HistoryStateRequest;
namespace HistoryView {
struct TextState;
struct StateRequest;
} // namespace HistoryView
constexpr auto FullSelection = TextSelection { 0xFFFF, 0xFFFF };
@ -82,6 +84,9 @@ class LayoutItemBase
: public RuntimeComposer<LayoutItemBase>
, public ClickHandlerHost {
public:
using TextState = HistoryView::TextState;
using StateRequest = HistoryView::StateRequest;
LayoutItemBase() {
}
@ -101,9 +106,9 @@ public:
return _height;
}
[[nodiscard]] virtual HistoryTextState getState(
[[nodiscard]] virtual TextState getState(
QPoint point,
HistoryStateRequest request) const;
StateRequest request) const;
[[nodiscard]] virtual TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const;

View File

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "history/view/history_view_cursor_state.h"
#include "ui/effects/round_checkbox.h"
#include "ui/text_options.h"
@ -33,6 +34,8 @@ namespace Overview {
namespace Layout {
namespace {
using TextState = HistoryView::TextState;
TextParseOptions _documentNameOptions = {
TextParseMultiline | TextParseRichText | TextParseLinks | TextParseMarkdown, // flags
0, // maxw
@ -350,9 +353,9 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const
paintCheckbox(p, { checkLeft, checkTop }, selected, context);
}
HistoryTextState Photo::getState(
TextState Photo::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
if (hasPoint(point)) {
return { parent(), _link };
}
@ -505,9 +508,9 @@ bool Video::iconAnimated() const {
return true;
}
HistoryTextState Video::getState(
TextState Video::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
bool loaded = _data->loaded();
if (hasPoint(point)) {
@ -676,9 +679,9 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
paintCheckbox(p, { checkLeft, checkTop }, selected, context);
}
HistoryTextState Voice::getState(
TextState Voice::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
const auto loaded = _data->loaded();
const auto nameleft = _st.songPadding.left()
@ -702,7 +705,7 @@ HistoryTextState Voice::getState(
: _openl;
return { parent(), link };
}
auto result = HistoryTextState(parent());
auto result = TextState(parent());
const auto statusmaxwidth = _width - nameleft - nameright;
const auto statusrect = rtlrect(
nameleft,
@ -714,7 +717,9 @@ HistoryTextState Voice::getState(
if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) {
auto textState = _details.getStateLeft(point - QPoint(nameleft, statustop), _width, _width);
result.link = textState.link;
result.cursor = textState.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState;
result.cursor = textState.uponSymbol
? HistoryView::CursorState::Text
: HistoryView::CursorState::None;
}
}
const auto namewidth = std::min(
@ -1000,9 +1005,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
paintCheckbox(p, { checkLeft, checkTop }, selected, context);
}
HistoryTextState Document::getState(
TextState Document::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
const auto loaded = _data->loaded()
|| Local::willStickerImageLoad(_data->mediaKey());
const auto wthumb = withThumb();
@ -1426,9 +1431,9 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P
paintCheckbox(p, { checkLeft, checkTop }, selected, context);
}
HistoryTextState Link::getState(
TextState Link::getState(
QPoint point,
HistoryStateRequest request) const {
StateRequest request) const {
int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left;
if (rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width).contains(point)) {
return { parent(), _photol };

View File

@ -12,8 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/radial_animation.h"
#include "styles/style_overview.h"
struct HistoryTextState;
struct HistoryStateRequest;
class HistoryMedia;
namespace style {
@ -202,9 +200,9 @@ public:
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
private:
not_null<PhotoData*> _data;
@ -224,9 +222,9 @@ public:
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
protected:
float64 dataProgress() const override;
@ -255,9 +253,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
protected:
float64 dataProgress() const override;
@ -290,9 +288,9 @@ public:
void initDimensions() override;
void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
virtual DocumentData *getDocument() const override {
return _data;
@ -334,9 +332,9 @@ public:
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override;
HistoryTextState getState(
TextState getState(
QPoint point,
HistoryStateRequest request) const override;
StateRequest request) const override;
protected:
const style::RoundCheckbox &checkboxStyle() const override;