Qt-based title widget ported from tdesktop
|
|
@ -77,6 +77,8 @@ PRIVATE
|
||||||
ui/paint/blobs.h
|
ui/paint/blobs.h
|
||||||
ui/paint/blobs_linear.cpp
|
ui/paint/blobs_linear.cpp
|
||||||
ui/paint/blobs_linear.h
|
ui/paint/blobs_linear.h
|
||||||
|
ui/platform/linux/ui_linux_wayland_integration.cpp
|
||||||
|
ui/platform/linux/ui_linux_wayland_integration.h
|
||||||
ui/platform/linux/ui_window_linux.cpp
|
ui/platform/linux/ui_window_linux.cpp
|
||||||
ui/platform/linux/ui_window_linux.h
|
ui/platform/linux/ui_window_linux.h
|
||||||
ui/platform/linux/ui_utility_linux.cpp
|
ui/platform/linux/ui_utility_linux.cpp
|
||||||
|
|
@ -95,6 +97,8 @@ PRIVATE
|
||||||
ui/platform/win/ui_window_win.h
|
ui/platform/win/ui_window_win.h
|
||||||
ui/platform/win/ui_utility_win.cpp
|
ui/platform/win/ui_utility_win.cpp
|
||||||
ui/platform/win/ui_utility_win.h
|
ui/platform/win/ui_utility_win.h
|
||||||
|
ui/platform/ui_platform_window_title.cpp
|
||||||
|
ui/platform/ui_platform_window_title.h
|
||||||
ui/platform/ui_platform_window.cpp
|
ui/platform/ui_platform_window.cpp
|
||||||
ui/platform/ui_platform_window.h
|
ui/platform/ui_platform_window.h
|
||||||
ui/platform/ui_platform_utility.h
|
ui/platform/ui_platform_utility.h
|
||||||
|
|
@ -228,6 +232,11 @@ if (NOT DESKTOP_APP_USE_PACKAGED_FONTS)
|
||||||
nice_target_sources(lib_ui ${src_loc} PRIVATE fonts/fonts.qrc)
|
nice_target_sources(lib_ui ${src_loc} PRIVATE fonts/fonts.qrc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
|
||||||
|
remove_target_sources(Telegram ${src_loc} ui/platform/linux/ui_linux_wayland_integration.cpp)
|
||||||
|
nice_target_sources(Telegram ${src_loc} PRIVATE ui/platform/linux/ui_linux_wayland_integration_dummy.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(lib_ui
|
target_include_directories(lib_ui
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${src_loc}
|
${src_loc}
|
||||||
|
|
|
||||||
BIN
icons/calls/call_shadow_left.png
Normal file
|
After Width: | Height: | Size: 100 B |
BIN
icons/calls/call_shadow_left@2x.png
Normal file
|
After Width: | Height: | Size: 125 B |
BIN
icons/calls/call_shadow_left@3x.png
Normal file
|
After Width: | Height: | Size: 139 B |
BIN
icons/calls/call_shadow_top.png
Normal file
|
After Width: | Height: | Size: 103 B |
BIN
icons/calls/call_shadow_top@2x.png
Normal file
|
After Width: | Height: | Size: 127 B |
BIN
icons/calls/call_shadow_top@3x.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
icons/calls/call_shadow_top_left.png
Normal file
|
After Width: | Height: | Size: 295 B |
BIN
icons/calls/call_shadow_top_left@2x.png
Normal file
|
After Width: | Height: | Size: 559 B |
BIN
icons/calls/call_shadow_top_left@3x.png
Normal file
|
After Width: | Height: | Size: 927 B |
46
ui/platform/linux/ui_linux_wayland_integration.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
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 "ui/platform/linux/ui_linux_wayland_integration.h"
|
||||||
|
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
|
||||||
|
#include <private/qwaylanddisplay_p.h>
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
|
#include <private/qwaylandshellsurface_p.h>
|
||||||
|
|
||||||
|
using QtWaylandClient::QWaylandWindow;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
WaylandIntegration::WaylandIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandIntegration *WaylandIntegration::Instance() {
|
||||||
|
if (!::Platform::IsWayland()) return nullptr;
|
||||||
|
static WaylandIntegration instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaylandIntegration::showWindowMenu(QWindow *window) {
|
||||||
|
if (const auto waylandWindow = static_cast<QWaylandWindow*>(
|
||||||
|
window->handle())) {
|
||||||
|
if (const auto seat = waylandWindow->display()->lastInputDevice()) {
|
||||||
|
if (const auto shellSurface = waylandWindow->shellSurface()) {
|
||||||
|
return shellSurface->showWindowMenu(seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Platform
|
||||||
|
} // namespace Ui
|
||||||
25
ui/platform/linux/ui_linux_wayland_integration.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
class QWindow;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
class WaylandIntegration {
|
||||||
|
public:
|
||||||
|
static WaylandIntegration *Instance();
|
||||||
|
bool showWindowMenu(QWindow *window);
|
||||||
|
|
||||||
|
private:
|
||||||
|
WaylandIntegration();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Platform
|
||||||
|
} // namespace Ui
|
||||||
29
ui/platform/linux/ui_linux_wayland_integration_dummy.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
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 "ui/platform/linux/ui_linux_wayland_integration.h"
|
||||||
|
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
WaylandIntegration::WaylandIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandIntegration *WaylandIntegration::Instance() {
|
||||||
|
if (!::Platform::IsWayland()) return nullptr;
|
||||||
|
static WaylandIntegration instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaylandIntegration::showWindowMenu(QWindow *window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Platform
|
||||||
|
} // namespace Ui
|
||||||
|
|
@ -8,16 +8,201 @@
|
||||||
|
|
||||||
#include "ui/ui_log.h"
|
#include "ui/ui_log.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
|
#include "base/platform/linux/base_xcb_utilities_linux.h"
|
||||||
|
#include "ui/platform/linux/ui_linux_wayland_integration.h"
|
||||||
|
#include "base/const_string.h"
|
||||||
#include "base/qt_adapters.h"
|
#include "base/qt_adapters.h"
|
||||||
#include "base/flat_set.h"
|
#include "base/flat_set.h"
|
||||||
|
|
||||||
#include <QtCore/QPoint>
|
#include <QtCore/QPoint>
|
||||||
#include <QtGui/QScreen>
|
#include <QtGui/QScreen>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <qpa/qplatformnativeinterface.h>
|
#include <qpa/qplatformnativeinterface.h>
|
||||||
|
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
#include <QtDBus/QDBusConnection>
|
||||||
|
#include <QtDBus/QDBusMessage>
|
||||||
|
#include <QtDBus/QDBusReply>
|
||||||
|
#include <QtDBus/QDBusVariant>
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QMargins);
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
|
||||||
|
|
||||||
|
constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs;
|
||||||
|
constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs;
|
||||||
|
constexpr auto kSettingsPortalInterface = "org.freedesktop.portal.Settings"_cs;
|
||||||
|
|
||||||
|
bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) {
|
||||||
|
const auto connection = base::Platform::XCB::GetConnectionFromQt();
|
||||||
|
if (!connection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto frameExtentsAtom = base::Platform::XCB::GetAtom(
|
||||||
|
connection,
|
||||||
|
kXCBFrameExtentsAtomName.utf16());
|
||||||
|
|
||||||
|
if (!frameExtentsAtom.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto extentsVector = std::vector<uint>{
|
||||||
|
uint(extents.left()),
|
||||||
|
uint(extents.right()),
|
||||||
|
uint(extents.top()),
|
||||||
|
uint(extents.bottom()),
|
||||||
|
};
|
||||||
|
|
||||||
|
xcb_change_property(
|
||||||
|
connection,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
window->winId(),
|
||||||
|
*frameExtentsAtom,
|
||||||
|
XCB_ATOM_CARDINAL,
|
||||||
|
32,
|
||||||
|
extentsVector.size(),
|
||||||
|
extentsVector.data());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnsetXCBFrameExtents(QWindow *window) {
|
||||||
|
const auto connection = base::Platform::XCB::GetConnectionFromQt();
|
||||||
|
if (!connection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto frameExtentsAtom = base::Platform::XCB::GetAtom(
|
||||||
|
connection,
|
||||||
|
kXCBFrameExtentsAtomName.utf16());
|
||||||
|
|
||||||
|
if (!frameExtentsAtom.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_delete_property(
|
||||||
|
connection,
|
||||||
|
window->winId(),
|
||||||
|
*frameExtentsAtom);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShowXCBWindowMenu(QWindow *window) {
|
||||||
|
const auto connection = base::Platform::XCB::GetConnectionFromQt();
|
||||||
|
if (!connection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto root = base::Platform::XCB::GetRootWindowFromQt();
|
||||||
|
if (!root.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto showWindowMenuAtom = base::Platform::XCB::GetAtom(
|
||||||
|
connection,
|
||||||
|
"_GTK_SHOW_WINDOW_MENU");
|
||||||
|
|
||||||
|
if (!showWindowMenuAtom.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto globalPos = QCursor::pos();
|
||||||
|
|
||||||
|
xcb_client_message_event_t xev;
|
||||||
|
xev.response_type = XCB_CLIENT_MESSAGE;
|
||||||
|
xev.type = *showWindowMenuAtom;
|
||||||
|
xev.sequence = 0;
|
||||||
|
xev.window = window->winId();
|
||||||
|
xev.format = 32;
|
||||||
|
xev.data.data32[0] = 0;
|
||||||
|
xev.data.data32[1] = globalPos.x();
|
||||||
|
xev.data.data32[2] = globalPos.y();
|
||||||
|
xev.data.data32[3] = 0;
|
||||||
|
xev.data.data32[4] = 0;
|
||||||
|
|
||||||
|
xcb_ungrab_pointer(connection, XCB_CURRENT_TIME);
|
||||||
|
xcb_send_event(
|
||||||
|
connection,
|
||||||
|
false,
|
||||||
|
*root,
|
||||||
|
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
|
||||||
|
| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
|
||||||
|
reinterpret_cast<const char*>(&xev));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleControls::Control GtkKeywordToTitleControl(const QString &keyword) {
|
||||||
|
if (keyword == qstr("minimize")) {
|
||||||
|
return TitleControls::Control::Minimize;
|
||||||
|
} else if (keyword == qstr("maximize")) {
|
||||||
|
return TitleControls::Control::Maximize;
|
||||||
|
} else if (keyword == qstr("close")) {
|
||||||
|
return TitleControls::Control::Close;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TitleControls::Control::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
std::optional<TitleControls::Layout> PortalTitleControlsLayout() {
|
||||||
|
auto message = QDBusMessage::createMethodCall(
|
||||||
|
kXDGDesktopPortalService.utf16(),
|
||||||
|
kXDGDesktopPortalObjectPath.utf16(),
|
||||||
|
kSettingsPortalInterface.utf16(),
|
||||||
|
"Read");
|
||||||
|
|
||||||
|
message.setArguments({
|
||||||
|
"org.gnome.desktop.wm.preferences",
|
||||||
|
"button-layout"
|
||||||
|
});
|
||||||
|
|
||||||
|
const QDBusReply<QVariant> reply = QDBusConnection::sessionBus().call(
|
||||||
|
message);
|
||||||
|
|
||||||
|
if (!reply.isValid() || !reply.value().canConvert<QDBusVariant>()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto valueVariant = qvariant_cast<QDBusVariant>(
|
||||||
|
reply.value()).variant();
|
||||||
|
|
||||||
|
if (!valueVariant.canConvert<QString>()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto valueBySides = valueVariant.toString().split(':');
|
||||||
|
|
||||||
|
std::vector<TitleControls::Control> controlsLeft;
|
||||||
|
ranges::transform(
|
||||||
|
valueBySides[0].split(','),
|
||||||
|
ranges::back_inserter(controlsLeft),
|
||||||
|
GtkKeywordToTitleControl);
|
||||||
|
|
||||||
|
std::vector<TitleControls::Control> controlsRight;
|
||||||
|
if (valueBySides.size() > 1) {
|
||||||
|
ranges::transform(
|
||||||
|
valueBySides[1].split(','),
|
||||||
|
ranges::back_inserter(controlsRight),
|
||||||
|
GtkKeywordToTitleControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TitleControls::Layout{
|
||||||
|
.left = controlsLeft,
|
||||||
|
.right = controlsRight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
bool IsApplicationActive() {
|
bool IsApplicationActive() {
|
||||||
return QApplication::activeWindow() != nullptr;
|
return QApplication::activeWindow() != nullptr;
|
||||||
|
|
@ -50,5 +235,71 @@ bool TranslucentWindowsSupported(QPoint globalPosition) {
|
||||||
void IgnoreAllActivation(not_null<QWidget*> widget) {
|
void IgnoreAllActivation(not_null<QWidget*> widget) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowExtentsSupported() {
|
||||||
|
#ifdef DESKTOP_APP_QT_PATCHED
|
||||||
|
if (::Platform::IsWayland()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // DESKTOP_APP_QT_PATCHED
|
||||||
|
|
||||||
|
namespace XCB = base::Platform::XCB;
|
||||||
|
if (!::Platform::IsWayland()
|
||||||
|
&& XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetWindowExtents(QWindow *window, const QMargins &extents) {
|
||||||
|
if (::Platform::IsWayland()) {
|
||||||
|
#ifdef DESKTOP_APP_QT_PATCHED
|
||||||
|
window->setProperty("WaylandCustomMargins", QVariant::fromValue<QMargins>(extents));
|
||||||
|
return true;
|
||||||
|
#else // DESKTOP_APP_QT_PATCHED
|
||||||
|
return false;
|
||||||
|
#endif // !DESKTOP_APP_QT_PATCHED
|
||||||
|
} else {
|
||||||
|
return SetXCBFrameExtents(window, extents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnsetWindowExtents(QWindow *window) {
|
||||||
|
if (::Platform::IsWayland()) {
|
||||||
|
#ifdef DESKTOP_APP_QT_PATCHED
|
||||||
|
window->setProperty("WaylandCustomMargins", QVariant());
|
||||||
|
return true;
|
||||||
|
#else // DESKTOP_APP_QT_PATCHED
|
||||||
|
return false;
|
||||||
|
#endif // !DESKTOP_APP_QT_PATCHED
|
||||||
|
} else {
|
||||||
|
return UnsetXCBFrameExtents(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShowWindowMenu(QWindow *window) {
|
||||||
|
if (const auto integration = WaylandIntegration::Instance()) {
|
||||||
|
return integration->showWindowMenu(window);
|
||||||
|
} else {
|
||||||
|
return ShowXCBWindowMenu(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleControls::Layout TitleControlsLayout() {
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
if (const auto portalLayout = PortalTitleControlsLayout()) {
|
||||||
|
return *portalLayout;
|
||||||
|
}
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
|
return TitleControls::Layout{
|
||||||
|
.right = {
|
||||||
|
TitleControls::Control::Minimize,
|
||||||
|
TitleControls::Control::Maximize,
|
||||||
|
TitleControls::Control::Close,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
|
|
||||||
class QPainter;
|
class QPainter;
|
||||||
class QPaintEvent;
|
class QPaintEvent;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include <QtCore/QPoint>
|
#include <QtCore/QPoint>
|
||||||
|
|
||||||
|
|
@ -23,5 +24,21 @@ inline constexpr bool UseMainQueueGeneric() {
|
||||||
return ::Platform::IsMacStoreBuild();
|
return ::Platform::IsMacStoreBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool WindowExtentsSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool SetWindowExtents(QWindow *window, const QMargins &extents) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool UnsetWindowExtents(QWindow *window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ShowWindowMenu(QWindow *window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -99,5 +99,15 @@ void DrainMainQueue() {
|
||||||
void IgnoreAllActivation(not_null<QWidget*> widget) {
|
void IgnoreAllActivation(not_null<QWidget*> widget) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TitleControls::Layout TitleControlsLayout() {
|
||||||
|
return TitleControls::Layout{
|
||||||
|
.left = {
|
||||||
|
TitleControls::Control::Close,
|
||||||
|
TitleControls::Control::Minimize,
|
||||||
|
TitleControls::Control::Maximize,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
|
|
||||||
class QPoint;
|
class QPoint;
|
||||||
class QPainter;
|
class QPainter;
|
||||||
class QPaintEvent;
|
class QPaintEvent;
|
||||||
|
|
@ -30,6 +32,12 @@ void IgnoreAllActivation(not_null<QWidget*> widget);
|
||||||
[[nodiscard]] constexpr bool UseMainQueueGeneric();
|
[[nodiscard]] constexpr bool UseMainQueueGeneric();
|
||||||
void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false.
|
void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false.
|
||||||
|
|
||||||
|
[[nodiscard]] bool WindowExtentsSupported();
|
||||||
|
bool SetWindowExtents(QWindow *window, const QMargins &extents);
|
||||||
|
bool UnsetWindowExtents(QWindow *window);
|
||||||
|
bool ShowWindowMenu(QWindow *window);
|
||||||
|
[[nodiscard]] TitleControls::Layout TitleControlsLayout();
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,31 @@
|
||||||
//
|
//
|
||||||
#include "ui/platform/ui_platform_window.h"
|
#include "ui/platform/ui_platform_window.h"
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
#include "ui/widgets/window.h"
|
#include "ui/widgets/window.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtGui/QWindow>
|
#include <QtGui/QWindow>
|
||||||
#include <QtGui/QtEvents>
|
#include <QtGui/QtEvents>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] const style::Shadow &Shadow() {
|
||||||
|
return st::callShadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
BasicWindowHelper::BasicWindowHelper(not_null<RpWidget*> window)
|
BasicWindowHelper::BasicWindowHelper(not_null<RpWidget*> window)
|
||||||
: _window(window) {
|
: _window(window) {
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
|
||||||
_window->setWindowFlag(Qt::Window);
|
_window->setWindowFlag(Qt::Window);
|
||||||
#else // Qt >= 5.9
|
|
||||||
_window->setWindowFlags(_window->windowFlags() | Qt::Window);
|
|
||||||
#endif // Qt >= 5.9
|
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<RpWidget*> BasicWindowHelper::body() {
|
not_null<RpWidget*> BasicWindowHelper::body() {
|
||||||
|
|
@ -100,8 +110,6 @@ void BasicWindowHelper::setupBodyTitleAreaEvents() {
|
||||||
&& (static_cast<QMouseEvent*>(e.get())->button()
|
&& (static_cast<QMouseEvent*>(e.get())->button()
|
||||||
== Qt::LeftButton)) {
|
== Qt::LeftButton)) {
|
||||||
_mousePressed = true;
|
_mousePressed = true;
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
|
||||||
} else if (e->type() == QEvent::MouseMove) {
|
} else if (e->type() == QEvent::MouseMove) {
|
||||||
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
|
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
|
||||||
if (_mousePressed
|
if (_mousePressed
|
||||||
|
|
@ -109,7 +117,6 @@ void BasicWindowHelper::setupBodyTitleAreaEvents() {
|
||||||
&& !_window->isFullScreen()
|
&& !_window->isFullScreen()
|
||||||
#endif // !Q_OS_WIN
|
#endif // !Q_OS_WIN
|
||||||
&& (hitTest() & WindowTitleHitTestFlag::Move)) {
|
&& (hitTest() & WindowTitleHitTestFlag::Move)) {
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (_window->isFullScreen()) {
|
if (_window->isFullScreen()) {
|
||||||
// On Windows we just jump out of fullscreen
|
// On Windows we just jump out of fullscreen
|
||||||
|
|
@ -121,10 +128,258 @@ void BasicWindowHelper::setupBodyTitleAreaEvents() {
|
||||||
_mousePressed = false;
|
_mousePressed = false;
|
||||||
_window->windowHandle()->startSystemMove();
|
_window->windowHandle()->startSystemMove();
|
||||||
}
|
}
|
||||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
|
||||||
}
|
}
|
||||||
}, body()->lifetime());
|
}, body()->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultWindowHelper::DefaultWindowHelper(not_null<RpWidget*> window)
|
||||||
|
: BasicWindowHelper(window)
|
||||||
|
, _title(Ui::CreateChild<DefaultTitleWidget>(window.get()))
|
||||||
|
, _body(Ui::CreateChild<RpWidget>(window.get())) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::init() {
|
||||||
|
window()->setWindowFlag(Qt::FramelessWindowHint);
|
||||||
|
|
||||||
|
if (WindowExtentsSupported()) {
|
||||||
|
window()->setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
window()->widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
_title->setGeometry(
|
||||||
|
resizeArea().left(),
|
||||||
|
resizeArea().top(),
|
||||||
|
width - resizeArea().left() - resizeArea().right(),
|
||||||
|
_title->st()->height);
|
||||||
|
}, _title->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
window()->sizeValue(),
|
||||||
|
_title->heightValue()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int titleHeight) {
|
||||||
|
const auto sizeWithoutMargins = size
|
||||||
|
.shrunkBy({ 0, titleHeight, 0, 0 })
|
||||||
|
.shrunkBy(resizeArea());
|
||||||
|
|
||||||
|
const auto topLeft = QPoint(
|
||||||
|
resizeArea().left(),
|
||||||
|
resizeArea().top() + titleHeight);
|
||||||
|
|
||||||
|
_body->setGeometry(QRect(topLeft, sizeWithoutMargins));
|
||||||
|
}, _body->lifetime());
|
||||||
|
|
||||||
|
window()->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (resizeArea().isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Painter p(window());
|
||||||
|
|
||||||
|
if (hasShadow()) {
|
||||||
|
Ui::Shadow::paint(
|
||||||
|
p,
|
||||||
|
QRect(QPoint(), window()->size()).marginsRemoved(resizeArea()),
|
||||||
|
window()->width(),
|
||||||
|
Shadow());
|
||||||
|
} else {
|
||||||
|
paintBorders(p);
|
||||||
|
}
|
||||||
|
}, window()->lifetime());
|
||||||
|
|
||||||
|
window()->shownValue(
|
||||||
|
) | rpl::start_with_next([=](bool shown) {
|
||||||
|
if (shown) {
|
||||||
|
updateWindowExtents();
|
||||||
|
}
|
||||||
|
}, window()->lifetime());
|
||||||
|
|
||||||
|
window()->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
if (e->type() == QEvent::MouseButtonPress) {
|
||||||
|
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
|
||||||
|
const auto currentPoint = mouseEvent->windowPos().toPoint();
|
||||||
|
const auto edges = edgesFromPos(currentPoint);
|
||||||
|
|
||||||
|
if (mouseEvent->button() == Qt::LeftButton && edges) {
|
||||||
|
window()->windowHandle()->startSystemResize(edges);
|
||||||
|
}
|
||||||
|
} else if (e->type() == QEvent::Move
|
||||||
|
|| e->type() == QEvent::Resize
|
||||||
|
|| e->type() == QEvent::WindowStateChange) {
|
||||||
|
updateWindowExtents();
|
||||||
|
}
|
||||||
|
}, window()->lifetime());
|
||||||
|
|
||||||
|
QCoreApplication::instance()->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<RpWidget*> DefaultWindowHelper::body() {
|
||||||
|
return _body;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultWindowHelper::hasShadow() const {
|
||||||
|
const auto center = window()->geometry().center();
|
||||||
|
return WindowExtentsSupported() && TranslucentWindowsSupported(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMargins DefaultWindowHelper::resizeArea() const {
|
||||||
|
if (window()->isMaximized() || window()->isFullScreen()) {
|
||||||
|
return QMargins();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Shadow().extend;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Edges DefaultWindowHelper::edgesFromPos(const QPoint &pos) const {
|
||||||
|
if (pos.x() <= resizeArea().left()) {
|
||||||
|
if (pos.y() <= resizeArea().top()) {
|
||||||
|
return Qt::LeftEdge | Qt::TopEdge;
|
||||||
|
} else if (pos.y() >= (window()->height() - resizeArea().bottom())) {
|
||||||
|
return Qt::LeftEdge | Qt::BottomEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt::LeftEdge;
|
||||||
|
} else if (pos.x() >= (window()->width() - resizeArea().right())) {
|
||||||
|
if (pos.y() <= resizeArea().top()) {
|
||||||
|
return Qt::RightEdge | Qt::TopEdge;
|
||||||
|
} else if (pos.y() >= (window()->height() - resizeArea().bottom())) {
|
||||||
|
return Qt::RightEdge | Qt::BottomEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt::RightEdge;
|
||||||
|
} else if (pos.y() <= resizeArea().top()) {
|
||||||
|
return Qt::TopEdge;
|
||||||
|
} else if (pos.y() >= (window()->height() - resizeArea().bottom())) {
|
||||||
|
return Qt::BottomEdge;
|
||||||
|
} else {
|
||||||
|
return Qt::Edges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultWindowHelper::eventFilter(QObject *obj, QEvent *e) {
|
||||||
|
// doesn't work with RpWidget::events() for some reason
|
||||||
|
if (e->type() == QEvent::MouseMove
|
||||||
|
&& obj->isWidgetType()
|
||||||
|
&& static_cast<QWidget*>(window()) == static_cast<QWidget*>(obj)) {
|
||||||
|
const auto mouseEvent = static_cast<QMouseEvent*>(e);
|
||||||
|
const auto currentPoint = mouseEvent->windowPos().toPoint();
|
||||||
|
const auto edges = edgesFromPos(currentPoint);
|
||||||
|
|
||||||
|
if (mouseEvent->buttons() == Qt::NoButton) {
|
||||||
|
updateCursor(edges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::eventFilter(obj, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::setTitle(const QString &title) {
|
||||||
|
_title->setText(title);
|
||||||
|
window()->setWindowTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::setTitleStyle(const style::WindowTitle &st) {
|
||||||
|
_title->setStyle(st);
|
||||||
|
_title->setGeometry(
|
||||||
|
resizeArea().left(),
|
||||||
|
resizeArea().top(),
|
||||||
|
window()->width() - resizeArea().left() - resizeArea().right(),
|
||||||
|
_title->st()->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::setMinimumSize(QSize size) {
|
||||||
|
const auto sizeWithMargins = size
|
||||||
|
.grownBy({ 0, _title->height(), 0, 0 })
|
||||||
|
.grownBy(resizeArea());
|
||||||
|
window()->setMinimumSize(sizeWithMargins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::setFixedSize(QSize size) {
|
||||||
|
const auto sizeWithMargins = size
|
||||||
|
.grownBy({ 0, _title->height(), 0, 0 })
|
||||||
|
.grownBy(resizeArea());
|
||||||
|
window()->setFixedSize(sizeWithMargins);
|
||||||
|
_title->setResizeEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::setGeometry(QRect rect) {
|
||||||
|
window()->setGeometry(rect
|
||||||
|
.marginsAdded({ 0, _title->height(), 0, 0 })
|
||||||
|
.marginsAdded(resizeArea()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::paintBorders(QPainter &p) {
|
||||||
|
const auto titleBackground = window()->isActiveWindow()
|
||||||
|
? _title->st()->bgActive
|
||||||
|
: _title->st()->bg;
|
||||||
|
|
||||||
|
const auto defaultTitleBackground = window()->isActiveWindow()
|
||||||
|
? st::defaultWindowTitle.bgActive
|
||||||
|
: st::defaultWindowTitle.bg;
|
||||||
|
|
||||||
|
const auto borderColor = QBrush(titleBackground).isOpaque()
|
||||||
|
? titleBackground
|
||||||
|
: defaultTitleBackground;
|
||||||
|
|
||||||
|
p.fillRect(
|
||||||
|
0,
|
||||||
|
resizeArea().top(),
|
||||||
|
resizeArea().left(),
|
||||||
|
window()->height() - resizeArea().top() - resizeArea().bottom(),
|
||||||
|
borderColor);
|
||||||
|
|
||||||
|
p.fillRect(
|
||||||
|
window()->width() - resizeArea().right(),
|
||||||
|
resizeArea().top(),
|
||||||
|
resizeArea().right(),
|
||||||
|
window()->height() - resizeArea().top() - resizeArea().bottom(),
|
||||||
|
borderColor);
|
||||||
|
|
||||||
|
p.fillRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
window()->width(),
|
||||||
|
resizeArea().top(),
|
||||||
|
borderColor);
|
||||||
|
|
||||||
|
p.fillRect(
|
||||||
|
0,
|
||||||
|
window()->height() - resizeArea().bottom(),
|
||||||
|
window()->width(),
|
||||||
|
resizeArea().bottom(),
|
||||||
|
borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::updateWindowExtents() {
|
||||||
|
if (hasShadow()) {
|
||||||
|
Platform::SetWindowExtents(
|
||||||
|
window()->windowHandle(),
|
||||||
|
resizeArea());
|
||||||
|
|
||||||
|
_extentsSet = true;
|
||||||
|
} else if (_extentsSet) {
|
||||||
|
Platform::UnsetWindowExtents(window()->windowHandle());
|
||||||
|
_extentsSet = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultWindowHelper::updateCursor(Qt::Edges edges) {
|
||||||
|
if (((edges & Qt::LeftEdge) && (edges & Qt::TopEdge))
|
||||||
|
|| ((edges & Qt::RightEdge) && (edges & Qt::BottomEdge))) {
|
||||||
|
window()->setCursor(QCursor(Qt::SizeFDiagCursor));
|
||||||
|
} else if (((edges & Qt::LeftEdge) && (edges & Qt::BottomEdge))
|
||||||
|
|| ((edges & Qt::RightEdge) && (edges & Qt::TopEdge))) {
|
||||||
|
window()->setCursor(QCursor(Qt::SizeBDiagCursor));
|
||||||
|
} else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) {
|
||||||
|
window()->setCursor(QCursor(Qt::SizeHorCursor));
|
||||||
|
} else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) {
|
||||||
|
window()->setCursor(QCursor(Qt::SizeVerCursor));
|
||||||
|
} else {
|
||||||
|
window()->unsetCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ using WindowTitleHitTestFlags = base::flags<WindowTitleHitTestFlag>;
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
|
||||||
|
class DefaultTitleWidget;
|
||||||
|
|
||||||
class BasicWindowHelper {
|
class BasicWindowHelper {
|
||||||
public:
|
public:
|
||||||
explicit BasicWindowHelper(not_null<RpWidget*> window);
|
explicit BasicWindowHelper(not_null<RpWidget*> window);
|
||||||
|
|
@ -57,6 +59,35 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DefaultWindowHelper final : public QObject, public BasicWindowHelper {
|
||||||
|
public:
|
||||||
|
explicit DefaultWindowHelper(not_null<RpWidget*> window);
|
||||||
|
|
||||||
|
not_null<RpWidget*> body() override;
|
||||||
|
void setTitle(const QString &title) override;
|
||||||
|
void setTitleStyle(const style::WindowTitle &st) override;
|
||||||
|
void setMinimumSize(QSize size) override;
|
||||||
|
void setFixedSize(QSize size) override;
|
||||||
|
void setGeometry(QRect rect) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
[[nodiscard]] bool hasShadow() const;
|
||||||
|
[[nodiscard]] QMargins resizeArea() const;
|
||||||
|
[[nodiscard]] Qt::Edges edgesFromPos(const QPoint &pos) const;
|
||||||
|
void paintBorders(QPainter &p);
|
||||||
|
void updateWindowExtents();
|
||||||
|
void updateCursor(Qt::Edges edges);
|
||||||
|
|
||||||
|
const not_null<DefaultTitleWidget*> _title;
|
||||||
|
const not_null<RpWidget*> _body;
|
||||||
|
bool _extentsSet = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
|
[[nodiscard]] std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
|
||||||
not_null<RpWidget*> window);
|
not_null<RpWidget*> window);
|
||||||
|
|
||||||
|
|
@ -65,7 +96,7 @@ private:
|
||||||
if (auto special = CreateSpecialWindowHelper(window)) {
|
if (auto special = CreateSpecialWindowHelper(window)) {
|
||||||
return special;
|
return special;
|
||||||
}
|
}
|
||||||
return std::make_unique<BasicWindowHelper>(window);
|
return std::make_unique<DefaultWindowHelper>(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
||||||
334
ui/platform/ui_platform_window_title.cpp
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
// This file is part of Desktop App Toolkit,
|
||||||
|
// a set of libraries for developing nice desktop applications.
|
||||||
|
//
|
||||||
|
// For license and copyright information please follow this link:
|
||||||
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||||
|
//
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
#include "styles/palette.h"
|
||||||
|
#include "base/algorithm.h"
|
||||||
|
#include "base/event_filter.h"
|
||||||
|
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
#include <QtGui/QtEvents>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
TitleControls::TitleControls(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::WindowTitle &st,
|
||||||
|
Fn<void(bool maximized)> maximize)
|
||||||
|
: _st(&st)
|
||||||
|
, _minimize(parent, _st->minimize)
|
||||||
|
, _maximizeRestore(parent, _st->maximize)
|
||||||
|
, _close(parent, _st->close)
|
||||||
|
, _maximizedState(parent->windowState()
|
||||||
|
& (Qt::WindowMaximized | Qt::WindowFullScreen))
|
||||||
|
, _activeState(parent->isActiveWindow()) {
|
||||||
|
init(std::move(maximize));
|
||||||
|
|
||||||
|
_close->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto active = window()->isActiveWindow();
|
||||||
|
if (_activeState != active) {
|
||||||
|
_activeState = active;
|
||||||
|
updateButtonsState();
|
||||||
|
}
|
||||||
|
}, _close->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::setStyle(const style::WindowTitle &st) {
|
||||||
|
_st = &st;
|
||||||
|
updateButtonsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<const style::WindowTitle*> TitleControls::st() const {
|
||||||
|
return _st;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect TitleControls::geometry() const {
|
||||||
|
auto result = QRect();
|
||||||
|
const auto add = [&](auto &&control) {
|
||||||
|
if (!control->isHidden()) {
|
||||||
|
result = result.united(control->geometry());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
add(_minimize);
|
||||||
|
add(_maximizeRestore);
|
||||||
|
add(_close);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<RpWidget*> TitleControls::parent() const {
|
||||||
|
return static_cast<RpWidget*>(_close->parentWidget());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QWidget*> TitleControls::window() const {
|
||||||
|
return _close->window();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::init(Fn<void(bool maximized)> maximize) {
|
||||||
|
_minimize->setClickedCallback([=] {
|
||||||
|
window()->setWindowState(
|
||||||
|
window()->windowState() | Qt::WindowMinimized);
|
||||||
|
_minimize->clearState();
|
||||||
|
});
|
||||||
|
_minimize->setPointerCursor(false);
|
||||||
|
_maximizeRestore->setClickedCallback([=] {
|
||||||
|
if (maximize) {
|
||||||
|
maximize(!_maximizedState);
|
||||||
|
} else {
|
||||||
|
window()->setWindowState(_maximizedState
|
||||||
|
? Qt::WindowNoState
|
||||||
|
: Qt::WindowMaximized);
|
||||||
|
}
|
||||||
|
_maximizeRestore->clearState();
|
||||||
|
});
|
||||||
|
_maximizeRestore->setPointerCursor(false);
|
||||||
|
_close->setClickedCallback([=] {
|
||||||
|
window()->close();
|
||||||
|
_close->clearState();
|
||||||
|
});
|
||||||
|
_close->setPointerCursor(false);
|
||||||
|
|
||||||
|
parent()->widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
updateControlsPosition();
|
||||||
|
}, _close->lifetime());
|
||||||
|
|
||||||
|
const auto winIdEventFilter = std::make_shared<QObject*>(nullptr);
|
||||||
|
*winIdEventFilter = base::install_event_filter(
|
||||||
|
window(),
|
||||||
|
[=](not_null<QEvent*> e) {
|
||||||
|
if (!*winIdEventFilter || e->type() != QEvent::WinIdChange) {
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
window()->windowHandle(),
|
||||||
|
&QWindow::windowStateChanged,
|
||||||
|
[=](Qt::WindowState state) {
|
||||||
|
handleWindowStateChanged(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
base::take(*winIdEventFilter)->deleteLater();
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
_activeState = parent()->isActiveWindow();
|
||||||
|
updateButtonsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::setResizeEnabled(bool enabled) {
|
||||||
|
_resizeEnabled = enabled;
|
||||||
|
updateControlsPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::raise() {
|
||||||
|
_minimize->raise();
|
||||||
|
_maximizeRestore->raise();
|
||||||
|
_close->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::updateControlsPosition() {
|
||||||
|
const auto controlsLayout = TitleControlsLayout();
|
||||||
|
auto controlsLeft = controlsLayout.left;
|
||||||
|
auto controlsRight = controlsLayout.right;
|
||||||
|
|
||||||
|
if (!_resizeEnabled) {
|
||||||
|
controlsLeft.erase(
|
||||||
|
ranges::remove(controlsLeft, Control::Maximize),
|
||||||
|
end(controlsLeft));
|
||||||
|
|
||||||
|
controlsRight.erase(
|
||||||
|
ranges::remove(controlsRight, Control::Maximize),
|
||||||
|
end(controlsRight));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges::contains(controlsLeft, Control::Minimize)
|
||||||
|
|| ranges::contains(controlsRight, Control::Minimize)) {
|
||||||
|
_minimize->show();
|
||||||
|
} else {
|
||||||
|
_minimize->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges::contains(controlsLeft, Control::Maximize)
|
||||||
|
|| ranges::contains(controlsRight, Control::Maximize)) {
|
||||||
|
_maximizeRestore->show();
|
||||||
|
} else {
|
||||||
|
_maximizeRestore->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges::contains(controlsLeft, Control::Close)
|
||||||
|
|| ranges::contains(controlsRight, Control::Close)) {
|
||||||
|
_close->show();
|
||||||
|
} else {
|
||||||
|
_close->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateControlsPositionBySide(controlsLeft, false);
|
||||||
|
updateControlsPositionBySide(controlsRight, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::updateControlsPositionBySide(
|
||||||
|
const std::vector<Control> &controls,
|
||||||
|
bool right) {
|
||||||
|
const auto preparedControls = right
|
||||||
|
? (ranges::view::reverse(controls) | ranges::to_vector)
|
||||||
|
: controls;
|
||||||
|
|
||||||
|
auto position = 0;
|
||||||
|
for (const auto &control : preparedControls) {
|
||||||
|
switch (control) {
|
||||||
|
case Control::Minimize:
|
||||||
|
if (right) {
|
||||||
|
_minimize->moveToRight(position, 0);
|
||||||
|
} else {
|
||||||
|
_minimize->moveToLeft(position, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
position += _minimize->width();
|
||||||
|
break;
|
||||||
|
case Control::Maximize:
|
||||||
|
if (right) {
|
||||||
|
_maximizeRestore->moveToRight(position, 0);
|
||||||
|
} else {
|
||||||
|
_maximizeRestore->moveToLeft(position, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
position += _maximizeRestore->width();
|
||||||
|
break;
|
||||||
|
case Control::Close:
|
||||||
|
if (right) {
|
||||||
|
_close->moveToRight(position, 0);
|
||||||
|
} else {
|
||||||
|
_close->moveToLeft(position, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
position += _close->width();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::handleWindowStateChanged(Qt::WindowState state) {
|
||||||
|
if (state == Qt::WindowMinimized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto maximized = (state == Qt::WindowMaximized)
|
||||||
|
|| (state == Qt::WindowFullScreen);
|
||||||
|
if (_maximizedState != maximized) {
|
||||||
|
_maximizedState = maximized;
|
||||||
|
updateButtonsState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleControls::updateButtonsState() {
|
||||||
|
const auto minimize = _activeState
|
||||||
|
? &_st->minimizeIconActive
|
||||||
|
: &_st->minimize.icon;
|
||||||
|
const auto minimizeOver = _activeState
|
||||||
|
? &_st->minimizeIconActiveOver
|
||||||
|
: &_st->minimize.iconOver;
|
||||||
|
_minimize->setIconOverride(minimize, minimizeOver);
|
||||||
|
if (_maximizedState) {
|
||||||
|
const auto restore = _activeState
|
||||||
|
? &_st->restoreIconActive
|
||||||
|
: &_st->restoreIcon;
|
||||||
|
const auto restoreOver = _activeState
|
||||||
|
? &_st->restoreIconActiveOver
|
||||||
|
: &_st->restoreIconOver;
|
||||||
|
_maximizeRestore->setIconOverride(restore, restoreOver);
|
||||||
|
} else {
|
||||||
|
const auto maximize = _activeState
|
||||||
|
? &_st->maximizeIconActive
|
||||||
|
: &_st->maximize.icon;
|
||||||
|
const auto maximizeOver = _activeState
|
||||||
|
? &_st->maximizeIconActiveOver
|
||||||
|
: &_st->maximize.iconOver;
|
||||||
|
_maximizeRestore->setIconOverride(maximize, maximizeOver);
|
||||||
|
}
|
||||||
|
const auto close = _activeState
|
||||||
|
? &_st->closeIconActive
|
||||||
|
: &_st->close.icon;
|
||||||
|
const auto closeOver = _activeState
|
||||||
|
? &_st->closeIconActiveOver
|
||||||
|
: &_st->close.iconOver;
|
||||||
|
_close->setIconOverride(close, closeOver);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultTitleWidget::DefaultTitleWidget(not_null<RpWidget*> parent)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _controls(this, st::defaultWindowTitle)
|
||||||
|
, _shadow(this, st::titleShadow) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<const style::WindowTitle*> DefaultTitleWidget::st() const {
|
||||||
|
return _controls.st();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::setText(const QString &text) {
|
||||||
|
window()->setWindowTitle(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::setStyle(const style::WindowTitle &st) {
|
||||||
|
_controls.setStyle(st);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::setResizeEnabled(bool enabled) {
|
||||||
|
_controls.setResizeEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
const auto active = window()->isActiveWindow();
|
||||||
|
QPainter(this).fillRect(
|
||||||
|
e->rect(),
|
||||||
|
active ? _controls.st()->bgActive : _controls.st()->bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::resizeEvent(QResizeEvent *e) {
|
||||||
|
_shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
|
if (e->button() == Qt::LeftButton) {
|
||||||
|
_mousePressed = true;
|
||||||
|
} else if (e->button() == Qt::RightButton) {
|
||||||
|
ShowWindowMenu(window()->windowHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
if (e->button() == Qt::LeftButton) {
|
||||||
|
_mousePressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::mouseMoveEvent(QMouseEvent *e) {
|
||||||
|
if (_mousePressed) {
|
||||||
|
window()->windowHandle()->startSystemMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultTitleWidget::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
|
const auto state = window()->windowState();
|
||||||
|
if (state & Qt::WindowMaximized) {
|
||||||
|
window()->setWindowState(state & ~Qt::WindowMaximized);
|
||||||
|
} else {
|
||||||
|
window()->setWindowState(state | Qt::WindowMaximized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Platform
|
||||||
|
} // namespace Ui
|
||||||
100
ui/platform/ui_platform_window_title.h
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
// This file is part of Desktop App Toolkit,
|
||||||
|
// a set of libraries for developing nice desktop applications.
|
||||||
|
//
|
||||||
|
// For license and copyright information please follow this link:
|
||||||
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
|
#include <QtCore/QRect>
|
||||||
|
#include <QtCore/QPoint>
|
||||||
|
|
||||||
|
namespace style {
|
||||||
|
struct WindowTitle;
|
||||||
|
} // namespace style
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class IconButton;
|
||||||
|
class PlainShadow;
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
class TitleControls final {
|
||||||
|
public:
|
||||||
|
TitleControls(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::WindowTitle &st,
|
||||||
|
Fn<void(bool maximized)> maximize = nullptr);
|
||||||
|
|
||||||
|
void setStyle(const style::WindowTitle &st);
|
||||||
|
[[nodiscard]] not_null<const style::WindowTitle*> st() const;
|
||||||
|
[[nodiscard]] QRect geometry() const;
|
||||||
|
void setResizeEnabled(bool enabled);
|
||||||
|
void raise();
|
||||||
|
|
||||||
|
enum class Control {
|
||||||
|
Unknown,
|
||||||
|
Minimize,
|
||||||
|
Maximize,
|
||||||
|
Close,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Layout {
|
||||||
|
std::vector<Control> left;
|
||||||
|
std::vector<Control> right;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] not_null<RpWidget*> parent() const;
|
||||||
|
[[nodiscard]] not_null<QWidget*> window() const;
|
||||||
|
|
||||||
|
void init(Fn<void(bool maximized)> maximize);
|
||||||
|
void updateButtonsState();
|
||||||
|
void updateControlsPosition();
|
||||||
|
void updateControlsPositionBySide(
|
||||||
|
const std::vector<Control> &controls,
|
||||||
|
bool right);
|
||||||
|
void handleWindowStateChanged(Qt::WindowState state = Qt::WindowNoState);
|
||||||
|
|
||||||
|
not_null<const style::WindowTitle*> _st;
|
||||||
|
|
||||||
|
object_ptr<Ui::IconButton> _minimize;
|
||||||
|
object_ptr<Ui::IconButton> _maximizeRestore;
|
||||||
|
object_ptr<Ui::IconButton> _close;
|
||||||
|
|
||||||
|
bool _maximizedState = false;
|
||||||
|
bool _activeState = false;
|
||||||
|
bool _resizeEnabled = true;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultTitleWidget : public RpWidget {
|
||||||
|
public:
|
||||||
|
explicit DefaultTitleWidget(not_null<RpWidget*> parent);
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<const style::WindowTitle*> st() const;
|
||||||
|
void setText(const QString &text);
|
||||||
|
void setStyle(const style::WindowTitle &st);
|
||||||
|
void setResizeEnabled(bool enabled);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TitleControls _controls;
|
||||||
|
object_ptr<Ui::PlainShadow> _shadow;
|
||||||
|
bool _mousePressed = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Platform
|
||||||
|
} // namespace Ui
|
||||||
|
|
@ -44,5 +44,27 @@ void IgnoreAllActivation(not_null<QWidget*> widget) {
|
||||||
ShowWindow(handle, SW_SHOW);
|
ShowWindow(handle, SW_SHOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShowWindowMenu(QWindow *window) {
|
||||||
|
const auto pos = QCursor::pos();
|
||||||
|
|
||||||
|
SendMessage(
|
||||||
|
HWND(window->winId()),
|
||||||
|
WM_SYSCOMMAND,
|
||||||
|
SC_MOUSEMENU,
|
||||||
|
MAKELPARAM(pos.x(), pos.y()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleControls::Layout TitleControlsLayout() {
|
||||||
|
return TitleControls::Layout{
|
||||||
|
.right = {
|
||||||
|
TitleControls::Control::Minimize,
|
||||||
|
TitleControls::Control::Maximize,
|
||||||
|
TitleControls::Control::Close,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
|
|
||||||
#include <QtCore/QPoint>
|
#include <QtCore/QPoint>
|
||||||
|
|
||||||
class QPainter;
|
class QPainter;
|
||||||
|
|
@ -40,5 +42,17 @@ inline constexpr bool UseMainQueueGeneric() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool WindowExtentsSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool SetWindowExtents(QWindow *window, const QMargins &extents) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool UnsetWindowExtents(QWindow *window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
||||||
|
|
@ -19,170 +19,6 @@
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
|
||||||
TitleControls::TitleControls(
|
|
||||||
not_null<RpWidget*> parent,
|
|
||||||
const style::WindowTitle &st,
|
|
||||||
Fn<void(bool maximized)> maximize)
|
|
||||||
: _st(&st)
|
|
||||||
, _minimize(parent, _st->minimize)
|
|
||||||
, _maximizeRestore(parent, _st->maximize)
|
|
||||||
, _close(parent, _st->close)
|
|
||||||
, _maximizedState(parent->windowState()
|
|
||||||
& (Qt::WindowMaximized | Qt::WindowFullScreen))
|
|
||||||
, _activeState(parent->isActiveWindow()) {
|
|
||||||
init(std::move(maximize));
|
|
||||||
|
|
||||||
_close->paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
const auto active = window()->isActiveWindow();
|
|
||||||
if (_activeState != active) {
|
|
||||||
_activeState = active;
|
|
||||||
updateButtonsState();
|
|
||||||
}
|
|
||||||
}, _close->lifetime());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::setStyle(const style::WindowTitle &st) {
|
|
||||||
_st = &st;
|
|
||||||
updateButtonsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<const style::WindowTitle*> TitleControls::st() const {
|
|
||||||
return _st;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect TitleControls::geometry() const {
|
|
||||||
auto result = QRect();
|
|
||||||
const auto add = [&](auto &&control) {
|
|
||||||
if (!control->isHidden()) {
|
|
||||||
result = result.united(control->geometry());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
add(_minimize);
|
|
||||||
add(_maximizeRestore);
|
|
||||||
add(_close);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<RpWidget*> TitleControls::parent() const {
|
|
||||||
return static_cast<RpWidget*>(_close->parentWidget());
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<QWidget*> TitleControls::window() const {
|
|
||||||
return _close->window();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::init(Fn<void(bool maximized)> maximize) {
|
|
||||||
_minimize->setClickedCallback([=] {
|
|
||||||
window()->setWindowState(
|
|
||||||
window()->windowState() | Qt::WindowMinimized);
|
|
||||||
_minimize->clearState();
|
|
||||||
});
|
|
||||||
_minimize->setPointerCursor(false);
|
|
||||||
_maximizeRestore->setClickedCallback([=] {
|
|
||||||
if (maximize) {
|
|
||||||
maximize(!_maximizedState);
|
|
||||||
} else {
|
|
||||||
window()->setWindowState(_maximizedState
|
|
||||||
? Qt::WindowNoState
|
|
||||||
: Qt::WindowMaximized);
|
|
||||||
}
|
|
||||||
_maximizeRestore->clearState();
|
|
||||||
});
|
|
||||||
_maximizeRestore->setPointerCursor(false);
|
|
||||||
_close->setClickedCallback([=] {
|
|
||||||
window()->close();
|
|
||||||
_close->clearState();
|
|
||||||
});
|
|
||||||
_close->setPointerCursor(false);
|
|
||||||
|
|
||||||
parent()->widthValue(
|
|
||||||
) | rpl::start_with_next([=](int width) {
|
|
||||||
updateControlsPosition();
|
|
||||||
}, _close->lifetime());
|
|
||||||
|
|
||||||
window()->createWinId();
|
|
||||||
QObject::connect(
|
|
||||||
window()->windowHandle(),
|
|
||||||
&QWindow::windowStateChanged,
|
|
||||||
[=](Qt::WindowState state) { handleWindowStateChanged(state); });
|
|
||||||
_activeState = parent()->isActiveWindow();
|
|
||||||
updateButtonsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::setResizeEnabled(bool enabled) {
|
|
||||||
_resizeEnabled = enabled;
|
|
||||||
updateControlsVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::raise() {
|
|
||||||
_minimize->raise();
|
|
||||||
_maximizeRestore->raise();
|
|
||||||
_close->raise();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::updateControlsPosition() {
|
|
||||||
auto right = 0;
|
|
||||||
_close->moveToRight(right, 0); right += _close->width();
|
|
||||||
_maximizeRestore->moveToRight(right, 0);
|
|
||||||
if (_resizeEnabled) {
|
|
||||||
right += _maximizeRestore->width();
|
|
||||||
}
|
|
||||||
_minimize->moveToRight(right, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::updateControlsVisibility() {
|
|
||||||
_maximizeRestore->setVisible(_resizeEnabled);
|
|
||||||
updateControlsPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::handleWindowStateChanged(Qt::WindowState state) {
|
|
||||||
if (state == Qt::WindowMinimized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto maximized = (state == Qt::WindowMaximized)
|
|
||||||
|| (state == Qt::WindowFullScreen);
|
|
||||||
if (_maximizedState != maximized) {
|
|
||||||
_maximizedState = maximized;
|
|
||||||
updateButtonsState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleControls::updateButtonsState() {
|
|
||||||
const auto minimize = _activeState
|
|
||||||
? &_st->minimizeIconActive
|
|
||||||
: &_st->minimize.icon;
|
|
||||||
const auto minimizeOver = _activeState
|
|
||||||
? &_st->minimizeIconActiveOver
|
|
||||||
: &_st->minimize.iconOver;
|
|
||||||
_minimize->setIconOverride(minimize, minimizeOver);
|
|
||||||
if (_maximizedState) {
|
|
||||||
const auto restore = _activeState
|
|
||||||
? &_st->restoreIconActive
|
|
||||||
: &_st->restoreIcon;
|
|
||||||
const auto restoreOver = _activeState
|
|
||||||
? &_st->restoreIconActiveOver
|
|
||||||
: &_st->restoreIconOver;
|
|
||||||
_maximizeRestore->setIconOverride(restore, restoreOver);
|
|
||||||
} else {
|
|
||||||
const auto maximize = _activeState
|
|
||||||
? &_st->maximizeIconActive
|
|
||||||
: &_st->maximize.icon;
|
|
||||||
const auto maximizeOver = _activeState
|
|
||||||
? &_st->maximizeIconActiveOver
|
|
||||||
: &_st->maximize.iconOver;
|
|
||||||
_maximizeRestore->setIconOverride(maximize, maximizeOver);
|
|
||||||
}
|
|
||||||
const auto close = _activeState
|
|
||||||
? &_st->closeIconActive
|
|
||||||
: &_st->close.icon;
|
|
||||||
const auto closeOver = _activeState
|
|
||||||
? &_st->closeIconActiveOver
|
|
||||||
: &_st->close.iconOver;
|
|
||||||
_close->setIconOverride(close, closeOver);
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleWidget::TitleWidget(not_null<RpWidget*> parent)
|
TitleWidget::TitleWidget(not_null<RpWidget*> parent)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _controls(this, st::defaultWindowTitle)
|
, _controls(this, st::defaultWindowTitle)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
|
|
@ -38,41 +39,6 @@ enum class HitTestResult {
|
||||||
TopLeft,
|
TopLeft,
|
||||||
};
|
};
|
||||||
|
|
||||||
class TitleControls final {
|
|
||||||
public:
|
|
||||||
TitleControls(
|
|
||||||
not_null<RpWidget*> parent,
|
|
||||||
const style::WindowTitle &st,
|
|
||||||
Fn<void(bool maximized)> maximize = nullptr);
|
|
||||||
|
|
||||||
void setStyle(const style::WindowTitle &st);
|
|
||||||
[[nodiscard]] not_null<const style::WindowTitle*> st() const;
|
|
||||||
[[nodiscard]] QRect geometry() const;
|
|
||||||
void setResizeEnabled(bool enabled);
|
|
||||||
void raise();
|
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]] not_null<RpWidget*> parent() const;
|
|
||||||
[[nodiscard]] not_null<QWidget*> window() const;
|
|
||||||
|
|
||||||
void init(Fn<void(bool maximized)> maximize);
|
|
||||||
void updateControlsVisibility();
|
|
||||||
void updateButtonsState();
|
|
||||||
void updateControlsPosition();
|
|
||||||
void handleWindowStateChanged(Qt::WindowState state = Qt::WindowNoState);
|
|
||||||
|
|
||||||
not_null<const style::WindowTitle*> _st;
|
|
||||||
|
|
||||||
object_ptr<Ui::IconButton> _minimize;
|
|
||||||
object_ptr<Ui::IconButton> _maximizeRestore;
|
|
||||||
object_ptr<Ui::IconButton> _close;
|
|
||||||
|
|
||||||
bool _maximizedState = false;
|
|
||||||
bool _activeState = false;
|
|
||||||
bool _resizeEnabled = true;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class TitleWidget : public RpWidget {
|
class TitleWidget : public RpWidget {
|
||||||
public:
|
public:
|
||||||
explicit TitleWidget(not_null<RpWidget*> parent);
|
explicit TitleWidget(not_null<RpWidget*> parent);
|
||||||
|
|
|
||||||
|
|
@ -1582,3 +1582,17 @@ defaultWindowTitle: WindowTitle {
|
||||||
|
|
||||||
windowShadow: icon {{ "window_shadow", windowShadowFg }};
|
windowShadow: icon {{ "window_shadow", windowShadowFg }};
|
||||||
windowShadowShift: 1px;
|
windowShadowShift: 1px;
|
||||||
|
|
||||||
|
callRadius: 6px;
|
||||||
|
callShadow: Shadow {
|
||||||
|
left: icon {{ "calls/call_shadow_left", windowShadowFg }};
|
||||||
|
topLeft: icon {{ "calls/call_shadow_top_left", windowShadowFg }};
|
||||||
|
top: icon {{ "calls/call_shadow_top", windowShadowFg }};
|
||||||
|
topRight: icon {{ "calls/call_shadow_top_left-flip_horizontal", windowShadowFg }};
|
||||||
|
right: icon {{ "calls/call_shadow_left-flip_horizontal", windowShadowFg }};
|
||||||
|
bottomRight: icon {{ "calls/call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }};
|
||||||
|
bottom: icon {{ "calls/call_shadow_top-flip_vertical", windowShadowFg }};
|
||||||
|
bottomLeft: icon {{ "calls/call_shadow_top_left-flip_vertical", windowShadowFg }};
|
||||||
|
extend: margins(9px, 8px, 9px, 10px);
|
||||||
|
fallback: windowShadowFgFallback;
|
||||||
|
}
|
||||||
|
|
|
||||||