×
Namespaces

Variants
Actions
(Difference between revisions)

How to create flexible Portrait - Landscape rotation layout in Qt

From Nokia Developer Wiki
Jump to: navigation, search
hamishwillee (Talk | contribs)
m (Hamishwillee - Bot update - Add ArticleMetaData)
hamishwillee (Talk | contribs)
m (Text replace - "<code cpp>" to "<code cpp-qt>")
 
Line 52: Line 52:
  
 
Class Declaration and Constructors
 
Class Declaration and Constructors
<code cpp>
+
<code cpp-qt>
  
 
class ProxyLayout : public QLayout
 
class ProxyLayout : public QLayout
Line 63: Line 63:
 
The main API methods
 
The main API methods
  
<code cpp>
+
<code cpp-qt>
 
public:
 
public:
 
     void addLayout(QLayout *layout);
 
     void addLayout(QLayout *layout);
Line 71: Line 71:
 
Re-implementation of QLayout Methods, as needed
 
Re-implementation of QLayout Methods, as needed
  
<code cpp>
+
<code cpp-qt>
 
public:
 
public:
 
     void addItem(QLayoutItem *item);
 
     void addItem(QLayoutItem *item);
Line 87: Line 87:
 
The next methods are an event filter and helper internal methods. It is used to monitor resize events delivered to a widget the layout is set to.
 
The next methods are an event filter and helper internal methods. It is used to monitor resize events delivered to a widget the layout is set to.
  
<code cpp>
+
<code cpp-qt>
 
  protected:
 
  protected:
 
     bool eventFilter(QObject *obj, QEvent *event);
 
     bool eventFilter(QObject *obj, QEvent *event);
Line 98: Line 98:
 
And finally, internal management variables and methods. Those are protected.
 
And finally, internal management variables and methods. Those are protected.
  
<code cpp>
+
<code cpp-qt>
 
  private:
 
  private:
 
     QList<QLayout*> m_layoutList;
 
     QList<QLayout*> m_layoutList;
Line 113: Line 113:
  
  
<code cpp>
+
<code cpp-qt>
 
void ProxyLayout::addLayout(QLayout *layout)
 
void ProxyLayout::addLayout(QLayout *layout)
 
