Switch MainWindow to Ui::RpWindow

This commit is contained in:
Ilya Fedin 2021-07-26 01:30:56 +04:00 committed by John Preston
parent 4ef2d3b957
commit 1ebf27bfa1
29 changed files with 116 additions and 1933 deletions

View File

@ -873,7 +873,6 @@ PRIVATE
platform/linux/notifications_manager_linux.h
platform/linux/specific_linux.cpp
platform/linux/specific_linux.h
platform/linux/window_title_linux.h
platform/mac/file_utilities_mac.mm
platform/mac/file_utilities_mac.h
platform/mac/launcher_mac.mm
@ -888,7 +887,6 @@ PRIVATE
platform/mac/specific_mac_p.mm
platform/mac/specific_mac_p.h
platform/mac/window_title_mac.mm
platform/mac/window_title_mac.h
platform/mac/touchbar/items/mac_formatter_item.h
platform/mac/touchbar/items/mac_formatter_item.mm
platform/mac/touchbar/items/mac_pinned_chats_item.h
@ -919,8 +917,6 @@ PRIVATE
platform/win/notifications_manager_win.h
platform/win/specific_win.cpp
platform/win/specific_win.h
platform/win/window_title_win.cpp
platform/win/window_title_win.h
platform/win/windows_app_user_model_id.cpp
platform/win/windows_app_user_model_id.h
platform/win/windows_dlls.cpp
@ -1099,9 +1095,6 @@ PRIVATE
window/window_session_controller.h
window/window_slide_animation.cpp
window/window_slide_animation.h
window/window_title_qt.cpp
window/window_title_qt.h
window/window_title.h
window/window_top_bar_wrap.h
window/themes/window_theme.cpp
window/themes/window_theme.h
@ -1140,13 +1133,6 @@ PRIVATE
stdafx.h
)
if (NOT LINUX)
remove_target_sources(Telegram ${src_loc}
window/window_title_qt.cpp
window/window_title_qt.h
)
endif()
if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_xdp_file_dialog.cpp

View File

