Finished implementing maximize for linux.

Added FloatingContainerForc*TitleBar to switch between native and custom titlebar.

Co-authored-by: SleepProgger <SleepProgger@users.noreply.github.com>
This commit is contained in:
helywin 2019-09-12 10:44:12 +08:00 committed by Some Guy
parent 48fb999bd0
commit 533d174abc
10 changed files with 533 additions and 37 deletions

View File

@ -83,6 +83,9 @@ static void updateDockAreaFocusStyle(CDockAreaWidget* DockArea, bool Focused)
#ifdef Q_OS_LINUX
static void updateFloatingWidgetFocusStyle(CFloatingDockContainer* FloatingWidget, bool Focused)
{
if(FloatingWidget->hasNativeTitleBar()){
return;
}
auto TitleBar = qobject_cast<CFloatingWidgetTitleBar*>(FloatingWidget->titleBarWidget());
if (!TitleBar)
{

View File

@ -368,8 +368,6 @@ void DockManagerPrivate::restoreDockAreasIndices()
}
}
//============================================================================
void DockManagerPrivate::emitTopLevelEvents()
{
@ -476,6 +474,10 @@ CDockManager::CDockManager(QWidget *parent) :
{
d->FocusController = new CDockFocusController(this);
}
#ifdef Q_OS_LINUX
window()->installEventFilter(this);
#endif
}
//============================================================================
@ -489,6 +491,53 @@ CDockManager::~CDockManager()
delete d;
}
//============================================================================
#ifdef Q_OS_LINUX
bool CDockManager::eventFilter(QObject *obj, QEvent *e){
// Emulate Qt:Tool behaviour.
// Required because on some WMs Tool windows can't be maximized.
// Window always on top of the MainWindow.
if(e->type() == QEvent::WindowActivate){
for(auto _window : floatingWidgets()){
if(!_window->isVisible() || window()->isMinimized()){
continue;
}
// setWindowFlags(Qt::WindowStaysOnTopHint) will hide the window and thus requires a show call.
// This then leads to flickering and a nasty endless loop (also buggy behaviour on Ubuntu).
// So we just do it ourself.
internal::xcb_update_prop(true, _window->window()->winId(), "_NET_WM_STATE", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STAYS_ON_TOP");
}
}
else if(e->type() == QEvent::WindowDeactivate){
for(auto _window : floatingWidgets()){
if(!_window->isVisible() || window()->isMinimized()){
continue;
}
internal::xcb_update_prop(false, _window->window()->winId(), "_NET_WM_STATE", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STAYS_ON_TOP");
_window->raise();
}
}
// Sync minimize with MainWindow
if(e->type() == QEvent::WindowStateChange){
for(auto _window : floatingWidgets()){
if(! _window->isVisible()){
continue;
}
if(window()->isMinimized()){
_window->showMinimized();
} else {
_window->setWindowState(_window->windowState() & (~Qt::WindowMinimized));
}
}
if(!window()->isMinimized()){
QApplication::setActiveWindow(window());
}
}
return Super::eventFilter(obj, e);
}
#endif
//============================================================================
void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget)

View File

@ -178,9 +178,17 @@ public:
FloatingContainerHasWidgetIcon = 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon
HideSingleCentralWidgetTitleBar = 0x100000, //!< If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden
//!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget
FocusHighlighting = 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar
EqualSplitOnInsertion = 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter
FloatingContainerForceNativeTitleBar = 0x800000, //!< Linux only ! Forces all FloatingContainer to use the native title bar. This might break docking for FloatinContainer on some Window Managers (like Kwin/KDE).
//!< If neither this nor FloatingContainerForceCustomTitleBar is set (the default) native titlebars are used except on known bad systems.
//! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0".
FloatingContainerForceCustomTitleBar = 0x1000000,//!< Linux only ! Forces all FloatingContainer to use a custom title bar.
//!< If neither this nor FloatingContainerForceNativeTitleBar is set (the default) native titlebars are used except on known bad systems.
//! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0".
DefaultDockAreaButtons = DockAreaHasCloseButton
| DockAreaHasUndockButton
| DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons
@ -464,6 +472,10 @@ public:
widget->setFocus(Qt::OtherFocusReason);
}
#ifdef Q_OS_LINUX
bool eventFilter(QObject *obj, QEvent *e) override;
#endif
public slots:
/**
* Opens the perspective with the given name.

View File

@ -376,6 +376,7 @@ struct FloatingDockContainerPrivate
#ifdef Q_OS_LINUX
QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr;
bool IsResizing = false;
#endif
/**
@ -410,10 +411,11 @@ struct FloatingDockContainerPrivate
void setWindowTitle(const QString &Text)
{
#ifdef Q_OS_LINUX
TitleBar->setTitle(Text);
#else
_this->setWindowTitle(Text);
if(TitleBar){
TitleBar->setTitle(Text);
}
#endif
_this->setWindowTitle(Text);
}
/**
@ -604,13 +606,36 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) :
SLOT(onDockAreasAddedOrRemoved()));
#ifdef Q_OS_LINUX
d->TitleBar = new CFloatingWidgetTitleBar(this);
setWindowFlags(windowFlags() | Qt::Tool);
QDockWidget::setWidget(d->DockContainer);
QDockWidget::setFloating(true);
QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures);
setTitleBarWidget(d->TitleBar);
connect(d->TitleBar, SIGNAL(closeRequested()), SLOT(close()));
QDockWidget::setWidget(d->DockContainer);
QDockWidget::setFloating(true);
QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures);
// KDE doesn't seem to fire MoveEvents while moving windows, so for now no native titlebar for everything using KWin.
QString window_manager = internal::windowManager().toUpper().split(" ")[0];
bool native_window = window_manager != "KWIN";
// FloatingContainerForce*TitleBar is overwritten by the "ADS_UseNativeTitle" environment variable if set.
auto env = qgetenv("ADS_UseNativeTitle").toUpper();
if (env == "1"){
native_window = true;
} else if (env == "0"){
native_window = false;
} else if ( DockManager->testConfigFlag( CDockManager::FloatingContainerForceNativeTitleBar )){
native_window = true;
} else if ( DockManager->testConfigFlag( CDockManager::FloatingContainerForceCustomTitleBar )){
native_window = false;
}
if(native_window){
setTitleBarWidget(new QWidget());
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
} else {
d->TitleBar = new CFloatingWidgetTitleBar(this);
setTitleBarWidget(d->TitleBar);
setWindowFlags(Qt::Window | Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint);
d->TitleBar->enableCloseButton(isClosable());
connect(d->TitleBar, SIGNAL(closeRequested()), SLOT(close()));
connect(d->TitleBar, &CFloatingWidgetTitleBar::maximizeRequested,
this, &CFloatingDockContainer::onMaximizeRequest);
}
#else
setWindowFlags(
Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
@ -629,9 +654,7 @@ CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget *DockArea) :
CFloatingDockContainer(DockArea->dockManager())
{
d->DockContainer->addDockArea(DockArea);
#ifdef Q_OS_LINUX
d->TitleBar->enableCloseButton(isClosable());
#endif
auto TopLevelDockWidget = topLevelDockWidget();
if (TopLevelDockWidget)
{
@ -646,9 +669,6 @@ CFloatingDockContainer::CFloatingDockContainer(CDockWidget *DockWidget) :
CFloatingDockContainer(DockWidget->dockManager())
{
d->DockContainer->addDockWidget(CenterDockWidgetArea, DockWidget);
#ifdef Q_OS_LINUX
d->TitleBar->enableCloseButton(isClosable());
#endif
auto TopLevelDockWidget = topLevelDockWidget();
if (TopLevelDockWidget)
{
@ -678,12 +698,18 @@ CDockContainerWidget* CFloatingDockContainer::dockContainer() const
//============================================================================
void CFloatingDockContainer::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
Super::changeEvent(event);
if ((event->type() == QEvent::ActivationChange) && isActiveWindow())
{
ADS_PRINT("FloatingWidget::changeEvent QEvent::ActivationChange ");
d->zOrderIndex = ++zOrderCounter;
return;
#ifdef Q_OS_LINUX
if(d->DraggingState == DraggingFloatingWidget){
d->titleMouseReleaseEvent();
d->DraggingState = DraggingInactive;
}
#endif
}
}
@ -825,13 +851,19 @@ void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos,
#ifndef Q_OS_LINUX
Q_UNUSED(MouseEventHandler)
#endif
#ifdef Q_OS_LINUX
if (!isMaximized()) {
resize(Size);
d->DragStartMousePosition = DragStartMousePos;
}
#else
resize(Size);
d->setState(DragState);
d->DragStartMousePosition = DragStartMousePos;
#endif
d->setState(DragState);
#ifdef Q_OS_LINUX
if (DraggingFloatingWidget == DragState)
{
setAttribute(Qt::WA_X11NetWmWindowTypeDock, true);
d->MouseEventHandler = MouseEventHandler;
if (d->MouseEventHandler)
{
@ -839,7 +871,13 @@ void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos,
}
}
#endif
#ifdef Q_OS_LINUX
if (!isMaximized()) {
moveFloating();
}
#else
moveFloating();
#endif
show();
}
@ -850,7 +888,6 @@ void CFloatingDockContainer::moveFloating()
const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition
- QPoint(BorderSize, 0);
move(moveToPos);
switch (d->DraggingState)
{
case DraggingMousePressed:
@ -949,11 +986,16 @@ bool CFloatingDockContainer::restoreState(CDockingStateReader &Stream,
{
return false;
}
onDockAreasAddedOrRemoved();
#ifdef Q_OS_LINUX
if(d->TitleBar){
d->TitleBar->setMaximizedIcon(windowState() == Qt::WindowMaximized);
}
#endif
return true;
}
//============================================================================
bool CFloatingDockContainer::hasTopLevelDockWidget() const
{
@ -977,19 +1019,17 @@ void CFloatingDockContainer::finishDragging()
{
ADS_PRINT("CFloatingDockContainer::finishDragging");
#ifdef Q_OS_LINUX
setAttribute(Qt::WA_X11NetWmWindowTypeDock, false);
setWindowOpacity(1);
activateWindow();
if (d->MouseEventHandler)
{
d->MouseEventHandler->releaseMouse();
d->MouseEventHandler = nullptr;
}
setWindowOpacity(1);
activateWindow();
if (d->MouseEventHandler)
{
d->MouseEventHandler->releaseMouse();
d->MouseEventHandler = nullptr;
}
#endif
d->titleMouseReleaseEvent();
d->titleMouseReleaseEvent();
}
#ifdef Q_OS_MACOS
//============================================================================
bool CFloatingDockContainer::event(QEvent *e)
@ -1092,6 +1132,71 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event)
}
#endif
#ifdef Q_OS_LINUX
void CFloatingDockContainer::onMaximizeRequest()
{
if(windowState() == Qt::WindowMaximized){
showNormal();
}else{
showMaximized();
}
}
void CFloatingDockContainer::showNormal(bool fixGeometry)
{
if (windowState() == Qt::WindowMaximized)
{
QRect oldNormal = normalGeometry();
Super::showNormal();
if(fixGeometry)
{
setGeometry(oldNormal);
}
}
if(d->TitleBar){
d->TitleBar->setMaximizedIcon(false);
}
}
void CFloatingDockContainer::showMaximized()
{
Super::showMaximized();
if(d->TitleBar){
d->TitleBar->setMaximizedIcon(true);
}
}
bool CFloatingDockContainer::isMaximized() const
{
return windowState() == Qt::WindowMaximized;
}
void CFloatingDockContainer::show(){
// Prevent this window from showing in the taskbar and pager (alt+tab)
internal::xcb_add_prop(true, winId(), "_NET_WM_STATE", "_NET_WM_STATE_SKIP_TASKBAR");
internal::xcb_add_prop(true, winId(), "_NET_WM_STATE", "_NET_WM_STATE_SKIP_PAGER");
Super::show();
}
void CFloatingDockContainer::resizeEvent(QResizeEvent *event){
d->IsResizing = true;
Super::resizeEvent(event);
}
void CFloatingDockContainer::moveEvent(QMoveEvent *event){
Super::moveEvent(event);
if(!d->IsResizing && event->spontaneous()){
d->DraggingState = DraggingFloatingWidget;
d->updateDropOverlays(QCursor::pos());
}
d->IsResizing = false;
}
bool CFloatingDockContainer::hasNativeTitleBar(){
return d->TitleBar == nullptr;
}
#endif
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -185,6 +185,11 @@ protected: // reimplements QWidget
virtual void moveEvent(QMoveEvent *event) override;
#endif
#ifdef Q_OS_LINUX
virtual void moveEvent(QMoveEvent *event) override;
virtual void resizeEvent(QResizeEvent *event) override;
#endif
#ifdef Q_OS_WIN
/**
* Native event filter for handling WM_MOVING messages on Windows
@ -194,7 +199,7 @@ protected: // reimplements QWidget
public:
using Super = QWidget;
using Super = tFloatingWidgetBase;
/**
* Create empty floating widget - required for restore state
@ -248,6 +253,40 @@ public:
* function of the internal container widget.
*/
QList<CDockWidget*> dockWidgets() const;
#ifdef Q_OS_LINUX
/**
* This is a function that responds to FloatingWidgetTitleBar::maximizeRequest()
* Maximize or normalize the container size.
*/
void onMaximizeRequest();
/**
* Normalize (Unmaximize) the window.
* fixGeometry parameter fixes a "bug" in QT where immediately after calling showNormal
* geometry is not set properly.
* Set this true when moving the window immediately after normalizing.
*/
void showNormal(bool fixGeometry=false);
/**
* Maximizes the window.
*/
void showMaximized();
/**
* Returns if the window is currently maximized or not.
*/
bool isMaximized() const;
/**
* Patched show to prevent the window from appearing in the taskbar.
*/
void show();
bool hasNativeTitleBar();
#endif
}; // class FloatingDockContainer
}
// namespace ads

