Save IV window geometry.

This commit is contained in:
John Preston 2024-03-23 12:42:22 +04:00
parent d9ed3a7d3f
commit fd833dff35
15 changed files with 271 additions and 13 deletions

View file

@ -995,6 +995,8 @@ PRIVATE
intro/intro_step.h
intro/intro_widget.cpp
intro/intro_widget.h
iv/iv_delegate_impl.cpp
iv/iv_delegate_impl.h
iv/iv_instance.cpp
iv/iv_instance.h
lang/lang_cloud_manager.cpp

View file

@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_updates.h"
#include "calls/calls_instance.h"
#include "countries/countries_manager.h"
#include "iv/iv_delegate_impl.h"
#include "iv/iv_instance.h"
#include "lang/lang_file_parser.h"
#include "lang/lang_translator.h"
@ -163,7 +164,8 @@ Application::Application()
, _domain(std::make_unique<Main::Domain>(cDataFile()))
, _exportManager(std::make_unique<Export::Manager>())
, _calls(std::make_unique<Calls::Instance>())
, _iv(std::make_unique<Iv::Instance>())
, _iv(std::make_unique<Iv::Instance>(
Ui::CreateChild<Iv::DelegateImpl>(this)))
, _langpack(std::make_unique<Lang::Instance>())
, _langCloudManager(std::make_unique<Lang::CloudManager>(langpack()))
, _emojiKeywords(std::make_unique<ChatHelpers::EmojiKeywords>())
@ -1368,6 +1370,25 @@ Window::Controller *Application::windowFor(
return activePrimaryWindow();
}
Window::Controller *Application::findWindow(
not_null<QWidget*> widget) const {
const auto window = widget->window();
if (_lastActiveWindow && _lastActiveWindow->widget() == window) {
return _lastActiveWindow;
}
for (const auto &[account, primary] : _primaryWindows) {
if (primary->widget() == window) {
return primary.get();
}
}
for (const auto &[history, secondary] : _secondaryWindows) {
if (secondary->widget() == window) {
return secondary.get();
}
}
return nullptr;
}
Window::Controller *Application::activeWindow() const {
return _lastActiveWindow;
}

View file