{
 
{
Line 155: Line 155:
 
NOW, implement all required methods of a QLayout Subclass, and point them to the actual activated layout
 
NOW, implement all required methods of a QLayout Subclass, and point them to the actual activated layout
  
<code cpp>
+
<code cpp-qt>
 
void ProxyLayout::addItem(QLayoutItem *item) {
 
void ProxyLayout::addItem(QLayoutItem *item) {
 
         if (m_numOfClients<0) {
 
         if (m_numOfClients<0) {
Line 234: Line 234:
 
And finally a event filter handling resize event
 
And finally a event filter handling resize event
  
<code cpp>
+
<code cpp-qt>
 
bool ProxyLayout::eventFilter(QObject *obj, QEvent *event)
 
bool ProxyLayout::eventFilter(QObject *obj, QEvent *event)
 
{
 
{
Line 280: Line 280:
  
 
Register event handler to the widget you are setting layout:
 
Register event handler to the widget you are setting layout:
<code cpp>
+
<code cpp-qt>
 
ProxyLayout *layout = new ProxyLayout();
 
ProxyLayout *layout = new ProxyLayout();
 
layout->addLayout(...);
 
layout->addLayout(...);
Line 290: Line 290:
 
'''Example for constructor of a MainWindow class:'''
 
'''Example for constructor of a MainWindow class:'''
  
<code cpp>
+
<code cpp-qt>
 
MainWindow::MainWindow(QWidget *parent)
 
MainWindow::MainWindow(QWidget *parent)
 
     : QMainWindow(parent)
 
     : QMainWindow(parent)

Latest revision as of 04:17, 11 October 2012

Article Metadata
Tested with
Devices(s): 5800 XpressMusic, N900
Compatibility
Platform(s): S60 5th Edition, Maemo 5
Symbian
Article
Keywords: QLayout, Rotation, Portrait, Landscape
Created: juergenm (20 Mar 2010)
Last edited: hamishwillee (11 Oct 2012)

Contents

[edit] Overview

This document describes, a comfortable and flexible way, how to manage two different layouts for a specific widget, where one is automatically used when the device is in portrait orientation, and the other when the device is in landscape orientation.

There is no limitation in the kind of QLayout derived layout implementation that can be used/assigned.

There is no need to duplicate subwidgets that shall be visible in our target widget in both views/orientation. They can simply be assigned to both, the portrait and the landscape layout.

The concept of this approach is, to create a subclass from QLayout, as a ProxyLayout class. Two independent layouts are made known to this ProxyLayout class. The ProxyLayout class implements the required re-implementations of a QLayout in the way, that it passes all those calls to the selected (portrait or landscape) layout, which was made known to it.

This snippet can be self-signed.

Main code snippets are shown here. A complete small sample test project with a fully re-usable ProxyLayout (portrait-landscape-layout) class is available.

[edit] Preconditions

This article was created and verified based on Qt 4.6.2 for Symbian, and tested on 5800 Xpress Music with Firmware Version v31.0.101. It should work on other firmware and phones with Symbian rotation capability, and it should basically also work on Maemo; all under Qt.

Note, that this methodology is some kind of hack, and certain limitations may apply on the used sub-layouts. (HBox, VBox and grid layout at least should work.) Other than that, both - the assigned portrait and landscape layouts- must include the SAME client widgets. E.g. if some widget is only part of the portrait sub-layout, but not of the landscape sublayout, then this widget will be placed "uncontrolled" on the screen in landscape mode. This is certainly an area for further improvement work.

[edit] Header file

Class Declaration and Constructors

class ProxyLayout : public QLayout
{
public:
ProxyLayout();

The main API methods

public:
void addLayout(QLayout *layout);
void removeLayout(QLayout *layout);

Re-implementation of QLayout Methods, as needed

public:
void addItem(QLayoutItem *item);
QSize sizeHint() const;
QSize minimumSize() const;
void setGeometry(const QRect &rect);
bool hasHeightForWidth() const;
int heightForWidth( int ) const;
QLayoutItem * itemAt(int index ) const;
QLayoutItem * takeAt ( int index );
int count() const;

The next methods are an event filter and helper internal methods. It is used to monitor resize events delivered to a widget the layout is set to.

 protected:
bool eventFilter(QObject *obj, QEvent *event);
 
private:
void activateLayout(int idx);
void setupParentFilter();

And finally, internal management variables and methods. Those are protected.

 private:
QList<QLayout*> m_layoutList;
int m_currentLayoutIdx;
bool filterInstalled;
};


[edit] Source file

FIRST, some basic methods that implement the quite universal engine of this portrait landscape manager.


void ProxyLayout::addLayout(QLayout *layout)
{
m_layoutList.append(layout);
layout->setParent(this);
activateLayout(m_layoutList.size() - 1);
}
 
void ProxyLayout::removeLayout(QLayout *layout)
{
int idx = layouts.indexOf(layout);
if (idx > -1) {
layouts.removeAt(idx);
if (currentLayoutIdx > idx) {
currentLayoutIdx--;
} else if (currentLayoutIdx == idx) {
if (currentLayoutIdx >= layouts.size())
currentLayoutIdx = layouts.size() - 1;
activateLayout(currentLayoutIdx);
}
}
}
 
void ProxyLayout::activateLayout(int idx)
{
if(-1 < idx && idx < m_layoutList.size()) {
if (idx != m_currentLayoutIdx) {
m_currentLayoutIdx = idx;
activate(); //inherited from QLayout
update(); //inherited from QLayout
QWidget *w = parentWidget();
if(w)
w->updateGeometry();
}
} else
qDebug() << "ERROR: selecting unassigned MultLayoutClient. IDX=" << idx
<< " numOfClients=" << m_layoutList.size();
}

NOW, implement all required methods of a QLayout Subclass, and point them to the actual activated layout

void ProxyLayout::addItem(QLayoutItem *item) {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return;
}
m_layoutList[getLayoutIdx()]->addItem(item);
}
 
QSize ProxyLayout::sizeHint() const {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return QSize(0,0);
}
return (m_layoutList[getLayoutIdx()]->sizeHint());
}
 
QSize ProxyLayout::minimumSize() const {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return QSize(0,0);
}
return( m_layoutList[getLayoutIdx()]->minimumSize());
}
 
void ProxyLayout::setGeometry(const QRect &rect) {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return ;
}
m_layoutList[getLayoutIdx()]->setGeometry(rect);
}
 
bool ProxyLayout::hasHeightForWidth() const {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return false;
}
return (m_layoutList[getLayoutIdx()]->hasHeightForWidth());
}
 
int ProxyLayout::heightForWidth( int n) const {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return 0;
}
return( m_layoutList[getLayoutIdx()]->heightForWidth( n ));
}
 
 
QLayoutItem * ProxyLayout::itemAt(int index ) const {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return 0;
}
return ( m_layoutList[getLayoutIdx()]->itemAt(index ));
}
 
 
QLayoutItem * ProxyLayout::takeAt ( int index ) {
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return 0;
}
return (m_layoutList[getLayoutIdx()]->takeAt(index ));
}
 
