27#include "moc_qgsdockablewidgethelper.cpp"
29using namespace Qt::StringLiterals;
39std::function<QString()> QgsDockableWidgetHelper::sAppStylesheetFunction = [] {
return QString(); };
40QMainWindow *QgsDockableWidgetHelper::sOwnerWindow =
nullptr;
42QgsDockableWidgetHelper::QgsDockableWidgetHelper(
43 const QString &windowTitle,
45 QMainWindow *ownerWindow,
46 const QString &dockId,
47 const QStringList &tabifyWith,
50 Qt::DockWidgetArea defaultDockArea,
55 , mDialogGeometry( 0, 0, 0, 0 )
56 , mWindowTitle( windowTitle )
57 , mOwnerWindow( ownerWindow )
58 , mTabifyWith( tabifyWith )
60 , mUuid( QUuid::createUuid().toString() )
61 , mSettingKeyDockId( dockId )
64 switch ( openingMode )
67 isDocked = sSettingsIsDocked->valueWithDefaultOverride( defaultIsDocked, mSettingKeyDockId );
77 mDockArea = sSettingsDockArea->valueWithDefaultOverride( defaultDockArea, mSettingKeyDockId );
78 mIsDockFloating = mDockArea == Qt::DockWidgetArea::NoDockWidgetArea;
79 toggleDockMode( isDocked );
82QgsDockableWidgetHelper::~QgsDockableWidgetHelper()
86 if ( !mSettingKeyDockId.isEmpty() )
87 sSettingsDockGeometry->setValue( mDock->saveGeometry(), mSettingKeyDockId );
90 mOwnerWindow->removeDockWidget( mDock );
92 mDock->setWidget(
nullptr );
93 mWidget->setParent(
nullptr );
102 mDialogGeometry = mDialog->geometry();
104 if ( !mSettingKeyDockId.isEmpty() )
105 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
107 mDialog->layout()->removeWidget( mWidget );
108 mDialog->deleteLater();
113void QgsDockableWidgetHelper::writeXml( QDomElement &viewDom )
115 viewDom.setAttribute( u
"isDocked"_s, mIsDocked );
119 mDockGeometry = mDock->geometry();
120 mIsDockFloating = mDock->isFloating();
122 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
125 viewDom.setAttribute( u
"x"_s, mDockGeometry.x() );
126 viewDom.setAttribute( u
"y"_s, mDockGeometry.y() );
127 viewDom.setAttribute( u
"width"_s, mDockGeometry.width() );
128 viewDom.setAttribute( u
"height"_s, mDockGeometry.height() );
129 viewDom.setAttribute( u
"floating"_s, mIsDockFloating );
130 viewDom.setAttribute( u
"area"_s, mDockArea );
131 viewDom.setAttribute( u
"uuid"_s, mUuid );
135 const QList<QDockWidget *> tabSiblings = mOwnerWindow ? mOwnerWindow->tabifiedDockWidgets( mDock ) : QList<QDockWidget *>();
136 QDomElement tabSiblingsElement = viewDom.ownerDocument().createElement( u
"tab_siblings"_s );
137 for ( QDockWidget *dock : tabSiblings )
139 QDomElement siblingElement = viewDom.ownerDocument().createElement( u
"sibling"_s );
140 siblingElement.setAttribute( u
"uuid"_s, dock->property(
"dock_uuid" ).toString() );
141 siblingElement.setAttribute( u
"object_name"_s, dock->objectName() );
142 tabSiblingsElement.appendChild( siblingElement );
144 viewDom.appendChild( tabSiblingsElement );
148 mDialogGeometry = mDialog->geometry();
150 viewDom.setAttribute( u
"d_x"_s, mDialogGeometry.x() );
151 viewDom.setAttribute( u
"d_y"_s, mDialogGeometry.y() );
152 viewDom.setAttribute( u
"d_width"_s, mDialogGeometry.width() );
153 viewDom.setAttribute( u
"d_height"_s, mDialogGeometry.height() );
156void QgsDockableWidgetHelper::readXml(
const QDomElement &viewDom )
158 mUuid = viewDom.attribute( u
"uuid"_s, mUuid );
161 int x = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
162 int y = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
163 int w = viewDom.attribute( u
"d_width"_s, u
"200"_s ).toInt();
164 int h = viewDom.attribute( u
"d_height"_s, u
"200"_s ).toInt();
165 mDialogGeometry = QRect( x, y, w, h );
167 mDialog->setGeometry( mDialogGeometry );
171 int x = viewDom.attribute( u
"x"_s, u
"0"_s ).toInt();
172 int y = viewDom.attribute( u
"y"_s, u
"0"_s ).toInt();
173 int w = viewDom.attribute( u
"width"_s, u
"200"_s ).toInt();
174 int h = viewDom.attribute( u
"height"_s, u
"200"_s ).toInt();
175 mDockGeometry = QRect( x, y, w, h );
176 mIsDockFloating = viewDom.attribute( u
"floating"_s, u
"0"_s ).toInt();
177 mDockArea =
static_cast<Qt::DockWidgetArea
>( viewDom.attribute( u
"area"_s, QString::number( Qt::RightDockWidgetArea ) ).toInt() );
179 if ( mDockArea == Qt::DockWidgetArea::NoDockWidgetArea && !mIsDockFloating )
181 mDockArea = Qt::RightDockWidgetArea;
184 QStringList tabSiblings;
185 const QDomElement tabSiblingsElement = viewDom.firstChildElement( u
"tab_siblings"_s );
186 const QDomNodeList tabSiblingNodes = tabSiblingsElement.childNodes();
187 for (
int i = 0; i < tabSiblingNodes.size(); ++i )
189 const QDomElement tabSiblingElement = tabSiblingNodes.at( i ).toElement();
191 QString tabId = tabSiblingElement.attribute( u
"uuid"_s );
192 if ( tabId.isEmpty() )
193 tabId = tabSiblingElement.attribute( u
"object_name"_s );
194 if ( !tabId.isEmpty() )
195 tabSiblings.append( tabId );
198 setupDockWidget( tabSiblings );
203 mDock->setProperty(
"dock_uuid", mUuid );
207void QgsDockableWidgetHelper::setWidget( QWidget *widget )
210 if ( mWidget && mOwnerWindow )
212 mWidget->setParent( mOwnerWindow );
216 mDialog->layout()->removeWidget( mWidget );
220 mDock->setWidget(
nullptr );
224 toggleDockMode( mIsDocked );
232QDialog *QgsDockableWidgetHelper::dialog()
234 return mDialog.data();
237void QgsDockableWidgetHelper::toggleDockMode(
bool docked )
240 if ( mWidget && mOwnerWindow )
242 mWidget->setParent( mOwnerWindow );
248 mDockGeometry = mDock->geometry();
249 mIsDockFloating = mDock->isFloating();
251 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
253 mDock->setWidget(
nullptr );
255 mOwnerWindow->removeDockWidget( mDock );
263 if ( !mSettingKeyDockId.isEmpty() )
264 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
266 mDialogGeometry = mDialog->geometry();
269 mDialog->layout()->removeWidget( mWidget );
276 if ( !mSettingKeyDockId.isEmpty() )
277 sSettingsIsDocked->setValue( mIsDocked, mSettingKeyDockId );
287 mDock->setWindowTitle( mWindowTitle );
288 mDock->setWidget( mWidget );
289 mDock->setObjectName( mObjectName );
290 mDock->setProperty(
"dock_uuid", mUuid );
293 if ( !mSettingKeyDockId.isEmpty() )
295 connect( mDock, &QgsDockWidget::dockLocationChanged,
this, [
this]( Qt::DockWidgetArea area ) { sSettingsDockArea->setValue( area, mSettingKeyDockId ); } );
299 mDockGeometry = mDock->geometry();
300 mIsDockFloating = mDock->isFloating();
302 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
306 if ( mOptions.testFlag( Option::PermanentWidget ) )
307 mDock->installEventFilter(
this );
309 connect( mDock, &QgsDockWidget::visibilityChanged,
this, &QgsDockableWidgetHelper::visibilityChanged );
310 mDock->setUserVisible(
true );
311 emit visibilityChanged(
true );
318 if ( mOptions.testFlag( Option::PermanentWidget ) )
319 mDialog =
new QgsNonRejectableDialog(
nullptr, Qt::Window );
321 mDialog =
new QDialog(
nullptr, Qt::Window );
322 mDialog->setStyleSheet( sAppStylesheetFunction() );
324 mDialog->setWindowTitle( mWindowTitle );
325 mDialog->setObjectName( mObjectName );
327 if ( mOptions.testFlag( Option::PermanentWidget ) )
328 mDialog->installEventFilter(
this );
330 QVBoxLayout *vl =
new QVBoxLayout();
331 vl->setContentsMargins( 0, 0, 0, 0 );
332 vl->addWidget( mWidget );
334 if ( !mSettingKeyDockId.isEmpty() )
336 mDialog->restoreGeometry( sSettingsDialogGeometry->value( mSettingKeyDockId ).toByteArray() );
340 if ( !mDockGeometry.isEmpty() )
341 mDialog->setGeometry( mDockGeometry );
342 else if ( !mDialogGeometry.isEmpty() )
343 mDialog->setGeometry( mDialogGeometry );
345 mDialog->setLayout( vl );
349 connect( mDialog, &QDialog::finished,
this, [
this]() {
350 mDialogGeometry = mDialog->geometry();
352 emit visibilityChanged(
false );
355 emit visibilityChanged(
true );
357 emit dockModeToggled( docked );
360void QgsDockableWidgetHelper::setUserVisible(
bool visible )
368 mDialog->setWindowState( mDialog->windowState() & ~Qt::WindowMinimized );
369 mDialog->activateWindow();
370 emit visibilityChanged(
true );
375 emit visibilityChanged(
false );
380 mDock->setUserVisible( visible );
384void QgsDockableWidgetHelper::reject()
396void QgsDockableWidgetHelper::setWindowTitle(
const QString &title )
398 mWindowTitle = title;
401 mDialog->setWindowTitle( title );
405 mDock->setWindowTitle( title );
409void QgsDockableWidgetHelper::setDockObjectName(
const QString &name )
414 mDialog->setObjectName( name );
418 mDock->setObjectName( name );
422QString QgsDockableWidgetHelper::dockObjectName()
const
427void QgsDockableWidgetHelper::setSettingKeyDockId(
const QString &
id )
429 mSettingKeyDockId = id;
432bool QgsDockableWidgetHelper::isUserVisible()
const
436 return mDialog->isVisible();
440 return mDock->isUserVisible();
445void QgsDockableWidgetHelper::setupDockWidget(
const QStringList &tabSiblings )
450 mDock->setFloating( mIsDockFloating );
452 if ( mDockGeometry.isEmpty() && mOwnerWindow )
454 const QFontMetrics fm( mOwnerWindow->font() );
455 const int initialDockSize = fm.horizontalAdvance(
'0' ) * 75;
456 mDockGeometry = QRect(
static_cast<int>( mOwnerWindow->rect().width() * 0.75 ),
static_cast<int>( mOwnerWindow->rect().height() * 0.5 ), initialDockSize, initialDockSize );
458 if ( mOwnerWindow && !tabSiblings.isEmpty() )
462 else if ( mOwnerWindow && mOptions.testFlag( Option::RaiseTab ) )
466 else if ( mOwnerWindow )
468 mOwnerWindow->addDockWidget( mDockArea, mDock );
473 QMetaObject::invokeMethod(
476 if ( mIsDockFloating && sSettingsDockGeometry->exists( mSettingKeyDockId ) )
477 mDock->restoreGeometry( sSettingsDockGeometry->value( mSettingKeyDockId ).toByteArray() );
478 else if ( mIsDockFloating )
479 mDock->setGeometry( mDockGeometry );
485QToolButton *QgsDockableWidgetHelper::createDockUndockToolButton()
487 QToolButton *toggleButton =
new QToolButton;
489 toggleButton->setCheckable(
true );
490 toggleButton->setChecked( mIsDocked );
491 toggleButton->setEnabled(
true );
493 connect( toggleButton, &QToolButton::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
497QAction *QgsDockableWidgetHelper::createDockUndockAction(
const QString &title, QWidget *parent )
499 QAction *toggleAction =
new QAction( title, parent );
501 toggleAction->setCheckable(
true );
502 toggleAction->setChecked( mIsDocked );
503 toggleAction->setEnabled(
true );
505 connect( toggleAction, &QAction::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
509bool QgsDockableWidgetHelper::eventFilter( QObject *watched, QEvent *event )
511 if ( watched == mDialog )
513 if ( event->type() == QEvent::Close )
517 emit visibilityChanged(
false );
521 else if ( watched == mDock )
523 if ( event->type() == QEvent::Close )
527 emit visibilityChanged(
false );
531 return QObject::eventFilter( watched, event );
538QgsNonRejectableDialog::QgsNonRejectableDialog( QWidget *parent, Qt::WindowFlags f )
539 : QDialog( parent, f )
542void QgsNonRejectableDialog::reject()
DockableWidgetInitialState
Dockable widget initial states.
@ ForceDocked
Force the widget to be docked.
@ ForceDialog
Force the widget to be shown in a dialog.
@ RestorePreviousState
Restore the previous state of this dock.
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.
void addTabifiedDockWidget(QMainWindow *window, Qt::DockWidgetArea area, QDockWidget *dockWidget, const QStringList &tabifyWith, bool raiseTab)
Add a dock widget to the given area and tabify it (if other dock widgets exist in the same area).