View File

@ -38,12 +38,205 @@
#include "IconProvider.h"
#include "ads_globals.h"
#ifdef Q_OS_LINUX
#include <QX11Info>
#include <QSettings>
#include <QFile>
#endif
#include <QApplication>
namespace ads
{
namespace internal
{
#ifdef Q_OS_LINUX
static QString _window_manager;
static QHash<QString, xcb_atom_t> _xcb_atom_cache;
xcb_atom_t xcb_get_atom(const char *name){
if (!QX11Info::isPlatformX11()){
return XCB_ATOM_NONE;
}
auto key = QString(name);
if(_xcb_atom_cache.contains(key)){
return _xcb_atom_cache[key];
}
xcb_connection_t *connection = QX11Info::connection();
xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
if(!reply){
return XCB_ATOM_NONE;
}
xcb_atom_t atom = reply->atom;
if(atom == XCB_ATOM_NONE){
ADS_PRINT("Unknown Atom response from XServer: " << name);
} else {
_xcb_atom_cache.insert(key, atom);
}
free(reply);
return atom;
}
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
{
auto connection = QX11Info::connection();
xcb_atom_t type_atom = xcb_get_atom(type);
xcb_atom_t prop_atom = xcb_get_atom(prop);
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.sequence = 0;
event.window = window;
event.type = type_atom;
event.data.data32[0] = set ? 1 : 0;
event.data.data32[1] = prop_atom;
event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
event.data.data32[3] = 0;
event.data.data32[4] = 0;
xcb_send_event(connection, 0, window,
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE,
(const char *)&event);
xcb_flush(connection);
}
xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type){
if (!QX11Info::isPlatformX11()){
return nullptr;
}
xcb_connection_t *connection = QX11Info::connection();
xcb_atom_t type_atom = xcb_get_atom(type);
if (type_atom == XCB_ATOM_NONE){
return nullptr;
}
xcb_get_property_cookie_t request = xcb_get_property_unchecked(connection, 0, window, type_atom, atom_type, 0, 1024);
xcb_get_property_reply_t *reply = xcb_get_property_reply(connection, request, nullptr);
if(reply && reply->type != atom_type){
ADS_PRINT("ATOM TYPE MISMATCH (" << type <<"). Expected: " << atom_type << " but got " << reply->type);
free(reply);
return nullptr;
}
return reply;
}
template <typename T>
void xcb_get_prop_list(WId window, const char *type, QVector<T> &ret, unsigned int atom_type){
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, atom_type);
if (reply && reply->format == 32 && reply->type == atom_type && reply->value_len > 0) {
const xcb_atom_t *data = static_cast<const T *>(xcb_get_property_value(reply));
ret.resize(reply->value_len);
memcpy((void *)&ret.first(), (void *)data, reply->value_len * sizeof(T));
}
free(reply);
}
QString xcb_get_prop_string(WId window, const char *type){
QString ret;
// try utf8 first
xcb_atom_t utf_atom = xcb_get_atom("UTF8_STRING");
if(utf_atom != XCB_ATOM_NONE){
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, utf_atom);
if (reply && reply->format == 8 && reply->type == utf_atom) {
const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
ret = QString::fromUtf8(value, xcb_get_property_value_length(reply));
free(reply);
return ret;
}
free(reply);
}
// Fall back to XCB_ATOM_STRING
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, XCB_ATOM_STRING);
if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
ret = QString::fromLatin1(value, xcb_get_property_value_length(reply));
}
free(reply);
return ret;
}
bool xcb_dump_props(WId window, const char *type){
QVector<xcb_atom_t> atoms;
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
qDebug() << "\n\n!!!" << type << " - " << atoms.length();
xcb_connection_t *connection = QX11Info::connection();
for(auto atom : atoms){
auto foo = xcb_get_atom_name(connection, atom);
auto bar = xcb_get_atom_name_reply(connection, foo, nullptr);
qDebug() << "\t" << xcb_get_atom_name_name(bar);
free(bar);
}
return true;
}
void xcb_add_prop(bool state, WId window, const char *type, const char *prop){
if (!QX11Info::isPlatformX11()){
return;
}
xcb_atom_t prop_atom = xcb_get_atom(prop);
xcb_atom_t type_atom = xcb_get_atom(type);
if(prop_atom == XCB_ATOM_NONE || type_atom == XCB_ATOM_NONE){
return;
}
QVector<xcb_atom_t> atoms;
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
int index = atoms.indexOf(prop_atom);
if(state && index == -1){
atoms.push_back(prop_atom);
} else if(!state && index >= 0){
atoms.remove(index);
}
xcb_connection_t *connection = QX11Info::connection();
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData());
xcb_flush(connection);
}
QString detectWindowManagerX11(){
// Tries to detect the windowmanager via X11.
// See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
if (!QX11Info::isPlatformX11()){
return "UNKNOWN";
}
xcb_connection_t *connection = QX11Info::connection();
xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
if(!first_screen){
ADS_PRINT("No screen found via XCB.");
return "UNKNOWN";
}
// Get supporting window ()
xcb_window_t root = first_screen->root;
xcb_window_t support_win = 0;
QVector<xcb_window_t> sup_windows;
xcb_get_prop_list(root, "_NET_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_WINDOW);
if(sup_windows.length() == 0){
// This doesn't seem to be in use anymore, but wmctrl does the same so lets play safe.
// Both XCB_ATOM_CARDINAL and XCB_ATOM_WINDOW break down to a uint32_t, so reusing sup_windows should be fine.
xcb_get_prop_list(root, "_WIN_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_CARDINAL);
}
if(sup_windows.length() == 0){
ADS_PRINT("Failed to get the supporting window on non EWMH comform WM.");
return "UNKNOWN";
}
support_win = sup_windows[0];
QString ret = xcb_get_prop_string(support_win, "_NET_WM_NAME");
if(ret.length() == 0){
ADS_PRINT("Empty WM name occured.");
return "UNKNOWN";
}
return ret;
}
QString windowManager(){
if(_window_manager.length() == 0){
_window_manager = detectWindowManagerX11();
}
return _window_manager;
}
#endif
//============================================================================
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To)
{

View File

@ -37,6 +37,10 @@
#include <QDebug>
#include <QStyle>
#ifdef Q_OS_LINUX
#include <xcb/xcb.h>
#endif
QT_FORWARD_DECLARE_CLASS(QAbstractButton)
#ifndef ADS_STATIC
@ -122,6 +126,7 @@ enum eBitwiseOperator
BitwiseOr
};
namespace internal
{
static const bool RestoreTesting = true;
@ -129,6 +134,33 @@ static const bool Restore = false;
static const char* const ClosedProperty = "close";
static const char* const DirtyProperty = "dirty";
#ifdef Q_OS_LINUX
// Utils to directly communicate with the X server
/**
* Get atom from cache or request it from the XServer.
*/
xcb_atom_t xcb_get_atom(const char *name);
/**
* Add a property to a window. Only works on "hidden" windows.
*/
void xcb_add_prop(bool state, WId window, const char *type, const char *prop);
/**
* Updates up to two window properties. Can be set on a visible window.
*/
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2 = nullptr);
/**
* Only for debugging purposes.
*/
bool xcb_dump_props(WId window, const char *type);
/**
* Gets the active window manager from the X11 Server.
* Requires a EWMH conform window manager (Allmost all common used ones are).
* Returns "UNKNOWN" otherwise.
*/
QString windowManager();
#endif
/**
* Replace the from widget in the given splitter with the To widget
*/

View File

@ -46,6 +46,7 @@ namespace ads
using tTabLabel = CElidingLabel;
using tCloseButton = QToolButton;
using tMaximizeButton = QToolButton;
/**
* @brief Private data class of public interface CFloatingWidgetTitleBar
@ -56,6 +57,7 @@ struct FloatingWidgetTitleBarPrivate
QLabel *IconLabel = nullptr;
tTabLabel *TitleLabel;
tCloseButton *CloseButton = nullptr;
tMaximizeButton* MaximizeButton = nullptr;
CFloatingDockContainer *FloatingWidget = nullptr;
eDragState DragState = DraggingInactive;
@ -83,6 +85,10 @@ void FloatingWidgetTitleBarPrivate::createLayout()
CloseButton->setObjectName("floatingTitleCloseButton");
CloseButton->setAutoRaise(true);
MaximizeButton = new tMaximizeButton();
MaximizeButton->setObjectName("floatingTitleMaximizeButton");
MaximizeButton->setAutoRaise(true);
// The standard icons do does not look good on high DPI screens
QIcon CloseIcon;
QPixmap normalPixmap = _this->style()->standardPixmap(
@ -97,6 +103,12 @@ void FloatingWidgetTitleBarPrivate::createLayout()
CloseButton->setFocusPolicy(Qt::NoFocus);
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
_this->setMaximizedIcon(false);
MaximizeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
MaximizeButton->setVisible(true);
MaximizeButton->setFocusPolicy(Qt::NoFocus);
_this->connect(MaximizeButton, &QPushButton::clicked, _this, &CFloatingWidgetTitleBar::maximizeRequested);
QFontMetrics fm(TitleLabel->font());
int Spacing = qRound(fm.height() / 4.0);
@ -107,6 +119,7 @@ void FloatingWidgetTitleBarPrivate::createLayout()
_this->setLayout(Layout);
Layout->addWidget(TitleLabel, 1);
Layout->addSpacing(Spacing);
Layout->addWidget(MaximizeButton);
Layout->addWidget(CloseButton);
Layout->setAlignment(Qt::AlignCenter);
@ -147,7 +160,7 @@ void CFloatingWidgetTitleBar::mouseReleaseEvent(QMouseEvent *ev)
d->DragState = DraggingInactive;
if (d->FloatingWidget)
{
d->FloatingWidget->finishDragging();
d->FloatingWidget->finishDragging();
}
Super::mouseReleaseEvent(ev);
}
@ -165,6 +178,12 @@ void CFloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *ev)
// move floating window
if (DraggingFloatingWidget == d->DragState)
{
#ifdef Q_OS_LINUX
if(d->FloatingWidget->isMaximized())
{
d->FloatingWidget->showNormal(true);
}
#endif
d->FloatingWidget->moveFloating();
Super::mouseMoveEvent(ev);
return;
@ -186,11 +205,43 @@ void CFloatingWidgetTitleBar::setTitle(const QString &Text)
d->TitleLabel->setText(Text);
}
//============================================================================
void CFloatingWidgetTitleBar::updateStyle()
{
internal::repolishStyle(this, internal::RepolishDirectChildren);
}
void CFloatingWidgetTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
emit maximizeRequested();
event->accept();
}
else
{
QWidget::mouseDoubleClickEvent(event);
}
}
void CFloatingWidgetTitleBar::setMaximizedIcon(bool maximized)
{
if (maximized)
{
QIcon normalIcon;
auto normalPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarNormalButton, 0, d->MaximizeButton);
normalIcon.addPixmap(normalPixmap, QIcon::Normal);
normalIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
d->MaximizeButton->setIcon(normalIcon);
}
else
{
QIcon MaxIcon;
auto maxPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMaxButton, 0, d->MaximizeButton);
MaxIcon.addPixmap(maxPixmap, QIcon::Normal);
MaxIcon.addPixmap(internal::createTransparentPixmap(maxPixmap, 0.25), QIcon::Disabled);
d->MaximizeButton->setIcon(MaxIcon);
}
}
} // namespace ads

View File

@ -55,6 +55,7 @@ protected:
virtual void mousePressEvent(QMouseEvent *ev) override;
virtual void mouseReleaseEvent(QMouseEvent *ev) override;
virtual void mouseMoveEvent(QMouseEvent *ev) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
public:
using Super = QWidget;
@ -80,11 +81,21 @@ public:
*/
void updateStyle();
/**
* Change the maximize button icon according to current windows state
*/
void setMaximizedIcon(bool maximized);
signals:
/**
* This signal is emitted, if the close button is clicked.
*/
void closeRequested();
/**
* This signal is emitted, if the maximize button is clicked.
*/
void maximizeRequested();
};
} // namespace ads
#endif // FLOATINGWIDGETTITLEBAR_H

View File

@ -72,6 +72,7 @@ SOURCES += \
unix {
HEADERS += linux/FloatingWidgetTitleBar.h
SOURCES += linux/FloatingWidgetTitleBar.cpp
QT += x11extras
}
isEmpty(PREFIX){