Compare commits
No commits in common. "e151d8c304de3edc65ce9ebd4b38e55415bdfc28" and "20817b7e424d6ecdb974806dde5da6eac8c4ea29" have entirely different histories.
e151d8c304
...
20817b7e42
4 changed files with 79 additions and 192 deletions
8
flake.lock
generated
8
flake.lock
generated
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue