mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Add test_text test app.
This commit is contained in:
parent
ffd54c452c
commit
a98f559066
6 changed files with 491 additions and 1 deletions
|
@ -37,6 +37,10 @@ include(cmake/td_scheme.cmake)
|
|||
include(cmake/td_ui.cmake)
|
||||
include(cmake/generate_appdata_changelog.cmake)
|
||||
|
||||
if (DESKTOP_APP_TEST_APPS)
|
||||
include(cmake/tests.cmake)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
include(cmake/generate_midl.cmake)
|
||||
generate_midl(Telegram ${src_loc}
|
||||
|
|
219
Telegram/SourceFiles/tests/test_main.cpp
Normal file
219
Telegram/SourceFiles/tests/test_main.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "tests/test_main.h"
|
||||
|
||||
#include "base/invoke_queued.h"
|
||||
#include "base/integration.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/painter.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QScreen>
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
|
||||
#include <qpa/qplatformscreen.h>
|
||||
|
||||
namespace Test {
|
||||
|
||||
bool App::notifyOrInvoke(QObject *receiver, QEvent *e) {
|
||||
if (e->type() == base::InvokeQueuedEvent::Type()) {
|
||||
static_cast<base::InvokeQueuedEvent*>(e)->invoke();
|
||||
return true;
|
||||
}
|
||||
return QApplication::notify(receiver, e);
|
||||
}
|
||||
|
||||
bool App::nativeEventFilter(
|
||||
const QByteArray &eventType,
|
||||
void *message,
|
||||
native_event_filter_result *result) {
|
||||
registerEnterFromEventLoop();
|
||||
return false;
|
||||
}
|
||||
|
||||
void App::checkForEmptyLoopNestingLevel() {
|
||||
// _loopNestingLevel == _eventNestingLevel means that we had a
|
||||
// native event in a nesting loop that didn't get a notify() call
|
||||
// after. That means we already have exited the nesting loop and
|
||||
// there must not be any postponed calls with that nesting level.
|
||||
if (_loopNestingLevel == _eventNestingLevel) {
|
||||
Assert(_postponedCalls.empty()
|
||||
|| _postponedCalls.back().loopNestingLevel < _loopNestingLevel);
|
||||
Assert(!_previousLoopNestingLevels.empty());
|
||||
|
||||
_loopNestingLevel = _previousLoopNestingLevels.back();
|
||||
_previousLoopNestingLevels.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void App::postponeCall(FnMut<void()> &&callable) {
|
||||
Expects(callable != nullptr);
|
||||
Expects(_eventNestingLevel >= _loopNestingLevel);
|
||||
|
||||
checkForEmptyLoopNestingLevel();
|
||||
_postponedCalls.push_back({
|
||||
_loopNestingLevel,
|
||||
std::move(callable)
|
||||
});
|
||||
}
|
||||
|
||||
void App::processPostponedCalls(int level) {
|
||||
while (!_postponedCalls.empty()) {
|
||||
auto &last = _postponedCalls.back();
|
||||
if (last.loopNestingLevel != level) {
|
||||
break;
|
||||
}
|
||||
auto taken = std::move(last);
|
||||
_postponedCalls.pop_back();
|
||||
taken.callable();
|
||||
}
|
||||
}
|
||||
|
||||
void App::incrementEventNestingLevel() {
|
||||
++_eventNestingLevel;
|
||||
}
|
||||
|
||||
void App::decrementEventNestingLevel() {
|
||||
Expects(_eventNestingLevel >= _loopNestingLevel);
|
||||
|
||||
if (_eventNestingLevel == _loopNestingLevel) {
|
||||
_loopNestingLevel = _previousLoopNestingLevels.back();
|
||||
_previousLoopNestingLevels.pop_back();
|
||||
}
|
||||
const auto processTillLevel = _eventNestingLevel - 1;
|
||||
processPostponedCalls(processTillLevel);
|
||||
checkForEmptyLoopNestingLevel();
|
||||
_eventNestingLevel = processTillLevel;
|
||||
|
||||
Ensures(_eventNestingLevel >= _loopNestingLevel);
|
||||
}
|
||||
|
||||
void App::registerEnterFromEventLoop() {
|
||||
Expects(_eventNestingLevel >= _loopNestingLevel);
|
||||
|
||||
if (_eventNestingLevel > _loopNestingLevel) {
|
||||
_previousLoopNestingLevels.push_back(_loopNestingLevel);
|
||||
_loopNestingLevel = _eventNestingLevel;
|
||||
}
|
||||
}
|
||||
|
||||
bool App::notify(QObject *receiver, QEvent *e) {
|
||||
if (QThread::currentThreadId() != _mainThreadId) {
|
||||
return notifyOrInvoke(receiver, e);
|
||||
}
|
||||
|
||||
const auto wrap = createEventNestingLevel();
|
||||
if (e->type() == QEvent::UpdateRequest) {
|
||||
const auto weak = QPointer<QObject>(receiver);
|
||||
_widgetUpdateRequests.fire({});
|
||||
if (!weak) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return notifyOrInvoke(receiver, e);
|
||||
}
|
||||
|
||||
rpl::producer<> App::widgetUpdateRequests() const {
|
||||
return _widgetUpdateRequests.events();
|
||||
}
|
||||
|
||||
void BaseIntegration::enterFromEventLoop(FnMut<void()> &&method) {
|
||||
app().customEnterFromEventLoop(std::move(method));
|
||||
}
|
||||
|
||||
bool BaseIntegration::logSkipDebug() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseIntegration::logMessageDebug(const QString &message) {
|
||||
}
|
||||
|
||||
void BaseIntegration::logMessage(const QString &message) {
|
||||
}
|
||||
|
||||
void UiIntegration::postponeCall(FnMut<void()> &&callable) {
|
||||
app().postponeCall(std::move(callable));
|
||||
}
|
||||
|
||||
void UiIntegration::registerLeaveSubscription(not_null<QWidget*> widget) {
|
||||
}
|
||||
|
||||
void UiIntegration::unregisterLeaveSubscription(not_null<QWidget*> widget) {
|
||||
}
|
||||
|
||||
QString UiIntegration::emojiCacheFolder() {
|
||||
return QDir().currentPath() + "/tests/" + name() + "/emoji";
|
||||
}
|
||||
|
||||
QString UiIntegration::openglCheckFilePath() {
|
||||
return QDir().currentPath() + "/tests/" + name() + "/opengl";
|
||||
}
|
||||
|
||||
QString UiIntegration::angleBackendFilePath() {
|
||||
return QDir().currentPath() + "/test/" + name() + "/angle";
|
||||
}
|
||||
|
||||
} // namespace Test
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
using namespace Test;
|
||||
|
||||
auto app = App(argc, argv);
|
||||
app.installNativeEventFilter(&app);
|
||||
|
||||
const auto ratio = app.devicePixelRatio();
|
||||
const auto useRatio = std::clamp(qCeil(ratio), 1, 3);
|
||||
style::SetDevicePixelRatio(useRatio);
|
||||
|
||||
const auto screen = App::primaryScreen();
|
||||
const auto dpi = screen->logicalDotsPerInch();
|
||||
const auto basePair = screen->handle()->logicalBaseDpi();
|
||||
const auto baseMiddle = (basePair.first + basePair.second) * 0.5;
|
||||
const auto screenExact = dpi / baseMiddle;
|
||||
const auto screenScale = int(base::SafeRound(screenExact * 20)) * 5;
|
||||
const auto chosen = std::clamp(
|
||||
screenScale,
|
||||
style::kScaleMin,
|
||||
style::MaxScaleForRatio(useRatio));
|
||||
|
||||
BaseIntegration base(argc, argv);
|
||||
base::Integration::Set(&base);
|
||||
|
||||
UiIntegration ui;
|
||||
Ui::Integration::Set(&ui);
|
||||
|
||||
InvokeQueued(&app, [=] {
|
||||
new Ui::Animations::Manager();
|
||||
style::StartManager(chosen);
|
||||
|
||||
Ui::Emoji::Init();
|
||||
|
||||
const auto window = new Ui::RpWindow();
|
||||
window->setGeometry(
|
||||
{ scale(100), scale(100), scale(800), scale(600) });
|
||||
window->show();
|
||||
|
||||
window->setMinimumSize({ scale(240), scale(320) });
|
||||
|
||||
test(window, window->body());
|
||||
});
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
namespace crl {
|
||||
|
||||
rpl::producer<> on_main_update_requests() {
|
||||
return Test::app().widgetUpdateRequests();
|
||||
}
|
||||
|
||||
} // namespace crl
|
110
Telegram/SourceFiles/tests/test_main.h
Normal file
110
Telegram/SourceFiles/tests/test_main.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
#include "base/integration.h"
|
||||
#include "ui/style/style_core_scale.h"
|
||||
#include "ui/integration.h"
|
||||
|
||||
#include <crl/crl.h>
|
||||
#include <rpl/rpl.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
class RpWindow;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Test {
|
||||
|
||||
[[nodiscard]] QString name();
|
||||
|
||||
void test(not_null<Ui::RpWindow*> window, not_null<Ui::RpWidget*> widget);
|
||||
|
||||
[[nodiscard]] inline int scale(int value) {
|
||||
return style::ConvertScale(value);
|
||||
};
|
||||
|
||||
class App final : public QApplication, public QAbstractNativeEventFilter {
|
||||
public:
|
||||
using QApplication::QApplication;
|
||||
|
||||
template <typename Callable>
|
||||
auto customEnterFromEventLoop(Callable &&callable) {
|
||||
registerEnterFromEventLoop();
|
||||
const auto wrap = createEventNestingLevel();
|
||||
return callable();
|
||||
}
|
||||
|
||||
void postponeCall(FnMut<void()> &&callable);
|
||||
|
||||
[[nodiscard]] rpl::producer<> widgetUpdateRequests() const;
|
||||
|
||||
private:
|
||||
struct PostponedCall {
|
||||
int loopNestingLevel = 0;
|
||||
FnMut<void()> callable;
|
||||
};
|
||||
|
||||
auto createEventNestingLevel() {
|
||||
incrementEventNestingLevel();
|
||||
return gsl::finally([=] { decrementEventNestingLevel(); });
|
||||
}
|
||||
|
||||
void checkForEmptyLoopNestingLevel();
|
||||
void processPostponedCalls(int level);
|
||||
void incrementEventNestingLevel();
|
||||
void decrementEventNestingLevel();
|
||||
void registerEnterFromEventLoop();
|
||||
|
||||
bool notifyOrInvoke(QObject *receiver, QEvent *e);
|
||||
bool notify(QObject *receiver, QEvent *e) override;
|
||||
bool nativeEventFilter(
|
||||
const QByteArray &eventType,
|
||||
void *message,
|
||||
native_event_filter_result *result) override;
|
||||
|
||||
rpl::event_stream<> _widgetUpdateRequests;
|
||||
Qt::HANDLE _mainThreadId = QThread::currentThreadId();
|
||||
int _eventNestingLevel = 0;
|
||||
int _loopNestingLevel = 0;
|
||||
std::vector<int> _previousLoopNestingLevels;
|
||||
std::vector<PostponedCall> _postponedCalls;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] inline App &app() {
|
||||
return *static_cast<App*>(QCoreApplication::instance());
|
||||
}
|
||||
|
||||
class BaseIntegration final : public base::Integration {
|
||||
public:
|
||||
using Integration::Integration;
|
||||
|
||||
void enterFromEventLoop(FnMut<void()> &&method);
|
||||
bool logSkipDebug();
|
||||
void logMessageDebug(const QString &message);
|
||||
void logMessage(const QString &message);
|
||||
};
|
||||
|
||||
class UiIntegration final : public Ui::Integration {
|
||||
public:
|
||||
void postponeCall(FnMut<void()> &&callable);
|
||||
void registerLeaveSubscription(not_null<QWidget*> widget);
|
||||
void unregisterLeaveSubscription(not_null<QWidget*> widget);
|
||||
QString emojiCacheFolder();
|
||||
QString openglCheckFilePath();
|
||||
QString angleBackendFilePath();
|
||||
};
|
||||
|
||||
} // namespace Test
|
113
Telegram/SourceFiles/tests/test_text.cpp
Normal file
113
Telegram/SourceFiles/tests/test_text.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "tests/test_main.h"
|
||||
|
||||
#include "base/invoke_queued.h"
|
||||
#include "base/integration.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/text/text.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/painter.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
|
||||
namespace Test {
|
||||
|
||||
QString name() {
|
||||
return u"text"_q;
|
||||
}
|
||||
|
||||
void test(not_null<Ui::RpWindow*> window, not_null<Ui::RpWidget*> body) {
|
||||
auto text = new Ui::Text::String(scale(64));
|
||||
|
||||
const auto like = QString::fromUtf8("\xf0\x9f\x91\x8d");
|
||||
const auto dislike = QString::fromUtf8("\xf0\x9f\x91\x8e");
|
||||
const auto hebrew = QString() + QChar(1506) + QChar(1460) + QChar(1489);
|
||||
|
||||
auto data = TextWithEntities();
|
||||
data.append(
|
||||
u"Lorem ipsu7m dolor sit amet, "_q
|
||||
).append(Ui::Text::Bold(
|
||||
u"consectetur adipiscing: "_q
|
||||
+ hebrew
|
||||
+ u" elit, sed do eiusmod tempor incididunt test"_q
|
||||
)).append(Ui::Text::Wrapped(Ui::Text::Bold(
|
||||
u". ut labore et dolore magna aliqua."_q
|
||||
+ like
|
||||
+ dislike
|
||||
+ u"Ut enim ad minim veniam"_q
|
||||
), EntityType::Italic)).append(
|
||||
u", quis nostrud exercitation ullamco laboris nisi ut aliquip ex \
|
||||
ea commodo consequat. Duis aute irure dolor in reprehenderit in \
|
||||
voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa \
|
||||
qui officia deserunt mollit anim id est laborum."_q
|
||||
).append(u"\n\n"_q).append(hebrew).append("\n\n").append(
|
||||
"Duisauteiruredolorinreprehenderitinvoluptatevelitessecillumdoloreeu\
|
||||
fugiatnullapariaturExcepteursintoccaecatcupidatatnonproident, sunt in culpa \
|
||||
qui officia deserunt mollit anim id est laborum. \
|
||||
Duisauteiruredolorinreprehenderitinvoluptate.");
|
||||
data.append(data);
|
||||
//data.append("hi\n\nguys");
|
||||
text->setMarkedText(st::defaultTextStyle, data);
|
||||
|
||||
body->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(body);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
|
||||
const auto width = body->width();
|
||||
const auto height = body->height();
|
||||
|
||||
p.fillRect(clip, QColor(255, 255, 255));
|
||||
|
||||
const auto border = QColor(0, 128, 0, 16);
|
||||
auto skip = scale(20);
|
||||
p.fillRect(0, 0, skip, height, border);
|
||||
p.fillRect(skip, 0, width - skip, skip, border);
|
||||
p.fillRect(skip, height - skip, width - skip, skip, border);
|
||||
p.fillRect(width - skip, skip, skip, height - skip * 2, border);
|
||||
|
||||
const auto inner = body->rect().marginsRemoved(
|
||||
{ skip, skip, skip, skip });
|
||||
|
||||
p.fillRect(QRect{
|
||||
inner.x(),
|
||||
inner.y(),
|
||||
inner.width(),
|
||||
text->countHeight(inner.width())
|
||||
}, QColor(128, 0, 0, 16));
|
||||
|
||||
auto widths = text->countLineWidths(inner.width());
|
||||
auto top = 0;
|
||||
for (const auto width : widths) {
|
||||
p.fillRect(QRect{
|
||||
inner.x(),
|
||||
inner.y() + top,
|
||||
width,
|
||||
st::defaultTextStyle.font->height
|
||||
}, QColor(0, 0, 128, 16));
|
||||
top += st::defaultTextStyle.font->height;
|
||||
}
|
||||
|
||||
text->draw(p, {
|
||||
.position = inner.topLeft(),
|
||||
.availableWidth = inner.width(),
|
||||
});
|
||||
|
||||
//const auto to = QRectF(
|
||||
// inner.marginsRemoved({ 0, inner.height() / 2, 0, 0 }));
|
||||
//const auto t = u"hi\n\nguys"_q;
|
||||
//p.drawText(to, t);
|
||||
}, body->lifetime());
|
||||
}
|
||||
|
||||
} // namespace Test
|
44
Telegram/cmake/tests.cmake
Normal file
44
Telegram/cmake/tests.cmake
Normal file
|
@ -0,0 +1,44 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
add_executable(test_text WIN32)
|
||||
init_target(test_text "(tests)")
|
||||
|
||||
target_include_directories(test_text PRIVATE ${src_loc})
|
||||
|
||||
nice_target_sources(test_text ${src_loc}
|
||||
PRIVATE
|
||||
tests/test_main.cpp
|
||||
tests/test_main.h
|
||||
tests/test_text.cpp
|
||||
)
|
||||
|
||||
nice_target_sources(test_text ${res_loc}
|
||||
PRIVATE
|
||||
qrc/emoji_1.qrc
|
||||
qrc/emoji_2.qrc
|
||||
qrc/emoji_3.qrc
|
||||
qrc/emoji_4.qrc
|
||||
qrc/emoji_5.qrc
|
||||
qrc/emoji_6.qrc
|
||||
qrc/emoji_7.qrc
|
||||
qrc/emoji_8.qrc
|
||||
)
|
||||
|
||||
target_link_libraries(test_text
|
||||
PRIVATE
|
||||
desktop-app::lib_base
|
||||
desktop-app::lib_crl
|
||||
desktop-app::lib_ui
|
||||
desktop-app::external_qt
|
||||
desktop-app::external_qt_static_plugins
|
||||
)
|
||||
|
||||
set_target_properties(test_text PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_dependencies(Telegram test_text)
|
||||
|
||||
target_prepare_qrc(test_text)
|
2
cmake
2
cmake
|
@ -1 +1 @@
|
|||
Subproject commit 7b11e62e2a40a3dab7f039d4953f1514c73cb6d5
|
||||
Subproject commit af43537447ebdc2a241c0cdad615e794a840665a
|
Loading…
Add table
Reference in a new issue