2 changes: 2 additions & 0 deletions images/images.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,12 @@
<file>themes/default/mActionZoomToLayer.png</file>
<file>themes/default/mActionZoomToSelected.png</file>
<file>themes/default/mIconClose.png</file>
<file>themes/default/mIconCollapse.png</file>
<file>themes/default/mIconConnect.png</file>
<file>themes/default/mIconDbSchema.png</file>
<file>themes/default/mIconDelete.png</file>
<file>themes/default/mIconEditable.png</file>
<file>themes/default/mIconExpand.png</file>
<file>themes/default/mIconFavourites.png</file>
<file>themes/default/mIconFirst.png</file>
<file>themes/default/mIconGeometryLayer.png</file>
Expand Down
Binary file added images/themes/default/mIconCollapse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/themes/default/mIconExpand.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/app/qgslabelinggui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
{
connect( quadrantRadios[i], SIGNAL( toggled( bool ) ), this, SLOT( updateQuadrant() ) );
}

// Label tab collapsed groupboxes
chkBuffer->setCollapsed( true );
mFontMultiLineGroupBox->setCollapsed( true );
chkFormattedNumbers->setCollapsed( true );
chkScaleBasedVisibility->setCollapsed( true );

// Data defined tab collapsed groupboxes
mBufferAttributesPropertiesGroupBox->setCollapsed( true );
mFontAttributePropertiesGroupBox->setCollapsed( true );

}

QgsLabelingGui::~QgsLabelingGui()
Expand Down
232 changes: 161 additions & 71 deletions src/gui/qgscollapsiblegroupbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,112 +20,202 @@
#include "qgsapplication.h"
#include "qgslogger.h"

#include <QToolButton>
#include <QMouseEvent>
#include <QStyleOptionGroupBox>
#include <QStylePainter>
#include <QLayout>
#include <QProxyStyle>
#include <QSettings>

class QgsCollapsibleGroupBoxStyle : public QProxyStyle
{
public:
QgsCollapsibleGroupBoxStyle( QStyle * style = 0 ) : QProxyStyle( style ) {}

void drawPrimitive( PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget ) const
{
if ( element == PE_IndicatorCheckBox )
{
const QgsCollapsibleGroupBox* groupBox =
dynamic_cast<const QgsCollapsibleGroupBox*>( widget );
if ( groupBox )
{
return drawPrimitive( groupBox->isCollapsed() ?
PE_IndicatorArrowRight : PE_IndicatorArrowDown,
option, painter, widget );
}
}
return QProxyStyle::drawPrimitive( element, option, painter, widget );
}
};
QIcon QgsCollapsibleGroupBox::mCollapseIcon;
QIcon QgsCollapsibleGroupBox::mExpandIcon;

QgsCollapsibleGroupBox::QgsCollapsibleGroupBox( QWidget *parent )
: QGroupBox( parent ), mCollapsed( false )
: QGroupBox( parent ), mCollapsed( false ), mSaveState( true )
{
init();
}

QgsCollapsibleGroupBox::QgsCollapsibleGroupBox( const QString &title,
QWidget *parent )
: QGroupBox( title, parent ), mCollapsed( false )
: QGroupBox( title, parent ), mCollapsed( false ), mSaveState( true )
{
init();
}

void QgsCollapsibleGroupBox::init()
QgsCollapsibleGroupBox::~QgsCollapsibleGroupBox()
{
setStyle( new QgsCollapsibleGroupBoxStyle( QApplication::style() ) );
connect( this, SIGNAL( toggled( bool ) ), this, SLOT( setToggled( bool ) ) );
saveState();
}

