AeroSun
AeroSun

Reputation: 2561

QLayout and discrete widget representation - how to?

Lets, for example, we have QHBoxLayout inside QMainWindow.

Lets we have set of the widgets inside above layout.

But (!) few of widgets have discrete visual representations. In another words they have few states dependent on available space.

For example:

  1. if there are too much available space - it must look like big image + some text
  2. if there available minimal space - it must look like little image
  3. if there available few more than minimal space - it must look like button + label
  4. etc...

So when the user change the main window size our "dynamic" widgets must show own representation dependent on available space.

How it could be achieved in Qt?

UPD: the closest behavior present in the Microsoft ribbon interface

UPD2: the QML closest behavior present in gif below (on part where window resized by user)

enter image description here

UPD3: more complex example - each panel in the menu panel change content elements view and count that depends from available space enter image description here

Upvotes: 2

Views: 570

Answers (2)

jonspaceharper
jonspaceharper

Reputation: 4367

For this answer, I will use Qt-equivalent terms, not the official MS Ribbon terminology.

An Overview

You are actually looking at a number of layouts, in a pattern like so:

QToolbar
| (Layout)
+--> QGroupBox/QToolButton
|     | (Layout)
|     +----->Button
|     +----->Button
+--> QGroupBox/QToolButton

The Pattern

Let's start with just the QGroupBox that populates helps sort our buttons into groups.

Consider that our group box may hold both our dynamic QToolButton and regular widgets. When the available space shrinks, the layout:

  • Calculates the minimum space required for fixed-sized widgets and the minimumSizeHint() values of the widgets without fixed size policies.
  • Apportions the remaining space based on the growth policy of each widget.
  • However, our dynamic tool button may or may not change size depending on available space.
  • Now we have to start checking if the children of the layout are dynamic or not, using qobject_cast.
  • When we find a dynamic button, we have to determine if it can shrink or not. If it does, we cache this and its preferred, smaller size.
  • If it's going to shrink, we have to recalculate our minimum size.
  • And then we have to keep going, hoping that shrinking the buttons during resize doesn't cause any tricky tie-breakers.
  • Tie breakers: if two buttons can collapse to a smaller size, which one goess first? We have to account for that, too. Also, MS uses rows of three buttons. We can't collapse one from Qt::ToolButtonTextBesideIcon to Qt::ToolButtonIconOnly, we have to collapse a set of three.
  • Three horizontal buttons can collapse to three vertical buttons. This, too, needs to be calculated and cached.

There is hope.

We can simplify by making each container only able to hold dynamic tool buttons. Then we don't have to deal with tricky issues like fixed sized widgets and we only need to deal with one type of widget.

Microsoft has also helpfully implemented this behavior for us. You can learn a lot about the behavioral constraints of your layout and child widgets by empirical observation. When do groups collapse? If a group collapses, do other groups expand to take up the space? (The answer to that is no, by the way.) How do buttons group together as they collapse? Are some special in how they collapse and expand (constraints on their expansion and collapsing behavior)?

Qt has also implemented several layouts for us to study and get an idea of how they work and how they are different. QGridLayout is a promising basis, particularly if you do some math to change the row span of widgets on the fly (this is for grouping buttons as they collapse from vertical layout to sets of three horizontal buttons).

In Summary

Completely answering your question is far too broad to be on-topic on SO, but I hope this info guides you where you need to go.

Upvotes: 1

Maxim Paperno
Maxim Paperno

Reputation: 4869

There's no magic solution for this. You'll need to implement events/actions to take when the container is resized. It can be as simple or complex as you need. There are several different ways to go about actually implementing it, depending on complexity and scope involved (it is just a few items? the whole UI? etc...).

Here is a very basic (crude but effective) example which moves a toolbar to either be on the same line as another toolbar (for wider window) or to its own line (for narrower window). MdiChild in this case is a QWidget implementation which, obviously, contains other widgets and manages their layout.

void MdiChild::resizeEvent(QResizeEvent * event)
{
  QWidget::resizeEvent(event);
  if (size().width() > ui->topToolbarLayout->sizeHint().width() + ui->botToolbarLayout->sizeHint().width() + 30) {
    ui->botToolbarLayout->removeWidget(modelsToolbar);
    ui->topToolbarLayout->insertWidget(1, modelsToolbar);
  }
  else {
    ui->topToolbarLayout->removeWidget(modelsToolbar);
    ui->botToolbarLayout->insertWidget(0, modelsToolbar);
  }
}

As you can see it has to take the container size into account, and the most direct way to do that is in the QWidget::resizeEvent(). You could do anything here you want... change text buttons to icons, hide/show/move stuff... whatever.

Instead of handling the visual changes in the container, you could of course hook up signals/slots to the contained widgets... eg. a custom signal emit availableSizeChanged(QSize size) from the resizeEvent() handler connect()ed to the contained widget(s).

You may find QStateMachine handy to maintain visual state in a formal manner (this is what QML uses for it's state[s] property and transition system).

You could also implement your own QLayout as others have suggested. Though the scope of the question itself suggests you may not need to. The question is quite general IMHO. The two examples provided have massive differences in terms of complexity.

Upvotes: 2

Related Questions