mirror of https://github.com/procxx/kepka.git
New layout of service messages (complex shape of a bubble).
This commit is contained in:
parent
a5cbbba12d
commit
19cacd0efb
|
@ -27,7 +27,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "styles/style_overview.h"
|
||||
#include "lang.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "audio.h"
|
||||
#include "application.h"
|
||||
#include "fileuploader.h"
|
||||
|
@ -2150,7 +2151,7 @@ namespace {
|
|||
mainEmojiMap.clear();
|
||||
otherEmojiMap.clear();
|
||||
|
||||
Dialogs::Layout::clearStyleSheets();
|
||||
Data::clearGlobalStructures();
|
||||
|
||||
clearAllImages();
|
||||
}
|
||||
|
@ -2735,7 +2736,11 @@ namespace {
|
|||
|
||||
uchar rPoint = uchar(componentsPoint[0]), gPoint = uchar(componentsPoint[1]), bPoint = uchar(componentsPoint[2]);
|
||||
_introPointHoverColor = style::color(rPoint, gPoint, bPoint);
|
||||
if (App::main()) App::main()->updateScrollColors();
|
||||
|
||||
if (App::main()) {
|
||||
App::main()->updateScrollColors();
|
||||
HistoryLayout::serviceColorsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
const style::color &msgServiceBg() {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "data/data_abstract_structure.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
using DataStructures = OrderedSet<AbstractStructure**>;
|
||||
NeverFreedPointer<DataStructures> structures;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
void registerAbstractStructure(AbstractStructure **p) {
|
||||
structures.makeIfNull();
|
||||
structures->insert(p);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void clearGlobalStructures() {
|
||||
if (!structures) return;
|
||||
for (auto &p : *structures) {
|
||||
delete (*p);
|
||||
*p = nullptr;
|
||||
}
|
||||
structures.clear();
|
||||
}
|
||||
|
||||
} // namespace Data
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Data {
|
||||
|
||||
// This module suggests a way to hold global data structures, that are
|
||||
// created on demand and deleted at the end of the app launch.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// class MyData : public Data::AbstractStruct { .. data .. };
|
||||
// Data::GlobalStructurePointer<MyData> myData;
|
||||
// .. somewhere when needed ..
|
||||
// myData.createIfNull();
|
||||
|
||||
class AbstractStructure {
|
||||
public:
|
||||
virtual ~AbstractStructure() = 0;
|
||||
};
|
||||
inline AbstractStructure::~AbstractStructure() = default;
|
||||
|
||||
namespace internal {
|
||||
|
||||
void registerAbstractStructure(AbstractStructure **p);
|
||||
|
||||
} // namespace
|
||||
|
||||
// Must be created in global scope!
|
||||
// Structure is derived from AbstractStructure.
|
||||
template <typename Structure>
|
||||
class GlobalStructurePointer {
|
||||
public:
|
||||
GlobalStructurePointer() = default;
|
||||
GlobalStructurePointer(const GlobalStructurePointer<Structure> &other) = delete;
|
||||
GlobalStructurePointer &operator=(const GlobalStructurePointer<Structure> &other) = delete;
|
||||
|
||||
void createIfNull() {
|
||||
if (!_p) {
|
||||
_p = new Structure();
|
||||
internal::registerAbstractStructure(&_p);
|
||||
}
|
||||
}
|
||||
Structure *operator->() {
|
||||
t_assert(_p != nullptr);
|
||||
return static_cast<Structure*>(_p);
|
||||
}
|
||||
const Structure *operator->() const {
|
||||
t_assert(_p != nullptr);
|
||||
return static_cast<const Structure*>(_p);
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return _p != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractStructure *_p;
|
||||
|
||||
};
|
||||
|
||||
// This method should be called at the end of the app launch.
|
||||
// It will destroy all data structures created by Data::GlobalStructurePointer.
|
||||
void clearGlobalStructures();
|
||||
|
||||
} // namespace Data
|
|
@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "data/drafts.h"
|
||||
#include "data/data_drafts.h"
|
||||
|
||||
#include "historywidget.h"
|
||||
#include "mainwidget.h"
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "stdafx.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "dialogs/dialogs_list.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "localstorage.h"
|
||||
|
@ -138,27 +139,18 @@ void paintRow(Painter &p, History *history, HistoryItem *item, HistoryDraft *dra
|
|||
history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
||||
}
|
||||
|
||||
class UnreadBadgeStyle : public StyleSheet {
|
||||
class UnreadBadgeStyleData : public Data::AbstractStructure {
|
||||
public:
|
||||
QImage circle;
|
||||
QPixmap left[4], right[4];
|
||||
style::color bg[4] = { st::dialogsUnreadBg, st::dialogsUnreadBgActive, st::dialogsUnreadBgMuted, st::dialogsUnreadBgMutedActive };
|
||||
};
|
||||
StyleSheetPointer<UnreadBadgeStyle> unreadBadgeStyle;
|
||||
Data::GlobalStructurePointer<UnreadBadgeStyleData> unreadBadgeStyle;
|
||||
|
||||
void createCircleMask(int size) {
|
||||
if (!unreadBadgeStyle->circle.isNull()) return;
|
||||
|
||||
unreadBadgeStyle->circle = QImage(size, size, QImage::Format::Format_Grayscale8);
|
||||
{
|
||||
QPainter pcircle(&unreadBadgeStyle->circle);
|
||||
pcircle.setRenderHint(QPainter::HighQualityAntialiasing, true);
|
||||
pcircle.fillRect(0, 0, size, size, QColor(0, 0, 0));
|
||||
pcircle.setPen(Qt::NoPen);
|
||||
pcircle.setBrush(QColor(255, 255, 255));
|
||||
pcircle.drawEllipse(0, 0, size, size);
|
||||
}
|
||||
unreadBadgeStyle->circle.setDevicePixelRatio(cRetinaFactor());
|
||||
unreadBadgeStyle->circle = style::createCircleMask(size);
|
||||
}
|
||||
|
||||
QImage colorizeCircleHalf(int size, int half, int xoffset, style::color color) {
|
||||
|
@ -177,9 +169,9 @@ void paintUnreadBadge(Painter &p, const QRect &rect, bool active, bool muted) {
|
|||
style::color bg = unreadBadgeStyle->bg[index];
|
||||
if (unreadBadgeStyle->left[index].isNull()) {
|
||||
int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor();
|
||||
createCircleMask(imgsize);
|
||||
unreadBadgeStyle->left[index] = QPixmap::fromImage(colorizeCircleHalf(imgsize, imgsizehalf, 0, bg));
|
||||
unreadBadgeStyle->right[index] = QPixmap::fromImage(colorizeCircleHalf(imgsize, imgsizehalf, imgsize - imgsizehalf, bg));
|
||||
createCircleMask(size);
|
||||
unreadBadgeStyle->left[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(imgsize, imgsizehalf, 0, bg));
|
||||
unreadBadgeStyle->right[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(imgsize, imgsizehalf, imgsize - imgsizehalf, bg));
|
||||
}
|
||||
|
||||
int bar = rect.width() - 2 * sizehalf;
|
||||
|
@ -280,30 +272,5 @@ void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool o
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using StyleSheets = OrderedSet<StyleSheet**>;
|
||||
NeverFreedPointer<StyleSheets> styleSheets;
|
||||
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void registerStyleSheet(StyleSheet **p) {
|
||||
styleSheets.makeIfNull();
|
||||
styleSheets->insert(p);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void clearStyleSheets() {
|
||||
if (!styleSheets) return;
|
||||
for (auto &p : *styleSheets) {
|
||||
delete (*p);
|
||||
*p = nullptr;
|
||||
}
|
||||
styleSheets.clear();
|
||||
}
|
||||
|
||||
} // namespace Layout
|
||||
} // namespace Dialogs
|
||||
|
|
|
@ -38,44 +38,5 @@ void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool o
|
|||
void paintUnreadCount(Painter &p, const QString &text, int x, int y, style::align align, bool active, bool muted, int *outUnreadWidth);
|
||||
void paintUnreadBadge(Painter &p, const QRect &rect, bool active, bool muted);
|
||||
|
||||
// This will be moved somewhere outside as soon as anyone starts using that.
|
||||
class StyleSheet {
|
||||
public:
|
||||
virtual ~StyleSheet() = 0;
|
||||
};
|
||||
inline StyleSheet::~StyleSheet() = default;
|
||||
|
||||
namespace internal {
|
||||
|
||||
void registerStyleSheet(StyleSheet **p);
|
||||
|
||||
} // namespace
|
||||
|
||||
// Must be created in global scope!
|
||||
template <typename T>
|
||||
class StyleSheetPointer {
|
||||
public:
|
||||
StyleSheetPointer() = default;
|
||||
StyleSheetPointer(const StyleSheetPointer<T> &other) = delete;
|
||||
StyleSheetPointer &operator=(const StyleSheetPointer<T> &other) = delete;
|
||||
|
||||
void createIfNull() {
|
||||
if (!_p) {
|
||||
_p = new T();
|
||||
internal::registerStyleSheet(&_p);
|
||||
}
|
||||
}
|
||||
T *operator->() {
|
||||
t_assert(_p != nullptr);
|
||||
return static_cast<T*>(_p);
|
||||
}
|
||||
|
||||
private:
|
||||
StyleSheet *_p;
|
||||
|
||||
};
|
||||
|
||||
void clearStyleSheets();
|
||||
|
||||
} // namespace Layout
|
||||
} // namespace Dialogs
|
||||
|
|
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "data/drafts.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "lang.h"
|
||||
#include "application.h"
|
||||
#include "mainwindow.h"
|
||||
|
|
|
@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "core/click_handler_types.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "lang.h"
|
||||
#include "mainwidget.h"
|
||||
#include "application.h"
|
||||
|
@ -7940,9 +7941,7 @@ void HistoryService::setServiceText(const QString &text) {
|
|||
}
|
||||
|
||||
void HistoryService::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {
|
||||
int left = 0, width = 0, height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom(); // two small margins
|
||||
countPositionAndSize(left, width);
|
||||
if (width < 1) return;
|
||||
int height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom();
|
||||
|
||||
int dateh = 0, unreadbarh = 0;
|
||||
if (auto date = Get<HistoryMessageDate>()) {
|
||||
|
@ -7962,47 +7961,8 @@ void HistoryService::draw(Painter &p, const QRect &r, TextSelection selection, u
|
|||
height -= unreadbarh;
|
||||
}
|
||||
|
||||
uint64 fullAnimMs = App::main() ? App::main()->animActiveTimeStart(this) : 0;
|
||||
if (fullAnimMs > 0 && fullAnimMs <= ms) {
|
||||
int animms = ms - fullAnimMs;
|
||||
if (animms > st::activeFadeInDuration + st::activeFadeOutDuration) {
|
||||
App::main()->stopAnimActive();
|
||||
} else {
|
||||
int skiph = st::msgServiceMargin.top() - st::msgServiceMargin.bottom();
|
||||
|
||||
textstyleSet(&st::inTextStyle);
|
||||
float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration));
|
||||
float64 o = p.opacity();
|
||||
p.setOpacity(o * dt);
|
||||
p.fillRect(0, skiph, _history->width, _height - skiph, textstyleCurrent()->selectOverlay->b);
|
||||
p.setOpacity(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (_media) {
|
||||
height -= st::msgServiceMargin.top() + _media->height();
|
||||
int32 left = st::msgServiceMargin.left() + (width - _media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
|
||||
p.translate(left, top);
|
||||
_media->draw(p, r.translated(-left, -top), toMediaSelection(selection), ms);
|
||||
p.translate(-left, -top);
|
||||
}
|
||||
|
||||
QRect trect(QRect(left, st::msgServiceMargin.top(), width, height).marginsAdded(-st::msgServicePadding));
|
||||
|
||||
if (width > _maxw) {
|
||||
left += (width - _maxw) / 2;
|
||||
width = _maxw;
|
||||
}
|
||||
App::roundRect(p, left, st::msgServiceMargin.top(), width, height, App::msgServiceBg(), (selection == FullSelection) ? ServiceSelectedCorners : ServiceCorners);
|
||||
|
||||
textstyleSet(&st::serviceTextStyle);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st::msgServiceColor);
|
||||
p.setFont(st::msgServiceFont);
|
||||
_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, selection);
|
||||
|
||||
textstyleRestore();
|
||||
HistoryLayout::PaintContext context(ms, r, selection);
|
||||
HistoryLayout::ServiceMessagePainter::paint(p, this, context, height);
|
||||
|
||||
if (int skiph = dateh + unreadbarh) {
|
||||
p.translate(0, -skiph);
|
||||
|
|
|
@ -2770,9 +2770,12 @@ struct HistoryServicePinned : public BaseComponent<HistoryServicePinned> {
|
|||
ClickHandlerPtr lnk;
|
||||
};
|
||||
|
||||
namespace HistoryLayout {
|
||||
class ServiceMessagePainter;
|
||||
} // namespace HistoryLayout
|
||||
|
||||
class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> {
|
||||
public:
|
||||
|
||||
static HistoryService *create(History *history, const MTPDmessageService &msg) {
|
||||
return _create(history, msg);
|
||||
}
|
||||
|
@ -2836,6 +2839,7 @@ public:
|
|||
~HistoryService();
|
||||
|
||||
protected:
|
||||
friend class HistoryLayout::ServiceMessagePainter;
|
||||
|
||||
HistoryService(History *history, const MTPDmessageService &msg);
|
||||
HistoryService(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags = 0, int32 from = 0);
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "history/history_service_layout.h"
|
||||
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "mainwidget.h"
|
||||
|
||||
namespace HistoryLayout {
|
||||
namespace {
|
||||
|
||||
enum CircleMask {
|
||||
NormalMask = 0x00,
|
||||
InvertedMask = 0x01,
|
||||
};
|
||||
enum CircleMaskMultiplier {
|
||||
MaskMultiplier = 0x04,
|
||||
};
|
||||
enum CornerVerticalSide {
|
||||
CornerTop = 0x00,
|
||||
CornerBottom = 0x02,
|
||||
};
|
||||
enum CornerHorizontalSide {
|
||||
CornerLeft = 0x00,
|
||||
CornerRight = 0x01,
|
||||
};
|
||||
|
||||
class ServiceMessageStyleData : public Data::AbstractStructure {
|
||||
public:
|
||||
// circle[CircleMask value]
|
||||
QImage circle[2];
|
||||
|
||||
// corners[(CircleMask value) * MaskMultiplier | (CornerVerticalSide value) | (CornerHorizontalSide value)]
|
||||
QPixmap corners[8];
|
||||
};
|
||||
Data::GlobalStructurePointer<ServiceMessageStyleData> serviceMessageStyle;
|
||||
|
||||
void createCircleMasks() {
|
||||
serviceMessageStyle.createIfNull();
|
||||
if (!serviceMessageStyle->circle[NormalMask].isNull()) return;
|
||||
|
||||
int size = st::msgRadius * 2;
|
||||
serviceMessageStyle->circle[NormalMask] = style::createCircleMask(size);
|
||||
serviceMessageStyle->circle[InvertedMask] = style::createInvertedCircleMask(size);
|
||||
}
|
||||
|
||||
QPixmap circleCorner(int corner) {
|
||||
if (serviceMessageStyle->corners[corner].isNull()) {
|
||||
int size = st::msgRadius * cIntRetinaFactor();
|
||||
|
||||
int xoffset = 0, yoffset = 0;
|
||||
if (corner & CornerRight) {
|
||||
xoffset = size;
|
||||
}
|
||||
if (corner & CornerBottom) {
|
||||
yoffset = size;
|
||||
}
|
||||
int maskType = corner / MaskMultiplier;
|
||||
auto part = QRect(xoffset, yoffset, size, size);
|
||||
auto result = style::colorizeImage(serviceMessageStyle->circle[maskType], App::msgServiceBg(), part);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
serviceMessageStyle->corners[corner] = App::pixmapFromImageInPlace(std_::move(result));
|
||||
}
|
||||
return serviceMessageStyle->corners[corner];
|
||||
}
|
||||
|
||||
enum class SideStyle {
|
||||
Rounded,
|
||||
Plain,
|
||||
Inverted,
|
||||
};
|
||||
|
||||
// Returns amount of pixels already painted vertically (so you can skip them in the complex rect shape).
|
||||
int paintBubbleSide(Painter &p, int x, int y, int width, SideStyle style, CornerVerticalSide side) {
|
||||
if (style == SideStyle::Rounded) {
|
||||
auto left = circleCorner((NormalMask * MaskMultiplier) | side | CornerLeft);
|
||||
int leftWidth = left.width() / cIntRetinaFactor();
|
||||
p.drawPixmap(x, y, left);
|
||||
|
||||
auto right = circleCorner((NormalMask * MaskMultiplier) | side | CornerRight);
|
||||
int rightWidth = right.width() / cIntRetinaFactor();
|
||||
p.drawPixmap(x + width - rightWidth, y, right);
|
||||
|
||||
int cornerHeight = left.height() / cIntRetinaFactor();
|
||||
p.fillRect(x + leftWidth, y, width - leftWidth - rightWidth, cornerHeight, App::msgServiceBg());
|
||||
return cornerHeight;
|
||||
} else if (style == SideStyle::Inverted) {
|
||||
// CornerLeft and CornerRight are inverted for SideStyle::Inverted sprites.
|
||||
auto left = circleCorner((InvertedMask * MaskMultiplier) | side | CornerRight);
|
||||
int leftWidth = left.width() / cIntRetinaFactor();
|
||||
p.drawPixmap(x - leftWidth, y, left);
|
||||
|
||||
auto right = circleCorner((InvertedMask * MaskMultiplier) | side | CornerLeft);
|
||||
p.drawPixmap(x + width, y, right);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void paintBubblePart(Painter &p, int x, int y, int width, int height, SideStyle topStyle, SideStyle bottomStyle) {
|
||||
if (int skip = paintBubbleSide(p, x, y, width, topStyle, CornerTop)) {
|
||||
y += skip;
|
||||
height -= skip;
|
||||
}
|
||||
if (int skip = paintBubbleSide(p, x, y + height - st::msgRadius, width, bottomStyle, CornerBottom)) {
|
||||
height -= skip;
|
||||
}
|
||||
|
||||
p.fillRect(x, y, width, height, App::msgServiceBg());
|
||||
}
|
||||
|
||||
} // namepsace
|
||||
|
||||
void ServiceMessagePainter::paint(Painter &p, const HistoryService *message, const PaintContext &context, int height) {
|
||||
int left = 0, width = 0;
|
||||
message->countPositionAndSize(left, width);
|
||||
if (width < 1) return;
|
||||
|
||||
uint64 fullAnimMs = App::main() ? App::main()->animActiveTimeStart(message) : 0;
|
||||
if (fullAnimMs > 0 && fullAnimMs <= context.ms) {
|
||||
int animms = context.ms - fullAnimMs;
|
||||
if (animms > st::activeFadeInDuration + st::activeFadeOutDuration) {
|
||||
App::main()->stopAnimActive();
|
||||
} else {
|
||||
int skiph = st::msgServiceMargin.top() - st::msgServiceMargin.bottom();
|
||||
|
||||
textstyleSet(&st::inTextStyle);
|
||||
float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration));
|
||||
float64 o = p.opacity();
|
||||
p.setOpacity(o * dt);
|
||||
p.fillRect(0, skiph, message->history()->width, message->height() - skiph, textstyleCurrent()->selectOverlay->b);
|
||||
p.setOpacity(o);
|
||||
}
|
||||
}
|
||||
|
||||
textstyleSet(&st::serviceTextStyle);
|
||||
|
||||
if (auto media = message->getMedia()) {
|
||||
height -= st::msgServiceMargin.top() + media->height();
|
||||
int32 left = st::msgServiceMargin.left() + (width - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
|
||||
p.translate(left, top);
|
||||
media->draw(p, context.clip.translated(-left, -top), message->toMediaSelection(context.selection), context.ms);
|
||||
p.translate(-left, -top);
|
||||
}
|
||||
|
||||
QRect trect(QRect(left, st::msgServiceMargin.top(), width, height).marginsAdded(-st::msgServicePadding));
|
||||
|
||||
paintBubble(p, left, width, message->_text, trect);
|
||||
|
||||
if (width > message->maxWidth()) {
|
||||
left += (width - message->maxWidth()) / 2;
|
||||
width = message->maxWidth();
|
||||
}
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st::msgServiceColor);
|
||||
p.setFont(st::msgServiceFont);
|
||||
message->_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, context.selection, false);
|
||||
|
||||
textstyleRestore();
|
||||
}
|
||||
|
||||
void ServiceMessagePainter::paintBubble(Painter &p, int left, int width, const Text &text, const QRect &textRect) {
|
||||
createCircleMasks();
|
||||
|
||||
auto lineWidths = countLineWidths(text, textRect);
|
||||
|
||||
int y = st::msgServiceMargin.top();
|
||||
SideStyle topStyle = SideStyle::Rounded, bottomStyle;
|
||||
for (int i = 0, count = lineWidths.size(); i < count; ++i) {
|
||||
auto lineWidth = lineWidths.at(i);
|
||||
if (i + 1 < count) {
|
||||
auto nextLineWidth = lineWidths.at(i + 1);
|
||||
if (nextLineWidth > lineWidth) {
|
||||
bottomStyle = SideStyle::Inverted;
|
||||
} else if (nextLineWidth < lineWidth) {
|
||||
bottomStyle = SideStyle::Rounded;
|
||||
} else {
|
||||
bottomStyle = SideStyle::Plain;
|
||||
}
|
||||
} else {
|
||||
bottomStyle = SideStyle::Rounded;
|
||||
}
|
||||
|
||||
auto richWidth = lineWidth + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto richHeight = st::msgServiceFont->height;
|
||||
if (topStyle == SideStyle::Rounded) {
|
||||
richHeight += st::msgServicePadding.top();
|
||||
} else if (topStyle == SideStyle::Inverted) {
|
||||
richHeight -= st::msgServicePadding.bottom();
|
||||
}
|
||||
if (bottomStyle == SideStyle::Rounded) {
|
||||
richHeight += st::msgServicePadding.bottom();
|
||||
} else if (bottomStyle == SideStyle::Inverted) {
|
||||
richHeight -= st::msgServicePadding.top();
|
||||
}
|
||||
paintBubblePart(p, left + ((width - richWidth) / 2), y, richWidth, richHeight, topStyle, bottomStyle);
|
||||
y += richHeight;
|
||||
|
||||
if (bottomStyle == SideStyle::Inverted) {
|
||||
topStyle = SideStyle::Rounded;
|
||||
} else if (bottomStyle == SideStyle::Rounded) {
|
||||
topStyle = SideStyle::Inverted;
|
||||
} else {
|
||||
topStyle = SideStyle::Plain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<int> ServiceMessagePainter::countLineWidths(const Text &text, const QRect &textRect) {
|
||||
int linesCount = qMax(textRect.height() / st::msgServiceFont->height, 1);
|
||||
QVector<int> lineWidths;
|
||||
lineWidths.reserve(linesCount);
|
||||
text.countLineWidths(textRect.width(), &lineWidths);
|
||||
|
||||
int minDelta = 4 * st::msgRadius;
|
||||
for (int i = 0, count = lineWidths.size(); i < count; ++i) {
|
||||
int width = qMax(lineWidths.at(i), 0);
|
||||
if (i > 0) {
|
||||
int widthBefore = lineWidths.at(i - 1);
|
||||
if (width < widthBefore && width + minDelta > widthBefore) {
|
||||
width = widthBefore;
|
||||
}
|
||||
}
|
||||
if (i + 1 < count) {
|
||||
int widthAfter = lineWidths.at(i + 1);
|
||||
if (width < widthAfter && width + minDelta > widthAfter) {
|
||||
width = widthAfter;
|
||||
}
|
||||
}
|
||||
if (width > lineWidths.at(i)) {
|
||||
lineWidths[i] = width;
|
||||
if (i > 0) {
|
||||
int widthBefore = lineWidths.at(i - 1);
|
||||
if (widthBefore != width && widthBefore < width + minDelta && widthBefore + minDelta > width) {
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lineWidths;
|
||||
}
|
||||
|
||||
void serviceColorsUpdated() {
|
||||
if (serviceMessageStyle) {
|
||||
for (auto &corner : serviceMessageStyle->corners) {
|
||||
corner = QPixmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace HistoryLayout
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace HistoryLayout {
|
||||
|
||||
struct PaintContext {
|
||||
PaintContext(uint64 ms, const QRect &clip, TextSelection selection)
|
||||
: ms(ms)
|
||||
, clip(clip)
|
||||
, selection(selection) {
|
||||
}
|
||||
uint64 ms;
|
||||
const QRect &clip;
|
||||
TextSelection selection;
|
||||
};
|
||||
|
||||
class ServiceMessagePainter {
|
||||
public:
|
||||
static void paint(Painter &p, const HistoryService *message, const PaintContext &context, int height);
|
||||
|
||||
private:
|
||||
static void paintBubble(Painter &p, int left, int width, const Text &text, const QRect &textRect);
|
||||
static QVector<int> countLineWidths(const Text &text, const QRect &textRect);
|
||||
|
||||
};
|
||||
|
||||
void serviceColorsUpdated();
|
||||
|
||||
} // namespace HistoryLayout
|
|
@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "window/section_memento.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/top_bar_widget.h"
|
||||
#include "data/drafts.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "dialogswidget.h"
|
||||
|
|
|
@ -100,4 +100,22 @@ QImage colorizeImage(const QImage &src, const color &c, const QRect &r) {
|
|||
return result;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
QImage createCircleMask(int size, const QColor &bg, const QColor &fg) {
|
||||
int realSize = size * cIntRetinaFactor();
|
||||
auto result = QImage(size, size, QImage::Format::Format_Grayscale8);
|
||||
{
|
||||
QPainter pcircle(&result);
|
||||
pcircle.setRenderHint(QPainter::HighQualityAntialiasing, true);
|
||||
pcircle.fillRect(0, 0, size, size, bg);
|
||||
pcircle.setPen(Qt::NoPen);
|
||||
pcircle.setBrush(fg);
|
||||
pcircle.drawEllipse(0, 0, size, size);
|
||||
}
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace style
|
||||
|
|
|
@ -59,6 +59,20 @@ void stopManager();
|
|||
|
||||
QImage colorizeImage(const QImage &src, const color &c, const QRect &r);
|
||||
|
||||
namespace internal {
|
||||
|
||||
QImage createCircleMask(int size, const QColor &bg, const QColor &fg);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline QImage createCircleMask(int size) {
|
||||
return internal::createCircleMask(size, QColor(0, 0, 0), QColor(255, 255, 255));
|
||||
}
|
||||
|
||||
inline QImage createInvertedCircleMask(int size) {
|
||||
return internal::createCircleMask(size, QColor(255, 255, 255), QColor(0, 0, 0));
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
|
||||
inline QRect centersprite(const QRect &inRect, const style::sprite &sprite) {
|
||||
|
|
|
@ -928,7 +928,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void draw(int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection = { 0, 0 }) {
|
||||
void draw(int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection = { 0, 0 }, bool fullWidthSelection = true) {
|
||||
if (_t->isEmpty()) return;
|
||||
|
||||
_blocksSize = _t->_blocks.size();
|
||||
|
@ -947,6 +947,7 @@ public:
|
|||
_yToElide = _yTo;
|
||||
}
|
||||
_selection = selection;
|
||||
_fullWidthSelection = fullWidthSelection;
|
||||
_wLeft = _w = w;
|
||||
_str = _t->_text.unicode();
|
||||
|
||||
|
@ -1266,20 +1267,21 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool selectFromStart = (_selection.to > _lineStart) && (_lineStart > 0) && (_selection.from <= _lineStart);
|
||||
bool selectTillEnd = (_selection.to >= _lineEnd) && (_lineEnd < _t->_text.size()) && (_selection.from < _lineEnd) && (!_endBlock || _endBlock->type() != TextBlockTSkip);
|
||||
if (_fullWidthSelection) {
|
||||
bool selectFromStart = (_selection.to > _lineStart) && (_lineStart > 0) && (_selection.from <= _lineStart);
|
||||
bool selectTillEnd = (_selection.to >= _lineEnd) && (_lineEnd < _t->_text.size()) && (_selection.from < _lineEnd) && (!_endBlock || _endBlock->type() != TextBlockTSkip);
|
||||
|
||||
if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) {
|
||||
if (x > _x) {
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) {
|
||||
if (x > _x) {
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) {
|
||||
if (x < _x + _wLeft) {
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) {
|
||||
if (x < _x + _wLeft) {
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
|
||||
if (trimmedLineEnd == _lineStart && !elidedLine) return true;
|
||||
|
||||
if (!elidedLine) initParagraphBidi(); // if was not inited
|
||||
|
@ -2322,6 +2324,7 @@ private:
|
|||
QPen _originalPen;
|
||||
int32 _yFrom, _yTo, _yToElide;
|
||||
TextSelection _selection = { 0, 0 };
|
||||
bool _fullWidthSelection = true;
|
||||
const QChar *_str = nullptr;
|
||||
|
||||
// current paragraph data
|
||||
|
@ -2663,129 +2666,53 @@ void Text::removeSkipBlock() {
|
|||
}
|
||||
}
|
||||
|
||||
int32 Text::countWidth(int32 w) const {
|
||||
QFixed width = w;
|
||||
if (width < _minResizeWidth) width = _minResizeWidth;
|
||||
if (width >= _maxWidth) {
|
||||
int Text::countWidth(int width) const {
|
||||
if (QFixed(width) >= _maxWidth) {
|
||||
return _maxWidth.ceil().toInt();
|
||||
}
|
||||
|
||||
QFixed minWidthLeft = width, widthLeft = width, last_rBearing = 0, last_rPadding = 0;
|
||||
bool longWordLine = true;
|
||||
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
|
||||
ITextBlock *b = *i;
|
||||
TextBlockType _btype = b->type();
|
||||
int32 blockHeight = countBlockHeight(b, _font);
|
||||
|
||||
if (_btype == TextBlockTNewline) {
|
||||
last_rBearing = b->f_rbearing();
|
||||
last_rPadding = b->f_rpadding();
|
||||
if (widthLeft < minWidthLeft) {
|
||||
minWidthLeft = widthLeft;
|
||||
}
|
||||
widthLeft = width - (b->f_width() - last_rBearing);
|
||||
|
||||
longWordLine = true;
|
||||
continue;
|
||||
QFixed maxLineWidth = 0;
|
||||
enumerateLines(width, [&maxLineWidth](QFixed lineWidth, int lineHeight) {
|
||||
if (lineWidth > maxLineWidth) {
|
||||
maxLineWidth = lineWidth;
|
||||
}
|
||||
auto b__f_lpadding = b->f_lpadding();
|
||||
auto b__f_rbearing = b->f_rbearing(); // cache
|
||||
QFixed newWidthLeft = widthLeft - b__f_lpadding - last_rBearing - (last_rPadding + b->f_width() - b__f_rbearing);
|
||||
if (newWidthLeft >= 0) {
|
||||
last_rBearing = b__f_rbearing;
|
||||
last_rPadding = b->f_rpadding();
|
||||
widthLeft = newWidthLeft;
|
||||
|
||||
longWordLine = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_btype == TextBlockTText) {
|
||||
TextBlock *t = static_cast<TextBlock*>(b);
|
||||
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
|
||||
last_rPadding += b__f_lpadding;
|
||||
|
||||
longWordLine = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
QFixed f_wLeft = widthLeft;
|
||||
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
|
||||
bool wordEndsHere = (j->f_width() >= 0);
|
||||
QFixed j_width = wordEndsHere ? j->f_width() : -j->f_width();
|
||||
|
||||
QFixed newWidthLeft = widthLeft - b__f_lpadding - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
|
||||
b__f_lpadding = 0;
|
||||
if (newWidthLeft >= 0) {
|
||||
last_rBearing = j->f_rbearing();
|
||||
last_rPadding = j->f_rpadding();
|
||||
widthLeft = newWidthLeft;
|
||||
|
||||
if (wordEndsHere) {
|
||||
longWordLine = false;
|
||||
}
|
||||
if (wordEndsHere || longWordLine) {
|
||||
f_wLeft = widthLeft;
|
||||
f = j + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f != j) {
|
||||
j = f;
|
||||
widthLeft = f_wLeft;
|
||||
j_width = (j->f_width() >= 0) ? j->f_width() : -j->f_width();
|
||||
}
|
||||
|
||||
last_rBearing = j->f_rbearing();
|
||||
last_rPadding = j->f_rpadding();
|
||||
if (widthLeft < minWidthLeft) {
|
||||
minWidthLeft = widthLeft;
|
||||
}
|
||||
widthLeft = width - (j_width - last_rBearing);
|
||||
|
||||
longWordLine = true;
|
||||
f = j + 1;
|
||||
f_wLeft = widthLeft;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
last_rBearing = b__f_rbearing;
|
||||
last_rPadding = b->f_rpadding();
|
||||
if (widthLeft < minWidthLeft) {
|
||||
minWidthLeft = widthLeft;
|
||||
}
|
||||
widthLeft = width - (b->f_width() - last_rBearing);
|
||||
|
||||
longWordLine = true;
|
||||
continue;
|
||||
}
|
||||
if (widthLeft < minWidthLeft) {
|
||||
minWidthLeft = widthLeft;
|
||||
}
|
||||
|
||||
return (width - minWidthLeft).ceil().toInt();
|
||||
});
|
||||
return maxLineWidth.ceil().toInt();
|
||||
}
|
||||
|
||||
int32 Text::countHeight(int32 w) const {
|
||||
QFixed width = w;
|
||||
if (width < _minResizeWidth) width = _minResizeWidth;
|
||||
if (width >= _maxWidth) {
|
||||
int Text::countHeight(int width) const {
|
||||
if (QFixed(width) >= _maxWidth) {
|
||||
return _minHeight;
|
||||
}
|
||||
int result = 0;
|
||||
enumerateLines(width, [&result](QFixed lineWidth, int lineHeight) {
|
||||
result += lineHeight;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
int32 result = 0, lineHeight = 0;
|
||||
void Text::countLineWidths(int width, QVector<int> *lineWidths) const {
|
||||
enumerateLines(width, [lineWidths](QFixed lineWidth, int lineHeight) {
|
||||
lineWidths->push_back(lineWidth.ceil().toInt());
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void Text::enumerateLines(int w, Callback callback) const {
|
||||
QFixed width = w;
|
||||
if (width < _minResizeWidth) width = _minResizeWidth;
|
||||
|
||||
int lineHeight = 0;
|
||||
QFixed widthLeft = width, last_rBearing = 0, last_rPadding = 0;
|
||||
bool longWordLine = true;
|
||||
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
|
||||
ITextBlock *b = *i;
|
||||
for_const (auto b, _blocks) {
|
||||
TextBlockType _btype = b->type();
|
||||
int32 blockHeight = countBlockHeight(b, _font);
|
||||
int blockHeight = countBlockHeight(b, _font);
|
||||
|
||||
if (_btype == TextBlockTNewline) {
|
||||
if (!lineHeight) lineHeight = blockHeight;
|
||||
result += lineHeight;
|
||||
callback(width - widthLeft, lineHeight);
|
||||
|
||||
lineHeight = 0;
|
||||
last_rBearing = b->f_rbearing();
|
||||
last_rPadding = b->f_rpadding();
|
||||
|
@ -2820,8 +2747,8 @@ int32 Text::countHeight(int32 w) const {
|
|||
}
|
||||
|
||||
QFixed f_wLeft = widthLeft;
|
||||
int32 f_lineHeight = lineHeight;
|
||||
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
|
||||
int f_lineHeight = lineHeight;
|
||||
for (auto j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
|
||||
bool wordEndsHere = (j->f_width() >= 0);
|
||||
QFixed j_width = wordEndsHere ? j->f_width() : -j->f_width();
|
||||
|
||||
|
@ -2852,7 +2779,8 @@ int32 Text::countHeight(int32 w) const {
|
|||
j_width = (j->f_width() >= 0) ? j->f_width() : -j->f_width();
|
||||
}
|
||||
|
||||
result += lineHeight;
|
||||
callback(width - widthLeft, lineHeight);
|
||||
|
||||
lineHeight = qMax(0, blockHeight);
|
||||
last_rBearing = j->f_rbearing();
|
||||
last_rPadding = j->f_rpadding();
|
||||
|
@ -2866,7 +2794,8 @@ int32 Text::countHeight(int32 w) const {
|
|||
continue;
|
||||
}
|
||||
|
||||
result += lineHeight;
|
||||
callback(width - widthLeft, lineHeight);
|
||||
|
||||
lineHeight = qMax(0, blockHeight);
|
||||
last_rBearing = b__f_rbearing;
|
||||
last_rPadding = b->f_rpadding();
|
||||
|
@ -2876,20 +2805,18 @@ int32 Text::countHeight(int32 w) const {
|
|||
continue;
|
||||
}
|
||||
if (widthLeft < width) {
|
||||
result += lineHeight;
|
||||
callback(width - widthLeft, lineHeight);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Text::replaceFont(style::font f) {
|
||||
_font = f;
|
||||
}
|
||||
|
||||
void Text::draw(QPainter &painter, int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection) const {
|
||||
void Text::draw(QPainter &painter, int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, TextSelection selection, bool fullWidthSelection) const {
|
||||
// painter.fillRect(QRect(left, top, w, countHeight(w)), QColor(0, 0, 0, 32)); // debug
|
||||
TextPainter p(&painter, this);
|
||||
p.draw(left, top, w, align, yFrom, yTo, selection);
|
||||
p.draw(left, top, w, align, yFrom, yTo, selection, fullWidthSelection);
|
||||
}
|
||||
|
||||
void Text::drawElided(QPainter &painter, int32 left, int32 top, int32 w, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd, bool breakEverywhere, TextSelection selection) const {
|
||||
|
|
|
@ -93,8 +93,9 @@ public:
|
|||
Text &operator=(const Text &other);
|
||||
Text &operator=(Text &&other);
|
||||
|
||||
int32 countWidth(int32 width) const;
|
||||
int32 countHeight(int32 width) const;
|
||||
int countWidth(int width) const;
|
||||
int countHeight(int width) const;
|
||||
void countLineWidths(int width, QVector<int> *lineWidths) const;
|
||||
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
|
||||
void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap());
|
||||
void setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions);
|
||||
|
@ -115,7 +116,7 @@ public:
|
|||
|
||||
void replaceFont(style::font f); // does not recount anything, use at your own risk!
|
||||
|
||||
void draw(QPainter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const;
|
||||
void draw(QPainter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }, bool fullWidthSelection = true) const;
|
||||
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const;
|
||||
void drawLeft(QPainter &p, int32 left, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const {
|
||||
draw(p, rtl() ? (outerw - left - width) : left, top, width, align, yFrom, yTo, selection);
|
||||
|
@ -215,6 +216,12 @@ private:
|
|||
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
|
||||
void enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const;
|
||||
|
||||
// Template method for countWidth(), countHeight(), countLineWidths().
|
||||
// callback(lineWidth, lineHeight) will be called for all lines with:
|
||||
// QFixed lineWidth, int lineHeight
|
||||
template <typename Callback>
|
||||
void enumerateLines(int w, Callback callback) const;
|
||||
|
||||
void recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir = Qt::LayoutDirectionAuto);
|
||||
|
||||
// clear() deletes all blocks and calls this method
|
||||
|
|
|
@ -127,7 +127,7 @@ SOURCES += \
|
|||
./SourceFiles/core/click_handler.cpp \
|
||||
./SourceFiles/core/click_handler_types.cpp \
|
||||
./SourceFiles/core/observer.cpp \
|
||||
./SourceFiles/data/drafts.cpp \
|
||||
./SourceFiles/data/data_drafts.cpp \
|
||||
./SourceFiles/dialogs/dialogs_indexed_list.cpp \
|
||||
./SourceFiles/dialogs/dialogs_layout.cpp \
|
||||
./SourceFiles/dialogs/dialogs_list.cpp \
|
||||
|
@ -275,7 +275,7 @@ HEADERS += \
|
|||
./SourceFiles/core/observer.h \
|
||||
./SourceFiles/core/vector_of_moveable.h \
|
||||
./SourceFiles/core/version.h \
|
||||
./SourceFiles/data/drafts.h \
|
||||
./SourceFiles/data/data_drafts.h \
|
||||
./SourceFiles/dialogs/dialogs_common.h \
|
||||
./SourceFiles/dialogs/dialogs_indexed_list.h \
|
||||
./SourceFiles/dialogs/dialogs_layout.h \
|
||||
|
|
|
@ -1213,7 +1213,8 @@
|
|||
<ClCompile Include="SourceFiles\core\basic_types.cpp" />
|
||||
<ClCompile Include="SourceFiles\core\click_handler.cpp" />
|
||||
<ClCompile Include="SourceFiles\core\click_handler_types.cpp" />
|
||||
<ClCompile Include="SourceFiles\data\drafts.cpp" />
|
||||
<ClCompile Include="SourceFiles\data\data_abstract_structure.cpp" />
|
||||
<ClCompile Include="SourceFiles\data\data_drafts.cpp" />
|
||||
<ClCompile Include="SourceFiles\core\observer.cpp" />
|
||||
<ClCompile Include="SourceFiles\dialogswidget.cpp" />
|
||||
<ClCompile Include="SourceFiles\dialogs\dialogs_indexed_list.cpp" />
|
||||
|
@ -1226,6 +1227,7 @@
|
|||
<ClCompile Include="SourceFiles\history.cpp" />
|
||||
<ClCompile Include="SourceFiles\historywidget.cpp" />
|
||||
<ClCompile Include="SourceFiles\history\field_autocomplete.cpp" />
|
||||
<ClCompile Include="SourceFiles\history\history_service_layout.cpp" />
|
||||
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_internal.cpp" />
|
||||
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_item.cpp" />
|
||||
<ClCompile Include="SourceFiles\inline_bots\inline_bot_result.cpp" />
|
||||
|
@ -1439,7 +1441,8 @@
|
|||
<ClInclude Include="SourceFiles\core\observer.h" />
|
||||
<ClInclude Include="SourceFiles\core\vector_of_moveable.h" />
|
||||
<ClInclude Include="SourceFiles\core\version.h" />
|
||||
<ClInclude Include="SourceFiles\data\drafts.h" />
|
||||
<ClInclude Include="SourceFiles\data\data_abstract_structure.h" />
|
||||
<ClInclude Include="SourceFiles\data\data_drafts.h" />
|
||||
<ClInclude Include="SourceFiles\dialogs\dialogs_common.h" />
|
||||
<ClInclude Include="SourceFiles\dialogs\dialogs_indexed_list.h" />
|
||||
<ClInclude Include="SourceFiles\dialogs\dialogs_layout.h" />
|
||||
|
@ -1460,6 +1463,7 @@
|
|||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history/field_autocomplete.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="SourceFiles\history\history_common.h" />
|
||||
<ClInclude Include="SourceFiles\history\history_service_layout.h" />
|
||||
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_internal.h" />
|
||||
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_item.h" />
|
||||
<ClInclude Include="SourceFiles\inline_bots\inline_bot_result.h" />
|
||||
|
@ -1865,6 +1869,7 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/ui/twidget.h"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="SourceFiles\ui\style\style_abstract_data.h" />
|
||||
<ClInclude Include="SourceFiles\ui\style\style_core.h" />
|
||||
<ClInclude Include="SourceFiles\ui\style\style_core_color.h" />
|
||||
<ClInclude Include="SourceFiles\ui\style\style_core_font.h" />
|
||||
|
|
|
@ -1170,9 +1170,6 @@
|
|||
<ClCompile Include="GeneratedFiles\styles\style_dialogs.cpp">
|
||||
<Filter>GeneratedFiles\styles</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SourceFiles\data\drafts.cpp">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SourceFiles\window\section_widget.cpp">
|
||||
<Filter>SourceFiles\window</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1290,6 +1287,15 @@
|
|||
<ClCompile Include="SourceFiles\dialogs\dialogs_row.cpp">
|
||||
<Filter>SourceFiles\dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SourceFiles\history\history_service_layout.cpp">
|
||||
<Filter>SourceFiles\history</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SourceFiles\data\data_drafts.cpp">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SourceFiles\data\data_abstract_structure.cpp">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="SourceFiles\stdafx.h">
|
||||
|
@ -1469,9 +1475,6 @@
|
|||
<ClInclude Include="GeneratedFiles\styles\style_dialogs.h">
|
||||
<Filter>GeneratedFiles\styles</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SourceFiles\data\drafts.h">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GeneratedFiles\styles\style_profile.h">
|
||||
<Filter>GeneratedFiles\styles</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1514,6 +1517,18 @@
|
|||
<ClInclude Include="SourceFiles\ui\buttons\history_down_button.h">
|
||||
<Filter>SourceFiles\ui\buttons</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SourceFiles\history\history_service_layout.h">
|
||||
<Filter>SourceFiles\history</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SourceFiles\ui\style\style_abstract_data.h">
|
||||
<Filter>SourceFiles\ui\style</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SourceFiles\data\data_abstract_structure.h">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SourceFiles\data\data_drafts.h">
|
||||
<Filter>SourceFiles\data</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="SourceFiles\application.h">
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
0716C99A1D08225000797B22 /* dialogs.style in Resources */ = {isa = PBXBuildFile; fileRef = 0716C9981D08225000797B22 /* dialogs.style */; };
|
||||
0716C9A01D08251C00797B22 /* style_dialogs.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0716C99C1D08251C00797B22 /* style_dialogs.cpp */; };
|
||||
0716C9A11D08251C00797B22 /* style_history.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0716C99E1D08251C00797B22 /* style_history.cpp */; };
|
||||
0716C9A51D08256C00797B22 /* drafts.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0716C9A31D08256C00797B22 /* drafts.cpp */; };
|
||||
0716C9A51D08256C00797B22 /* data_drafts.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0716C9A31D08256C00797B22 /* data_drafts.cpp */; };
|
||||
0716C9A71D08258A00797B22 /* history.style in Resources */ = {isa = PBXBuildFile; fileRef = 0716C9A61D08258A00797B22 /* history.style */; };
|
||||
0716C9AA1D0825A800797B22 /* history_down_button.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0716C9A81D0825A800797B22 /* history_down_button.cpp */; };
|
||||
071AD8D21C5E8E6D008C9E90 /* zip.c in Compile Sources */ = {isa = PBXBuildFile; fileRef = 071AD8D11C5E8E6D008C9E90 /* zip.c */; };
|
||||
|
@ -455,8 +455,8 @@
|
|||
0716C99D1D08251C00797B22 /* style_dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = style_dialogs.h; path = GeneratedFiles/styles/style_dialogs.h; sourceTree = SOURCE_ROOT; };
|
||||
0716C99E1D08251C00797B22 /* style_history.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = style_history.cpp; path = GeneratedFiles/styles/style_history.cpp; sourceTree = SOURCE_ROOT; };
|
||||
0716C99F1D08251C00797B22 /* style_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = style_history.h; path = GeneratedFiles/styles/style_history.h; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A31D08256C00797B22 /* drafts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = drafts.cpp; path = SourceFiles/data/drafts.cpp; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A41D08256C00797B22 /* drafts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = drafts.h; path = SourceFiles/data/drafts.h; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A31D08256C00797B22 /* data_drafts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = data_drafts.cpp; path = SourceFiles/data/data_drafts.cpp; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A41D08256C00797B22 /* data_drafts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = data_drafts.h; path = SourceFiles/data/data_drafts.h; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A61D08258A00797B22 /* history.style */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = history.style; path = SourceFiles/history/history.style; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A81D0825A800797B22 /* history_down_button.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = history_down_button.cpp; path = SourceFiles/ui/buttons/history_down_button.cpp; sourceTree = SOURCE_ROOT; };
|
||||
0716C9A91D0825A800797B22 /* history_down_button.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = history_down_button.h; path = SourceFiles/ui/buttons/history_down_button.h; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -938,8 +938,8 @@
|
|||
0716C9A21D08255E00797B22 /* data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0716C9A31D08256C00797B22 /* drafts.cpp */,
|
||||
0716C9A41D08256C00797B22 /* drafts.h */,
|
||||
0716C9A31D08256C00797B22 /* data_drafts.cpp */,
|
||||
0716C9A41D08256C00797B22 /* data_drafts.h */,
|
||||
);
|
||||
name = data;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1932,7 +1932,7 @@
|
|||
4BF3F8D0797BC8A0C1FAD13C /* introphone.cpp in Compile Sources */,
|
||||
4978DE680549639AE9AA9CA6 /* introsignup.cpp in Compile Sources */,
|
||||
076B1C551CBFC6F2002C0BC2 /* click_handler.cpp in Compile Sources */,
|
||||
0716C9A51D08256C00797B22 /* drafts.cpp in Compile Sources */,
|
||||
0716C9A51D08256C00797B22 /* data_drafts.cpp in Compile Sources */,
|
||||
8B22E794EFF0EAFF964A3043 /* introstart.cpp in Compile Sources */,
|
||||
74343521EECC740F777DAFE6 /* pspecific_mac.cpp in Compile Sources */,
|
||||
26A81090DC8B5BCF7278FDFF /* qrc_telegram.cpp in Compile Sources */,
|
||||
|
|
Loading…
Reference in New Issue