216 lines
7.6 KiB
Text
216 lines
7.6 KiB
Text
---
|
|
title: "Item Size and Position"
|
|
index: 2
|
|
---
|
|
# {frontmatter.title}
|
|
|
|
> [!TIP]
|
|
> Read the entire page, understanding this is critical to building a well designed shell.
|
|
|
|
@@QtQuick.Item has two sets of size properties, actual size (@@QtQuick.Item.width and @@QtQuick.Item.height)
|
|
and implicit / desired (@@QtQuick.Item.implicitWidth and @@QtQuick.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 (@@QtQuick.Item.x and @@QtQuick.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.
|
|
|
|
```qml
|
|
@@QtQuick.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
|
|
|
|
@@QtQuick.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 @@QtQml.Binding
|
|
to control the child item's actual size and position.
|
|
```qml
|
|
@@QtQuick.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.
|
|
@@QtQuick.Binding { wrapper.child.x: wrapper.margin }
|
|
@@QtQuick.Binding { wrapper.child.y: wrapper.margin }
|
|
@@QtQuick.Binding { wrapper.child.width: wrapper.width - wrapper.margin * 2 }
|
|
@@QtQuick.Binding { wrapper.child.height: wrapper.height - wrapper.margin * 2 }
|
|
}
|
|
```
|
|
Note: @@Quickshell.Widgets.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](https://doc.qt.io/qt-6/qtquick-positioning-anchors.html).
|
|
|
|
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.
|
|
```qml
|
|
@@QtQuick.Item {
|
|
property real margin: 5
|
|
|
|
implicitWidth: child.implicitWidth + margin * 2
|
|
implicitHeight: child.implicitHeight + margin * 2
|
|
|
|
@@QtQuick.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 @@QtQuick.Item.childrenRect
|
|
to determine the size of a child item, such as in the example below:
|
|
|
|
```qml
|
|
@@QtQuick.Item {
|
|
implicitWidth: childrenRect.width
|
|
implicitHeight: childrenRect.height
|
|
|
|
@@QtQuick.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
|
|
@@Quickshell.Widgets.MarginWrapperManager and a set of components based on it.
|
|
|
|
@@Quickshell.Widgets.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:
|
|
```qml
|
|
@@QtQuick.Item {
|
|
@@Quickshell.Widgets.MarginWrapperManager { margin: 5 }
|
|
|
|
// Automatically detected by MarginWrapperManager as the
|
|
// primary child of the container and sized accordingly.
|
|
@@QtQuick.Rectangle {
|
|
implicitWidth: 50
|
|
implicitHeight: 50
|
|
}
|
|
}
|
|
```
|
|
|
|
Or as a reusable component:
|
|
```qml
|
|
@@QtQuick.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.
|
|
@@Quickshell.Widgets.MarginWrapperManager {
|
|
id: manager
|
|
margin: 5 // the default value of margin
|
|
}
|
|
}
|
|
```
|
|
|
|
Quickshell bundles three of the most commonly used wrappers, which are implemented similarly
|
|
to the example above:
|
|
- @@Quickshell.Widgets.WrapperItem
|
|
- @@Quickshell.Widgets.WrapperRectangle
|
|
- @@Quickshell.Widgets.WrapperMouseArea
|
|
|
|
## Layouts
|
|
|
|
QtQuick comes with a set of layout types in the
|
|
[QtQuick.Layouts](https://doc.qt.io/qt-6/qtquicklayouts-overview.html) 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.
|