int ProxyLayout::count () const {
((ProxyLayout*)this)->setupParentFilter();
if (m_numOfClients<0) {
qDebug() << "ERROR: using MultLayout before assigning client.";
return 0;
}
return( m_layoutList[getLayoutIdx()]->count());
}

And finally a event filter handling resize event

bool ProxyLayout::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
QSize qs = resizeEvent->size();
qreal diff, minDiff = 1000.0, ratio = (qreal)qs.width() / qs.height();
if (ratio > 1.0)
ratio = -1.0 / ratio;
int ind = -1;
QSize sz;
for (int i= 0; i < m_layoutList.size(); ++i) {
sz = m_layoutList.at(i)->sizeHint();
diff = (qreal)sz.width()/sz.height();
if (diff > 1.0)
diff = -1.0 / diff;
diff = qAbs(diff - ratio);
if (diff < minDiff) {
minDiff = diff;
ind = i;
}
}
if (ind > -1)
activateLayout(ind);
return false;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}

[edit] Postconditions

ProxyLayout is a pseudo layout class, that looks like a layout class, but effectively just manages the switching between two assigned layouts, from which one is intended for portrait usage, and the other for landscape usage. Usage:

  • create any layouts for portrait and landscape usage
  • create an instance of this ProxyLayout.
  • assign the pre-created layouts to the ProxyLayout instance by using the addLayout method.
  • assing ProxyLayout to your widget.

Register event handler to the widget you are setting layout:

ProxyLayout *layout = new ProxyLayout();
layout->addLayout(...);
layout->addLayout(...);
QWidget *w = new QWidget();
w->setLayout(layout);

Example for constructor of a MainWindow class:

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
#ifdef Q_WS_MAEMO_5
setAttribute(Qt::WA_Maemo5AutoOrientation, true);
#endif
button1 = new QPushButton("One");
button2 = new QPushButton("Two");
button3 = new QPushButton("Three");
button4 = new QPushButton("Four");
 
QMenuBar *bar = win.menuBar();
bar->addAction("Action 1");
bar->addAction("Action 2");
bar->addAction("Action 3");
 
layoutPortrait = new QVBoxLayout;
layoutPortrait->addWidget(button1);
layoutPortrait->addWidget(button2);
layoutPortrait->addWidget(button3);
layoutPortrait->addWidget(button4);
 
layoutLandscape = new QHBoxLayout;
layoutLandscape->addWidget(button1);
layoutLandscape->addWidget(button2);
layoutLandscape->addWidget(button3);
layoutLandscape->addWidget(button4);
 
autoLayout = new ProxyLayout();
autoLayout->addLayout(layoutPortrait);
autoLayout->addLayout(layoutLandscape);
 
centralWidget = new QWidget();
centralWidget->setLayout(autoLayout);
setCentralWidget(centralWidget);
}

[edit] Complete Source Code of ProxyLayout class Example and Test application

The complete source code of a class that implements the here described methodology can be found, to gether with a very simple demo application that makes use of it, on the following page:


[edit] References

The idea for this was taken up from some very old Qt forum discussion, where unfortunately no code was published (to my knowledge): http://lists.trolltech.com/qt-interest/2004-01/thread00746-0.html

Other related references:

This page was last modified on 11 October 2012, at 04:17.
114 page views in the last 30 days.
×