@ -47,6 +47,7 @@ class Session;
namespace Iv {
class Instance;
class DelegateImpl;
} // namespace Iv
namespace Ui {
@ -165,6 +166,8 @@ public:
bool hasActiveWindow(not_null<Main::Session*> session) const;
[[nodiscard]] bool savingPositionFor(
not_null<Window::Controller*> window) const;
[[nodiscard]] Window::Controller *findWindow(
not_null<QWidget*> widget) const;
[[nodiscard]] Window::Controller *activeWindow() const;
[[nodiscard]] Window::Controller *activePrimaryWindow() const;
[[nodiscard]] Window::Controller *separateWindowForAccount(

View file

@ -134,6 +134,8 @@ QByteArray Settings::serialize() const {
LogPosition(_windowPosition, u"Window"_q);
const auto mediaViewPosition = Serialize(_mediaViewPosition);
LogPosition(_mediaViewPosition, u"Viewer"_q);
const auto ivPosition = Serialize(_ivPosition);
LogPosition(_ivPosition, u"IV"_q);
const auto proxy = _proxy.serialize();
const auto skipLanguages = _skipTranslationLanguages.current();
@ -209,7 +211,8 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_playbackDeviceId.current())
+ Serialize::stringSize(_captureDeviceId.current())
+ Serialize::stringSize(_callPlaybackDeviceId.current())
+ Serialize::stringSize(_callCaptureDeviceId.current());
+ Serialize::stringSize(_callCaptureDeviceId.current())
+ Serialize::bytearraySize(ivPosition);
auto result = QByteArray();
result.reserve(size);
@ -353,7 +356,8 @@ QByteArray Settings::serialize() const {
<< _playbackDeviceId.current()
<< _captureDeviceId.current()
<< _callPlaybackDeviceId.current()
<< _callCaptureDeviceId.current();
<< _callCaptureDeviceId.current()
<< ivPosition;
}
Ensures(result.size() == size);
@ -469,6 +473,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
base::flat_set<QString> recentEmojiSkip;
qint32 trayIconMonochrome = (_trayIconMonochrome.current() ? 1 : 0);
qint32 ttlVoiceClickTooltipHidden = _ttlVoiceClickTooltipHidden.current() ? 1 : 0;
QByteArray ivPosition;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@ -747,6 +752,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
? QString()
: legacyCallCaptureDeviceId;
}
if (!stream.atEnd()) {
stream >> ivPosition;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@ -945,6 +953,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_recentEmojiSkip = std::move(recentEmojiSkip);
_trayIconMonochrome = (trayIconMonochrome == 1);
_ttlVoiceClickTooltipHidden = (ttlVoiceClickTooltipHidden == 1);
if (!ivPosition.isEmpty()) {
_ivPosition = Deserialize(ivPosition);
}
}
QString Settings::getSoundPath(const QString &key) const {

View file

@ -863,6 +863,13 @@ public:
_ttlVoiceClickTooltipHidden = value;
}
[[nodiscard]] const WindowPosition &ivPosition() const {
return _ivPosition;
}
void setIvPosition(const WindowPosition &position) {
_ivPosition = position;
}
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
@ -990,6 +997,7 @@ private:
std::optional<uint64> _macRoundIconDigest;
rpl::variable<bool> _storiesClickTooltipHidden = false;
rpl::variable<bool> _ttlVoiceClickTooltipHidden = false;
WindowPosition _ivPosition;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View file

@ -127,7 +127,7 @@ constexpr auto kMaxOriginalEntryLines = 8192;
if (const auto controller = my.sessionWindow.get()) {
if (const auto iv = webpage->iv.get()) {
const auto hash = ExtractHash(webpage, text);
Core::App().iv().show(controller->uiShow(), iv, hash);
Core::App().iv().show(controller, iv, hash);
return;
} else {
HiddenUrlClickHandler::Open(webpage->url, context.other);

View file

@ -194,8 +194,11 @@ namespace {
} // namespace
Controller::Controller(Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox)
: _updateStyles([=] {
Controller::Controller(
not_null<Delegate*> delegate,
Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox)
: _delegate(delegate)
, _updateStyles([=] {
const auto str = EscapeForScriptString(ComputeStyles());
if (_webview) {
_webview->eval("IV.updateStyles('" + str + "');");
@ -359,9 +362,15 @@ void Controller::createWindow() {
updateTitleGeometry(width);
}, _subtitle->lifetime());
window->setGeometry({ 200, 200, 600, 800 });
window->setGeometry(_delegate->ivGeometry());
window->setMinimumSize({ st::windowMinWidth, st::windowMinHeight });
window->geometryValue(
) | rpl::distinct_until_changed(
) | rpl::skip(1) | rpl::start_with_next([=] {
_delegate->ivSaveGeometry(window);
}, window->lifetime());
_container = Ui::CreateChild<Ui::RpWidget>(window->window());
rpl::combine(
window->sizeValue(),

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/invoke_queued.h"
#include "base/object_ptr.h"
#include "base/unique_qptr.h"
#include "iv/iv_delegate.h"
#include "ui/effects/animations.h"
#include "ui/text/text.h"
@ -46,7 +47,9 @@ struct ShareBoxDescriptor {
class Controller final {
public:
explicit Controller(Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox);
Controller(
not_null<Delegate*> delegate,
Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox);
~Controller();
struct Event {
@ -115,6 +118,8 @@ private:
void showShareMenu();
void destroyShareMenu();
const not_null<Delegate*> _delegate;
std::unique_ptr<Ui::RpWindow> _window;
std::unique_ptr<Ui::RpWidget> _subtitleWrap;
rpl::variable<QString> _subtitleText;

View file

@ -0,0 +1,19 @@
/*
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
namespace Iv {
class Delegate {
public:
virtual void ivSetLastSourceWindow(not_null<QWidget*> window) = 0;
[[nodiscard]] virtual QRect ivGeometry() const = 0;
virtual void ivSaveGeometry(not_null<QWidget*> window) = 0;
};
} // namespace Iv

View file

@ -0,0 +1,120 @@
/*
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 "iv/iv_delegate_impl.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "mainwindow.h"
#include "window/main_window.h"
#include "window/window_controller.h"
#include "styles/style_window.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <QtGui/QWindow>
namespace Iv {
namespace {
[[nodiscard]] Core::WindowPosition DefaultPosition() {
auto center = qApp->primaryScreen()->geometry().center();
const auto moncrc = [&] {
if (const auto active = Core::App().activeWindow()) {
const auto widget = active->widget();
center = widget->geometry().center();
if (const auto screen = widget->screen()) {
return Platform::ScreenNameChecksum(screen->name());
}
}
return Core::App().settings().windowPosition().moncrc;
}();
return {
.moncrc = moncrc,
.scale = cScale(),
.x = (center.x() - st::ivWidthDefault / 2),
.y = (center.y() - st::ivHeightDefault / 2),
.w = st::ivWidthDefault,
.h = st::ivHeightDefault,
};
}
} // namespace
void DelegateImpl::ivSetLastSourceWindow(not_null<QWidget*> window) {
_lastSourceWindow = window;
}
QRect DelegateImpl::ivGeometry() const {
const auto found = _lastSourceWindow
? Core::App().findWindow(_lastSourceWindow)
: nullptr;
const auto saved = Core::App().settings().ivPosition();
const auto adjusted = Core::AdjustToScale(saved, u"IV"_q);
const auto initial = DefaultPosition();
auto result = initial.rect();
if (const auto window = found ? found : Core::App().activeWindow()) {
result = window->widget()->countInitialGeometry(
adjusted,
initial,
{ st::ivWidthMin, st::ivHeightMin });
}
return result;
}
void DelegateImpl::ivSaveGeometry(not_null<QWidget*> window) {
if (!window->windowHandle()) {
return;
}
const auto state = window->windowHandle()->windowState();
if (state == Qt::WindowMinimized) {
return;
}
const auto &savedPosition = Core::App().settings().ivPosition();
auto realPosition = savedPosition;
if (state == Qt::WindowMaximized) {
realPosition.maximized = 1;
realPosition.moncrc = 0;
DEBUG_LOG(("IV Pos: Saving maximized position."));
} else {
auto r = window->geometry();
realPosition.x = r.x();
realPosition.y = r.y();
realPosition.w = r.width();
realPosition.h = r.height();
realPosition.scale = cScale();
realPosition.maximized = 0;
realPosition.moncrc = 0;
DEBUG_LOG(("IV Pos: "
"Saving non-maximized position: %1, %2, %3, %4"
).arg(realPosition.x
).arg(realPosition.y
).arg(realPosition.w
).arg(realPosition.h));
}
realPosition = Window::PositionWithScreen(
realPosition,
window,
{ st::ivWidthMin, st::ivHeightMin });
if (realPosition.w >= st::ivWidthMin
&& realPosition.h >= st::ivHeightMin
&& realPosition != savedPosition) {
DEBUG_LOG(("IV Pos: "
"Writing: %1, %2, %3, %4 (scale %5%, maximized %6)")
.arg(realPosition.x)
.arg(realPosition.y)
.arg(realPosition.w)
.arg(realPosition.h)
.arg(realPosition.scale)
.arg(Logs::b(realPosition.maximized)));
Core::App().settings().setIvPosition(realPosition);
Core::App().saveSettingsDelayed();
}
}
} // namespace Iv

View file

@ -0,0 +1,27 @@
/*
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 "iv/iv_delegate.h"
namespace Iv {
class DelegateImpl final : public Delegate {
public:
DelegateImpl() = default;
void ivSetLastSourceWindow(not_null<QWidget*> window) override;
[[nodiscard]] QRect ivGeometry() const override;
void ivSaveGeometry(not_null<QWidget*> window) override;
private:
QPointer<QWidget> _lastSourceWindow;
};
} // namespace Iv

View file

@ -62,6 +62,7 @@ constexpr auto kKeepLoadingParts = 8;
class Shown final : public base::has_weak_ptr {
public:
Shown(
not_null<Delegate*> delegate,
std::shared_ptr<Main::SessionShow> show,
not_null<Data*> data,
QString hash);
@ -143,6 +144,7 @@ private:
int64 total = 0);
void requestFail(Webview::DataRequest request);
const not_null<Delegate*> _delegate;
const not_null<Main::Session*> _session;
std::shared_ptr<Main::SessionShow> _show;
QString _id;
@ -166,10 +168,12 @@ private:
};
Shown::Shown(
not_null<Delegate*> delegate,
std::shared_ptr<Main::SessionShow> show,
not_null<Data*> data,
QString hash)
: _session(&show->session())
: _delegate(delegate)
, _session(&show->session())
, _show(show) {
prepare(data, hash);
}
@ -398,7 +402,9 @@ void Shown::createController() {
const auto showShareBox = [=](ShareBoxDescriptor &&descriptor) {
return shareBox(std::move(descriptor));
};
_controller = std::make_unique<Controller>(std::move(showShareBox));
_controller = std::make_unique<Controller>(
_delegate,
std::move(showShareBox));
_controller->events(
) | rpl::start_to_stream(_events, _controller->lifetime());
@ -795,10 +801,19 @@ void Shown::minimize() {
}
}
Instance::Instance() = default;
Instance::Instance(not_null<Delegate*> delegate) : _delegate(delegate) {
}
Instance::~Instance() = default;
void Instance::show(
not_null<Window::SessionController*> controller,
not_null<Data*> data,
QString hash) {
_delegate->ivSetLastSourceWindow(controller->widget());
show(controller->uiShow(), data, hash);
}
void Instance::show(
std::shared_ptr<Main::SessionShow> show,
not_null<Data*> data,
@ -813,7 +828,7 @@ void Instance::show(
_shown->moveTo(data, hash);
return;
}
_shown = std::make_unique<Shown>(show, data, hash);
_shown = std::make_unique<Shown>(_delegate, show, data, hash);
_shownSession = session;
_shown->events() | rpl::start_with_next([=](Controller::Event event) {
using Type = Controller::Event::Type;

View file

@ -7,11 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "iv/iv_delegate.h"
namespace Main {
class Session;
class SessionShow;
} // namespace Main
namespace Window {
class SessionController;
} // namespace Window
namespace Iv {
class Data;
@ -19,9 +25,13 @@ class Shown;
class Instance final {
public:
Instance();
explicit Instance(not_null<Delegate*> delegate);
~Instance();
void show(
not_null<Window::SessionController*> controller,
not_null<Data*> data,
QString hash);
void show(
std::shared_ptr<Main::SessionShow> show,
not_null<Data*> data,
@ -42,6 +52,8 @@ private:
void processJoinChannel(const QString &context);
void requestFull(not_null<Main::Session*> session, const QString &id);
const not_null<Delegate*> _delegate;
std::unique_ptr<Shown> _shown;
Main::Session *_shownSession = nullptr;
base::flat_set<not_null<Main::Session*>> _tracking;

View file

@ -319,6 +319,11 @@ windowArchiveToast: Toast(defaultToast) {
maxWidth: boxWideWidth;
}
ivWidthMin: 380px;
ivHeightMin: 480px;
ivWidthDefault: 600px;
ivHeightDefault: 800px;
// Windows specific
winQuitIcon: icon {{ "win_quit", windowFg }};

View file

@ -15,6 +15,7 @@ PRIVATE
iv/iv_controller.h
iv/iv_data.cpp
iv/iv_data.h
iv/iv_delegate.h
iv/iv_pch.h
iv/iv_prepare.cpp
iv/iv_prepare.h