Compare commits

..

No commits in common. "e151d8c304de3edc65ce9ebd4b38e55415bdfc28" and "20817b7e424d6ecdb974806dde5da6eac8c4ea29" have entirely different histories.

4 changed files with 79 additions and 192 deletions

8
flake.lock generated
View file

@ -23,11 +23,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1747564942, "lastModified": 1747385375,
"narHash": "sha256-XBs7dhqsY3vdrtukt9Qwcqz8CSixMnm/XvpqD+kn+TA=", "narHash": "sha256-GhaFgcAftH3rY/SeLlNL/mdO8icNDXihVFwSY9ypE9U=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "823456b58e83880e8f6bfaa785ac949975817d2a", "rev": "49ad86f42285e8beccf77509c662005785f1bae1",
"revCount": 529, "revCount": 518,
"type": "git", "type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell" "url": "https://git.outfoxxed.me/quickshell/quickshell"
}, },

View file

@ -32,7 +32,7 @@ const remarkParseAtTypes: RemarkPlugin<[]> = () => {
const node = rawNode as Md.Literal; const node = rawNode as Md.Literal;
node.value = node.value.replace( node.value = node.value.replace(
/@@((?<module>([A-Z]\w*\.)*)(?<type>([A-Z]\w*))\.?)?((?<member>[a-z]\w*)((?<function>\(\))|(?<signal>\(s\)))?)?(?=[$.,;:)\s]|$)/g, /@@((?<module>([A-Z]\w*\.)*)(?<type>([A-Z]\w*))\.?)?((?<member>[a-z]\w*)((?<function>\(\))|(?<signal>\(s\)))?)?(?=[$.,;:\s]|$)/g,
(_full, ...args) => { (_full, ...args) => {
type Capture = { type Capture = {
module: string | undefined; module: string | undefined;

View file

@ -145,8 +145,7 @@ more correct code should you chose to use it.
> [!NOTE] > [!NOTE]
> Nix users should note that qmlls will not be able to pick up qml modules > Nix users should note that qmlls will not be able to pick up qml modules
> that are not in `QML2_IMPORT_PATH`. The easiest way to ensure this is by setting > that are not in `QML2_IMPORT_PATH`.
> `qt.enable` to `true` and installing the quickshell package globally.
# Next steps # Next steps

View file

@ -6,219 +6,107 @@ import MD_Title from "@components/MD_Title.tsx"
# {frontmatter.title} # {frontmatter.title}
> [!TIP] QtQuick has multiple ways to position components. This page has instructions for where and how
> Read the entire page, understanding this is critical to building a well designed shell. to use them.
@@QtQuick.Item has two sets of size properties, actual size (@@QtQuick.Item.width and @@QtQuick.Item.height) ## <MD_Title titleVar={2}> Anchors </MD_Title>
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 Anchors can be used to position components relative to another neighboring component.
their own implicit size, and their actual size to detetermine the actual size of their children. It is faster than [manual positioning](#manual-positioning) and covers a lot of simple
If managed by a container, an Item should not set its own size, and should instead allow use cases.
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 The [Qt Documentation: Positioning with Anchors](https://doc.qt.io/qt-6/qtquick-positioning-anchors.html)
parent to children. page has comprehensive documentation of anchors.
In addition to size, Items also have position properties (@@QtQuick.Item.x and @@QtQuick.Item.y). ## <MD_Title titleVar={2}> Layouts </MD_Title>
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] Layouts are useful when you have many components that need to be positioned relative to
> Many QtQuick Items have *zero size* by default (both implicit and actual). eachother such as a list.
>
> 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 The [Qt Documentation: Layouts Overview](https://doc.qt.io/qt-6/qtquicklayouts-overview.html)
Below is an example container which adds a margin to a rectangle, and interacts properly page has good documentation of the basic layout types and how to use them.
with other container types.
> [!note/Note:]
```qml > Layouts by default have a nonzero spacing.
@@QtQuick.Item {
property real margin: 5 ## <MD_Title titleVar={2}> Manual Positioning </MD_Title>
// Set the implicit size of the containing item to the size of If layouts and anchors can't easily fulfill your usecase, you can also manually position and size
// the contained item, plus the margin on each side. components by setting their @@QtQuick.Item.x, @@QtQuick.Item.y, @@QtQuick.Item.width and @@QtQuick.Item.height
implicitWidth: child.implicitWidth + margin * 2 properties, which are relative to the parent component.
implicitHeight: child.implicitHeight + margin * 2
This example puts a 100x100px blue rectangle at x=20,y=40 in the parent item. Ensure the size
@@QtQuick.Rectangle { of the parent is large enough for its content or positioning based on them will break.
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 ```qml
@@QtQuick.Item { @@QtQuick.Item {
// make sure the component is large enough to fit its children
implicitWidth: childrenRect.width implicitWidth: childrenRect.width
implicitHeight: childrenRect.height implicitHeight: childrenRect.height
@@QtQuick.Rectangle { @@QtQuick.Rectangle {
anchors.fill: parent color: "blue"
x: 20
implicitWidth: 50 y: 40
implicitHeight: 50 width: 100
height: 100
} }
} }
``` ```
While the snippet above might look like it should work, it is actually hiding a nasty bug. ## <MD_Title titleVar={2}> Notes </MD_Title>
As stated at the top of the page, an item's implicit size should be used to determine ### <MD_Title titleVar={3}> Component Size </MD_Title>
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, The @@QtQuick.Item.implicitHeight and @@QtQuick.Item.implicitWidth properties control the
not their *implicit* geometry. This results in the container item's size having an indirect _base size_ of a component, before layouts are applied. These properties are _not_ the same as
dependency on itself, in what is known as a *binding loop*. @@QtQuick.Item.height and @@QtQuick.Item.width which are the final size of the component.
You should nearly always use the implicit size properties when creating a component,
however using the normal width and height properties is fine if you know an
item will never go in a layout.
If we were to try to figure out what implicitWidth is by hand, it would look something like this: This example component puts a colored rectangle behind some text, and will act the same
way in a layout as the text by itself.
*container.implicitWidth = container.childrenRect.width = child.width = container.width (via anchor) ```qml {filename="TextWithBkgColor.qml"}
= container.implicitWidth = ... (repeats forever)* @@QtQuick.Rectangle {
implicitWidth: text.implicitWidth
implicitHeight: text.implicitHeight
which isn't a valid definition. @@QtQuick.Text {
id: text
text: "hello!"
}
}
```
### MarginWrapper components If you want to size your component based on multiple others or use any other math you can.
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 ```qml {filename="PaddedTexts.qml"}
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 { @@QtQuick.Item {
@@Quickshell.Widgets.MarginWrapperManager { // width of both texts plus 5
margin: 5 implicitWidth: text1.implicitWidth + text2.implicitWidth + 5
// By default, MarginWrapperManager centers the child // max height of both texts plus 5
// instead of resizing it when encountering constraints. implicitHeight: Math.min(text1.implicitHeight, text2.implicitHeight) + 5
resizeChild: true
@@QtQuick.Text {
id: text1
text: "text1"
} }
// Automatically detected by MarginWrapperManager as the @@QtQuick.Text {
// primary child of the container and sized accordingly. id: text2
@@QtQuick.Rectangle { anchors.left: text1.left
implicitWidth: 50 text: "text2"
implicitHeight: 50
} }
} }
``` ```
Or as a reusable component: ### <MD_Title titleVar={3}> Coordinate space </MD_Title>
```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 You should always position or size components relative to the closest possible
// of the container and actual size of the child. parent. Often this is just the @@QtQuick.Item.parent property.
@@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 Refrain from using things like the size of your screen to size a component,
to the example above: as this will break as soon as anything up the component hierarchy changes, such
- @@Quickshell.Widgets.WrapperItem as adding padding to a bar.
- @@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.