Fix shortcut messages sizing / emoji panel.

This commit is contained in:
John Preston 2024-02-29 14:41:17 +04:00
parent aad8e989d8
commit 8545a14763
21 changed files with 253 additions and 82 deletions

View file

@ -89,6 +89,9 @@ mtpRequestId EditMessage(
: emptyFlag)
| (options.scheduled
? MTPmessages_EditMessage::Flag::f_schedule_date
: emptyFlag)
| (item->isBusinessShortcut()
? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
: emptyFlag);
const auto id = item->isScheduled()
@ -105,7 +108,7 @@ mtpRequestId EditMessage(
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled),
MTPint() // quick_reply_shortcut_id
MTP_int(item->shortcutId())
)).done([=](
const MTPUpdates &result,
[[maybe_unused]] mtpRequestId requestId) {

View file

@ -895,6 +895,7 @@ void EditCaptionBox::save() {
auto options = Api::SendOptions();
options.scheduled = item->isScheduled() ? item->date() : 0;
options.shortcutId = item->shortcutId();
if (!_preparedList.files.empty()) {
if ((_albumType != Ui::AlbumType::None)

View file

@ -23,6 +23,7 @@ struct ComposeFeatures {
bool autocompleteHashtags = true;
bool autocompleteMentions = true;
bool autocompleteCommands = true;
bool commonTabbedPanel = true;
};
} // namespace ChatHelpers

View file

@ -60,7 +60,8 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
MTP_int(data.vttl_period().value_or_empty()));
}, [&](const MTPDmessage &data) {
return MTP_message(
MTP_flags(data.vflags().v | MTPDmessage::Flag::f_quick_reply_shortcut_id),
MTP_flags(data.vflags().v
| MTPDmessage::Flag::f_quick_reply_shortcut_id),
data.vid(),
data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
MTPint(), // from_boosts_applied

View file

@ -96,6 +96,18 @@ public:
[[nodiscard]] static constexpr DraftKey ScheduledEdit() {
return kScheduledDraftIndex + kEditDraftShift;
}
[[nodiscard]] static constexpr DraftKey Shortcut(
BusinessShortcutId shortcutId) {
return (shortcutId < 0 || shortcutId >= ServerMaxMsgId)
? None()
: (kShortcutDraftShift + shortcutId);
}
[[nodiscard]] static constexpr DraftKey ShortcutEdit(
BusinessShortcutId shortcutId) {
return (shortcutId < 0 || shortcutId >= ServerMaxMsgId)
? None()
: (kShortcutDraftShift + kEditDraftShift + shortcutId);
}
[[nodiscard]] static constexpr DraftKey FromSerialized(qint64 value) {
return value;
@ -156,6 +168,7 @@ private:
static constexpr auto kScheduledDraftIndex = -3;
static constexpr auto kEditDraftShift = ServerMaxMsgId.bare;
static constexpr auto kCloudDraftShift = 2 * ServerMaxMsgId.bare;
static constexpr auto kShortcutDraftShift = 3 * ServerMaxMsgId.bare;
static constexpr auto kEditDraftShiftOld = 0x3FFF'FFFF;
int64 _value = 0;

View file

@ -108,6 +108,7 @@ struct EntryState {
Replies,
SavedSublist,
ContextMenu,
ShortcutMessages,
};
Key key;

View file

@ -2108,7 +2108,7 @@ bool HistoryItem::allowsEdit(TimeId now) const {
}
bool HistoryItem::canBeEdited() const {
if ((!isRegular() && !isScheduled())
if ((!isRegular() && !isScheduled() && !isBusinessShortcut())
|| Has<HistoryMessageVia>()
|| Has<HistoryMessageForwarded>()) {
return false;

View file

@ -114,6 +114,9 @@ using ForwardPanel = Controls::ForwardPanel;
} // namespace
const ChatHelpers::PauseReason kDefaultPanelsLevel
= ChatHelpers::PauseReason::TabbedPanel;
class FieldHeader final : public Ui::RpWidget {
public:
FieldHeader(
@ -760,7 +763,10 @@ MessageToEdit FieldHeader::queryToEdit() {
}
return {
.fullId = item->fullId(),
.options = { .scheduled = item->isScheduled() ? item->date() : 0 },
.options = {
.scheduled = item->isScheduled() ? item->date() : 0,
.shortcutId = item->shortcutId(),
},
};
}
@ -788,21 +794,24 @@ ComposeControls::ComposeControls(
: st::defaultComposeControls)
, _features(descriptor.features)
, _parent(parent)
, _panelsParent(descriptor.panelsParent
? descriptor.panelsParent
: _parent.get())
, _show(std::move(descriptor.show))
, _session(&_show->session())
, _regularWindow(descriptor.regularWindow)
, _ownedSelector(_regularWindow
, _ownedSelector((_regularWindow && _features.commonTabbedPanel)
? nullptr
: std::make_unique<ChatHelpers::TabbedSelector>(
_parent,
_panelsParent,
ChatHelpers::TabbedSelectorDescriptor{
.show = _show,
.st = _st.tabbed,
.level = Window::GifPauseReason::TabbedPanel,
.level = descriptor.panelsLevel,
.mode = ChatHelpers::TabbedSelector::Mode::Full,
.features = _features,
}))
, _selector(_regularWindow
, _selector((_regularWindow && _features.commonTabbedPanel)
? _regularWindow->tabbedSelector()
: not_null(_ownedSelector.get()))
, _mode(descriptor.mode)
@ -876,6 +885,12 @@ void ComposeControls::updateTopicRootId(MsgId topicRootId) {
_header->updateTopicRootId(_topicRootId);
}
void ComposeControls::updateShortcutId(BusinessShortcutId shortcutId) {
unregisterDraftSources();
_shortcutId = shortcutId;
registerDraftSource();
}
void ComposeControls::setHistory(SetHistoryArgs &&args) {
_showSlowmodeError = std::move(args.showSlowmodeError);
_sendActionFactory = std::move(args.sendActionFactory);
@ -1592,7 +1607,7 @@ void ComposeControls::initField() {
&& Data::AllowEmojiWithoutPremium(_history->peer, emoji);
};
const auto suggestions = Ui::Emoji::SuggestionsController::Init(
_parent,
_panelsParent,
_field,
_session,
{
@ -1828,6 +1843,10 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const {
return (type == DraftType::Edit)
? Key::ScheduledEdit()
: Key::Scheduled();
case Section::ShortcutMessages:
return (type == DraftType::Edit)
? Key::ShortcutEdit(_shortcutId)
: Key::Shortcut(_shortcutId);
}
return Key::None();
}
@ -2053,7 +2072,9 @@ rpl::producer<SendActionUpdate> ComposeControls::sendActionUpdates() const {
}
void ComposeControls::initTabbedSelector() {
if (!_regularWindow || _regularWindow->hasTabbedSelectorOwnership()) {
if (!_regularWindow
|| !_features.commonTabbedPanel
|| _regularWindow->hasTabbedSelectorOwnership()) {
createTabbedPanel();
} else {
setTabbedPanel(nullptr);
@ -2686,7 +2707,7 @@ void ComposeControls::updateAttachBotsMenu() {
return;
}
_attachBotsMenu = InlineBots::MakeAttachBotsMenu(
_parent,
_panelsParent,
_regularWindow,
_history->peer,
_sendActionFactory,
@ -2729,7 +2750,7 @@ void ComposeControls::escape() {
bool ComposeControls::pushTabbedSelectorToThirdSection(
not_null<Data::Thread*> thread,
const Window::SectionShow &params) {
if (!_tabbedPanel || !_regularWindow) {
if (!_tabbedPanel || !_regularWindow || !_features.commonTabbedPanel) {
return true;
//} else if (!_canSendMessages) {
// Core::App().settings().setTabbedReplacedWithInfo(true);
@ -2764,7 +2785,7 @@ void ComposeControls::createTabbedPanel() {
.nonOwnedSelector = _ownedSelector ? nullptr : _selector.get(),
};
setTabbedPanel(std::make_unique<TabbedPanel>(
_parent,
_panelsParent,
std::move(descriptor)));
_tabbedPanel->setDesiredHeightValues(
st::emojiPanHeightRatio,
@ -2787,7 +2808,7 @@ void ComposeControls::setTabbedPanel(
}
void ComposeControls::toggleTabbedSelectorMode() {
if (!_history || !_regularWindow) {
if (!_history || !_regularWindow || !_features.commonTabbedPanel) {
return;
}
if (_tabbedPanel) {
@ -3248,7 +3269,7 @@ void ComposeControls::applyInlineBotQuery(
}
if (!_inlineResults) {
_inlineResults = std::make_unique<InlineBots::Layout::Widget>(
_parent,
_panelsParent,
_regularWindow);
_inlineResults->setResultSelectedCallback([=](
InlineBots::ResultSelected result) {

View file

@ -37,6 +37,7 @@ class TabbedSelector;
struct FileChosen;
struct PhotoChosen;
class Show;
enum class PauseReason;
} // namespace ChatHelpers
namespace Data {
@ -95,6 +96,8 @@ enum class ComposeControlsMode {
Scheduled,
};
extern const ChatHelpers::PauseReason kDefaultPanelsLevel;
struct ComposeControlsDescriptor {
const style::ComposeControls *stOverride = nullptr;
std::shared_ptr<ChatHelpers::Show> show;
@ -104,6 +107,8 @@ struct ComposeControlsDescriptor {
Window::SessionController *regularWindow = nullptr;
rpl::producer<ChatHelpers::FileChosen> stickerOrEmojiChosen;
rpl::producer<QString> customPlaceholder;
QWidget *panelsParent = nullptr;
ChatHelpers::PauseReason panelsLevel = kDefaultPanelsLevel;
QString voiceCustomCancelText;
bool voiceLockFromBottom = false;
ChatHelpers::ComposeFeatures features;
@ -137,6 +142,7 @@ public:
[[nodiscard]] Main::Session &session() const;
void setHistory(SetHistoryArgs &&args);
void updateTopicRootId(MsgId topicRootId);
void updateShortcutId(BusinessShortcutId shortcutId);
void setCurrentDialogsEntryState(Dialogs::EntryState state);
[[nodiscard]] PeerData *sendAsPeer() const;
@ -343,6 +349,7 @@ private:
const style::ComposeControls &_st;
const ChatHelpers::ComposeFeatures _features;
const not_null<QWidget*> _parent;
const not_null<QWidget*> _panelsParent;
const std::shared_ptr<ChatHelpers::Show> _show;
const not_null<Main::Session*> _session;
@ -353,6 +360,7 @@ private:
History *_history = nullptr;
MsgId _topicRootId = 0;
BusinessShortcutId _shortcutId = 0;
Fn<bool()> _showSlowmodeError;
Fn<Api::SendAction()> _sendActionFactory;
rpl::variable<int> _slowmodeSecondsLeft;

View file

@ -551,7 +551,7 @@ void BottomInfo::layoutReactionsText() {
QSize BottomInfo::countOptimalSize() {
if (_data.flags & Data::Flag::Shortcut) {
return { st::historySendStateSpace / 2, st::msgDateFont->height };
return { st::historyShortcutStateSpace, st::msgDateFont->height };
}
auto width = 0;
if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {

View file

@ -102,7 +102,9 @@ bool HasEditMessageAction(
|| item->hasFailed()
|| item->isEditingMedia()
|| !request.selectedItems.empty()
|| (context != Context::History && context != Context::Replies)) {
|| (context != Context::History
&& context != Context::Replies
&& context != Context::ShortcutMessages)) {
return false;
}
const auto peer = item->history()->peer;

View file

@ -2166,6 +2166,21 @@ void ListWidget::paintEvent(QPaintEvent *e) {
context.translate(0, top);
p.translate(0, -top);
paintUserpics(p, context, clip);
paintDates(p, context, clip);
_reactionsManager->paint(p, context);
_emojiInteractions->paint(p);
}
void ListWidget::paintUserpics(
Painter &p,
const Ui::ChatPaintContext &context,
QRect clip) {
if (_context == Context::ShortcutMessages) {
return;
}
const auto session = &controller()->session();
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
// stop the enumeration if the userpic is below the painted rect
if (userpicTop >= clip.top() + clip.height()) {
@ -2210,6 +2225,15 @@ void ListWidget::paintEvent(QPaintEvent *e) {
}
return true;
});
}
void ListWidget::paintDates(
Painter &p,
const Ui::ChatPaintContext &context,
QRect clip) {
if (_context == Context::ShortcutMessages) {
return;
}
auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
@ -2256,9 +2280,6 @@ void ListWidget::paintEvent(QPaintEvent *e) {
}
return true;
});
_reactionsManager->paint(p, context);
_emojiInteractions->paint(p);
}
void ListWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {

View file

@ -600,6 +600,15 @@ private:
void showPremiumStickerTooltip(
not_null<const HistoryView::Element*> view);
void paintUserpics(
Painter &p,
const Ui::ChatPaintContext &context,
QRect clip);
void paintDates(
Painter &p,
const Ui::ChatPaintContext &context,
QRect clip);
// 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.
//

View file

@ -167,7 +167,12 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
const auto bottom = top + height;
_innerDesiredHeight = desired;
_innerWrap->setVisibleTopBottom(top, bottom);
LOG(("TOP: %1, HEIGHT: %2, DESIRED: %3, TILL: %4").arg(top).arg(height).arg(desired).arg(std::max(desired - bottom, 0)));
_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
//const auto bottom = _scroll->scrollTop() + _scroll->height();
//_innerDesiredHeight = desired;
//_innerWrap->setVisibleTopBottom(_scroll->scrollTop(), bottom);
//_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
}, _innerWrap->lifetime());
return _innerWrap->entity();
@ -217,7 +222,12 @@ rpl::producer<int> ContentWidget::desiredHeightValue() const {
_innerWrap->entity()->desiredHeightValue(),
_scrollTopSkip.value(),
_scrollBottomSkip.value()
) | rpl::map(_1 + _2 + _3);
//) | rpl::map(_1 + _2 + _3);
) | rpl::map([=](int desired, int, int) {
return desired
+ _scrollTopSkip.current()
+ _scrollBottomSkip.current();
});
}
rpl::producer<bool> ContentWidget::desiredShadowVisibility() const {

View file

@ -30,7 +30,7 @@ LayerWidget::LayerWidget(
not_null<Window::SessionController*> controller,
not_null<Memento*> memento)
: _controller(controller)
, _content(this, controller, Wrap::Layer, memento) {
, _contentWrap(this, controller, Wrap::Layer, memento) {
setupHeightConsumers();
controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
}
@ -39,7 +39,7 @@ LayerWidget::LayerWidget(
not_null<Window::SessionController*> controller,
not_null<MoveMemento*> memento)
: _controller(controller)
, _content(memento->takeContent(this, Wrap::Layer)) {
, _contentWrap(memento->takeContent(this, Wrap::Layer)) {
setupHeightConsumers();
controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
}
@ -64,17 +64,17 @@ void LayerWidget::floatPlayerToggleGifsPaused(bool paused) {
auto LayerWidget::floatPlayerGetSection(Window::Column column)
-> not_null<::Media::Player::FloatSectionDelegate*> {
Expects(_content != nullptr);
Expects(_contentWrap != nullptr);
return _content;
return _contentWrap;
}
void LayerWidget::floatPlayerEnumerateSections(Fn<void(
not_null<::Media::Player::FloatSectionDelegate*> widget,
Window::Column widgetColumn)> callback) {
Expects(_content != nullptr);
Expects(_contentWrap != nullptr);
callback(_content, Window::Column::Second);
callback(_contentWrap, Window::Column::Second);
}
bool LayerWidget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
@ -87,9 +87,9 @@ void LayerWidget::floatPlayerDoubleClickEvent(
}
void LayerWidget::setupHeightConsumers() {
Expects(_content != nullptr);
Expects(_contentWrap != nullptr);
_content->scrollTillBottomChanges(
_contentWrap->scrollTillBottomChanges(
) | rpl::filter([this] {
if (!_inResize) {
return true;
@ -100,10 +100,10 @@ void LayerWidget::setupHeightConsumers() {
resizeToWidth(width());
}, lifetime());
_content->grabbingForExpanding(
_contentWrap->grabbingForExpanding(
) | rpl::start_with_next([=](bool grabbing) {
if (grabbing) {
_savedHeight = _contentHeight;
_savedHeight = _contentWrapHeight;
_savedHeightAnimation = base::take(_heightAnimation);
setContentHeight(_desiredHeight);
} else {
@ -112,7 +112,7 @@ void LayerWidget::setupHeightConsumers() {
}
}, lifetime());
_content->desiredHeightValue(
_contentWrap->desiredHeightValue(
) | rpl::start_with_next([this](int height) {
if (!height) {
// New content arrived.
@ -128,32 +128,32 @@ void LayerWidget::setupHeightConsumers() {
_heightAnimated = true;
_heightAnimation.start([=] {
setContentHeight(_heightAnimation.value(_desiredHeight));
}, _contentHeight, _desiredHeight, st::slideDuration);
}, _contentWrapHeight, _desiredHeight, st::slideDuration);
resizeToWidth(width());
}
}, lifetime());
}
void LayerWidget::setContentHeight(int height) {
if (_contentHeight == height) {
if (_contentWrapHeight == height) {
return;
}
_contentHeight = height;
LOG(("CONTENT WRAP HEIGHT: %1 -> %2").arg(_contentWrapHeight).arg(height));
_contentWrapHeight = height;
if (_inResize) {
_pendingResize = true;
} else if (_content) {
} else if (_contentWrap) {
resizeToWidth(width());
}
}
void LayerWidget::showFinished() {
floatPlayerShowVisible();
_content->showFast();
_contentWrap->showFast();
}
void LayerWidget::parentResized() {
if (!_content) {
if (!_contentWrap) {
return;
}
@ -163,7 +163,7 @@ void LayerWidget::parentResized() {
Ui::FocusPersister persister(this);
restoreFloatPlayerDelegate();
auto memento = std::make_shared<MoveMemento>(std::move(_content));
auto memento = std::make_shared<MoveMemento>(std::move(_contentWrap));
// We want to call hideSpecialLayer synchronously to avoid glitches,
// but we can't destroy LayerStackWidget from its' resizeEvent,
@ -209,7 +209,7 @@ bool LayerWidget::takeToThirdSection() {
//
//Ui::FocusPersister persister(this);
//auto localCopy = _controller;
//auto memento = MoveMemento(std::move(_content));
//auto memento = MoveMemento(std::move(_contentWrap));
//localCopy->hideSpecialLayer(anim::type::instant);
//// When creating third section in response to the window
@ -235,7 +235,7 @@ bool LayerWidget::takeToThirdSection() {
bool LayerWidget::showSectionInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) {
if (_content && _content->showInternal(memento, params)) {
if (_contentWrap && _contentWrap->showInternal(memento, params)) {
if (params.activation != anim::activation::background) {
_controller->parentController()->hideLayer();
}
@ -245,7 +245,7 @@ bool LayerWidget::showSectionInternal(
}
bool LayerWidget::closeByOutsideClick() const {
return _content ? _content->closeByOutsideClick() : true;
return _contentWrap ? _contentWrap->closeByOutsideClick() : true;
}
int LayerWidget::MinimalSupportedWidth() {
@ -254,19 +254,48 @@ int LayerWidget::MinimalSupportedWidth() {
}
int LayerWidget::resizeGetHeight(int newWidth) {
if (!parentWidget() || !_content || !newWidth) {
if (!parentWidget() || !_contentWrap || !newWidth) {
return 0;
}
constexpr auto kMaxAttempts = 16;
auto attempts = 0;
while (true) {
_inResize = true;
{
const auto &parentSize = parentWidget()->size();
const auto windowWidth = parentSize.width();
const auto windowHeight = parentSize.height();
const auto newLeft = (windowWidth - newWidth) / 2;
const auto newTop = std::clamp(
windowHeight / 24,
st::infoLayerTopMinimal,
st::infoLayerTopMaximal);
const auto newBottom = newTop;
const auto bottomRadius = st::boxRadius;
const auto maxVisibleHeight = windowHeight - newTop;
// Top rounding is included in _contentWrapHeight.
auto desiredHeight = _contentWrapHeight + bottomRadius;
accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
// First resize content to new width and get the new desired height.
const auto contentLeft = 0;
const auto contentTop = 0;
const auto contentBottom = bottomRadius;
const auto contentWidth = newWidth;
auto contentHeight = desiredHeight - contentTop - contentBottom;
LOG(("ATTEMPT %1: WIDTH %2, WRAP HEIGHT %3, SCROLL TILL BOTTOM: %4"
).arg(attempts + 1
).arg(newWidth
).arg(_contentWrapHeight
).arg(_contentWrap->scrollTillBottom(contentHeight)));
}
const auto newGeometry = countGeometry(newWidth);
_inResize = false;
if (!_pendingResize) {
const auto oldGeometry = geometry();
if (newGeometry != oldGeometry) {
_content->forceContentRepaint();
_contentWrap->forceContentRepaint();
}
if (newGeometry.topLeft() != oldGeometry.topLeft()) {
move(newGeometry.topLeft());
@ -292,8 +321,8 @@ QRect LayerWidget::countGeometry(int newWidth) {
const auto bottomRadius = st::boxRadius;
const auto maxVisibleHeight = windowHeight - newTop;
// Top rounding is included in _contentHeight.
auto desiredHeight = _contentHeight + bottomRadius;
// Top rounding is included in _contentWrapHeight.
auto desiredHeight = _contentWrapHeight + bottomRadius;
accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
// First resize content to new width and get the new desired height.
@ -302,10 +331,11 @@ QRect LayerWidget::countGeometry(int newWidth) {
const auto contentBottom = bottomRadius;
const auto contentWidth = newWidth;
auto contentHeight = desiredHeight - contentTop - contentBottom;
const auto scrollTillBottom = _content->scrollTillBottom(contentHeight);
const auto scrollTillBottom = _contentWrap->scrollTillBottom(
contentHeight);
auto additionalScroll = std::min(scrollTillBottom, newBottom);
const auto expanding = (_desiredHeight > _contentHeight);
const auto expanding = (_desiredHeight > _contentWrapHeight);
desiredHeight += additionalScroll;
contentHeight += additionalScroll;
@ -313,11 +343,11 @@ QRect LayerWidget::countGeometry(int newWidth) {
if (_tillBottom) {
additionalScroll += contentBottom;
}
_contentTillBottom = _tillBottom && !_content->scrollBottomSkip();
_contentTillBottom = _tillBottom && !_contentWrap->scrollBottomSkip();
if (_contentTillBottom) {
contentHeight += contentBottom;
}
_content->updateGeometry({
_contentWrap->updateGeometry({
contentLeft,
contentTop,
contentWidth,
@ -328,8 +358,8 @@ QRect LayerWidget::countGeometry(int newWidth) {
}
void LayerWidget::doSetInnerFocus() {
if (_content) {
_content->setInnerFocus();
if (_contentWrap) {
_contentWrap->setInnerFocus();
}
}
@ -342,7 +372,7 @@ void LayerWidget::paintEvent(QPaintEvent *e) {
if (!_tillBottom) {
const auto bottom = QRect{ 0, height() - radius, width(), radius };
if (clip.intersects(bottom)) {
if (const auto rounding = _content->bottomSkipRounding()) {
if (const auto rounding = _contentWrap->bottomSkipRounding()) {
rounding->paint(p, rect(), RectPart::FullBottom);
} else {
Ui::FillRoundRect(p, bottom, st::boxBg, {
@ -351,11 +381,11 @@ void LayerWidget::paintEvent(QPaintEvent *e) {
}
}
} else if (!_contentTillBottom) {
const auto rounding = _content->bottomSkipRounding();
const auto rounding = _contentWrap->bottomSkipRounding();
const auto &color = rounding ? rounding->color() : st::boxBg;
p.fillRect(0, height() - radius, width(), radius, color);
}
if (_content->animatingShow()) {
if (_contentWrap->animatingShow()) {
const auto top = QRect{ 0, 0, width(), radius };
if (clip.intersects(top)) {
Ui::FillRoundRect(p, top, st::boxBg, {

View file

@ -73,10 +73,10 @@ private:
[[nodiscard]] QRect countGeometry(int newWidth);
not_null<Window::SessionController*> _controller;
object_ptr<WrapWidget> _content;
object_ptr<WrapWidget> _contentWrap;
int _desiredHeight = 0;
int _contentHeight = 0;
int _contentWrapHeight = 0;
int _savedHeight = 0;
Ui::Animations::Simple _heightAnimation;
Ui::Animations::Simple _savedHeightAnimation;

View file

@ -50,8 +50,8 @@ void SectionWidget::init() {
return (_content != nullptr);
}) | rpl::start_with_next([=](QSize size, int) {
const auto expanding = false;
const auto additionalScroll = st::boxRadius;
const auto full = !_content->scrollBottomSkip();
const auto additionalScroll = (full ? st::boxRadius : 0);
const auto height = size.height() - (full ? 0 : st::boxRadius);
const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
_content->updateGeometry(

View file

@ -117,17 +117,17 @@ Widget::Widget(
}
if (_pinnedToBottom) {
const auto processHeight = [=](int bottomHeight, int height) {
setScrollBottomSkip(bottomHeight);
const auto processHeight = [=] {
setScrollBottomSkip(_pinnedToBottom->height());
_pinnedToBottom->moveToLeft(
_pinnedToBottom->x(),
height - bottomHeight);
height() - _pinnedToBottom->height());
};
_inner->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
_pinnedToBottom->resizeToWidth(s.width());
processHeight(_pinnedToBottom->height(), height());
//processHeight();
}, _pinnedToBottom->lifetime());
rpl::combine(

View file

@ -98,6 +98,7 @@ public:
private:
void outerResized(QSize outer);
void updateComposeControlsPosition();
// ListDelegate interface.
Context listContext() override;
@ -505,7 +506,7 @@ void ShortcutMessages::outerResized(QSize outer) {
? base::make_optional(_scroll->scrollTop())
: 0;
_skipScrollEvent = true;
_inner->resizeToWidth(contentWidth, _scroll->height());
_inner->resizeToWidth(contentWidth, st::boxWidth);
_skipScrollEvent = false;
if (!_scroll->isHidden()) {
@ -514,25 +515,34 @@ void ShortcutMessages::outerResized(QSize outer) {
}
updateInnerVisibleArea();
}
_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
updateComposeControlsPosition();
_cornerButtons.updatePositions();
}
void ShortcutMessages::updateComposeControlsPosition() {
const auto bottom = _scroll->parentWidget()->height();
const auto controlsHeight = _composeControls->heightCurrent();
_composeControls->move(0, bottom - controlsHeight + st::boxRadius);
_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
}
void ShortcutMessages::setupComposeControls() {
_shortcutId.value() | rpl::start_with_next([=](BusinessShortcutId id) {
_composeControls->updateShortcutId(id);
}, lifetime());
const auto state = Dialogs::EntryState{
.key = Dialogs::Key{ _history },
.section = Dialogs::EntryState::Section::ShortcutMessages,
.currentReplyTo = replyTo(),
};
_composeControls->setCurrentDialogsEntryState(state);
_composeControls->setHistory({
.history = _history.get(),
.writeRestriction = rpl::single(Controls::WriteRestriction()),
});
_composeControls->height(
) | rpl::start_with_next([=](int height) {
const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop());
_controlsWrap->resize(width(), height);
if (wasMax) {
listScrollTo(_scroll->scrollTopMax());
}
}, lifetime());
_composeControls->cancelRequests(
) | rpl::start_with_next([=] {
listCancelRequest();
@ -637,20 +647,58 @@ void ShortcutMessages::setupComposeControls() {
_controlsWrap->widthValue() | rpl::start_with_next([=](int width) {
_composeControls->resizeToWidth(width);
}, _controlsWrap->lifetime());
_composeControls->height() | rpl::start_with_next([=](int height) {
_controlsWrap->resize(_controlsWrap->width(), height);
}, _controlsWrap->lifetime());
_composeControls->height(
) | rpl::start_with_next([=](int height) {
const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop());
_controlsWrap->resize(width(), height - st::boxRadius);
updateComposeControlsPosition();
if (wasMax) {
listScrollTo(_scroll->scrollTopMax());
}
}, lifetime());
}
QPointer<Ui::RpWidget> ShortcutMessages::createPinnedToBottom(
not_null<Ui::RpWidget*> parent) {
auto placeholder = rpl::deferred([=] {
return _shortcutId.value();
}) | rpl::map([=](BusinessShortcutId id) {
return _session->data().shortcutMessages().lookupShortcut(id).name;
}) | rpl::map([=](const QString &shortcut) {
return (shortcut == u"away"_q)
? tr::lng_away_message_placeholder()
: (shortcut == u"hello"_q)
? tr::lng_greeting_message_placeholder()
: tr::lng_replies_message_placeholder();
}) | rpl::flatten_latest();
_controlsWrap = std::make_unique<Ui::RpWidget>(parent);
_composeControls = std::make_unique<ComposeControls>(
_controlsWrap.get(),
_controller,
[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
ComposeControls::Mode::Normal,
SendMenu::Type::Disabled);
dynamic_cast<Ui::RpWidget*>(_scroll->parentWidget()),
ComposeControlsDescriptor{
.show = _controller->uiShow(),
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
listShowPremiumToast(emoji);
},
.mode = HistoryView::ComposeControlsMode::Normal,
.sendMenuType = SendMenu::Type::Disabled,
.regularWindow = _controller,
.stickerOrEmojiChosen = _controller->stickerOrEmojiChosen(),
.customPlaceholder = std::move(placeholder),
.panelsLevel = Window::GifPauseReason::Layer,
.voiceCustomCancelText = tr::lng_record_cancel_stories(tr::now),
.voiceLockFromBottom = true,
.features = {
.sendAs = false,
.ttlInfo = false,
.botCommandSend = false,
.silentBroadcastToggle = false,
.attachBotsMenu = false,
.megagroupSet = false,
.commonTabbedPanel = false,
},
});
setupComposeControls();

View file

@ -517,6 +517,7 @@ FileLoadTask::FileLoadTask(
, _caption(caption)
, _spoiler(spoiler) {
Expects(to.options.scheduled
|| to.options.shortcutId
|| !to.replaceMediaOf
|| IsServerMsgId(to.replaceMediaOf));

View file

@ -301,6 +301,7 @@ historySentInvertedIcon: icon {{ "history_sent", historyIconFgInverted, point(2p
historyReceivedIcon: icon {{ "history_received", historyOutIconFg, point(2px, 4px) }};
historyReceivedSelectedIcon: icon {{ "history_received", historyOutIconFgSelected, point(2px, 4px) }};
historyReceivedInvertedIcon: icon {{ "history_received", historyIconFgInverted, point(2px, 4px) }};
historyShortcutStateSpace: 18px;
historyViewsSpace: 8px;
historyViewsWidth: 20px;