void QgsCollapsibleGroupBox::showEvent( QShowEvent * event )
void QgsCollapsibleGroupBox::init()
{
QGroupBox::showEvent( event );
// collapse if needed - any calls to setCollapsed() before have no effect
if ( isCheckable() && ! isChecked() && ! isCollapsed() )
setCollapsed( true );
// init icons
if ( mCollapseIcon.isNull() )
{
mCollapseIcon = QgsApplication::getThemeIcon( "/mIconCollapse.png" );
mExpandIcon = QgsApplication::getThemeIcon( "/mIconExpand.png" );
}

// collapse button
mCollapseButton = new QToolButton( this );
mCollapseButton->setObjectName( "collapseButton" );
mCollapseButton->setAutoRaise( true );
mCollapseButton->setFixedSize( 16, 16 );
// TODO set size (as well as margins) depending on theme, in updateStyle()
mCollapseButton->setIconSize( QSize( 12, 12 ) );
mCollapseButton->setIcon( mCollapseIcon );

connect( mCollapseButton, SIGNAL( clicked() ), this, SLOT( toggleCollapsed() ) );
connect( this, SIGNAL( toggled( bool ) ), this, SLOT( checkToggled( bool ) ) );
}

void QgsCollapsibleGroupBox::setCollapsed( bool collapse )
void QgsCollapsibleGroupBox::showEvent( QShowEvent * event )
{
if ( ! isVisible() )
return;
loadState();

mCollapsed = collapse;
updateStyle();

// minimize layout margins and save for subsequent restore
if ( collapse )
// expand if needed - any calls to setCollapsed() before only set mCollapsed
if ( mCollapsed )
{
if ( layout() )
{
mMargins = layout()->contentsMargins();
layout()->setContentsMargins( 1, 1, 1, 1 );
}
setCollapsed( mCollapsed );
}
else
{
if ( layout() )
{
layout()->setContentsMargins( mMargins );
}
/* manually expanding (already default) on show may scroll scroll areas;
still emit signal for connections using expanded state */
emit collapsedStateChanged( this );
}
event->accept();
}

// if we are collapsing, save hidden widgets in a list
if ( collapse )
void QgsCollapsibleGroupBox::mouseReleaseEvent( QMouseEvent *event )
{
// catch mouse release over title when non checkable, to collapse/expand
if ( !isCheckable() && event->button() == Qt::LeftButton )
{
mHiddenWidgets.clear();
foreach ( QWidget *widget, findChildren<QWidget*>() )
if ( titleRect().contains( event->pos() ) )
{
if ( widget->isHidden() )
mHiddenWidgets << widget;
toggleCollapsed();
return;
}
}
// default behaviour - pass to QGroupBox
QGroupBox::mouseReleaseEvent( event );
}

// show/hide widgets
foreach ( QWidget *widget, findChildren<QWidget*>() )
widget->setHidden( collapse );
QRect QgsCollapsibleGroupBox::titleRect() const
{
QStyleOptionGroupBox box;
initStyleOption( &box );
return style()->subControlRect( QStyle::CC_GroupBox, &box,
QStyle::SC_GroupBoxLabel, this );
}

// if we are expanding, re-hide saved hidden widgets
if ( ! collapse )
{
foreach ( QWidget *widget, mHiddenWidgets )
{
widget->setHidden( true );
}
}
QString QgsCollapsibleGroupBox::saveKey() const
{
// save key for load/save state
// currently QgsCollapsibleGroupBox/window()/object
QString saveKey = "/" + objectName();
// QObject* parentWidget = parent();
// while ( parentWidget != NULL )
// {
// saveKey = "/" + parentWidget->objectName() + saveKey;
// parentWidget = parentWidget->parent();
// }
// if ( parent() != NULL )
// saveKey = "/" + parent()->objectName() + saveKey;
saveKey = "/" + window()->objectName() + saveKey;
saveKey = "QgsCollapsibleGroupBox" + saveKey;
return saveKey;
}

if ( mCollapsed )
emit collapsed( this );
else
emit expanded( this );
void QgsCollapsibleGroupBox::loadState()
{
if ( ! mSaveState )
return;

setUpdatesEnabled( false );

QSettings settings;
QString key = saveKey();
QVariant val = settings.value( key + "/checked" );
if ( ! val.isNull() )
setChecked( val.toBool() );
val = settings.value( key + "/collapsed" );
if ( ! val.isNull() )
setCollapsed( val.toBool() );

setUpdatesEnabled( true );
}

