26#include "moc_qgsdockablewidgethelper.cpp"
28using namespace Qt::StringLiterals;
38std::function<void( Qt::DockWidgetArea, QDockWidget *,
const QStringList &,
bool )> QgsDockableWidgetHelper::sAddTabifiedDockWidgetFunction =
39 []( Qt::DockWidgetArea, QDockWidget *,
const QStringList &, bool ) {};
40std::function<QString()> QgsDockableWidgetHelper::sAppStylesheetFunction = [] {
return QString(); };
41QMainWindow *QgsDockableWidgetHelper::sOwnerWindow =
nullptr;
43QgsDockableWidgetHelper::QgsDockableWidgetHelper(
44 const QString &windowTitle,
46 QMainWindow *ownerWindow,
47 const QString &dockId,
48 const QStringList &tabifyWith,
49 OpeningMode openingMode,
51 Qt::DockWidgetArea defaultDockArea,
56 , mDialogGeometry( 0, 0, 0, 0 )
57 , mWindowTitle( windowTitle )
58 , mOwnerWindow( ownerWindow )
59 , mTabifyWith( tabifyWith )
61 , mUuid( QUuid::createUuid().toString() )
62 , mSettingKeyDockId( dockId )
64 bool isDocked = sSettingsIsDocked->valueWithDefaultOverride( defaultIsDocked, mSettingKeyDockId );
65 if ( openingMode == OpeningMode::ForceDocked )
67 else if ( openingMode == OpeningMode::ForceDialog )
70 mDockArea = sSettingsDockArea->valueWithDefaultOverride( defaultDockArea, mSettingKeyDockId );
71 mIsDockFloating = mDockArea == Qt::DockWidgetArea::NoDockWidgetArea;
72 toggleDockMode( isDocked );
75QgsDockableWidgetHelper::~QgsDockableWidgetHelper()
79 if ( !mSettingKeyDockId.isEmpty() )
80 sSettingsDockGeometry->setValue( mDock->saveGeometry(), mSettingKeyDockId );
83 mOwnerWindow->removeDockWidget( mDock );
85 mDock->setWidget(
nullptr );
86 mWidget->setParent(
nullptr );
95 mDialogGeometry = mDialog->geometry();
97 if ( !mSettingKeyDockId.isEmpty() )
98 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
100 mDialog->layout()->removeWidget( mWidget );
101 mDialog->deleteLater();
106void QgsDockableWidgetHelper::writeXml( QDomElement &viewDom )
108 viewDom.setAttribute( u
"isDocked"_s, mIsDocked );
112 mDockGeometry = mDock->geometry();
113 mIsDockFloating = mDock->isFloating();
115 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
118 viewDom.setAttribute( u
"x"_s, mDockGeometry.x() );
119 viewDom.setAttribute( u
"y"_s, mDockGeometry.y() );
120 viewDom.setAttribute( u
"width"_s, mDockGeometry.width() );
121 viewDom.setAttribute( u
"height"_s, mDockGeometry.height() );
122 viewDom.setAttribute( u
"floating"_s, mIsDockFloating );
123 viewDom.setAttribute( u
"area"_s, mDockArea );
124 viewDom.setAttribute( u
"uuid"_s, mUuid );
128 const QList<QDockWidget *> tabSiblings = mOwnerWindow ? mOwnerWindow->tabifiedDockWidgets( mDock ) : QList<QDockWidget *>();
129 QDomElement tabSiblingsElement = viewDom.ownerDocument().createElement( u
"tab_siblings"_s );
130 for ( QDockWidget *dock : tabSiblings )
132 QDomElement siblingElement = viewDom.ownerDocument().createElement( u
"sibling"_s );
133 siblingElement.setAttribute( u
"uuid"_s, dock->property(
"dock_uuid" ).toString() );
134 siblingElement.setAttribute( u
"object_name"_s, dock->objectName() );
135 tabSiblingsElement.appendChild( siblingElement );
137 viewDom.appendChild( tabSiblingsElement );
141 mDialogGeometry = mDialog->geometry();
143 viewDom.setAttribute( u
"d_x"_s, mDialogGeometry.x() );
144 viewDom.setAttribute( u
"d_y"_s, mDialogGeometry.y() );
145 viewDom.setAttribute( u
"d_width"_s, mDialogGeometry.width() );
146 viewDom.setAttribute( u
"d_height"_s, mDialogGeometry.height() );
149void QgsDockableWidgetHelper::readXml(
const QDomElement &viewDom )
151 mUuid = viewDom.attribute( u
"uuid"_s, mUuid );
154 int x = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
155 int y = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
156 int w = viewDom.attribute( u
"d_width"_s, u
"200"_s ).toInt();
157 int h = viewDom.attribute( u
"d_height"_s, u
"200"_s ).toInt();
158 mDialogGeometry = QRect( x, y, w, h );
160 mDialog->setGeometry( mDialogGeometry );
164 int x = viewDom.attribute( u
"x"_s, u
"0"_s ).toInt();
165 int y = viewDom.attribute( u
"y"_s, u
"0"_s ).toInt();
166 int w = viewDom.attribute( u
"width"_s, u
"200"_s ).toInt();
167 int h = viewDom.attribute( u
"height"_s, u
"200"_s ).toInt();
168 mDockGeometry = QRect( x, y, w, h );
169 mIsDockFloating = viewDom.attribute( u
"floating"_s, u
"0"_s ).toInt();
170 mDockArea =
static_cast<Qt::DockWidgetArea
>( viewDom.attribute( u
"area"_s, QString::number( Qt::RightDockWidgetArea ) ).toInt() );
172 if ( mDockArea == Qt::DockWidgetArea::NoDockWidgetArea && !mIsDockFloating )
174 mDockArea = Qt::RightDockWidgetArea;
177 QStringList tabSiblings;
178 const QDomElement tabSiblingsElement = viewDom.firstChildElement( u
"tab_siblings"_s );
179 const QDomNodeList tabSiblingNodes = tabSiblingsElement.childNodes();
180 for (
int i = 0; i < tabSiblingNodes.size(); ++i )
182 const QDomElement tabSiblingElement = tabSiblingNodes.at( i ).toElement();
184 QString tabId = tabSiblingElement.attribute( u
"uuid"_s );
185 if ( tabId.isEmpty() )
186 tabId = tabSiblingElement.attribute( u
"object_name"_s );
187 if ( !tabId.isEmpty() )
188 tabSiblings.append( tabId );
191 setupDockWidget( tabSiblings );
196 mDock->setProperty(
"dock_uuid", mUuid );
200void QgsDockableWidgetHelper::setWidget( QWidget *widget )
203 if ( mWidget && mOwnerWindow )
205 mWidget->setParent( mOwnerWindow );
209 mDialog->layout()->removeWidget( mWidget );
213 mDock->setWidget(
nullptr );
217 toggleDockMode( mIsDocked );
225QDialog *QgsDockableWidgetHelper::dialog()
227 return mDialog.data();
230void QgsDockableWidgetHelper::toggleDockMode(
bool docked )
233 if ( mWidget && mOwnerWindow )
235 mWidget->setParent( mOwnerWindow );
241 mDockGeometry = mDock->geometry();
242 mIsDockFloating = mDock->isFloating();
244 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
246 mDock->setWidget(
nullptr );
248 mOwnerWindow->removeDockWidget( mDock );
256 if ( !mSettingKeyDockId.isEmpty() )
257 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
259 mDialogGeometry = mDialog->geometry();
262 mDialog->layout()->removeWidget( mWidget );
269 if ( !mSettingKeyDockId.isEmpty() )
270 sSettingsIsDocked->setValue( mIsDocked, mSettingKeyDockId );
280 mDock->setWindowTitle( mWindowTitle );
281 mDock->setWidget( mWidget );
282 mDock->setObjectName( mObjectName );
283 mDock->setProperty(
"dock_uuid", mUuid );
286 if ( !mSettingKeyDockId.isEmpty() )
288 connect( mDock, &QgsDockWidget::dockLocationChanged,
this, [
this]( Qt::DockWidgetArea area ) { sSettingsDockArea->setValue( area, mSettingKeyDockId ); } );
292 mDockGeometry = mDock->geometry();
293 mIsDockFloating = mDock->isFloating();
295 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
299 if ( mOptions.testFlag( Option::PermanentWidget ) )
300 mDock->installEventFilter(
this );
302 connect( mDock, &QgsDockWidget::visibilityChanged,
this, &QgsDockableWidgetHelper::visibilityChanged );
303 mDock->setUserVisible(
true );
304 emit visibilityChanged(
true );
311 if ( mOptions.testFlag( Option::PermanentWidget ) )
312 mDialog =
new QgsNonRejectableDialog(
nullptr, Qt::Window );
314 mDialog =
new QDialog(
nullptr, Qt::Window );
315 mDialog->setStyleSheet( sAppStylesheetFunction() );
317 mDialog->setWindowTitle( mWindowTitle );
318 mDialog->setObjectName( mObjectName );
320 if ( mOptions.testFlag( Option::PermanentWidget ) )
321 mDialog->installEventFilter(
this );
323 QVBoxLayout *vl =
new QVBoxLayout();
324 vl->setContentsMargins( 0, 0, 0, 0 );
325 vl->addWidget( mWidget );
327 if ( !mSettingKeyDockId.isEmpty() )
329 mDialog->restoreGeometry( sSettingsDialogGeometry->value( mSettingKeyDockId ).toByteArray() );
333 if ( !mDockGeometry.isEmpty() )
334 mDialog->setGeometry( mDockGeometry );
335 else if ( !mDialogGeometry.isEmpty() )
336 mDialog->setGeometry( mDialogGeometry );
338 mDialog->setLayout( vl );
342 connect( mDialog, &QDialog::finished,
this, [
this]() {
343 mDialogGeometry = mDialog->geometry();
345 emit visibilityChanged(
false );
348 emit visibilityChanged(
true );
350 emit dockModeToggled( docked );
353void QgsDockableWidgetHelper::setUserVisible(
bool visible )
361 mDialog->setWindowState( mDialog->windowState() & ~Qt::WindowMinimized );
362 mDialog->activateWindow();
371 mDock->setUserVisible( visible );
375void QgsDockableWidgetHelper::setWindowTitle(
const QString &title )
377 mWindowTitle = title;
380 mDialog->setWindowTitle( title );
384 mDock->setWindowTitle( title );
388void QgsDockableWidgetHelper::setDockObjectName(
const QString &name )
393 mDialog->setObjectName( name );
397 mDock->setObjectName( name );
401QString QgsDockableWidgetHelper::dockObjectName()
const
406bool QgsDockableWidgetHelper::isUserVisible()
const
410 return mDialog->isVisible();
414 return mDock->isUserVisible();
419void QgsDockableWidgetHelper::setupDockWidget(
const QStringList &tabSiblings )
424 mDock->setFloating( mIsDockFloating );
426 if ( mDockGeometry.isEmpty() && mOwnerWindow )
428 const QFontMetrics fm( mOwnerWindow->font() );
429 const int initialDockSize = fm.horizontalAdvance(
'0' ) * 75;
430 mDockGeometry = QRect(
static_cast<int>( mOwnerWindow->rect().width() * 0.75 ),
static_cast<int>( mOwnerWindow->rect().height() * 0.5 ), initialDockSize, initialDockSize );
432 if ( !tabSiblings.isEmpty() )
434 sAddTabifiedDockWidgetFunction( mDockArea, mDock, tabSiblings,
false );
436 else if ( mOptions.testFlag( Option::RaiseTab ) )
438 sAddTabifiedDockWidgetFunction( mDockArea, mDock, mTabifyWith,
true );
440 else if ( mOwnerWindow )
442 mOwnerWindow->addDockWidget( mDockArea, mDock );
447 QMetaObject::invokeMethod(
450 if ( mIsDockFloating && sSettingsDockGeometry->exists( mSettingKeyDockId ) )
451 mDock->restoreGeometry( sSettingsDockGeometry->value( mSettingKeyDockId ).toByteArray() );
452 else if ( mIsDockFloating )
453 mDock->setGeometry( mDockGeometry );
459QToolButton *QgsDockableWidgetHelper::createDockUndockToolButton()
461 QToolButton *toggleButton =
new QToolButton;
463 toggleButton->setCheckable(
true );
464 toggleButton->setChecked( mIsDocked );
465 toggleButton->setEnabled(
true );
467 connect( toggleButton, &QToolButton::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
471QAction *QgsDockableWidgetHelper::createDockUndockAction(
const QString &title, QWidget *parent )
473 QAction *toggleAction =
new QAction( title, parent );
475 toggleAction->setCheckable(
true );
476 toggleAction->setChecked( mIsDocked );
477 toggleAction->setEnabled(
true );
479 connect( toggleAction, &QAction::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
483bool QgsDockableWidgetHelper::eventFilter( QObject *watched, QEvent *event )
485 if ( watched == mDialog )
487 if ( event->type() == QEvent::Close )
491 emit visibilityChanged(
false );
495 else if ( watched == mDock )
497 if ( event->type() == QEvent::Close )
501 emit visibilityChanged(
false );
505 return QObject::eventFilter( watched, event );
512QgsNonRejectableDialog::QgsNonRejectableDialog( QWidget *parent, Qt::WindowFlags f )
513 : QDialog( parent, f )
516void QgsNonRejectableDialog::reject()
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A boolean settings entry.
A template class for enum and flag settings entry.
A variant settings entry.