@ -269,7 +269,7 @@ void Application::run() {
const auto currentGeometry = _window->widget()->geometry();
_mediaView = std::make_unique<Media::View::OverlayWidget>();
_window->widget()->setGeometry(currentGeometry);
_window->widget()->Ui::RpWidget::setGeometry(currentGeometry);
DEBUG_LOG(("Application Info: showing."));
_window->finishFirstShow();

View File

@ -41,7 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_intro.h"
#include "platform/platform_notifications_manager.h"
#include "base/platform/base_platform_info.h"
#include "ui/platform/ui_platform_utility.h"
#include "base/call_delayed.h"
#include "base/variant.h"
#include "window/notifications_manager.h"
@ -115,12 +114,7 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
}, lifetime());
setAttribute(Qt::WA_NoSystemBackground);
if (Ui::Platform::WindowExtentsSupported()) {
setAttribute(Qt::WA_TranslucentBackground);
} else {
setAttribute(Qt::WA_OpaquePaintEvent);
}
setAttribute(Qt::WA_OpaquePaintEvent);
}
void MainWindow::initHook() {
@ -226,10 +220,8 @@ void MainWindow::applyInitialWorkMode() {
void MainWindow::finishFirstShow() {
createTrayIconMenu();
initShadows();
applyInitialWorkMode();
createGlobalMenu();
firstShadowsUpdate();
windowDeactivateEvents(
) | rpl::start_with_next([=] {
@ -419,7 +411,7 @@ void MainWindow::showMainMenu() {
ensureLayerCreated();
_layer->showMainMenu(
object_ptr<Window::MainMenu>(this, sessionController()),
object_ptr<Window::MainMenu>(body(), sessionController()),
anim::type::normal);
}

View File

@ -21,128 +21,73 @@ using namespace KWayland::Client;
namespace Platform {
namespace internal {
class WaylandIntegration::Private : public QObject {
public:
Private();
[[nodiscard]] Registry &registry() {
return _registry;
}
[[nodiscard]] XdgExporter *xdgExporter() {
return _xdgExporter.get();
}
[[nodiscard]] PlasmaShell *plasmaShell() {
return _plasmaShell.get();
}
[[nodiscard]] AppMenuManager *appMenuManager() {
return _appMenuManager.get();
}
[[nodiscard]] QEventLoop &interfacesLoop() {
return _interfacesLoop;
}
[[nodiscard]] bool interfacesAnnounced() const {
return _interfacesAnnounced;
}
private:
ConnectionThread _connection;
ConnectionThread *_applicationConnection = nullptr;
Registry _registry;
Registry _applicationRegistry;
std::unique_ptr<XdgExporter> _xdgExporter;
std::unique_ptr<PlasmaShell> _plasmaShell;
std::unique_ptr<AppMenuManager> _appMenuManager;
QEventLoop _interfacesLoop;
bool _interfacesAnnounced = false;
struct WaylandIntegration::Private {
std::unique_ptr<ConnectionThread> connection;
Registry registry;
std::unique_ptr<XdgExporter> xdgExporter;
std::unique_ptr<PlasmaShell> plasmaShell;
std::unique_ptr<AppMenuManager> appMenuManager;
};
WaylandIntegration::Private::Private()
: _applicationConnection(ConnectionThread::fromApplication(this)) {
_applicationRegistry.create(_applicationConnection);
_applicationRegistry.setup();
connect(
_applicationConnection,
&ConnectionThread::connectionDied,
&_applicationRegistry,
&Registry::destroy);
connect(&_connection, &ConnectionThread::connected, [=] {
LOG(("Successfully connected to Wayland server at socket: %1")
.arg(_connection.socketName()));
_registry.create(&_connection);
_registry.setup();
});
connect(
&_connection,
&ConnectionThread::connectionDied,
&_registry,
&Registry::destroy);
connect(&_registry, &Registry::interfacesAnnounced, [=] {
_interfacesAnnounced = true;
if (_interfacesLoop.isRunning()) {
_interfacesLoop.quit();
}
});
connect(
&_applicationRegistry,
&Registry::exporterUnstableV2Announced,
[=](uint name, uint version) {
_xdgExporter = std::unique_ptr<XdgExporter>{
_applicationRegistry.createXdgExporter(name, version),
};
connect(
_applicationConnection,
&ConnectionThread::connectionDied,
_xdgExporter.get(),
&XdgExporter::destroy);
});
connect(
&_applicationRegistry,
&Registry::plasmaShellAnnounced,
[=](uint name, uint version) {
_plasmaShell = std::unique_ptr<PlasmaShell>{
_applicationRegistry.createPlasmaShell(name, version),
};
connect(
_applicationConnection,
&ConnectionThread::connectionDied,
_plasmaShell.get(),
&PlasmaShell::destroy);
});
connect(
&_applicationRegistry,
&Registry::appMenuAnnounced,
[=](uint name, uint version) {
_appMenuManager = std::unique_ptr<AppMenuManager>{
_applicationRegistry.createAppMenuManager(name, version),
};
connect(
_applicationConnection,
&ConnectionThread::connectionDied,
_appMenuManager.get(),
&AppMenuManager::destroy);
});
_connection.initConnection();
}
WaylandIntegration::WaylandIntegration()
: _private(std::make_unique<Private>()) {
_private->connection = std::unique_ptr<ConnectionThread>{
ConnectionThread::fromApplication(),
};
_private->registry.create(_private->connection.get());
_private->registry.setup();
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
&_private->registry,
&Registry::destroy);
QObject::connect(
&_private->registry,
&Registry::exporterUnstableV2Announced,
[=](uint name, uint version) {
_private->xdgExporter = std::unique_ptr<XdgExporter>{
_private->registry.createXdgExporter(name, version),
};
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
_private->xdgExporter.get(),
&XdgExporter::destroy);
});
QObject::connect(
&_private->registry,
&Registry::plasmaShellAnnounced,
[=](uint name, uint version) {
_private->plasmaShell = std::unique_ptr<PlasmaShell>{
_private->registry.createPlasmaShell(name, version),
};
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
_private->plasmaShell.get(),
&PlasmaShell::destroy);
});
QObject::connect(
&_private->registry,
&Registry::appMenuAnnounced,
[=](uint name, uint version) {
_private->appMenuManager = std::unique_ptr<AppMenuManager>{
_private->registry.createAppMenuManager(name, version),
};
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
_private->appMenuManager.get(),
&AppMenuManager::destroy);
});
}
WaylandIntegration::~WaylandIntegration() = default;
@ -153,20 +98,8 @@ WaylandIntegration *WaylandIntegration::Instance() {
return &instance;
}
void WaylandIntegration::waitForInterfaceAnnounce() {
Expects(!_private->interfacesLoop().isRunning());
if (!_private->interfacesAnnounced()) {
_private->interfacesLoop().exec();
}
}
bool WaylandIntegration::supportsXdgDecoration() {
return _private->registry().hasInterface(
Registry::Interface::XdgDecorationUnstableV1);
}
QString WaylandIntegration::nativeHandle(QWindow *window) {
if (const auto exporter = _private->xdgExporter()) {
if (const auto exporter = _private->xdgExporter.get()) {
if (const auto surface = Surface::fromWindow(window)) {
if (const auto exported = exporter->exportTopLevel(
surface,
@ -186,11 +119,11 @@ QString WaylandIntegration::nativeHandle(QWindow *window) {
}
bool WaylandIntegration::skipTaskbarSupported() {
return _private->plasmaShell();
return _private->plasmaShell != nullptr;
}
void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) {
const auto shell = _private->plasmaShell();
const auto shell = _private->plasmaShell.get();
if (!shell) {
return;
}
@ -212,7 +145,7 @@ void WaylandIntegration::registerAppMenu(
QWindow *window,
const QString &serviceName,
const QString &objectPath) {
const auto manager = _private->appMenuManager();
const auto manager = _private->appMenuManager.get();
if (!manager) {
return;
}

View File

@ -13,8 +13,7 @@ namespace internal {
class WaylandIntegration {
public:
[[nodiscard]] static WaylandIntegration *Instance();
void waitForInterfaceAnnounce();
[[nodiscard]] bool supportsXdgDecoration();
[[nodiscard]] QString nativeHandle(QWindow *window);
[[nodiscard]] bool skipTaskbarSupported();
void skipTaskbar(QWindow *window, bool skip);
@ -27,7 +26,7 @@ private:
WaylandIntegration();
~WaylandIntegration();
class Private;
struct Private;
const std::unique_ptr<Private> _private;
};

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace internal {
class WaylandIntegration::Private {
struct WaylandIntegration::Private {
};
WaylandIntegration::WaylandIntegration() {
@ -26,13 +26,6 @@ WaylandIntegration *WaylandIntegration::Instance() {
return &instance;
}
void WaylandIntegration::waitForInterfaceAnnounce() {
}
bool WaylandIntegration::supportsXdgDecoration() {
return false;
}
QString WaylandIntegration::nativeHandle(QWindow *window) {
return {};
}

View File

@ -971,7 +971,6 @@ void MainWindow::workmodeUpdated(Core::Settings::WorkMode mode) {
}
void MainWindow::unreadCounterChangedHook() {
setWindowTitle(titleText());
updateIconCounters();
}

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "base/platform/linux/base_linux_gtk_integration.h"
#include "ui/platform/linux/ui_linux_wayland_integration.h"
#include "platform/linux/linux_desktop_environment.h"
#include "platform/linux/linux_gtk_integration.h"
#include "platform/linux/linux_wayland_integration.h"
@ -60,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
using namespace Platform;
using BaseGtkIntegration = base::Platform::GtkIntegration;
using UiWaylandIntegration = Ui::Platform::WaylandIntegration;
using Platform::internal::WaylandIntegration;
using Platform::internal::GtkIntegration;
@ -799,7 +801,7 @@ void start() {
}
// wait for interface announce to know if native window frame is supported
if (const auto integration = WaylandIntegration::Instance()) {
if (const auto integration = UiWaylandIntegration::Instance()) {
integration->waitForInterfaceAnnounce();
}

View File

@ -1,47 +0,0 @@
/*
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 "platform/platform_window_title.h"
#include "platform/linux/linux_wayland_integration.h"
#include "base/object_ptr.h"
namespace Window {
namespace Theme {
int DefaultPreviewTitleHeight();
void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth);
} // namespace Theme
} // namespace Window
namespace Platform {
inline bool AllowNativeWindowFrameToggle() {
const auto waylandIntegration = internal::WaylandIntegration::Instance();
return !waylandIntegration
|| waylandIntegration->supportsXdgDecoration();
}
inline object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) {
return object_ptr<Window::TitleWidgetQt>(parent);
}
inline bool NativeTitleRequiresShadow() {
return false;
}
inline int PreviewTitleHeight() {
return Window::Theme::DefaultPreviewTitleHeight();
}
inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) {
return Window::Theme::DefaultPreviewWindowFramePaint(preview, palette, body, outerWidth);
}
} // namespace Platform

View File

@ -62,7 +62,6 @@ protected:
void handleActiveChangedHook() override;
void stateChangedHook(Qt::WindowState state) override;
void initHook() override;
void titleVisibilityChangedHook() override;
void unreadCounterChangedHook() override;
bool hasTrayIcon() const override {
@ -80,9 +79,6 @@ protected:
void psSetupTrayIcon();
virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0;
QTimer psUpdatedPositionTimer;
void initShadows() override;
void closeWithoutDestroy() override;
void createGlobalMenu() override;
@ -90,7 +86,6 @@ private:
friend class Private;
void hideAndDeactivate();
void updateTitleCounter();
void updateIconCounters();
[[nodiscard]] QIcon generateIconForTray(int counter, bool muted) const;

View File

@ -53,9 +53,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- (void) darkModeChanged:(NSNotification *)aNotification;
- (void) screenIsLocked:(NSNotification *)aNotification;
- (void) screenIsUnlocked:(NSNotification *)aNotification;
- (void) windowWillEnterFullScreen:(NSNotification *)aNotification;
- (void) windowWillExitFullScreen:(NSNotification *)aNotification;
- (void) windowDidExitFullScreen:(NSNotification *)aNotification;
@end // @interface MainWindowObserver
@ -67,43 +64,6 @@ namespace {
// fullscreen mode, after that we'll hide the window no matter what.
constexpr auto kHideAfterFullscreenTimeoutMs = 3000;
id FindClassInSubviews(NSView *parent, NSString *className) {
for (NSView *child in [parent subviews]) {
if ([child isKindOfClass:NSClassFromString(className)]) {
return child;
} else if (id inchild = FindClassInSubviews(child, className)) {
return inchild;
}
}
return nil;
}
#ifndef OS_MAC_OLD
class LayerCreationChecker : public QObject {
public:
LayerCreationChecker(NSView * __weak view, Fn<void()> callback)
: _weakView(view)
, _callback(std::move(callback)) {
QCoreApplication::instance()->installEventFilter(this);
}
protected:
bool eventFilter(QObject *object, QEvent *event) override {
if (!_weakView || [_weakView layer] != nullptr) {
_callback();
}
return QObject::eventFilter(object, event);
}
private:
NSView * __weak _weakView = nil;
Fn<void()> _callback;
};
#endif // OS_MAC_OLD
[[nodiscard]] QImage TrayIconBack(bool darkMode) {
static const auto WithColor = [](QColor color) {
return st::macTrayIcon.instance(color, 100);
@ -127,38 +87,16 @@ public:
not_null<Window::Controller*> controller,
rpl::producer<bool> canApplyMarkdown);
void setWindowBadge(const QString &str);
void setWindowTitle(const QString &str);
void updateNativeTitle();
void enableShadow(WId winId);
void willEnterFullScreen();
void willExitFullScreen();
void didExitFullScreen();
bool clipboardHasText();
~Private();
private:
void initCustomTitle();
void refreshWeakTitleReferences();
void enforceCorrectStyleMask();
not_null<MainWindow*> _public;
friend class MainWindow;
#ifdef OS_MAC_OLD
NSWindow *_nativeWindow = nil;
NSView *_nativeView = nil;
#else // OS_MAC_OLD
NSWindow * __weak _nativeWindow = nil;
NSView * __weak _nativeView = nil;
id __weak _nativeTitleWrapWeak = nil;
id __weak _nativeTitleWeak = nil;
std::unique_ptr<LayerCreationChecker> _layerCreationChecker;
#endif // !OS_MAC_OLD
bool _useNativeTitle = false;
bool _inFullScreen = false;
MainWindowObserver *_observer = nullptr;
NSPasteboard *_generalPasteboard = nullptr;
@ -198,18 +136,6 @@ private:
Core::App().setScreenIsLocked(false);
}
- (void) windowWillEnterFullScreen:(NSNotification *)aNotification {
_private->willEnterFullScreen();
}
- (void) windowWillExitFullScreen:(NSNotification *)aNotification {
_private->willExitFullScreen();
}
- (void) windowDidExitFullScreen:(NSNotification *)aNotification {
_private->didExitFullScreen();
}
@end // @implementation MainWindowObserver
namespace Platform {
@ -256,15 +182,12 @@ void MainWindow::Private::setWindowBadge(const QString &str) {
}
}
void MainWindow::Private::setWindowTitle(const QString &str) {
_public->setWindowTitle(str);
updateNativeTitle();
}
void MainWindow::Private::setNativeWindow(NSWindow *window, NSView *view) {
_nativeWindow = window;
_nativeView = view;
initCustomTitle();
auto inner = [_nativeWindow contentLayoutRect];
auto full = [_nativeView frame];
_public->_customTitleHeight = qMax(qRound(full.size.height - inner.size.height), 0);
}
void MainWindow::Private::initTouchBar(
@ -288,119 +211,6 @@ void MainWindow::Private::initTouchBar(
#endif
}
void MainWindow::Private::initCustomTitle() {
#ifndef OS_MAC_OLD
if (![_nativeWindow respondsToSelector:@selector(contentLayoutRect)]
|| ![_nativeWindow respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) {
return;
}
[_nativeWindow setTitlebarAppearsTransparent:YES];
[[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:_nativeWindow];
[[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:_nativeWindow];
[[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:_nativeWindow];
// Qt has bug with layer-backed widgets containing QOpenGLWidgets.
// See https://bugreports.qt.io/browse/QTBUG-64494
// Emulate custom title instead (code below).
//
// Tried to backport a fix, testing.
[_nativeWindow setStyleMask:[_nativeWindow styleMask] | NSFullSizeContentViewWindowMask];
auto inner = [_nativeWindow contentLayoutRect];
auto full = [_nativeView frame];
_public->_customTitleHeight = qMax(qRound(full.size.height - inner.size.height), 0);
// Qt still has some bug with layer-backed widgets containing QOpenGLWidgets.
// See https://github.com/telegramdesktop/tdesktop/issues/4150
// Tried to workaround it by catching the first moment we have CALayer created
// and explicitly setting contentsScale to window->backingScaleFactor there.
_layerCreationChecker = std::make_unique<LayerCreationChecker>(_nativeView, [=] {
if (_nativeView && _nativeWindow) {
if (CALayer *layer = [_nativeView layer]) {
LOG(("Window Info: Setting layer scale factor to: %1").arg([_nativeWindow backingScaleFactor]));
[layer setContentsScale: [_nativeWindow backingScaleFactor]];
_layerCreationChecker = nullptr;
}
} else {
_layerCreationChecker = nullptr;
}
});
// Disabled for now.
//_useNativeTitle = true;
//setWindowTitle(qsl("Telegram"));
#endif // !OS_MAC_OLD
}
void MainWindow::Private::refreshWeakTitleReferences() {
if (!_nativeWindow) {
return;
}
#ifndef OS_MAC_OLD
@autoreleasepool {
if (NSView *parent = [[_nativeWindow contentView] superview]) {
if (id titleWrap = FindClassInSubviews(parent, Q2NSString(strTitleWrapClass()))) {
if ([titleWrap respondsToSelector:@selector(setBackgroundColor:)]) {
if (id title = FindClassInSubviews(titleWrap, Q2NSString(strTitleClass()))) {
if ([title respondsToSelector:@selector(setAttributedStringValue:)]) {
_nativeTitleWrapWeak = titleWrap;
_nativeTitleWeak = title;
}
}
}
}
}
}
#endif // !OS_MAC_OLD
}
void MainWindow::Private::updateNativeTitle() {
if (!_useNativeTitle) {
return;
}
#ifndef OS_MAC_OLD
if (!_nativeTitleWrapWeak || !_nativeTitleWeak) {
refreshWeakTitleReferences();
}
if (_nativeTitleWrapWeak && _nativeTitleWeak) {
@autoreleasepool {
auto convertColor = [](QColor color) {
return [NSColor colorWithDeviceRed:color.redF() green:color.greenF() blue:color.blueF() alpha:color.alphaF()];
};
auto adjustFg = [](const style::color &st) {
// Weird thing with NSTextField taking NSAttributedString with
// NSForegroundColorAttributeName set to colorWithDeviceRed:green:blue
// with components all equal to 128 - it ignores it and prints black text!
auto color = st->c;
return (color.red() == 128 && color.green() == 128 && color.blue() == 128)
? QColor(129, 129, 129, color.alpha())
: color;
};
auto active = _public->isActiveWindow();
auto bgColor = (active ? st::titleBgActive : st::titleBg)->c;
auto fgColor = adjustFg(active ? st::titleFgActive : st::titleFg);
auto bgConverted = convertColor(bgColor);
auto fgConverted = convertColor(fgColor);
[_nativeTitleWrapWeak setBackgroundColor:bgConverted];
auto title = Q2NSString(_public->windowTitle());
NSDictionary *attributes = _inFullScreen
? nil
: [NSDictionary dictionaryWithObjectsAndKeys: fgConverted, NSForegroundColorAttributeName, bgConverted, NSBackgroundColorAttributeName, nil];
NSAttributedString *string = [[NSAttributedString alloc] initWithString:title attributes:attributes];
[_nativeTitleWeak setAttributedStringValue:string];
}
}
#endif // !OS_MAC_OLD
}
bool MainWindow::Private::clipboardHasText() {
auto currentChangeCount = static_cast<int>([_generalPasteboard changeCount]);
if (_generalPasteboardChangeCount != currentChangeCount) {
@ -410,32 +220,6 @@ bool MainWindow::Private::clipboardHasText() {
return _generalPasteboardHasText;
}
void MainWindow::Private::willEnterFullScreen() {
_inFullScreen = true;
_public->setTitleVisible(false);
}
void MainWindow::Private::willExitFullScreen() {
_inFullScreen = false;
_public->setTitleVisible(true);
enforceCorrectStyleMask();
}
void MainWindow::Private::didExitFullScreen() {
enforceCorrectStyleMask();
}
void MainWindow::Private::enforceCorrectStyleMask() {
if (_nativeWindow && _public->_customTitleHeight > 0) {
[_nativeWindow setStyleMask:[_nativeWindow styleMask] | NSFullSizeContentViewWindowMask];
}
}
void MainWindow::Private::enableShadow(WId winId) {
// [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask];
// [[(NSView*)winId window] setHasShadow:YES];
}
MainWindow::Private::~Private() {
[_observer release];
}
@ -449,11 +233,6 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
#endif // !OS_MAC_OLD
_hideAfterFullScreenTimer.setCallback([this] { hideAndDeactivate(); });
style::PaletteChanged(
) | rpl::start_with_next([=] {
_private->updateNativeTitle();
}, lifetime());
}
void MainWindow::closeWithoutDestroy() {
@ -475,8 +254,6 @@ void MainWindow::stateChangedHook(Qt::WindowState state) {
}
void MainWindow::handleActiveChangedHook() {
InvokeQueued(this, [this] { _private->updateNativeTitle(); });
// On macOS just remove trayIcon menu if the window is not active.
// So we will activate the window on click instead of showing the menu.
if (isActiveForTrayMenu()) {
@ -506,10 +283,6 @@ void MainWindow::initHook() {
void MainWindow::updateWindowIcon() {
}
void MainWindow::titleVisibilityChangedHook() {
updateTitleCounter();
}
void MainWindow::hideAndDeactivate() {
hide();
}
@ -591,12 +364,7 @@ void _placeCounter(QImage &img, int size, int count, style::color bg, style::col
img.setDevicePixelRatio(savedRatio);
}
void MainWindow::updateTitleCounter() {
_private->setWindowTitle(titleVisible() ? QString() : titleText());
}
void MainWindow::unreadCounterChangedHook() {
updateTitleCounter();
updateIconCounters();
}
@ -649,10 +417,6 @@ QIcon MainWindow::generateIconForTray(int counter, bool muted) const {
return result;
}
void MainWindow::initShadows() {
_private->enableShadow(winId());
}
void MainWindow::createGlobalMenu() {
const auto ensureWindowShown = [=] {
if (isHidden()) {

View File

@ -1,48 +0,0 @@
/*
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 "platform/platform_window_title.h"
namespace Ui {
class PlainShadow;
} // namespace Ui
namespace Platform {
class MainWindow;
class TitleWidget : public Window::TitleWidget {
public:
TitleWidget(MainWindow *parent, int height);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
private:
object_ptr<Ui::PlainShadow> _shadow;
QFont _font;
};
inline bool AllowNativeWindowFrameToggle() {
return false;
}
object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent);
inline bool NativeTitleRequiresShadow() {
return false;
}
int PreviewTitleHeight();
void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth);
} // namespace Platform

View File

@ -5,83 +5,18 @@ 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 "platform/mac/window_title_mac.h"
#include "platform/platform_window_title.h"
#include "mainwindow.h"
#include "ui/widgets/shadow.h"
#include "ui/image/image_prepare.h"
#include "core/application.h"
#include "styles/style_window.h"
#include "styles/style_media_view.h"
#include "platform/platform_main_window.h"
#include "window/window_controller.h"
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CFURL.h>
namespace Platform {
TitleWidget::TitleWidget(MainWindow *parent, int height)
: Window::TitleWidget(parent)
, _shadow(this, st::titleShadow) {
setAttribute(Qt::WA_OpaquePaintEvent);
resize(width(), height);
#ifndef OS_MAC_OLD
QStringList families = { qsl(".SF NS Text"), qsl("Helvetica Neue") };
for (auto family : families) {
_font.setFamily(family);
if (QFontInfo(_font).family() == _font.family()) {
break;
}
}
#endif // OS_MAC_OLD
if (QFontInfo(_font).family() == _font.family()) {
_font.setPixelSize((height * 15) / 24);
} else {
_font = st::normalFont;
}
Core::App().unreadBadgeChanges(
) | rpl::start_with_next([=] {
update();
}, lifetime());
}
void TitleWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto active = isActiveWindow();
p.fillRect(rect(), active ? st::titleBgActive : st::titleBg);
p.setFont(_font);
p.setPen(active ? st::titleFgActive : st::titleFg);
p.drawText(rect(), static_cast<MainWindow*>(parentWidget())->titleText(), style::al_center);
}
void TitleWidget::resizeEvent(QResizeEvent *e) {
_shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth);
}
void TitleWidget::mouseDoubleClickEvent(QMouseEvent *e) {
auto window = parentWidget();
if (window->windowState() == Qt::WindowMaximized) {
window->setWindowState(Qt::WindowNoState);
} else {
window->setWindowState(Qt::WindowMaximized);
}
}
object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) {
if (auto window = qobject_cast<Platform::MainWindow*>(parent)) {
if (auto height = window->getCustomTitleHeight()) {
return object_ptr<TitleWidget>(window, height);
}
}
return { nullptr };
}
// All the window decorations preview is done without taking cScale() into
// account, with 100% scale and without "px" dimensions, because thats
// how it will look in real launched macOS app.

View File

@ -7,16 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "window/window_title.h"
#include "window/window_title_qt.h"
#include "window/themes/window_theme_preview.h"
#include "base/object_ptr.h"
#include "base/platform/base_platform_info.h"
namespace Platform {
bool AllowNativeWindowFrameToggle();
object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent);
bool NativeTitleRequiresShadow();
inline bool NativeTitleRequiresShadow() {
return Platform::IsWindows();
}
int PreviewTitleHeight();
void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth);
@ -25,28 +23,10 @@ void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRe
// Platform dependent implementations.
#ifdef Q_OS_MAC
#include "platform/mac/window_title_mac.h"
#elif defined Q_OS_WIN // Q_OS_MAC
#include "platform/win/window_title_win.h"
#elif defined Q_OS_UNIX // Q_OS_MAC || Q_OS_WIN
#include "platform/linux/window_title_linux.h"
#else // Q_OS_MAC || Q_OS_WIN || Q_OS_UNIX
#ifndef Q_OS_MAC
namespace Platform {
inline bool AllowNativeWindowFrameToggle() {
return true;
}
inline object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) {
return object_ptr<Window::TitleWidgetQt>(parent);
}
inline bool NativeTitleRequiresShadow() {
return false;
}
inline int PreviewTitleHeight() {
return Window::Theme::DefaultPreviewTitleHeight();
}
@ -57,4 +37,4 @@ inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palet
} // namespace Platform
#endif // Q_OS_MAC || Q_OS_WIN || Q_OS_UNIX
#endif // !Q_OS_MAC

View File

@ -43,8 +43,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
HICON qt_pixmapToWinHICON(const QPixmap &);
Q_DECLARE_METATYPE(QMargins);
namespace ViewManagement = ABI::Windows::UI::ViewManagement;
namespace Platform {
@ -122,12 +120,6 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
if (!kTaskbarCreatedMsgId) {
kTaskbarCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated");
}
style::PaletteChanged(
) | rpl::start_with_next([=] {
if (_shadow) {
_shadow->setColor(st::windowShadowFg->c);
}
}, lifetime());
setupNativeWindowFrame();
using namespace rpl::mappers;
@ -163,15 +155,7 @@ void MainWindow::setupNativeWindowFrame() {
std::move(nativeFrame),
std::move(nightMode)
) | rpl::skip(1) | rpl::start_with_next([=](bool native, bool night) {
const auto nativeChanged = (_wasNativeFrame != native);
if (nativeChanged) {
_wasNativeFrame = native;
initShadows();
}
validateWindowTheme(native, night);
if (nativeChanged) {
fixMaximizedWindow();
}
}, lifetime());
}
@ -186,23 +170,12 @@ void MainWindow::TaskbarCreated() {
}
}
void MainWindow::shadowsUpdate(
Ui::Platform::WindowShadow::Changes changes,
WINDOWPOS *position) {
if (_shadow) {
_shadow->update(changes, position);
}
}
void MainWindow::shadowsActivate() {
_hasActiveFrame = true;
// _shadow->setColor(_shActive);
shadowsUpdate(Ui::Platform::WindowShadow::Change::Activate);
}
void MainWindow::shadowsDeactivate() {
_hasActiveFrame = false;
// _shadow->setColor(_shInactive);
}
void MainWindow::psShowTrayMenu() {
@ -327,7 +300,7 @@ bool MainWindow::initSizeFromSystem() {
if (!screen) {
return false;
}
setGeometry(screen->availableGeometry());
Ui::RpWidget::setGeometry(screen->availableGeometry());
return true;
}
@ -356,7 +329,6 @@ bool MainWindow::isActiveForTrayMenu() {
}
void MainWindow::unreadCounterChangedHook() {
setWindowTitle(titleText());
updateIconCounters();
}
@ -437,147 +409,9 @@ void MainWindow::initHook() {
}
}
psInitSysMenu();
}
void MainWindow::initShadows() {
if (Core::App().settings().nativeWindowFrame()) {
_shadow.reset();
} else {
_shadow.emplace(this, st::windowShadowFg->c);
}
updateCustomMargins();
firstShadowsUpdate();
}
void MainWindow::firstShadowsUpdate() {
using Change = Ui::Platform::WindowShadow::Change;
if ((windowState() & (Qt::WindowMinimized | Qt::WindowMaximized))
|| isHidden()) {
shadowsUpdate(Change::Hidden);
} else {
shadowsUpdate(Change::Moved | Change::Resized | Change::Shown);
}
}
void MainWindow::stateChangedHook(Qt::WindowState state) {
updateSystemMenu(state);
}
void MainWindow::psInitSysMenu() {
Qt::WindowStates states = windowState();
ps_menu = GetSystemMenu(ps_hWnd, FALSE);
updateSystemMenu(windowHandle()->windowState());
}
void MainWindow::updateSystemMenu(Qt::WindowState state) {
if (!ps_menu) return;
int menuToDisable = SC_RESTORE;
if (state == Qt::WindowMaximized) {
menuToDisable = SC_MAXIMIZE;
} else if (state == Qt::WindowMinimized) {
menuToDisable = SC_MINIMIZE;
}
int itemCount = GetMenuItemCount(ps_menu);
for (int i = 0; i < itemCount; ++i) {
MENUITEMINFO itemInfo = { 0 };
itemInfo.cbSize = sizeof(itemInfo);
itemInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
if (GetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) {
if (itemInfo.fType & MFT_SEPARATOR) {
continue;
}
if (itemInfo.wID && !(itemInfo.fState & MFS_DEFAULT)) {
UINT fOldState = itemInfo.fState, fState = itemInfo.fState & ~MFS_DISABLED;
if (itemInfo.wID == SC_CLOSE) {
fState |= MFS_DEFAULT;
} else if (itemInfo.wID == menuToDisable || (itemInfo.wID != SC_MINIMIZE && itemInfo.wID != SC_MAXIMIZE && itemInfo.wID != SC_RESTORE)) {
fState |= MFS_DISABLED;
}
itemInfo.fMask = MIIM_STATE;
itemInfo.fState = fState;
if (!SetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) {
DEBUG_LOG(("PS Error: could not set state %1 to menu item %2, old state %3, error %4").arg(fState).arg(itemInfo.wID).arg(fOldState).arg(GetLastError()));
DestroyMenu(ps_menu);
ps_menu = 0;
break;
}
}
} else {
DEBUG_LOG(("PS Error: could not get state, menu item %1 of %2, error %3").arg(i).arg(itemCount).arg(GetLastError()));
DestroyMenu(ps_menu);
ps_menu = 0;
break;
}
}
}
void MainWindow::updateCustomMargins() {
if (!ps_hWnd || _inUpdateMargins) {
return;
}
_inUpdateMargins = true;
const auto margins = computeCustomMargins();
if (const auto native = QGuiApplication::platformNativeInterface()) {
native->setWindowProperty(
windowHandle()->handle(),
qsl("WindowsCustomMargins"),
QVariant::fromValue<QMargins>(margins));
}
if (!_themeInited) {
_themeInited = true;
validateWindowTheme(
Core::App().settings().nativeWindowFrame(),
Window::Theme::IsNightMode());
}
_inUpdateMargins = false;
}
QMargins MainWindow::computeCustomMargins() {
if (Core::App().settings().nativeWindowFrame()) {
_deltaLeft = _deltaTop = _deltaRight = _deltaBottom = 0;
return QMargins();
}
auto r = RECT();
GetClientRect(ps_hWnd, &r);
auto a = r;
const auto style = GetWindowLongPtr(ps_hWnd, GWL_STYLE);
const auto styleEx = GetWindowLongPtr(ps_hWnd, GWL_EXSTYLE);
AdjustWindowRectEx(&a, style, false, styleEx);
auto margins = QMargins(a.left - r.left, a.top - r.top, r.right - a.right, r.bottom - a.bottom);
if (style & WS_MAXIMIZE) {
RECT w, m;
GetWindowRect(ps_hWnd, &w);
m = w;
HMONITOR hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST);
if (hMonitor) {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
m = mi.rcWork;
}
_deltaLeft = w.left - m.left;
_deltaTop = w.top - m.top;
_deltaRight = m.right - w.right;
_deltaBottom = m.bottom - w.bottom;
margins.setLeft(margins.left() - _deltaLeft);
margins.setRight(margins.right() - _deltaRight);
margins.setBottom(margins.bottom() - _deltaBottom);
margins.setTop(margins.top() - _deltaTop);
} else if (_deltaLeft != 0 || _deltaTop != 0 || _deltaRight != 0 || _deltaBottom != 0) {
RECT w;
GetWindowRect(ps_hWnd, &w);
SetWindowPos(ps_hWnd, 0, 0, 0, w.right - w.left - _deltaLeft - _deltaRight, w.bottom - w.top - _deltaBottom - _deltaTop, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
_deltaLeft = _deltaTop = _deltaRight = _deltaBottom = 0;
}
return margins;
validateWindowTheme(
Core::App().settings().nativeWindowFrame(),
Window::Theme::IsNightMode());
}
void MainWindow::validateWindowTheme(bool native, bool night) {
@ -673,25 +507,6 @@ void MainWindow::validateWindowTheme(bool native, bool night) {
SendMessage(ps_hWnd, WM_NCACTIVATE, _hasActiveFrame ? 1 : 0, 0);
}
void MainWindow::fixMaximizedWindow() {
auto r = RECT();
GetClientRect(ps_hWnd, &r);
const auto style = GetWindowLongPtr(ps_hWnd, GWL_STYLE);
const auto styleEx = GetWindowLongPtr(ps_hWnd, GWL_EXSTYLE);
AdjustWindowRectEx(&r, style, false, styleEx);
if (style & WS_MAXIMIZE) {
auto w = RECT();
GetWindowRect(ps_hWnd, &w);
if (const auto hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST)) {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
const auto m = mi.rcWork;
SetWindowPos(ps_hWnd, 0, 0, 0, m.right - m.left - _deltaLeft - _deltaRight, m.bottom - m.top - _deltaTop - _deltaBottom, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
}
}
}
void MainWindow::showFromTrayMenu() {
// If we try to activate() window before the trayIconMenu is hidden,
// then the window will be shown in semi-active state (Qt bug).
@ -707,10 +522,6 @@ HWND MainWindow::psHwnd() const {
return ps_hWnd;
}
HMENU MainWindow::psMenu() const {
return ps_menu;
}
void MainWindow::psDestroyIcons() {
if (ps_iconBig) {
DestroyIcon(ps_iconBig);
@ -735,7 +546,6 @@ MainWindow::~MainWindow() {
taskbarList.Reset();
}
if (ps_menu) DestroyMenu(ps_menu);
psDestroyIcons();
if (ps_tbHider_hWnd) DestroyWindow(ps_tbHider_hWnd);

View File

@ -8,12 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "platform/platform_main_window.h"
#include "ui/platform/win/ui_window_shadow_win.h"
#include "base/platform/win/base_windows_h.h"
#include "base/flags.h"
#include <QtCore/QTimer>
namespace Ui {
class PopupMenu;
} // namespace Ui
@ -27,11 +24,6 @@ public:
void showFromTrayMenu() override;
HWND psHwnd() const;
HMENU psMenu() const;
void psInitSysMenu();
void updateSystemMenu(Qt::WindowState state);
void updateCustomMargins();
void updateWindowIcon() override;
bool isActiveForTrayMenu() override;
@ -46,16 +38,6 @@ public:
// Custom shadows.
void shadowsActivate();
void shadowsDeactivate();
void shadowsUpdate(
Ui::Platform::WindowShadow::Changes changes,
WINDOWPOS *position = nullptr);
int deltaLeft() const {
return _deltaLeft;
}
int deltaTop() const {
return _deltaTop;
}
[[nodiscard]] bool hasTabletView() const;
@ -68,10 +50,6 @@ protected:
int32 screenNameChecksum(const QString &name) const override;
void unreadCounterChangedHook() override;
void initShadows() override;
void firstShadowsUpdate() override;
void stateChangedHook(Qt::WindowState state) override;
bool hasTrayIcon() const override {
return trayIcon;
}
@ -91,25 +69,16 @@ protected:
QRect computeDesktopRect() const override;
QTimer psUpdatedPositionTimer;
private:
struct Private;
void setupNativeWindowFrame();
void updateIconCounters();
QMargins computeCustomMargins();
void validateWindowTheme(bool native, bool night);
void psDestroyIcons();
void fixMaximizedWindow();
const std::unique_ptr<Private> _private;
std::optional<Ui::Platform::WindowShadow> _shadow;
bool _themeInited = false;
bool _inUpdateMargins = false;
bool _wasNativeFrame = false;
bool _hasActiveFrame = false;
// Workarounds for activation from tray icon.
@ -118,16 +87,10 @@ private:
HWND ps_hWnd = nullptr;
HWND ps_tbHider_hWnd = nullptr;
HMENU ps_menu = nullptr;
HICON ps_iconBig = nullptr;
HICON ps_iconSmall = nullptr;
HICON ps_iconOverlay = nullptr;
int _deltaLeft = 0;
int _deltaTop = 0;
int _deltaRight = 0;
int _deltaBottom = 0;
};
} // namespace Platform

View File

@ -1,136 +0,0 @@
/*
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 "platform/win/window_title_win.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "styles/style_window.h"
#include <QtGui/QWindow>
namespace Platform {
TitleWidget::TitleWidget(QWidget *parent)
: Window::TitleWidget(parent)
, _st(st::defaultWindowTitle)
, _minimize(this, _st.minimize)
, _maximizeRestore(this, _st.maximize)
, _close(this, _st.close)
, _shadow(this, st::titleShadow)
, _maximizedState(parent->window()->windowState() & Qt::WindowMaximized) {
_minimize->setClickedCallback([=] {
window()->setWindowState(
window()->windowState() | Qt::WindowMinimized);
_minimize->clearState();
});
_minimize->setPointerCursor(false);
_maximizeRestore->setClickedCallback([=] {
window()->setWindowState(_maximizedState
? Qt::WindowNoState
: Qt::WindowMaximized);
_maximizeRestore->clearState();
});
_maximizeRestore->setPointerCursor(false);
_close->setClickedCallback([=] {
window()->close();
_close->clearState();
});
_close->setPointerCursor(false);
setAttribute(Qt::WA_OpaquePaintEvent);
resize(width(), _st.height);
}
void TitleWidget::init() {
connect(
window()->windowHandle(),
&QWindow::windowStateChanged,
this,
[=](Qt::WindowState state) { windowStateChanged(state); });
_maximizedState = (window()->windowState() & Qt::WindowMaximized);
_activeState = isActiveWindow();
updateButtonsState();
}
void TitleWidget::paintEvent(QPaintEvent *e) {
auto active = isActiveWindow();
if (_activeState != active) {
_activeState = active;
updateButtonsState();
}
Painter(this).fillRect(rect(), active ? _st.bgActive : _st.bg);
}
void TitleWidget::updateControlsPosition() {
auto right = 0;
_close->moveToRight(right, 0); right += _close->width();
_maximizeRestore->moveToRight(right, 0); right += _maximizeRestore->width();
_minimize->moveToRight(right, 0);
}
void TitleWidget::resizeEvent(QResizeEvent *e) {
updateControlsPosition();
_shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth);
}
void TitleWidget::windowStateChanged(Qt::WindowState state) {
if (state == Qt::WindowMinimized) {
return;
}
const auto maximized = (state == Qt::WindowMaximized);
if (_maximizedState != maximized) {
_maximizedState = maximized;
updateButtonsState();
}
}
void TitleWidget::updateButtonsState() {
_minimize->setIconOverride(_activeState
? &_st.minimizeIconActive
: nullptr,
_activeState
? &_st.minimizeIconActiveOver
: nullptr);
if (_maximizedState) {
_maximizeRestore->setIconOverride(
_activeState
? &_st.restoreIconActive : &_st.restoreIcon,
_activeState
? &_st.restoreIconActiveOver
: &_st.restoreIconOver);
} else {
_maximizeRestore->setIconOverride(_activeState
? &_st.maximizeIconActive
: nullptr,
_activeState
? &_st.maximizeIconActiveOver
: nullptr);
}
_close->setIconOverride(_activeState
? &_st.closeIconActive
: nullptr,
_activeState
? &_st.closeIconActiveOver
: nullptr);
}
Window::HitTestResult TitleWidget::hitTest(const QPoint &p) const {
if (false
|| (_minimize->geometry().contains(p))
|| (_maximizeRestore->geometry().contains(p))
|| (_close->geometry().contains(p))
) {
return Window::HitTestResult::SysButton;
} else if (rect().contains(p)) {
return Window::HitTestResult::Caption;
}
return Window::HitTestResult::None;
}
} // namespace Platform

View File

@ -1,82 +0,0 @@
/*
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 "platform/platform_window_title.h"
#include "base/object_ptr.h"
namespace style {
struct WindowTitle;
} // namespace style
namespace Ui {
class IconButton;
class PlainShadow;
} // namespace Ui
namespace Window {
namespace Theme {
int DefaultPreviewTitleHeight();
void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth);
} // namespace Theme
} // namespace Window
namespace Platform {
class TitleWidget : public Window::TitleWidget {
public:
TitleWidget(QWidget *parent);
void init() override;
[[nodiscard]] Window::HitTestResult hitTest(
const QPoint &p) const override;
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void windowStateChanged(Qt::WindowState state = Qt::WindowNoState);
void updateButtonsState();
void updateControlsPosition();
const style::WindowTitle &_st;
object_ptr<Ui::IconButton> _minimize;
object_ptr<Ui::IconButton> _maximizeRestore;
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::PlainShadow> _shadow;
bool _maximizedState = false;
bool _activeState = false;
};
inline bool AllowNativeWindowFrameToggle() {
return true;
}
inline object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) {
return object_ptr<TitleWidget>(parent);
}
inline bool NativeTitleRequiresShadow() {
return true;
}
inline int PreviewTitleHeight() {
return Window::Theme::DefaultPreviewTitleHeight();
}
inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth) {
return Window::Theme::DefaultPreviewWindowFramePaint(preview, palette, body, outerWidth);
}
} // namespace Platform

View File

@ -63,7 +63,6 @@ SafeIniter::SafeIniter() {
LOAD_SYMBOL(LibPropSys, PSStringFromPropertyKey);
const auto LibDwmApi = LoadLibrary(L"dwmapi.dll");
LOAD_SYMBOL(LibDwmApi, DwmIsCompositionEnabled);
LOAD_SYMBOL(LibDwmApi, DwmSetWindowAttribute);
const auto LibPsApi = LoadLibrary(L"psapi.dll");

View File

@ -96,8 +96,6 @@ inline HRESULT(__stdcall *PSStringFromPropertyKey)(
// DWMAPI.DLL
inline HRESULT(__stdcall *DwmIsCompositionEnabled)(
_Out_ BOOL* pfEnabled);
inline HRESULT(__stdcall *DwmSetWindowAttribute)(
HWND hwnd,
DWORD dwAttribute,

View File

@ -7,12 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/win/windows_event_filter.h"
#include "platform/win/windows_dlls.h"
#include "platform/win/specific_win.h"
#include "core/sandbox.h"
#include "core/core_settings.h"
#include "core/application.h"
#include "ui/inactive_press.h"
#include "mainwindow.h"
#include "app.h"
@ -23,67 +21,6 @@ namespace {
EventFilter *instance = nullptr;
int menuShown = 0, menuHidden = 0;
bool IsCompositionEnabled() {
if (!Dlls::DwmIsCompositionEnabled) {
return false;
}
auto result = BOOL(FALSE);
const auto success = (Dlls::DwmIsCompositionEnabled(&result) == S_OK);
return success && result;
}
HWND FindTaskbarWindow(LPRECT rcMon = nullptr) {
HWND hTaskbar = nullptr;
RECT rcTaskbar, rcMatch;
while ((hTaskbar = FindWindowEx(
nullptr,
hTaskbar,
L"Shell_TrayWnd",
nullptr)) != nullptr) {
if (!rcMon) {
break; // OK, return first found
}
if (GetWindowRect(hTaskbar, &rcTaskbar)
&& IntersectRect(&rcMatch, &rcTaskbar, rcMon)) {
break; // OK, taskbar match monitor
}
}
return hTaskbar;
}
bool IsTaskbarAutoHidden(LPRECT rcMon = nullptr, PUINT pEdge = nullptr) {
HWND hTaskbar = FindTaskbarWindow(rcMon);
if (!hTaskbar) {
if (pEdge) {
*pEdge = (UINT)-1;
}
return false;
}
APPBARDATA state = {sizeof(state), hTaskbar};
APPBARDATA pos = {sizeof(pos), hTaskbar};
LRESULT lState = SHAppBarMessage(ABM_GETSTATE, &state);
bool bAutoHidden = (lState & ABS_AUTOHIDE);
if (SHAppBarMessage(ABM_GETTASKBARPOS, &pos)) {
if (pEdge) {
*pEdge = pos.uEdge;
}
} else {
LOG(("Failed to get taskbar pos"));
if (pEdge) {
*pEdge = ABE_BOTTOM;
}
}
return bAutoHidden;
}
} // namespace
EventFilter *EventFilter::CreateInstance(not_null<MainWindow*> window) {
@ -125,108 +62,18 @@ bool EventFilter::nativeEventFilter(
});
}
bool EventFilter::customWindowFrameEvent(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
LRESULT *result) {
switch (msg) {
case WM_NCPAINT: {
if (QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8) return false;
if (result) *result = 0;
} return true;
case WM_NCCALCSIZE: {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(hWnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
LPNCCALCSIZE_PARAMS params = (LPNCCALCSIZE_PARAMS)lParam;
LPRECT r = (wParam == TRUE) ? &params->rgrc[0] : (LPRECT)lParam;
HMONITOR hMonitor = MonitorFromPoint({ (r->left + r->right) / 2, (r->top + r->bottom) / 2 }, MONITOR_DEFAULTTONEAREST);
if (hMonitor) {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hMonitor, &mi)) {
*r = mi.rcWork;
UINT uEdge = (UINT)-1;
if (IsTaskbarAutoHidden(&mi.rcMonitor, &uEdge)) {
switch (uEdge) {
case ABE_LEFT: r->left += 1; break;
case ABE_RIGHT: r->right -= 1; break;
case ABE_TOP: r->top += 1; break;
case ABE_BOTTOM: r->bottom -= 1; break;
}
}
}
}
}
if (result) *result = 0;
return true;
}
case WM_NCACTIVATE: {
if (IsCompositionEnabled()) {
const auto res = DefWindowProc(hWnd, msg, wParam, -1);
if (result) *result = res;
} else {
// Thanks https://github.com/melak47/BorderlessWindow
if (result) *result = 1;
}
} return true;
case WM_NCHITTEST: {
if (!result) return false;
POINTS p = MAKEPOINTS(lParam);
RECT r;
GetWindowRect(hWnd, &r);
auto res = _window->hitTest(QPoint(p.x - r.left + _window->deltaLeft(), p.y - r.top + _window->deltaTop()));
switch (res) {
case Window::HitTestResult::Client:
case Window::HitTestResult::SysButton: *result = HTCLIENT; break;
case Window::HitTestResult::Caption: *result = HTCAPTION; break;
case Window::HitTestResult::Top: *result = HTTOP; break;
case Window::HitTestResult::TopRight: *result = HTTOPRIGHT; break;
case Window::HitTestResult::Right: *result = HTRIGHT; break;
case Window::HitTestResult::BottomRight: *result = HTBOTTOMRIGHT; break;
case Window::HitTestResult::Bottom: *result = HTBOTTOM; break;
case Window::HitTestResult::BottomLeft: *result = HTBOTTOMLEFT; break;
case Window::HitTestResult::Left: *result = HTLEFT; break;
case Window::HitTestResult::TopLeft: *result = HTTOPLEFT; break;
case Window::HitTestResult::None:
default: *result = HTTRANSPARENT; break;
};
} return true;
case WM_NCRBUTTONUP: {
SendMessage(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU, lParam);
} return true;
}
return false;
}
bool EventFilter::mainWindowEvent(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
LRESULT *result) {
using Change = Ui::Platform::WindowShadow::Change;
if (const auto tbCreatedMsgId = Platform::MainWindow::TaskbarCreatedMsgId()) {
if (msg == tbCreatedMsgId) {
Platform::MainWindow::TaskbarCreated();
}
}
if (!Core::App().settings().nativeWindowFrame()) {
if (customWindowFrameEvent(hWnd, msg, wParam, lParam, result)) {
return true;
}
}
switch (msg) {
case WM_TIMECHANGE: {
@ -246,91 +93,25 @@ bool EventFilter::mainWindowEvent(
} return false;
case WM_ACTIVATE: {
if (LOWORD(wParam) == WA_CLICKACTIVE) {
Ui::MarkInactivePress(_window, true);
}
if (LOWORD(wParam) != WA_INACTIVE) {
_window->shadowsActivate();
} else {
_window->shadowsDeactivate();
}
_window->update();
} return false;
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED: {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (_window->hasTabletView()
|| (GetWindowPlacement(hWnd, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED
|| wp.showCmd == SW_SHOWMINIMIZED))) {
_window->shadowsUpdate(Change::Hidden);
} else {
_window->shadowsUpdate(Change::Moved | Change::Resized, (WINDOWPOS*)lParam);
}
} return false;
case WM_SIZE: {
if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED || wParam == SIZE_MINIMIZED) {
if (wParam != SIZE_RESTORED || _window->windowState() != Qt::WindowNoState) {
Qt::WindowState state = Qt::WindowNoState;
if (wParam == SIZE_MAXIMIZED) {
state = Qt::WindowMaximized;
} else if (wParam == SIZE_MINIMIZED) {
state = Qt::WindowMinimized;
}
_window->windowHandle()->windowStateChanged(state);
} else {
if (wParam == SIZE_RESTORED && _window->windowState() == Qt::WindowNoState) {
_window->positionUpdated();
}
_window->updateCustomMargins();
const auto changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? Change::Hidden : (Change::Resized | Change::Shown);
_window->shadowsUpdate(changes);
}
} return false;
case WM_SHOWWINDOW: {
LONG style = GetWindowLongPtr(hWnd, GWL_STYLE);
const auto changes = Change::Resized | ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE))) ? Change::Shown : Change::Hidden);
_window->shadowsUpdate(changes);
} return false;
case WM_MOVE: {
_window->shadowsUpdate(Change::Moved);
_window->positionUpdated();
} return false;
case WM_SYSCOMMAND: {
if (wParam == SC_MOUSEMENU) {
POINTS p = MAKEPOINTS(lParam);
_window->updateSystemMenu(_window->windowHandle()->windowState());
TrackPopupMenu(_window->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0);
}
} return false;
case WM_COMMAND: {
if (HIWORD(wParam)) {
return false;
}
int cmd = LOWORD(wParam);
switch (cmd) {
case SC_CLOSE:
_window->close();
return true;
case SC_MINIMIZE:
_window->setWindowState(
_window->windowState() | Qt::WindowMinimized);
return true;
case SC_MAXIMIZE:
_window->setWindowState(Qt::WindowMaximized);
return true;
case SC_RESTORE:
_window->setWindowState(Qt::WindowNoState);
return true;
}
} return true;
case WM_SETTINGCHANGE: {
Core::App().settings().setSystemDarkMode(Platform::IsDarkMode());
} return false;

View File

@ -26,13 +26,6 @@ public:
private:
explicit EventFilter(not_null<MainWindow*> window);
bool customWindowFrameEvent(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
LRESULT *result);
not_null<MainWindow*> _window;
};

View File

@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/about_box.h"
#include "boxes/confirm_box.h"
#include "platform/platform_specific.h"
#include "platform/platform_window_title.h"
#include "ui/platform/ui_platform_window.h"
#include "base/platform/base_platform_info.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
@ -406,7 +406,7 @@ void SetupSystemIntegrationContent(
}, taskbar->lifetime());
}
}
if (Platform::AllowNativeWindowFrameToggle()) {
if (Ui::Platform::NativeWindowFrameSupported()) {
const auto nativeFrame = addCheckbox(
tr::lng_settings_native_frame(),
Core::App().settings().nativeWindowFrame());

View File

@ -9,9 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "platform/platform_specific.h"
#include "ui/platform/ui_platform_window.h"
#include "platform/platform_window_title.h"
#include "base/platform/base_platform_info.h"
#include "ui/platform/ui_platform_utility.h"
#include "history/history.h"
#include "window/window_session_controller.h"
#include "window/window_lock_widgets.h"
@ -161,9 +161,8 @@ QIcon CreateIcon(Main::Session *session) {
MainWindow::MainWindow(not_null<Controller*> controller)
: _controller(controller)
, _positionUpdatedTimer([=] { savePosition(); })
, _outdated(CreateOutdatedBar(this))
, _body(this)
, _titleText(qsl("Telegram")) {
, _outdated(CreateOutdatedBar(body()))
, _body(body()) {
style::PaletteChanged(
) | rpl::start_with_next([=] {
updatePalette();
@ -266,8 +265,6 @@ QRect MainWindow::desktopRect() const {
}
void MainWindow::init() {
Expects(!windowHandle());
createWinId();
initHook();
@ -293,7 +290,7 @@ void MainWindow::init() {
updatePalette();
if (Platform::AllowNativeWindowFrameToggle()) {
if (Ui::Platform::NativeWindowFrameSupported()) {
Core::App().settings().nativeWindowFrameChanges(
) | rpl::start_with_next([=](bool native) {
refreshTitleWidget();
@ -308,7 +305,6 @@ void MainWindow::init() {
void MainWindow::handleStateChanged(Qt::WindowState state) {
stateChangedHook(state);
updateShadowSize();
updateControlsGeometry();
if (state == Qt::WindowMinimized) {
controller().updateIsActiveBlur();
@ -381,27 +377,6 @@ void MainWindow::updatePalette() {
setPalette(p);
}
HitTestResult MainWindow::hitTest(const QPoint &p) const {
auto titleResult = _title ? _title->hitTest(p - _title->geometry().topLeft()) : Window::HitTestResult::None;
if (titleResult != Window::HitTestResult::None) {
return titleResult;
} else if (rect().contains(p)) {
return Window::HitTestResult::Client;
}
return Window::HitTestResult::None;
}
bool MainWindow::hasShadow() const {
const auto center = geometry().center();
return Ui::Platform::WindowExtentsSupported()
&& Ui::Platform::TranslucentWindowsSupported(center)
&& _title;
}
QRect MainWindow::inner() const {
return rect().marginsRemoved(_padding);
}
int MainWindow::computeMinWidth() const {
auto result = st::windowMinWidth;
if (const auto session = _controller->sessionController()) {
@ -412,38 +387,32 @@ int MainWindow::computeMinWidth() const {
if (_rightColumn) {
result += _rightColumn->width();
}
return result + _padding.left() + _padding.right();
return result;
}
int MainWindow::computeMinHeight() const {
const auto title = _title ? _title->height() : 0;
const auto outdated = [&] {
if (!_outdated) {
return 0;
}
_outdated->resizeToWidth(st::windowMinWidth - _padding.left() - _padding.right());
_outdated->resizeToWidth(st::windowMinWidth);
return _outdated->height();
}();
return title + outdated + st::windowMinHeight + _padding.top() + _padding.bottom();
return outdated + st::windowMinHeight;
}
void MainWindow::refreshTitleWidget() {
if (Platform::AllowNativeWindowFrameToggle()
if (Ui::Platform::NativeWindowFrameSupported()
&& Core::App().settings().nativeWindowFrame()) {
_title.destroy();
setNativeFrame(true);
if (Platform::NativeTitleRequiresShadow()) {
_titleShadow.create(this);
_titleShadow->show();
}
} else if ((_title = Platform::CreateTitleWidget(this))) {
_title->show();
_title->init();
} else {
setNativeFrame(false);
_titleShadow.destroy();
}
const auto withShadow = hasShadow();
windowHandle()->setFlag(Qt::NoDropShadowWindowHint, withShadow);
setAttribute(Qt::WA_OpaquePaintEvent, !withShadow);
}
void MainWindow::updateMinimumSize() {
@ -451,21 +420,13 @@ void MainWindow::updateMinimumSize() {
setMinimumHeight(computeMinHeight());
}
void MainWindow::updateShadowSize() {
_padding = hasShadow() && !isMaximized()
? st::callShadow.extend
: style::margins();
}
void MainWindow::recountGeometryConstraints() {
updateShadowSize();
updateMinimumSize();
updateControlsGeometry();
fixOrder();
}
void MainWindow::initSize() {
updateShadowSize();
updateMinimumSize();
if (initSizeFromSystem()) {
@ -565,7 +526,6 @@ void MainWindow::initSize() {
}
}
}
geometry += _padding;
DEBUG_LOG(("Window Pos: Setting first %1, %2, %3, %4").arg(geometry.x()).arg(geometry.y()).arg(geometry.width()).arg(geometry.height()));
setGeometry(geometry);
}
@ -574,18 +534,6 @@ void MainWindow::positionUpdated() {
_positionUpdatedTimer.callOnce(kSaveWindowPositionTimeout);
}
bool MainWindow::titleVisible() const {
return _title && !_title->isHidden();
}
void MainWindow::setTitleVisible(bool visible) {
if (_title && (_title->isHidden() == visible)) {
_title->setVisible(visible);
updateControlsGeometry();
}
titleVisibilityChangedHook();
}
int32 MainWindow::screenNameChecksum(const QString &name) const {
const auto bytes = name.toUtf8();
return base::crc32(bytes.constData(), bytes.size());
@ -605,15 +553,7 @@ void MainWindow::attachToTrayIcon(not_null<QSystemTrayIcon*> icon) {
});
}
void MainWindow::paintEvent(QPaintEvent *e) {
if (hasShadow() && !isMaximized()) {
QPainter p(this);
Ui::Shadow::paint(p, inner(), width(), st::callShadow);
}
}
void MainWindow::resizeEvent(QResizeEvent *e) {
updateShadowSize();
updateControlsGeometry();
}
@ -626,14 +566,10 @@ void MainWindow::leaveEventHook(QEvent *e) {
}
void MainWindow::updateControlsGeometry() {
const auto inner = this->inner();
const auto inner = body()->rect();
auto bodyLeft = inner.x();
auto bodyTop = inner.y();
auto bodyWidth = inner.width();
if (_title && !_title->isHidden()) {
_title->setGeometry(inner.x(), bodyTop, inner.width(), _title->height());
bodyTop += _title->height();
}
if (_titleShadow) {
_titleShadow->setGeometry(inner.x(), bodyTop, inner.width(), st::lineWidth);
}
@ -656,7 +592,7 @@ void MainWindow::updateUnreadCounter() {
}
const auto counter = Core::App().unreadBadge();
_titleText = (counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram");
setTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
unreadCounterChangedHook();
}
@ -683,7 +619,7 @@ void MainWindow::savePosition(Qt::WindowState state) {
realPosition.maximized = 1;
DEBUG_LOG(("Window Pos: Saving maximized position."));
} else {
auto r = geometry().marginsRemoved(_padding);
auto r = body()->mapToGlobal(body()->rect());
realPosition.x = r.x();
realPosition.y = r.y();
realPosition.w = r.width() - (_rightColumn ? _rightColumn->width() : 0);
@ -774,7 +710,7 @@ void MainWindow::showRightColumn(object_ptr<TWidget> widget) {
const auto wasRightWidth = _rightColumn ? _rightColumn->width() : 0;
_rightColumn = std::move(widget);
if (_rightColumn) {
_rightColumn->setParent(this);
_rightColumn->setParent(body());
_rightColumn->show();
_rightColumn->setFocus();
} else {
@ -799,12 +735,12 @@ void MainWindow::showRightColumn(object_ptr<TWidget> widget) {
int MainWindow::maximalExtendBy() const {
auto desktop = QDesktopWidget().availableGeometry(this);
return std::max(desktop.width() - inner().width(), 0);
return std::max(desktop.width() - body()->width(), 0);
}
bool MainWindow::canExtendNoMove(int extendBy) const {
auto desktop = QDesktopWidget().availableGeometry(this);
auto inner = geometry().marginsRemoved(_padding);
auto inner = body()->mapToGlobal(body()->rect());
auto innerRight = (inner.x() + inner.width() + extendBy);
auto desktopRight = (desktop.x() + desktop.width());
return innerRight <= desktopRight;
@ -812,7 +748,7 @@ bool MainWindow::canExtendNoMove(int extendBy) const {
int MainWindow::tryToExtendWidthBy(int addToWidth) {
auto desktop = QDesktopWidget().availableGeometry(this);
auto inner = geometry();
auto inner = body()->mapToGlobal(body()->rect());
accumulate_min(
addToWidth,
std::max(desktop.width() - inner.width(), 0));
@ -821,7 +757,7 @@ int MainWindow::tryToExtendWidthBy(int addToWidth) {
inner.x(),
desktop.x() + desktop.width() - newWidth);
if (inner.x() != newLeft || inner.width() != newWidth) {
setGeometry(newLeft, inner.y(), newWidth, inner.height());
setGeometry(QRect(newLeft, inner.y(), newWidth, inner.height()));
} else {
updateControlsGeometry();
}
@ -841,8 +777,6 @@ void MainWindow::launchDrag(
}
MainWindow::~MainWindow() {
_title.destroy();
// Otherwise:
// ~QWidget
// QWidgetPrivate::close_helper

View File

@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "window/window_title.h"
#include "ui/rp_widget.h"
#include "ui/widgets/rp_window.h"
#include "base/timer.h"
#include "base/object_ptr.h"
#include "core/core_settings.h"
@ -37,7 +36,7 @@ QImage LoadLogoNoMargin();
QIcon CreateIcon(Main::Session *session = nullptr);
void ConvertIconToBlack(QImage &image);
class MainWindow : public Ui::RpWidget {
class MainWindow : public Ui::RpWindow {
public:
explicit MainWindow(not_null<Controller*> controller);
virtual ~MainWindow();
@ -60,7 +59,6 @@ public:
[[nodiscard]] QRect desktopRect() const;
void init();
[[nodiscard]] HitTestResult hitTest(const QPoint &p) const;
void updateIsActive();
@ -77,12 +75,6 @@ public:
}
void positionUpdated();
bool titleVisible() const;
void setTitleVisible(bool visible);
QString titleText() const {
return _titleText;
}
void reActivateWindow();
void showRightColumn(object_ptr<TWidget> widget);
@ -112,22 +104,18 @@ public:
void clearWidgets();
QRect inner() const;
int computeMinWidth() const;
int computeMinHeight() const;
void recountGeometryConstraints();
virtual void updateControlsGeometry();
bool hasShadow() const;
bool minimizeToTray();
void updateGlobalMenu() {
updateGlobalMenuHook();
}
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void leaveEventHook(QEvent *e) override;
@ -154,9 +142,6 @@ protected:
virtual void stateChangedHook(Qt::WindowState state) {
}
virtual void titleVisibilityChangedHook() {
}
virtual void unreadCounterChangedHook() {
}
@ -180,10 +165,6 @@ protected:
virtual void createGlobalMenu() {
}
virtual void initShadows() {
}
virtual void firstShadowsUpdate() {
}
virtual bool initSizeFromSystem() {
return false;
@ -203,7 +184,6 @@ protected:
private:
void refreshTitleWidget();
void updateMinimumSize();
void updateShadowSize();
void updatePalette();
void initSize();
@ -214,7 +194,6 @@ private:
base::Timer _positionUpdatedTimer;
bool _positionInited = false;
object_ptr<TitleWidget> _title = { nullptr };
object_ptr<Ui::PlainShadow> _titleShadow = { nullptr };
object_ptr<Ui::RpWidget> _outdated;
object_ptr<Ui::RpWidget> _body;
@ -222,8 +201,6 @@ private:
QIcon _icon;
bool _usingSupportIcon = false;
QString _titleText;
style::margins _padding;
bool _isActive = false;

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/click_handler_types.h"
#include "export/export_manager.h"
#include "ui/platform/ui_platform_window.h"
#include "platform/platform_window_title.h"
#include "main/main_account.h"
#include "main/main_domain.h"
@ -267,7 +268,7 @@ void Controller::showSettings() {
int Controller::verticalShadowTop() const {
return (Platform::NativeTitleRequiresShadow()
&& Platform::AllowNativeWindowFrameToggle()
&& Ui::Platform::NativeWindowFrameSupported()
&& Core::App().settings().nativeWindowFrame())
? st::lineWidth
: 0;

View File

@ -1,44 +0,0 @@
/*
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 "ui/rp_widget.h"
namespace Window {
enum class HitTestResult {
None = 0,
Client,
SysButton,
Caption,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
TopLeft,
};
class TitleWidget : public Ui::RpWidget {
public:
using RpWidget::RpWidget;
virtual void init() {
}
virtual HitTestResult hitTest(const QPoint &p) const {
return HitTestResult::None;
}
virtual QRect iconRect() const {
return QRect();
}
};
} // namespace Window

View File

@ -1,417 +0,0 @@
/*
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 "window/window_title_qt.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
#include <QtCore/QCoreApplication>
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
namespace Window {
namespace {
[[nodiscard]] style::margins ShadowExtents() {
return st::callShadow.extend;
}
template <typename T>
void RemoveDuplicates(std::vector<T> &v) {
auto end = v.end();
for (auto it = v.begin(); it != end; ++it) {
end = std::remove(it + 1, end, *it);
}
v.erase(end, v.end());
}
} // namespace
TitleWidgetQt::TitleWidgetQt(QWidget *parent)
: TitleWidget(parent)
, _st(st::defaultWindowTitle)
, _minimize(this, _st.minimize)
, _maximizeRestore(this, _st.maximize)
, _close(this, _st.close)
, _shadow(this, st::titleShadow)
, _maximizedState(parent->window()->windowState() & Qt::WindowMaximized) {
_minimize->setClickedCallback([=] {
window()->setWindowState(
window()->windowState() | Qt::WindowMinimized);
_minimize->clearState();
});
_minimize->setPointerCursor(false);
_maximizeRestore->setClickedCallback([=] {
window()->setWindowState(_maximizedState
? Qt::WindowNoState
: Qt::WindowMaximized);
_maximizeRestore->clearState();
});
_maximizeRestore->setPointerCursor(false);
_close->setClickedCallback([=] {
window()->close();
_close->clearState();
});
_close->setPointerCursor(false);
Ui::Platform::TitleControlsLayoutChanged(
) | rpl::start_with_next([=] {
updateControlsPosition();
}, lifetime());
QCoreApplication::instance()->installEventFilter(this);
_windowWasFrameless = (window()->windowFlags()
& Qt::FramelessWindowHint) != 0;
if (!_windowWasFrameless) {
toggleFramelessWindow(true);
}
setAttribute(Qt::WA_OpaquePaintEvent);
resize(width(), _st.height);
updateWindowExtents();
}
TitleWidgetQt::~TitleWidgetQt() {
restoreCursor();
if (!_windowWasFrameless) {
toggleFramelessWindow(false);
}
if (_extentsSet) {
Ui::Platform::UnsetWindowExtents(window()->windowHandle());
}
}
void TitleWidgetQt::init() {
connect(
window()->windowHandle(),
&QWindow::windowStateChanged,
this,
[=](Qt::WindowState state) { windowStateChanged(state); });
connect(
window()->windowHandle(),
&QWindow::visibleChanged,
this,
[=](bool visible) { visibleChanged(visible); });
_maximizedState = (window()->windowState() & Qt::WindowMaximized);
_activeState = isActiveWindow();
updateButtonsState();
}
bool TitleWidgetQt::hasShadow() const {
const auto center = window()->geometry().center();
return Ui::Platform::WindowExtentsSupported()
&& Ui::Platform::TranslucentWindowsSupported(center);
}
Ui::IconButton *TitleWidgetQt::controlWidget(Control control) const {
switch (control) {
case Control::Minimize: return _minimize;
case Control::Maximize: return _maximizeRestore;
case Control::Close: return _close;
}
return nullptr;
}
void TitleWidgetQt::paintEvent(QPaintEvent *e) {
auto active = isActiveWindow();
if (_activeState != active) {
_activeState = active;
updateButtonsState();
}
Painter(this).fillRect(rect(), active ? _st.bgActive : _st.bg);
}
void TitleWidgetQt::toggleFramelessWindow(bool enabled) {
window()->windowHandle()->setFlag(Qt::FramelessWindowHint, enabled);
}
void TitleWidgetQt::updateWindowExtents() {
if (hasShadow()) {
Ui::Platform::SetWindowExtents(
window()->windowHandle(),
resizeArea());
_extentsSet = true;
} else if (_extentsSet) {
Ui::Platform::UnsetWindowExtents(window()->windowHandle());
_extentsSet = false;
}
}
void TitleWidgetQt::updateControlsPosition() {
const auto controlsLayout = Ui::Platform::TitleControlsLayout();
const auto controlsLeft = controlsLayout.left;
const auto controlsRight = controlsLayout.right;
const auto controlPresent = [&](Control control) {
return ranges::contains(controlsLeft, control)
|| ranges::contains(controlsRight, control);
};
if (controlPresent(Control::Minimize)) {
_minimize->show();
} else {
_minimize->hide();
}
if (controlPresent(Control::Maximize)) {
_maximizeRestore->show();
} else {
_maximizeRestore->hide();
}
if (controlPresent(Control::Close)) {
_close->show();
} else {
_close->hide();
}
updateControlsPositionBySide(controlsLeft, false);
updateControlsPositionBySide(controlsRight, true);
}
void TitleWidgetQt::updateControlsPositionBySide(
const std::vector<Control> &controls,
bool right) {
auto preparedControls = right
? (ranges::views::reverse(controls) | ranges::to_vector)
: controls;
RemoveDuplicates(preparedControls);
auto position = 0;
for (const auto &control : preparedControls) {
const auto widget = controlWidget(control);
if (!widget) {
continue;
}
if (right) {
widget->moveToRight(position, 0);
} else {
widget->moveToLeft(position, 0);
}
position += widget->width();
}
}
void TitleWidgetQt::resizeEvent(QResizeEvent *e) {
updateControlsPosition();
_shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth);
}
void TitleWidgetQt::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
_mousePressed = true;
} else if (e->button() == Qt::RightButton) {
Ui::Platform::ShowWindowMenu(window()->windowHandle());
}
}
void TitleWidgetQt::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
_mousePressed = false;
}
}
void TitleWidgetQt::mouseMoveEvent(QMouseEvent *e) {
if (_mousePressed) {
window()->windowHandle()->startSystemMove();
}
}
void TitleWidgetQt::mouseDoubleClickEvent(QMouseEvent *e) {
if (_maximizedState) {
window()->setWindowState(Qt::WindowNoState);
} else {
window()->setWindowState(Qt::WindowMaximized);
}
}
bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) {
if (e->type() == QEvent::MouseMove
|| e->type() == QEvent::MouseButtonPress) {
if (obj->isWidgetType()
&& window()->isAncestorOf(static_cast<QWidget*>(obj))) {
const auto mouseEvent = static_cast<QMouseEvent*>(e);
const auto currentPoint = mouseEvent->windowPos().toPoint();
const auto edges = edgesFromPos(currentPoint);
if (e->type() == QEvent::MouseMove
&& mouseEvent->buttons() == Qt::NoButton) {
if (_mousePressed) {
_mousePressed = false;
}
updateCursor(edges);
}
if (e->type() == QEvent::MouseButtonPress
&& mouseEvent->button() == Qt::LeftButton
&& edges) {
return window()->windowHandle()->startSystemResize(edges);
}
}
} else if (e->type() == QEvent::Leave) {
if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) {
restoreCursor();
}
} else if (e->type() == QEvent::Move
|| e->type() == QEvent::Resize) {
if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) {
updateWindowExtents();
}
}
return TitleWidget::eventFilter(obj, e);
}
void TitleWidgetQt::windowStateChanged(Qt::WindowState state) {
if (state == Qt::WindowMinimized) {
return;
}
const auto maximized = (state == Qt::WindowMaximized);
if (_maximizedState != maximized) {
_maximizedState = maximized;
updateButtonsState();
updateWindowExtents();
}
}
void TitleWidgetQt::visibleChanged(bool visible) {
if (visible) {
updateWindowExtents();
// workaround a bug in Qt 5.12, works ok in Qt 5.15
// https://github.com/telegramdesktop/tdesktop/issues/10119
if (!_windowWasFrameless) {
toggleFramelessWindow(true);
}
}
}
void TitleWidgetQt::updateButtonsState() {
_minimize->setIconOverride(_activeState
? &_st.minimizeIconActive
: nullptr,
_activeState
? &_st.minimizeIconActiveOver
: nullptr);
if (_maximizedState) {
_maximizeRestore->setIconOverride(
_activeState
? &_st.restoreIconActive : &_st.restoreIcon,
_activeState
? &_st.restoreIconActiveOver
: &_st.restoreIconOver);
} else {
_maximizeRestore->setIconOverride(_activeState
? &_st.maximizeIconActive
: nullptr,
_activeState
? &_st.maximizeIconActiveOver
: nullptr);
}
_close->setIconOverride(_activeState
? &_st.closeIconActive
: nullptr,
_activeState
? &_st.closeIconActiveOver
: nullptr);
}
QMargins TitleWidgetQt::resizeArea() const {
if (_maximizedState) {
return QMargins();
} else if (!hasShadow()) {
return QMargins(
st::windowResizeArea,
st::windowResizeArea,
st::windowResizeArea,
st::windowResizeArea);
}
return ShadowExtents();
}
Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) const {
const auto area = resizeArea();
if (area.isNull()) {
return Qt::Edges();
} else if (pos.x() <= area.left()) {
if (pos.y() <= area.top()) {
return Qt::LeftEdge | Qt::TopEdge;
} else if (pos.y() >= (window()->height() - area.bottom())) {
return Qt::LeftEdge | Qt::BottomEdge;
}
return Qt::LeftEdge;
} else if (pos.x() >= (window()->width() - area.right())) {
if (pos.y() <= area.top()) {
return Qt::RightEdge | Qt::TopEdge;
} else if (pos.y() >= (window()->height() - area.bottom())) {
return Qt::RightEdge | Qt::BottomEdge;
}
return Qt::RightEdge;
} else if (pos.y() <= area.top()) {
return Qt::TopEdge;
} else if (pos.y() >= (window()->height() - area.bottom())) {
return Qt::BottomEdge;
}
return Qt::Edges();
}
void TitleWidgetQt::updateCursor(Qt::Edges edges) {
if (!edges) {
restoreCursor();
return;
} else if (!QGuiApplication::overrideCursor()) {
_cursorOverriden = false;
}
if (!_cursorOverriden) {
_cursorOverriden = true;
QGuiApplication::setOverrideCursor(QCursor());
}
if (((edges & Qt::LeftEdge) && (edges & Qt::TopEdge))
|| ((edges & Qt::RightEdge) && (edges & Qt::BottomEdge))) {
QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeFDiagCursor));
} else if (((edges & Qt::LeftEdge) && (edges & Qt::BottomEdge))
|| ((edges & Qt::RightEdge) && (edges & Qt::TopEdge))) {
QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeBDiagCursor));
} else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) {
QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeHorCursor));
} else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) {
QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeVerCursor));
}
}
void TitleWidgetQt::restoreCursor() {
if (_cursorOverriden) {
_cursorOverriden = false;
QGuiApplication::restoreOverrideCursor();
}
}
} // namespace Window

View File

@ -1,77 +0,0 @@
/*
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 "window/window_title.h"
#include "ui/platform/ui_platform_window_title.h"
#include "base/object_ptr.h"
namespace style {
struct WindowTitle;
} // namespace style
namespace Ui {
class IconButton;
class PlainShadow;
} // namespace Ui
namespace Window {
class TitleWidgetQt : public TitleWidget {
public:
using Control = Ui::Platform::TitleControls::Control;
TitleWidgetQt(QWidget *parent);
~TitleWidgetQt();
void init() override;
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
private:
void windowStateChanged(Qt::WindowState state = Qt::WindowNoState);
void visibleChanged(bool visible);
void updateWindowExtents();
void updateButtonsState();
void updateControlsPosition();
void updateControlsPositionBySide(
const std::vector<Control> &controls,
bool right);
void toggleFramelessWindow(bool enabled);
bool hasShadow() const;
Ui::IconButton *controlWidget(Control control) const;
QMargins resizeArea() const;
Qt::Edges edgesFromPos(const QPoint &pos) const;
void updateCursor(Qt::Edges edges);
void restoreCursor();
const style::WindowTitle &_st;
object_ptr<Ui::IconButton> _minimize;
object_ptr<Ui::IconButton> _maximizeRestore;
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::PlainShadow> _shadow;
bool _maximizedState = false;
bool _activeState = false;
bool _windowWasFrameless = false;
bool _cursorOverriden = false;
bool _extentsSet = false;
bool _mousePressed = false;
};
} // namespace Window