void QgsCollapsibleGroupBox::saveState()
{
if ( ! mSaveState )
return;
QgsDebugMsg( "key = " + saveKey() + " objectName = " + objectName() );
QSettings settings;
QString key = saveKey();
settings.setValue( key + "/checked", isChecked() );
settings.setValue( key + "/collapsed", isCollapsed() );
}

void QgsCollapsibleGroupBox::checkToggled( bool chkd )
{
mCollapseButton->setEnabled( true ); // always keep enabled
// expand/collapse when toggled
if ( chkd && isCollapsed() )
setCollapsed( false );
else if ( ! chkd && ! isCollapsed() )
setCollapsed( true );
}

void QgsCollapsibleGroupBox::toggleCollapsed()
{
setCollapsed( !mCollapsed );
}

void QgsCollapsibleGroupBox::updateStyle()
{
setUpdatesEnabled( false );

// customize style sheet
// TODO: move to app stylesheet system, when appropriate
QString ss;
ss += "QgsCollapsibleGroupBox::title {";
ss += " subcontrol-origin: margin;";
ss += " subcontrol-position: top left;";
ss += " margin-left: 20px;"; // offset for disclosure triangle
ss += " margin-right: 5px;"; // a little bit of space on the right, to match space on the left
ss += "}";
setStyleSheet( ss );

// clear toolbutton default background and border
// TODO: move to app stylesheet system, when appropriate
QString ssd;
ssd = QString( "QgsCollapsibleGroupBox > QToolButton#%1 {" ).arg( mCollapseButton->objectName() );
ssd += " background-color: rgba(255, 255, 255, 0); border: none;";
ssd += "}";
mCollapseButton->setStyleSheet( ssd );

setUpdatesEnabled( true );
}

void QgsCollapsibleGroupBox::setCollapsed( bool collapse )
{
mCollapsed = collapse;

if ( !isVisible() )
return;

// for consistent look/spacing across platforms when collapsed
setFlat( collapse );
// avoid flicker in X11
QApplication::processEvents();
// set maximum height to hide contents - does this work in all envs?
// setMaximumHeight( collapse ? 25 : 16777215 );
setMaximumHeight( collapse ? titleRect().bottom() + 6 : 16777215 );
mCollapseButton->setIcon( collapse ? mExpandIcon : mCollapseIcon );

emit collapsedStateChanged( this );
}

35 changes: 24 additions & 11 deletions src/gui/qgscollapsiblegroupbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,51 @@
#include "qgisgui.h"

/** \ingroup gui
* A groupbox that collapses/expands when toggled and draws an expand/collapse icon in lieu of checkbox.
* Widget must be checkable for expand/collapse icon to appear.
* A groupbox that collapses/expands when toggled.
* @note Collapsible function not shown in promoted QtDesigner widgets.
*/

#include <QGroupBox>

class QToolButton;

class GUI_EXPORT QgsCollapsibleGroupBox : public QGroupBox
{
Q_OBJECT

public:
QgsCollapsibleGroupBox( QWidget *parent = 0 );
QgsCollapsibleGroupBox( const QString &title, QWidget *parent = 0 );

~QgsCollapsibleGroupBox();
bool isCollapsed() const { return mCollapsed; }
void setCollapsed( bool collapse );
void setSaveState( bool save ) { mSaveState = save; }

signals:
void collapsed( QWidget* );
void expanded( QWidget* );
void collapsedStateChanged( QWidget* );

public slots:
void setToggled( bool toggled ) { setCollapsed( ! toggled ); }
void setCollapsed( bool collapse );
void checkToggled( bool ckd );
void toggleCollapsed();
void updateStyle();

protected slots:
void loadState();
void saveState();

protected:
void init();
void showEvent( QShowEvent * event );
void showEvent( QShowEvent *event );
void mouseReleaseEvent( QMouseEvent *event );
QRect titleRect() const;
QString saveKey() const;

private:
bool mCollapsed;
QMargins mMargins;
QList< QWidget* > mHiddenWidgets;
bool mSaveState;
QToolButton* mCollapseButton;

static QIcon mCollapseIcon;
static QIcon mExpandIcon;
};

#endif
Loading