Quickshell


Positioning

TIP

Read the entire page, understanding this is critical to building a well designed shell.

Item has two sets of size properties, actual size ( Go to Item.width and Go to Item.height) and implicit / desired ( Go to Item.implicitWidth and Go to Item.implicitHeight).

Container items, such as layouts and wrappers, use the implicit size of their children to determine their own implicit size, and their actual size to detetermine the actual size of their children. If managed by a container, an Item should not set its own size, and should instead allow the container to determine it based on its implicit size.

Put simply, implicit size should flow from children to parents, while actual size should flow from parent to children.

In addition to size, Items also have position properties ( Go to Item.x and Go to Item.y). Similarly to actual size, (actual) position should not be set directly if your item is managed by a container, though there is no such thing as implicit position.

WARNING

Many QtQuick Items have zero size by default (both implicit and actual).

An invisible zero sized item (usually a custom container without implicit size set) is a common bug and often manifests as an item being laid out as if it took no space.

Quickshell will attempt to detect zero sized items when a window is initially made visible and log a warning, but it cannot detect all cases. Please be aware these exist.

Container Items

Below is an example container which adds a margin to a rectangle, and interacts properly with other container types.

Item {
  property real margin: 5
  
  // Set the implicit size of the containing item to the size of
  // the contained item, plus the margin on each side.
  implicitWidth: child.implicitWidth + margin * 2
  implicitHeight: child.implicitHeight + margin * 2

  Rectangle {
    id: child

    // Set the size of the child item relative to the actual size
    // of the parent item. If the parent item is constrained
    // or stretched the child's position and size will be similarly
    // constrained.
    x: parent.margin
    y: parent.margin
    width: parent.width - parent.margin * 2
    height: parent.height - parent.margin * 2
    
    // The child's implicit / desired size, which will be respected
    // by the container item as long as it is not constrained
    // or stretched.
    implicitWidth: 50
    implicitHeight: 50
  }
}

If we were to write this as a reusable component, we could use Binding to control the child item’s actual size and position.

Item {
  id: wrapper
  property real margin: 5
  required default property Item child

  // Set the item's visual children list to just the passed item.
  children: [child]

  implicitWidth: child.implicitWidth + margin * 2
  implicitHeight: child.implicitHeight + margin * 2

  // Bind the child's position and size.
  // Note that this syntax is exclusive to the Binding type.
  Binding { wrapper.child.x: wrapper.margin }
  Binding { wrapper.child.y: wrapper.margin }
  Binding { wrapper.child.width: wrapper.width - wrapper.margin * 2 }
  Binding { wrapper.child.height: wrapper.height - wrapper.margin * 2 }
}

Note: WrapperItem is a builtin component that adds margins similarly to this.

Reducing boilerplate with Anchors

We can reduce the amount of boilerplate we have to write using QtQuick Anchors.

Anchors exist as a shorthand way to achieve many common position and size bindings. See the linked qt documentation for more details on how to use them.

The following example is equivalent to the one above, but uses anchors instead of setting position and size directly. A similar change can be made to the Binding example.

Item {
  property real margin: 5
  
  implicitWidth: child.implicitWidth + margin * 2
  implicitHeight: child.implicitHeight + margin * 2

  Rectangle {
    id: child

    // "Fill" the space occupied by the parent, setting width
    anchors.fill: parent
    // Add a margin to all anchored sides.
    anchors.margins: parent.margin
    
    implicitWidth: 50
    implicitHeight: 50
  }
}

childrenRect and binding loops

The most common mistake made when creating container items is trying to use Go to Item.childrenRect to determine the size of a child item, such as in the example below:

Item {
  implicitWidth: childrenRect.width
  implicitHeight: childrenRect.height
  
  Rectangle {
    anchors.fill: parent

    implicitWidth: 50
    implicitHeight: 50
  }
}

While the snippet above might look like it should work, it is actually hiding a nasty bug.

As stated at the top of the page, an item’s implicit size should be used to determine its parent’s implicit size, and the parent’s actual size should be used to determine the child’s actual size. childrenRect breaks this pattern.

childrenRect encompasses the geometry of all child items, meaning their actual geometry, not their implicit geometry. This results in the container item’s size having an indirect dependency on itself, in what is known as a binding loop.

If we were to try to figure out what implicitWidth is by hand, it would look something like this:

container.implicitWidth = container.childrenRect.width = child.width = container.width (via anchor) = container.implicitWidth = … (repeats forever)

which isn’t a valid definition.

MarginWrapper components

To solve the boilerplate problem that often leads users to childrenRect, Quickshell comes with MarginWrapperManager and a set of components based on it.

MarginWrapperManager automatically handles the size and position relationship between a container item and a single child item, skipping most of the boilerplate in the above examples. See its linked documentation for more information on how to use it.

Rewriting the examples from the top of the page:

Item {
  MarginWrapperManager {
    margin: 5
    // By default, MarginWrapperManager centers the child
    // instead of resizing it when encountering constraints.
    resizeChild: true
  }
  
  // Automatically detected by MarginWrapperManager as the
  // primary child of the container and sized accordingly.
  Rectangle {
    implicitWidth: 50
    implicitHeight: 50
  }
}

Or as a reusable component:

Item {
  // A bidirectional binding to manager.margin,
  // where the default value is set.
  property alias margin: manager.margin
  
  // MarginWrapperManager tries to automatically detect
  // the primary child of the container, but exposing the
  // child property allows us to both access the child
  // externally and override it if automatic detection fails.
  property alias child: manager.margin

  // MarginWrapperManager automatically manages the implicit size
  // of the container and actual size of the child.
  MarginWrapperManager {
    id: manager
    resizeChild: true
    margin: 5 // the default value of margin
  }
}

Quickshell bundles three of the most commonly used wrappers, which are implemented similarly to the example above:

Layouts

QtQuick comes with a set of layout types in the QtQuick.Layouts module.

Layouts, such as the Row, Column and Grid layout, are extremely useful for positioning items adjacent to eachother. See the linked qt documentation for more details.

NOTE

Layouts have a default spacing of 5 pixels between items, not zero.