refactor guide pages to use content collections
also configuration->guide
This commit is contained in:
parent
a449f976c7
commit
b066a48976
13 changed files with 68 additions and 83 deletions
223
src/guide/positioning.mdx
Normal file
223
src/guide/positioning.mdx
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
---
|
||||
title: "Positioning"
|
||||
---
|
||||
import MD_Title from "@components/MD_Title.tsx"
|
||||
|
||||
# {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
|
||||
// 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.
|
||||
@@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
|
||||
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:
|
||||
- @@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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue