26#include "moc_qgsdockablewidgethelper.cpp"
28using namespace Qt::StringLiterals;
37std::function<void( Qt::DockWidgetArea, QDockWidget *,
const QStringList &,
bool )> QgsDockableWidgetHelper::sAddTabifiedDockWidgetFunction = []( Qt::DockWidgetArea, QDockWidget *,
const QStringList &, bool ) {};
38std::function<QString()> QgsDockableWidgetHelper::sAppStylesheetFunction = [] {
return QString(); };
39QMainWindow *QgsDockableWidgetHelper::sOwnerWindow =
nullptr;
41QgsDockableWidgetHelper::QgsDockableWidgetHelper(
const QString &windowTitle, QWidget *widget, QMainWindow *ownerWindow,
const QString &dockId,
const QStringList &tabifyWith, OpeningMode openingMode,
bool defaultIsDocked, Qt::DockWidgetArea defaultDockArea, Options options )
44 , mDialogGeometry( 0, 0, 0, 0 )
45 , mWindowTitle( windowTitle )
46 , mOwnerWindow( ownerWindow )
47 , mTabifyWith( tabifyWith )
49 , mUuid( QUuid::createUuid().toString() )
50 , mSettingKeyDockId( dockId )
52 bool isDocked = sSettingsIsDocked->valueWithDefaultOverride( defaultIsDocked, mSettingKeyDockId );
53 if ( openingMode == OpeningMode::ForceDocked )
55 else if ( openingMode == OpeningMode::ForceDialog )
58 mDockArea = sSettingsDockArea->valueWithDefaultOverride( defaultDockArea, mSettingKeyDockId );
59 mIsDockFloating = mDockArea == Qt::DockWidgetArea::NoDockWidgetArea;
60 toggleDockMode( isDocked );
63QgsDockableWidgetHelper::~QgsDockableWidgetHelper()
67 if ( !mSettingKeyDockId.isEmpty() )
68 sSettingsDockGeometry->setValue( mDock->saveGeometry(), mSettingKeyDockId );
71 mOwnerWindow->removeDockWidget( mDock );
73 mDock->setWidget(
nullptr );
74 mWidget->setParent(
nullptr );
83 mDialogGeometry = mDialog->geometry();
85 if ( !mSettingKeyDockId.isEmpty() )
86 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
88 mDialog->layout()->removeWidget( mWidget );
89 mDialog->deleteLater();
94void QgsDockableWidgetHelper::writeXml( QDomElement &viewDom )
96 viewDom.setAttribute( u
"isDocked"_s, mIsDocked );
100 mDockGeometry = mDock->geometry();
101 mIsDockFloating = mDock->isFloating();
103 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
106 viewDom.setAttribute( u
"x"_s, mDockGeometry.x() );
107 viewDom.setAttribute( u
"y"_s, mDockGeometry.y() );
108 viewDom.setAttribute( u
"width"_s, mDockGeometry.width() );
109 viewDom.setAttribute( u
"height"_s, mDockGeometry.height() );
110 viewDom.setAttribute( u
"floating"_s, mIsDockFloating );
111 viewDom.setAttribute( u
"area"_s, mDockArea );
112 viewDom.setAttribute( u
"uuid"_s, mUuid );
116 const QList<QDockWidget *> tabSiblings = mOwnerWindow ? mOwnerWindow->tabifiedDockWidgets( mDock ) : QList<QDockWidget *>();
117 QDomElement tabSiblingsElement = viewDom.ownerDocument().createElement( u
"tab_siblings"_s );
118 for ( QDockWidget *dock : tabSiblings )
120 QDomElement siblingElement = viewDom.ownerDocument().createElement( u
"sibling"_s );
121 siblingElement.setAttribute( u
"uuid"_s, dock->property(
"dock_uuid" ).toString() );
122 siblingElement.setAttribute( u
"object_name"_s, dock->objectName() );
123 tabSiblingsElement.appendChild( siblingElement );
125 viewDom.appendChild( tabSiblingsElement );
129 mDialogGeometry = mDialog->geometry();
131 viewDom.setAttribute( u
"d_x"_s, mDialogGeometry.x() );
132 viewDom.setAttribute( u
"d_y"_s, mDialogGeometry.y() );
133 viewDom.setAttribute( u
"d_width"_s, mDialogGeometry.width() );
134 viewDom.setAttribute( u
"d_height"_s, mDialogGeometry.height() );
137void QgsDockableWidgetHelper::readXml(
const QDomElement &viewDom )
139 mUuid = viewDom.attribute( u
"uuid"_s, mUuid );
142 int x = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
143 int y = viewDom.attribute( u
"d_x"_s, u
"0"_s ).toInt();
144 int w = viewDom.attribute( u
"d_width"_s, u
"200"_s ).toInt();
145 int h = viewDom.attribute( u
"d_height"_s, u
"200"_s ).toInt();
146 mDialogGeometry = QRect( x, y, w, h );
148 mDialog->setGeometry( mDialogGeometry );
152 int x = viewDom.attribute( u
"x"_s, u
"0"_s ).toInt();
153 int y = viewDom.attribute( u
"y"_s, u
"0"_s ).toInt();
154 int w = viewDom.attribute( u
"width"_s, u
"200"_s ).toInt();
155 int h = viewDom.attribute( u
"height"_s, u
"200"_s ).toInt();
156 mDockGeometry = QRect( x, y, w, h );
157 mIsDockFloating = viewDom.attribute( u
"floating"_s, u
"0"_s ).toInt();
158 mDockArea =
static_cast<Qt::DockWidgetArea
>( viewDom.attribute( u
"area"_s, QString::number( Qt::RightDockWidgetArea ) ).toInt() );
160 if ( mDockArea == Qt::DockWidgetArea::NoDockWidgetArea && !mIsDockFloating )
162 mDockArea = Qt::RightDockWidgetArea;
165 QStringList tabSiblings;
166 const QDomElement tabSiblingsElement = viewDom.firstChildElement( u
"tab_siblings"_s );
167 const QDomNodeList tabSiblingNodes = tabSiblingsElement.childNodes();
168 for (
int i = 0; i < tabSiblingNodes.size(); ++i )
170 const QDomElement tabSiblingElement = tabSiblingNodes.at( i ).toElement();
172 QString tabId = tabSiblingElement.attribute( u
"uuid"_s );
173 if ( tabId.isEmpty() )
174 tabId = tabSiblingElement.attribute( u
"object_name"_s );
175 if ( !tabId.isEmpty() )
176 tabSiblings.append( tabId );
179 setupDockWidget( tabSiblings );
184 mDock->setProperty(
"dock_uuid", mUuid );
188void QgsDockableWidgetHelper::setWidget( QWidget *widget )
191 if ( mWidget && mOwnerWindow )
193 mWidget->setParent( mOwnerWindow );
197 mDialog->layout()->removeWidget( mWidget );
201 mDock->setWidget(
nullptr );
205 toggleDockMode( mIsDocked );
213QDialog *QgsDockableWidgetHelper::dialog()
215 return mDialog.data();
218void QgsDockableWidgetHelper::toggleDockMode(
bool docked )
221 if ( mWidget && mOwnerWindow )
223 mWidget->setParent( mOwnerWindow );
229 mDockGeometry = mDock->geometry();
230 mIsDockFloating = mDock->isFloating();
232 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
234 mDock->setWidget(
nullptr );
236 mOwnerWindow->removeDockWidget( mDock );
244 if ( !mSettingKeyDockId.isEmpty() )
245 sSettingsDialogGeometry->setValue( mDialog->saveGeometry(), mSettingKeyDockId );
247 mDialogGeometry = mDialog->geometry();
250 mDialog->layout()->removeWidget( mWidget );
257 if ( !mSettingKeyDockId.isEmpty() )
258 sSettingsIsDocked->setValue( mIsDocked, mSettingKeyDockId );
268 mDock->setWindowTitle( mWindowTitle );
269 mDock->setWidget( mWidget );
270 mDock->setObjectName( mObjectName );
271 mDock->setProperty(
"dock_uuid", mUuid );
274 if ( !mSettingKeyDockId.isEmpty() )
276 connect( mDock, &QgsDockWidget::dockLocationChanged,
this, [
this]( Qt::DockWidgetArea area ) {
277 sSettingsDockArea->setValue( area, mSettingKeyDockId );
282 mDockGeometry = mDock->geometry();
283 mIsDockFloating = mDock->isFloating();
285 mDockArea = mOwnerWindow->dockWidgetArea( mDock );
289 if ( mOptions.testFlag( Option::PermanentWidget ) )
290 mDock->installEventFilter(
this );
292 connect( mDock, &QgsDockWidget::visibilityChanged,
this, &QgsDockableWidgetHelper::visibilityChanged );
293 mDock->setUserVisible(
true );
294 emit visibilityChanged(
true );
301 if ( mOptions.testFlag( Option::PermanentWidget ) )
302 mDialog =
new QgsNonRejectableDialog(
nullptr, Qt::Window );
304 mDialog =
new QDialog(
nullptr, Qt::Window );
305 mDialog->setStyleSheet( sAppStylesheetFunction() );
307 mDialog->setWindowTitle( mWindowTitle );
308 mDialog->setObjectName( mObjectName );
310 if ( mOptions.testFlag( Option::PermanentWidget ) )
311 mDialog->installEventFilter(
this );
313 QVBoxLayout *vl =
new QVBoxLayout();
314 vl->setContentsMargins( 0, 0, 0, 0 );
315 vl->addWidget( mWidget );
317 if ( !mSettingKeyDockId.isEmpty() )
319 mDialog->restoreGeometry( sSettingsDialogGeometry->value( mSettingKeyDockId ).toByteArray() );
323 if ( !mDockGeometry.isEmpty() )
324 mDialog->setGeometry( mDockGeometry );
325 else if ( !mDialogGeometry.isEmpty() )
326 mDialog->setGeometry( mDialogGeometry );
328 mDialog->setLayout( vl );
332 connect( mDialog, &QDialog::finished,
this, [
this]() {
333 mDialogGeometry = mDialog->geometry();
335 emit visibilityChanged(
false );
338 emit visibilityChanged(
true );
340 emit dockModeToggled( docked );
343void QgsDockableWidgetHelper::setUserVisible(
bool visible )
351 mDialog->setWindowState( mDialog->windowState() & ~Qt::WindowMinimized );
352 mDialog->activateWindow();
361 mDock->setUserVisible( visible );
365void QgsDockableWidgetHelper::setWindowTitle(
const QString &title )
367 mWindowTitle = title;
370 mDialog->setWindowTitle( title );
374 mDock->setWindowTitle( title );
378void QgsDockableWidgetHelper::setDockObjectName(
const QString &name )
383 mDialog->setObjectName( name );
387 mDock->setObjectName( name );
391QString QgsDockableWidgetHelper::dockObjectName()
const {
return mObjectName; }
393bool QgsDockableWidgetHelper::isUserVisible()
const
397 return mDialog->isVisible();
401 return mDock->isUserVisible();
406void QgsDockableWidgetHelper::setupDockWidget(
const QStringList &tabSiblings )
411 mDock->setFloating( mIsDockFloating );
413 if ( mDockGeometry.isEmpty() && mOwnerWindow )
415 const QFontMetrics fm( mOwnerWindow->font() );
416 const int initialDockSize = fm.horizontalAdvance(
'0' ) * 75;
417 mDockGeometry = QRect(
static_cast<int>( mOwnerWindow->rect().width() * 0.75 ),
static_cast<int>( mOwnerWindow->rect().height() * 0.5 ), initialDockSize, initialDockSize );
419 if ( !tabSiblings.isEmpty() )
421 sAddTabifiedDockWidgetFunction( mDockArea, mDock, tabSiblings,
false );
423 else if ( mOptions.testFlag( Option::RaiseTab ) )
425 sAddTabifiedDockWidgetFunction( mDockArea, mDock, mTabifyWith,
true );
427 else if ( mOwnerWindow )
429 mOwnerWindow->addDockWidget( mDockArea, mDock );
434 QMetaObject::invokeMethod( mDock, [
this] {
435 if (mIsDockFloating && sSettingsDockGeometry->exists( mSettingKeyDockId ) )
436 mDock->restoreGeometry( sSettingsDockGeometry->value( mSettingKeyDockId ).toByteArray() );
437 else if ( mIsDockFloating )
438 mDock->setGeometry( mDockGeometry ); }, Qt::QueuedConnection );
441QToolButton *QgsDockableWidgetHelper::createDockUndockToolButton()
443 QToolButton *toggleButton =
new QToolButton;
445 toggleButton->setCheckable(
true );
446 toggleButton->setChecked( mIsDocked );
447 toggleButton->setEnabled(
true );
449 connect( toggleButton, &QToolButton::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
453QAction *QgsDockableWidgetHelper::createDockUndockAction(
const QString &title, QWidget *parent )
455 QAction *toggleAction =
new QAction( title, parent );
457 toggleAction->setCheckable(
true );
458 toggleAction->setChecked( mIsDocked );
459 toggleAction->setEnabled(
true );
461 connect( toggleAction, &QAction::toggled,
this, &QgsDockableWidgetHelper::toggleDockMode );
465bool QgsDockableWidgetHelper::eventFilter( QObject *watched, QEvent *event )
467 if ( watched == mDialog )
469 if ( event->type() == QEvent::Close )
473 emit visibilityChanged(
false );
477 else if ( watched == mDock )
479 if ( event->type() == QEvent::Close )
483 emit visibilityChanged(
false );
487 return QObject::eventFilter( watched, event );
494QgsNonRejectableDialog::QgsNonRejectableDialog( QWidget *parent, Qt::WindowFlags f )
495 : QDialog( parent, f )
499void 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.