guide page versioning

This commit is contained in:
outfoxxed 2025-07-26 19:39:33 -07:00
parent d23bfbfd00
commit e7c807ac85
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
12 changed files with 54 additions and 10 deletions

View file

@ -0,0 +1,39 @@
---
title: "Distributing Configurations"
index: 9
---
If you plan to distribute your configuration to a lot of users, you should keep
a couple things in mind:
### API Breaks
As Quickshell is still in a somewhat early stage of development, Quickshell will
have API breaks for future versions.
You should have a way to track specific revisions to avoid breakage if a user
updates Quickshell before you can update your configuration.
With Nix, this should be as simple as tracking a specific revision.
For Arch, or any other distributions without a mechanism to do this, you may
want to include a package that builds a specific Quickshell revision with
your configuration.
### Configuration Paths
Quickshell can load configurations from a number of different paths.
The ideal path depends on how you distribute your config.
#### As dotfiles
If you distribute your config as a set of dotfiles, you should place it in
`$XDG_CONFIG_HOME/quickshell/<name>` (usually `~/.config/quickshell/<name>`).
You should name your config and refrain from using the bare `$XDG_CONFIG_HOME/quickshell`
directory, as that will make it harder for users to have any other configuration.
Any directory in the `$XDG_CONFIG_HOME/quickshell` can be used using the Quickshell command
by specifying `--config` or `-c`, like so: `qs -c <name>`.
#### As a package
Some configurations are distributed as distro packages. These packages should use a
path in `$XDG_CONFIG_DIRS`, usually `/etc/xdg` for their files.
As with dotfiles, named configurations should be used (`$CONFIG_DIR/quickshell/<name>`).

172
src/guide/v0_1_0/faq.md Normal file
View file

@ -0,0 +1,172 @@
---
title: "FAQ"
description: "Frequently Asked Questions"
index: 1000
---
> [!NOTE]
> This page is being actively expanded upon as more common questions come up.
>
> Make sure to also read the [Item Size and Position](/docs/guide/size-position) and
> [QML Language](/docs/guide/qml-language) pages for questions related to
> item sizing/positioning and QML in general.
## Misc
### What will breaking changes look like
Quickshell will have breaking changes in future releases. These changes can
span the APIs exposed by Quickshell, as well as best practice across all
APIs, but will not change the language syntax or anything exposed by Qt.
Most changes will be relatively trivial, though you may have to make the same
trivial change a considerable amount of times if you have a large configuration.
Migration guides will be provided between each release version.
### Should I use a process per widget
No. Using a process per widget will use significantly more memory than using a
single process.
## How do I
### Make a rounded window
Rounded windows are simply transparent, square windows, with a rounded rectangle
inside of them.
```qml
@@Quickshell.PanelWindow {
color: "transparent"
@@QtQuick.Rectangle {
// match the size of the window
anchors.fill: parent
radius: 5
color: "white" // your actual color
}
}
```
### Make a list of widgets
If you have a short list of items to display, such as a list of active music
players or system tray items, you want a @@QtQuick.Repeater, which is
usually combined with a @@QtQuick.Layouts.RowLayout or @@QtQuick.Layouts.ColumnLayout.
If you have a longer list, such as a list of entries in an application launcher
or a list that needs to be scrolled, you want a @@QtQuick.ListView instead.
### Run a program or script
Use @@Quickshell.Io.Process.
If you want the entire output of the process as a single string, use
@@Quickshell.Io.StdioCollector to collect the Process's stdio.
If the process is intended to run for a long time and stream in data,
e.g. a command that listens to window manager IPC commands, use
@@Quickshell.Io.SplitParser to return each datum as it arrives.
### Show widgets conditionally
Conditionally showing widgets can be done in two ways, simply using the @@QtQuick.Item.visible property,
or by using a @@QtQuick.Loader.
Depending on your use case, both the @@QtQuick.Loader and the @@QtQuick.Item.visible property
may make sense at equal complexity. If you want to unload a widget tree to save memory or
speed up load times, then you should use Loaders.
Note that you can also change out a Loader's component conditionally:
```qml
@@QtQuick.Loader {
readonly property Component thing1: ...
readonly property Component thing2: ...
sourceComponent: condition ? thing1 : thing2
}
```
### Round an image
The easiest way to round an image is with @@Quickshell.Widgets.ClippingWrapperRectangle,
which is a [MarginWrapper] component. This component will attempt to match the size of
its contained item.
```qml
@@Quickshell.Widgets.ClippingWrapperRectangle {
radius: 10
@@Quickshell.Widgets.IconImage { // or a normal @@QtQuick.Image
source: ...
implicitSize: ...
}
}
```
[MarginWrapper]: /docs/guide/size-position#marginwrapper-components
### Reference images and resources
By default, paths passed to components such as @@QtQuick.Image or
@@Quickshell.Io.FileView as strings are relative to Quickshell's working
directory. Usually, this is not the desired behavior.
To get a file path relative to the current QML file, you can use @@QtQml.Qt.resolvedUrl().
To get a file path relative to your shell's reserved cache, data, or state directories,
you can use @@Quickshell.Quickshell.cachePath(), @@Quickshell.Quickshell.dataPath() or
@@Quickshell.Quickshell.statePath(),
### Add a drop-shadow
Use @@QtQuick.Effects.RectangularShadow if you want a *rectangular*, *round rectangular*,
or *circular* drop shadow.
For any other shape, you will have to use a @@QtQuick.Effects.MultiEffect and set
@@QtQuick.Effects.MultiEffect.shadowEnabled, as well as its other shadow and blur
related properties.
### Get rid of the purple/black icons
The @@Quickshell.Quickshell.iconPath() function has three variants:
- One draws a purple/black square if the icon is missing.
- One allows you to specify a fallback icon if the desired one is missing.
- One returns an empty string if the icon is missing.
Either of the last two variants can be used to avoid the purple/black square.
### Work with an unsupported WM
Quickshell currently only bundles interfaces for working with Hyprland and i3,
however you can implement your own using @@Quickshell.Io.Socket or @@Quickshell.Io.Process,
which can be used to parse and send IPC messages.
### Open/close windows with commands
Quickshell doesn't come with a command to open or close a window; however, you can
make your own using @@Quickshell.Io.IpcHandler, allowing you to call functions
inside of Quickshell with a command. Said functions can change the
@@Quickshell.QsWindow.visible property of a window, or load/unload it using a
@@Quickshell.LazyLoader.
### Reduce memory usage
The main thing you can do to reduce the memory usage of a given configuration
is to use Loaders.
Loaders can be used to create objects only when needed, and destroy them when not needed.
@@QtQuick.Loader should be used if the component being loaded inherits from @@QtQuick.Item,
otherwise, a @@Quickshell.LazyLoader should be used.
## Something is broken
### There is a hole in my window
If you set a Rectangle's color to `"transparent"` and touch its `border` property,
you'll hit [QTBUG-137166](https://bugreports.qt.io/browse/QTBUG-137166), which
causes everything under the transparent rectangle to become invisible.
Adding a definition like `border.width: 0` seems to work around it, especially
if the only border property you wanted to set was radius.
### My window should not be opaque
If a window is created with an opaque background color, Quickshell will use
a window surface format that is opaque. This is done to reduce the amount of
processing the gpu must do to draw it.
If you change the background color of your window between opaque and transparent colors,
then this may affect you.
To tell Quickshell that you want to create a window capable of showing transparency,
use @@Quickshell.QsWindow.surfaceFormat to set `opaque` to false.

30
src/guide/v0_1_0/index.md Normal file
View file

@ -0,0 +1,30 @@
---
title: "Usage Guide"
description: "Configuring the shell"
index: -1
---
See the [Installation and Setup](/docs/guide/install-setup) page to get started.
To write a Quickshell config, start by following the
[Guided Introduction](/docs/guide/introduction), and skimming the
[QML Language Overview](/docs/guide/qml-language).
After that, read the [Item Size and Position](/docs/guide/size-position) page before
continuing on your own to learn how to lay out elements in QML. Laying out elements
in QML is significantly different from many other layout systems such as CSS.
To learn what features Quickshell offers and how to use them, use the
left sidebar and click through the `Quickshell Types` pages. The sidebar
also links QtQuick's type index, which contains most of the visual elements used
to build a UI.
You may also want to check out the [Official Examples] to see some code,
as well as review existing user configs. Do note that Quickshell has evolved
considerably over time and most configs don't reflect current best practice.
[Official Examples]: https://git.outfoxxed.me/quickshell/quickshell-examples
See also the [FAQ](/docs/guide/faq) page, and the [Matrix Space] or [Discord Server] (bridged) for help.
[Matrix Space]: https://matrix.to/#/#quickshell:outfoxxed.me
[Discord Server]: https://discord.gg/UtZeT3xNyT

View file

@ -0,0 +1,204 @@
---
title: "Installation & Setup"
index: 0
---
> [!NOTE]
> Quickshell is still in a somewhat early stage of development.
> There will be breaking changes before 1.0, however a migration guide will be provided.
## Installation
Since Quickshell 0.1, you can now choose whether to install by tracking the master branch,
or install by latest release.
Note that you may want to install some additional packages (names vary by distro):
- `qtsvg`: support for SVG image loading (bundled with most packages)
- `qtimageformats`: support for WEBP images as well as some less common ones
- `qtmultimedia`: support for playing videos, audio, etc
- `qt5compat`: extra visual effects, notably gaussian blur. @@QtQuick.Effects.MultiEffect is usually preferable
### Nix
Release versions of Quickshell are available from Nixpkgs under the `quickshell` package.
The Quickshell repo also has an embedded flake which can be used from either mirror:
- `git+https://git.outfoxxed.me/outfoxxed/quickshell`
- `github:quickshell-mirror/quickshell`
> [!NOTE]
> You can use `?ref=` to specify a tag if you want a tagged release.
```nix
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
quickshell = {
# add ?ref=<tag> to track a tag
url = "git+https://git.outfoxxed.me/outfoxxed/quickshell";
# THIS IS IMPORTANT
# Mismatched system dependencies will lead to crashes and other issues.
inputs.nixpkgs.follows = "nixpkgs";
};
};
}
```
The package is available as `quickshell.packages.<system>.default`, which can be added to
your `environment.systemPackages` or `home.packages` if you use home-manager.
### Arch
Quickshell is available from the aur under:
- the [quickshell](https://aur.archlinux.org/packages/quickshell) package for the latest release
- the [quickshell-git](https://aur.archlinux.org/packages/quickshell-git) package that tracks the master branch
> [!WARNING]
> When using an AUR package, Quickshell may break whenever Qt is updated.
> The AUR gives us no way to actually fix this, but Quickshell will attempt to
> warn you if it detects a breakage when updating. If warned of a breakage,
> please reinstall the package.
Install using the command below:
```sh
yay -S quickshell
# or
yay -S quickshell-git
```
(or your AUR helper of choice)
### Fedora
Quickshell is available from the [errornointernet/quickshell] COPR, as either:
- `quickshell` that tracks the latest release
- `quickshell-git` that tracks the master branch
[errornointernet/quickshell]: https://copr.fedorainfracloud.org/coprs/errornointernet/quickshell
Install using the command below:
```sh
sudo dnf copr enable errornointernet/quickshell
sudo dnf install quickshell
# or
sudo dnf install quickshell-git
```
### Guix
Release versions of Quickshell are available from the standard Guix repository
as `quickshell` from the `(gnu packages wm)` module.
Install using the command below:
```sh
guix install quickshell
```
You can also add `quickshell` to your Guix system configuration or Guix Home configuration.
For the git version, Quickshell's source repository works as a channel.
Add the following to your channel list:
```scheme
(channel
(name quickshell)
(url "https://git.outfoxxed.me/outfoxxed/quickshell")
(branch "master"))
```
However, since the package definition is located in the source repository, it cannot be used
as a channel out of the box. You can clone the repository and use `guix shell -f quickshell.scm`
to use the git version of the package.
### Manual build
See [BUILD.md](https://git.outfoxxed.me/quickshell/quickshell/src/branch/master/BUILD.md)
for build instructions and configurations.
## Editor configuration
If you want to write your own configuration, installing a QML grammar and the LSP is recommended.
Read the [Usage Guide](/docs/guide) after configuring your editor.
> [!NOTE]
> Qmlls versions prior to 6.8.2 do not require `-E`
### Emacs
Install the [yuja/tree-sitter-qml](https://github.com/yuja/tree-sitter-qmljs) tree-sitter grammar,
and the [xhcoding/qml-ts-mode](https://github.com/xhcoding/qml-ts-mode) mode.
Both are packaged for nix via [outfoxxed/nix-qml-support](https://git.outfoxxed.me/outfoxxed/nix-qml-support).
Either `lsp-mode` or `eglot` should be usable for LSP ([caveats below](#language-server)).
The author's personal emacs config uses `lsp-mode` and `qml-ts-mode` as follows:
```elisp
(use-package qml-ts-mode
:after lsp-mode
:config
(add-to-list 'lsp-language-id-configuration '(qml-ts-mode . "qml-ts"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection '("qmlls", "-E"))
:activation-fn (lsp-activate-on "qml-ts")
:server-id 'qmlls))
(add-hook 'qml-ts-mode-hook (lambda ()
(setq-local electric-indent-chars '(?\n ?\( ?\) ?{ ?} ?\[ ?\] ?\; ?,))
(lsp-deferred))))
```
### Neovim
Neovim has built-in syntax highlighting for QML, however tree-sitter highlighting
may work better than the built-in highlighting. You can install the grammar
using `:TSInstall qmljs`.
To use the language server ([caveats below](#language-server)),
install [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
and the following snippet to your configuration:
```lua
require("lspconfig").qmlls.setup {
cmd = {"qmlls", "-E"}
}
```
### Helix
Helix has built-in syntax highlighting for QML.
To use the language server, add the following snippet to your configuration:
```toml
[language-server.qmlls]
args = ["-E"]
command = "qmlls"
```
### Vscode
1. Install the [Official QML Support extension]
2. Enable the `qt-qml.qmlls.useQmlImportPathEnvVar` setting.
![](/assets/images/vscode-qml-env.png)
[Official QML Support extension]: https://marketplace.visualstudio.com/items?itemName=TheQtCompany.qt-qml
## Language Server
The QML language has an associated language server,
[qmlls](https://doc.qt.io/qt-6/qtqml-tooling-qmlls.html).
We recommend using it, as it will catch a lot of bad practice that may
make your configuration harder to maintain later.
We are aware of the following issues:
- Qmlls does not work well when a file is not correctly structured.
This means that completions and lints won't work unless braces are closed
correctly and such.
- Qmlls cannot handle Quickshell's Singleton, which means you won't see completions,
and usages of Singleton members may show a warning. We're still investigating
this problem and how to fix it.
- The LSP cannot provide any documentation for Quickshell types.
- `root:` imports cannot be resolved by the LSP.
Keeping in mind the above caveats, qmlls should be able to guide you towards a
more correct code should you choose to use it.
> [!NOTE]
> 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
> `qt.enable` to `true` and installing the quickshell package globally.
# Next steps
Create your first configuration by reading the [Intro](/docs/configuration/intro).

View file

@ -0,0 +1,802 @@
---
title: "Introduction"
index: 2
---
This page will walk you through the process of creating a simple bar/panel, and
introduce you to all the basic concepts involved. You can use the
[QML Language Reference](@docs/guide/qml-language) to learn about the syntax
of the QML language.
> [!NOTE]
> All the <a>Green Links</a> in code blocks will take you to the documentation,
> listing their respective types.
## Config Files
Quickshell searches the `quickshell` subfolder of every XDG standard config path
for configs. Usually, this is `~/.config/quickshell`.
Each named subfolder containing a `shell.qml` file is considered to be a config.
If the base `quickshell` folder contains a `shell.qml` file, subfolders will
not be considered.
A specific configuration can be picked using the `--config` or `-c` argument to Quickshell.
Configs located at other paths outside XDG standard, including raw qml files,
can be run with `--path` or `-p`.
> [!CAUTION]
> Many users use root imports, in the form `import "root:/path/to/module"`. These are an old
> Quickshell feature that will break the LSP and singletons. Keep that in mind if you decide
> to use them.
>
> A replacement without these issues is planned.
## Creating Windows
Quickshell has two main window types available:
- @@Quickshell.PanelWindow for bars, widgets, and overlays
- @@Quickshell.FloatingWindow for standard desktop windows
We'll start with an example:
```qml
import Quickshell // for PanelWindow
import QtQuick // for Text
@@Quickshell.PanelWindow {
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
// center the bar in its parent component (the window)
anchors.centerIn: parent
text: "hello world"
}
}
```
The above example creates a bar/panel on your currently focused monitor with
a centered piece of [text](https://doc.qt.io/qt-6/qml-qtquick-text.html).
It will also reserve space for itself on your monitor.
More information about available properties is available in the [type reference](@docs/types/Quickshell/PanelWindow).
## Running a process
Now that we have a piece of text, what if it did something useful?
To start with, let's make a clock. To get the time, we'll use the `date` command.
> [!note/Note]
> Quickshell can do more than just run processes. Read until the end for more information.
We can use a [Process](@docs/types/quickshell.io/process) object to run commands
and a @@Quickshell.Io.StdioCollector to read their output.
We'll listen to the @@Quickshell.Io.StdioCollector.streamFinished(s) signal with
a [signal handler](@docs/guide/qml-language/#signal-handlers)
to update the text on the clock.
> [!note/Note]
> Quickshell live-reloads your code. You can leave it open and edit the
> original file. The panel will reload when you save it.
```qml
import Quickshell
import Quickshell.Io // for Process
import QtQuick
@@Quickshell.PanelWindow {
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
// give the text an ID we can refer to elsewhere in the file
id: clock
anchors.centerIn: parent
// create a process management object
@@Quickshell.Io.Process {
// the command it will run, every argument is its own string
command: ["date"]
// run the command immediately
running: true
// process the stdout stream using a StdioCollector
// Use StdioCollector to retrieve the text the process sends
// to stdout.
stdout: @@Quickshell.Io.StdioCollector {
// Listen for the streamFinished signal, which is sent
// when the process closes stdout or exits.
onStreamFinished: clock.text = this.text // `this` can be omitted
}
}
}
}
```
## Running code at an interval
With the above example, your bar should now display the time, but it isn't updating!
Let's use a @@QtQml.Timer to fix that.
```qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.PanelWindow {
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
id: clock
anchors.centerIn: parent
@@Quickshell.Io.Process {
// give the process object an id so we can talk
// about it from the timer
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: clock.text = this.text
}
}
// use a timer to rerun the process at an interval
@@QtQml.Timer {
// 1000 milliseconds is 1 second
interval: 1000
// start the timer immediately
running: true
// run the timer again when it ends
repeat: true
// when the timer is triggered, set the running property of the
// process to true, which reruns it if stopped.
onTriggered: dateProc.running = true
}
}
}
```
## Reusable components
If you have multiple monitors, you might have noticed that your bar
is only on one of them. If not, you'll still want to **follow this section
to make sure your bar doesn't disappear if your monitor disconnects**.
We can use a @@Quickshell.Variants object to create instances of _non-widget items_.
(See @@QtQuick.Repeater for doing something similar with visual items.)
The @@Quickshell.Variants type creates instances of a @@QtQml.Component based on
a data model you supply. (A component is a re-usable tree of objects.)
The most common use of @@Quickshell.Variants in a shell is to create instances of
a window (your bar) based on your monitor list (the data model).
@@Quickshell.Variants will inject the values in the data model into each new
component's `modelData` property, which means we can easily pass each screen
to its own component. (See @@Quickshell.QsWindow.screen.)
```qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Variants {
model: Quickshell.screens;
delegate: @@QtQml.Component {
@@Quickshell.PanelWindow {
// the screen from the screens list will be injected into this
// property
required property var modelData
// we can then set the window's screen to the injected property
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
id: clock
anchors.centerIn: parent
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: clock.text = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
}
}
}
```
See also: [Property Bindings](@docs/guide/qml-language#property-bindings),
[Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
With this example, bars will be created and destroyed as you plug and unplug them,
due to the reactive nature of the @@Quickshell.Quickshell.screens property.
(See: [Reactive Bindings](@docs/guide/qml-language#reactive-bindings).)
Now there's an important problem you might have noticed: when the window
is created multiple times, we also make a new Process and Timer, which makes the
bar less efficient than it could be. We can fix this by moving the
Process and Timer outside of the window using @@Quickshell.Scope.
> [!caution/Error]
> This code will not work correctly.
```qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
@@Quickshell.Variants {
model: Quickshell.screens
delegate: @@QtQml.Component {
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
id: clock
anchors.centerIn: parent
}
}
}
}
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: clock.text = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
However, there is a problem with naively moving the Process and Timer out of the component.
_What about the `clock` that the process references?_
If you run the above example, you'll see something like this in the console every second:
```
WARN scene: **/shell.qml[36:-1]: ReferenceError: clock is not defined
WARN scene: **/shell.qml[36:-1]: ReferenceError: clock is not defined
WARN scene: **/shell.qml[36:-1]: ReferenceError: clock is not defined
WARN scene: **/shell.qml[36:-1]: ReferenceError: clock is not defined
WARN scene: **/shell.qml[36:-1]: ReferenceError: clock is not defined
```
This is because the `clock` object, even though it has an ID, cannot be referenced
outside of its component. Remember, components can be created _any number of times_,
including zero, so `clock` may not exist or there may be more than one, meaning
there isn't an object to refer to from here.
We can fix it with a [Property Definition](@docs/guide/qml-language#property-definitions).
We can define a property inside of the ShellRoot and reference it from the clock
text instead. Due to QML's [Reactive Bindings](@docs/guide/qml-language#reactive-bindings),
the clock text will be updated when we update the property for every clock that
currently exists.
```qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
id: root
// add a property in the root
property string time
@@Quickshell.Variants {
model: Quickshell.screens
delegate: @@QtQml.Component {
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
// remove the id as we don't need it anymore
anchors.centerIn: parent
// bind the text to the root object's time property
text: root.time
}
}
}
}
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
// update the property instead of the clock directly
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
Now we've fixed the problem, so there's nothing actually wrong with the
above code; however, we can make it more concise:
1. `Component`s can be defined implicitly, meaning we can remove the
component wrapping the window and place the window directly into the
`delegate` property.
2. The @@Quickshell.Variants.delegate property is a
[Default Property](@docs/guide/qml-language#the-default-property),
which means we can skip the `delegate:` part of the assignment.
We're already using the default property of @@Quickshell.ShellRoot to store our
Variants, Process, and Timer components among other things.
This is what our shell looks like with the above (optional) cleanup:
```qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
id: root
property string time
@@Quickshell.Variants {
model: Quickshell.screens
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
anchors.centerIn: parent
text: root.time
}
}
}
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
## Multiple files
In an example as small as this, it isn't a problem, but as the shell
grows it might be preferable to separate it into multiple files.
To start with, let's move the entire bar into a new file.
```qml
// shell.qml
import Quickshell
@@Quickshell.Scope {
Bar {}
}
```
```qml Bar.qml
// Bar.qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
id: root
property string time
@@Quickshell.Variants {
model: Quickshell.screens
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
@@QtQuick.Text {
anchors.centerIn: parent
text: root.time
}
}
}
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
See also: @@Quickshell.Scope
Any qml file that starts with an uppercase letter can be referenced this way.
We can bring in other folders as well using
[import statements](@docs/guide/qml-language#explicit-imports).
Now what about breaking out the clock? This is a bit more complex because
the clock component in the bar need to be dealt with, as well as the necessary
processes that make up the actual clock.
To start with, we can move the clock widget to a new file. For now, it's just a
single @@QtQuick.Text object, but the same concepts apply regardless of complexity.
```qml
// ClockWidget.qml
import QtQuick
@@QtQuick.Text {
// A property the creator of this type is required to set.
// Note that we could just set `text` instead, but don't because your
// clock probably will not be this simple.
required property string time
text: time
}
```
```qml
// Bar.qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
id: root
property string time
@@Quickshell.Variants {
model: Quickshell.screens
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
// the ClockWidget type we just created
ClockWidget {
anchors.centerIn: parent
time: root.time
}
}
}
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
While this example is larger than what we had before, we can now expand
on the clock widget without cluttering the bar file.
Let's deal with the clock's update logic now:
```qml
// Time.qml
import Quickshell
import Quickshell.Io
import QtQuick
@@Quickshell.Scope {
id: root
property string time
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
```qml
// Bar.qml
import Quickshell
@@Quickshell.Scope {
// the Time type we just created
Time { id: timeSource }
@@Quickshell.Variants {
model: Quickshell.screens
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
ClockWidget {
anchors.centerIn: parent
// now using the time from timeSource
time: timeSource.time
}
}
}
}
```
## Singletons
Now you might be thinking, why do we need the `Time` type in
our bar file, and the answer is we don't. We can make `Time`
a [Singleton](@docs/guide/qml-language#singletons).
A singleton object has only one instance, and is accessible from
any scope.
```qml
// Time.qml
// with this line our type becomes a Singleton
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
// your singletons should always have Singleton as the type
@@Quickshell.Singleton {
id: root
property string time
@@Quickshell.Io.Process {
id: dateProc
command: ["date"]
running: true
stdout: @@Quickshell.Io.StdioCollector {
onStreamFinished: root.time = this.text
}
}
@@QtQml.Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
```
```qml
// ClockWidget.qml
import QtQuick
@@QtQuick.Text {
// we no longer need time as an input
// directly access the time property from the Time singleton
text: Time.time
}
```
```qml
// Bar.qml
import Quickshell
@@Quickshell.Scope {
// no more time object
@@Quickshell.Variants {
model: Quickshell.screens
@@Quickshell.PanelWindow {
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
ClockWidget {
anchors.centerIn: parent
// no more time binding
}
}
}
}
```
## Quickshell Support Libraries
In addition to calling external processes, Quickshell comes with a large set of support libraries
for common OS integrations and tasks. These libraries are indexed on the left sidebar.
One of these integrations is @@Quickshell.SystemClock, which exposes the system time in an easy to
use way.
We can use @@Quickshell.SystemClock.date to get a Date object to display. The
@@QtQml.Qt.formatDateTime() function can be used to easily format the date
as shown below.
@@Quickshell.SystemClock.precision can be set to `Minutes` to improve battery life if you don't
show seconds on your clock, as Quickshell will have less work to do.
```qml
// Time.qml
pragma Singleton
import Quickshell
import QtQuick
@@Quickshell.Singleton {
id: root
// an expression can be broken across multiple lines using {}
readonly property string time: {
// The passed format string matches the default output of
// the `date` command.
Qt.formatDateTime(clock.date, "ddd MMM d hh:mm:ss AP t yyyy")
}
@@Quickshell.SystemClock {
id: clock
precision: SystemClock.Seconds
}
}
```

View file

@ -0,0 +1,4 @@
---
title: "Lists and Models"
index: 2
---

View file

@ -0,0 +1,890 @@
---
title: "QML Language"
index: 10
---
import Collapsible from "@components/Collapsible.astro";
Quickshell is configured using the Qt Modeling Language, or QML.
This page explains what you need to know about QML to start using Quickshell.
See also: [Qt Documentation: QML Tutorial](https://doc.qt.io/qt-6/qml-tutorial.html)
## Structure
Below is a QML document showing most of the syntax.
Keep it in mind as you read the detailed descriptions below.
> [!note/Notes:]
>
> - Semicolons are permitted basically everywhere, and recommended in
> functions and expressions.
> - While types can often be elided, we recommend you use them where
> possible to catch problems early instead of running into them unexpectedly later on.
```qml
// QML Import statement
import QtQuick 6.0
// Javascript import statement
import "myjs.js" as MyJs
// Root Object
@@QtQuick.Item {
// Id assignment
id: root
// Property declaration
property int myProp: 5;
// Property binding
width: 100
// Property binding
height: width
// Multiline property binding
prop: {
// ...
5
}
// Object assigned to a property
objProp: Object {
// ...
}
// Object assigned to the parent's default property
AnotherObject {
// ...
}
// Signal declaration
signal foo(bar: int)
// Signal handler
onSignal: console.log("received signal!")
// Property change signal handler
onWidthChanged: console.log(`width is now ${width}!`)
// Multiline signal handler
onOtherSignal: {
console.log("received other signal!");
console.log(`5 * 2 is ${dub(5)}`);
// ...
}
// Attached property signal handler
Component.onCompleted: MyJs.myfunction()
// Function
function dub(x: int): int {
return x * 2
}
// Inline component
component MyComponent: Object {
// ...
}
}
```
### Imports
#### Manual imports
Every QML File begins with a list of imports.
Import statements tell the QML engine where
to look for types you can create [objects](#objects) from.
A module import statement looks like this:
```qml
import <Module> [Major.Minor] [as <Namespace>]
```
- `Module` is the name of the module you want to import, such as `QtQuick`.
- `Major.Minor` is the version of the module you want to import.
- `Namespace` is an optional namespace to import types from the module under.
A subfolder import statement looks like this:
```qml
import "<directory>" [as <Namespace>]
```
- `directory` is the directory to import, relative to the current file.
- `Namespace` is an optional namespace to import types from the folder under.
A javascript import statement looks like this:
```qml
import "<filename>" as <Namespace>
```
- `filename` is the name of the javascript file to import.
- `Namespace` is the namespace functions and variables from the javascript
file will be made available under.
> [!NOTE]
> All _Module_ and _Namespace_ names must start with an uppercase letter.
> Attempting to use a lowercase namespace will result in an error.
##### Examples
```qml
import QtQuick
import QtQuick.Controls 6.0
import Quickshell as QS
import QtQuick.Layouts 6.0 as L
import "jsfile.js" as JsFile
```
See also: [Qt Documentation: Import syntax](https://doc.qt.io/qt-6/qtqml-syntax-imports.html)
#### Implicit imports
The QML engine will automatically import any [types](#creating-types) in neighboring files
with names that start with an uppercase letter.
```
root
|-MyButton.qml
|-shell.qml
```
In this example, `MyButton` will automatically be imported as a type usable from `shell.qml`
or any other neighboring files.
### Objects
Objects are instances of a type from an imported module.
The name of an object must start with an uppercase letter.
This will always distinguish an object from a property.
An object looks like this:
```qml
Name {
id: foo
// properties, functions, signals, etc...
}
```
Every object can contain [properties](#properties), [functions](#functions),
and [signals](#signals). You can find out what properties are available for a type
by looking it up in the [Type Reference](/docs/types/).
#### Properties
Every object may have any number of property assignment, with only one assignment per specific property.
Each assignment binds the named property to the given value/expression.
##### Property bindings
Expressions are snippets of javascript code assigned to a property.
The last line can serve as the return value.
Alternatively, an explicit return statement can also be used for multi-line expressions.
```qml
@@QtQuick.Item {
// simple expression
property: 5
// complex expression
property: 5 * 20 + this.otherProperty
// multiline expression
property: {
const foo = 5;
const bar = 10;
foo * bar
}
// multiline expression with return
property: {
// ...
return 5;
}
}
```
Semicolons, while optional, can be included on any line of a single or multi-line expression,
including the last line.
All property bindings are [_reactive_](#reactive-bindings). This means that whenever any
property the expression depends on is updated, the expression is re-evaluated and the property
is updated accordingly.
See also [Reactive bindings](#reactive-bindings) for more information
##### Property definitions
Properties can be defined inside of objects with the following syntax:
```qml
[required] [readonly] [default] property <type> <name>[: binding]
```
- `required` forces users of this type to assign this property. See [Creating Types](#creating-types) for details.
- `readonly` makes the property not assignable. Its binding will still be [reactive](#reactive-bindings).
- `default` makes the property the [default property](#the-default-property) of this type.
- `type` is the type of the property. You can use `var` if you don't know or don't care but be aware that `var` will
allow any value type.
- `name` is the name that the property is known as. It cannot start with an uppercase letter.
- `binding` is the property binding. See [Property bindings](#property-bindings) for details.
```qml
@@QtQuick.Item {
// normal property
property int foo: 3
// readonly property
readonly property string bar: "hi!"
// bound property
property var things: [ "foo", "bar" ]
}
```
Defining a property with the same name as one provided by the current object will override
the property of the type it is derived from in the current context.
Trying to assign to a property that does not exist is an error.
##### The default property
Types can have a _default property_ which must accept either an object or a list of objects.
The default property will allow you to assign a value to it without using the name of the property:
```qml
@@QtQuick.Item {
// normal property
foo: 3
// this item is assigned to the outer object's default property
@@QtQuick.Item {
}
}
```
If the default property is a list, you can put multiple objects into it the same way as you
would put a single object in:
```qml
@@QtQuick.Item {
// normal property
foo: 3
// this item is assigned to the outer object's default property
@@QtQuick.Item {
}
// this one is too
@@QtQuick.Item {
}
}
```
##### The `id` property
Every object has a special property called `id` that can be assigned to give
the object a name that it can be referred to throughout the current file.
The id must be lowercase.
```qml
@@QtQuick.Layouts.ColumnLayout {
@@QtQuick.Text {
id: text
text: "Hello World!"
}
@@QtQuick.Controls.Button {
text: "Make the text red";
onClicked: text.color = "red";
}
}
```
<Collapsible title="How is the `id` property different from normal properties?">
The `id` property isn't really a property, and doesn't do anything other than
expose the object to the current file. It is only called a property because it
uses very similar syntax to one, and is the only exception to standard property
definition rules. The name `id` is always reserved for the id property.
</Collapsible>
##### Property access scopes
Properties are "in scope" and usable in two cases.
1. They are defined for current type.
2. They are defined for the root type in the current file.
You can access the properties of any object by setting its [id property](#the-id-property),
or make sure the property you are accessing is from the current object using `this`.
The `parent` property is also defined for all objects, but may not always point to what it
looks like it should. Use the `id` property if `parent` does not do what you want.
In general, you should only access properties of the *current* object without an id. For
all other objects, you should refer to them by id when accessing properties.
```qml
@@QtQuick.Item {
property string rootDefinition
@@QtQuick.Item {
id: mid
property string midDefinition
@@QtQuick.Text {
property string innerDefinition
// legal - innerDefinition is defined on the current object
text: innerDefinition
// legal - innerDefinition is accessed via `this` to refer to the current object
text: this.innerDefinition
// legal - width is defined for Text
text: width
// legal - rootDefinition is defined on the root object
text: rootDefinition
// illegal - midDefinition is not defined on the root or current object
text: midDefinition
// legal - midDefinition is accessed via `mid`'s id.
text: mid.midDefinition
// legal - midDefinition is accessed via `parent`
text: parent.midDefinition
}
}
}
```
See also: [Qt Documentation: Scope and Naming Resolution](https://doc.qt.io/qt-6/qtqml-documents-scope.html)
#### Functions
Functions in QML can be declared everywhere [properties](#properties),
and follow the same [scoping rules](#property-access-scopes).
Function definition syntax:
```qml
function <name>(<paramname>[: <type>][, ...])[: returntype] {
// multiline expression (note that `return` is required)
}
```
Functions can be invoked in expressions. Expression reactivity carries through
functions, meaning if one of the properties a function depends on is re-evaluated,
every expression depending on the function is also re-evaluated.
```qml
@@QtQuick.Layouts.ColumnLayout {
property int clicks: 0
function makeClicksLabel(): string {
return "the button has been clicked " + clicks + " times!";
}
@@QtQuick.Controls.Button {
text: "click me"
onClicked: clicks += 1
}
@@QtQuick.Text {
text: makeClicksLabel()
}
}
```
In this example, every time the button is clicked, the label's count increases
by one as the value of `clicks` is changed, triggering a re-evaluation of `text` through
`makeClicksLabel`.
##### Lambdas
Functions can also be values, and you can assign them to properties or pass them to
other functions (callbacks). There is a shorter way to write these functions, known
as lambdas.
Lambda syntax:
```qml
<params> => <expression>
// params can take the following forms:
() => ... // 0 parameters
<name> => ... // 1 parameter
(<name>[, ...]) => ... // 1+ parameters
// the expression can be either a single or multiline expression.
... => <result>
... => {
return <result>;
}
```
Assigning functions to properties:
```qml
@@QtQuick.Item {
// using functions
function dub(number: int): int { return number * 2; }
property var operation: dub
// using lambdas
property var operation: number => number * 2
}
```
An overcomplicated click counter using a lambda callback:
```qml
@@QtQuick.Layouts.ColumnLayout {
property int clicks: 0
function incrementAndCall(callback) {
clicks += 1;
callback(clicks);
}
@@QtQuick.Controls.Button {
text: "click me"
onClicked: incrementAndCall(clicks => {
label.text = `the button was clicked ${clicks} time(s)!`;
})
}
@@QtQuick.Text {
id: label
text: "the button has not been clicked"
}
}
```
#### Signals
A signal is basically an event emitter you can connect to and receive updates from.
They can be declared everywhere [properties](#properties) and [functions](#functions)
can, and follow the same [scoping rules](#property-access-scopes).
See also: [Qt Documentation: Signal and Handler Event System](https://doc.qt.io/qt-6/qtqml-syntax-signals.html)
##### Signal definitions
A signal can be explicitly defined with the following syntax:
```qml
signal <name>(<paramname>: <type>[, ...])
```
##### Making connections
Signals all have a `connect(<function>)` method which invokes the given function
or signal when the signal is emitted.
```qml
@@QtQuick.Layouts.ColumnLayout {
property int clicks: 0
function updateText() {
clicks += 1;
label.text = `the button has been clicked ${clicks} times!`;
}
@@QtQuick.Controls.Button {
id: button
text: "click me"
}
@@QtQuick.Text {
id: label
text: "the button has not been clicked"
}
Component.onCompleted: {
button.clicked.connect(updateText)
}
}
```
`Component.onCompleted` will be addressed later in [Attached Properties](#attached-properties),
but for now, just know that it runs immediately once the object is fully initialized.
When the button is clicked, the button emits the @@QtQuick.Controls.Button.clicked(s)
signal, which we connected to `updateText`. The signal then invokes `updateText`,
which updates the counter and the text on the label.
##### Signal handlers
Signal handlers are a more concise way to make connections, and prior examples have used them.
When creating an object, there is a corresponding `on<Signal>` property implicitly defined for every
signal present on its type, which can be set to a function. Do note that the first letter of the
signal's name is capitalized.
Below is the same example from [Making Connections](#making-connections), but this time,
using the implicit signal handler property to handle @@QtQuick.Controls.Button.clicked(s).
```qml
@@QtQuick.Layouts.ColumnLayout {
property int clicks: 0
function updateText() {
clicks += 1;
label.text = `the button has been clicked ${clicks} times!`;
}
@@QtQuick.Controls.Button {
text: "click me"
onClicked: updateText()
}
@@QtQuick.Text {
id: label
text: "the button has not been clicked"
}
}
```
##### Indirect signal handlers
Signal handlers should be preferred, but there are times where it is not possible or inconvenient to define one.
In those cases, before resorting to `.connect`ing the properties, a @@QtQml.Connections object can be used to access them.
This is especially useful to connect to signals of a Singleton.
```qml
@@QtQuick.Item {
@@QtQuick.Controls.Button {
id: myButton
text "click me"
}
@@QtQml.Connections {
target: myButton
function onClicked() {
// ...
}
}
}
```
##### Property change signals
Every property has an associated signal, which powers QML's [reactive bindings](#reactive-bindings).
The signal is named `<propertyname>Changed` and works exactly the same as any other signal.
When it is not possible or inconvenient to directly define a signal handler, before resorting
to `.connect`ing the properties, a @@QtQml.Connections object can be used to access them.
```qml
@@QtQuick.Layouts.ColumnLayout {
@@QtQuick.Controls.CheckBox {
text: "check me"
onCheckStateChanged: {
label.text = labelText(checkState == Qt.Checked);
}
}
@@QtQuick.Text {
id: label
text: labelText(false)
}
function labelText(checked): string {
return `the checkbox is checked: ${checked}`;
}
}
```
In this example, we listen for changes to the @@QtQuick.Controls.CheckBox.checkState property of the CheckBox
using its change signal, `checkStateChanged` with the signal handler `onCheckStateChanged`.
Since text is also a property, we can do the same thing more concisely:
```qml
@@QtQuick.Layouts.ColumnLayout {
@@QtQuick.Controls.CheckBox {
id: checkbox
text: "check me"
}
@@QtQuick.Text {
id: label
text: labelText(checkbox.checkState == Qt.Checked)
}
function labelText(checked): string {
return `the checkbox is checked: ${checked}`;
}
}
```
And the function can also be inlined to an expression:
```qml
@@QtQuick.Layouts.ColumnLayout {
@@QtQuick.Controls.CheckBox {
id: checkbox
text: "check me"
}
@@QtQuick.Text {
id: label
text: {
const checked = checkbox.checkState == Qt.Checked;
return `the checkbox is checked: ${checked}`;
}
}
}
```
You can also remove the return statement if you wish.
##### Attached objects
Attached objects are additional objects that can be associated with an object
as decided by internal library code. The documentation for a type will
tell you if it can be used as an attached object and how.
Attached objects are accessed in the form `<Typename>.<member>` and can have
properties, functions and signals.
A good example is the @@QtQml.Component type,
which is attached to every object and often used to run code when an object initializes.
In this example, the text property is set inside the `Component.onCompleted` attached signal handler.
```qml
@@QtQuick.Text {
Component.onCompleted: {
text = "hello!"
}
}
```
#### Creating types
Every QML file with an uppercase name is implicitly a type, and can be used from
neighboring files or imported. (See [Imports](#imports).)
A type definition is just a normal object. All properties defined for the root object
are visible to the consumer of the type. Objects identified by [id properties](#the-id-property)
are not visible outside the file.
```qml
// MyText.qml
@@QtQuick.Rectangle {
required property string text
color: "red"
implicitWidth: textObj.implicitWidth
implicitHeight: textObj.implicitHeight
@@QtQuick.Text {
id: textObj
anchors.fill: parent
text: parent.text
}
}
// AnotherComponent.qml
@@QtQuick.Item {
MyText {
// The `text` property of `MyText` is required, so we must set it.
text: "Hello World!"
// `anchors` is a property of `Item` which `Rectangle` subclasses,
// so it is available on MyText.
anchors.centerIn: parent
// `color` is a property of `Rectangle`. Even though MyText sets it
// to "red", we can override it here.
color: "blue"
// `textObj` is has an `id` within MyText.qml but is not a property
// so we cannot access it.
textObj.color: "red" // illegal
}
}
```
##### Inline Components
Inline components work the same as any other type, but are created inside
another QML file. These components only work within the file, and can reference
IDs inside the file.
While inline components can be created anywhere inside a QML file, they are
scoped to the file itself and cannot be nested.
Example of an inline component:
```qml
@@QtQuick.Layouts.ColumnLayout {
id: layout
property real textPointSize: 10
MyText { text: "Thing 1" }
MyText { text: "Thing 2" }
MyText { text: "Thing 3" }
component MyText: @@QtQuick.Text {
// applied to all MyText instances
color: "red"
// references an id outside of the component
font.pointSize: layout.textPointSize
}
}
```
##### Singletons
QML Types can be easily made into a Singleton, meaning there is only one instance of the type.
To make a type of a Singleton, put `pragma Singleton` at the top of the file.
To ensure it behaves correctly with Quickshell, you should also make the
@@Quickshell.Singleton the root item of your type.
```qml
pragma Singleton
import ...
@@Quickshell.Singleton {
...
}
```
Once a type is a Singleton, its members can be accessed by name from neighboring
files.
## Concepts
### Reactive bindings
This section assumes knowledge of: [Properties](#properties), [Signals](#signals),
and [Functions](#functions).
See also the [Qt documentation](https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html).
Reactivity is when a property is updated based on updates to another property.
Every time one of the properties in a binding change, the binding is re-evaluated
and the bound property takes the new result. Any bindings that depend on that property
are then re-evaluated and so forth.
Bindings can be created in two different ways:
##### Automatic bindings
A reactive binding occurs automatically when you use one or more properties in the definition
of another property.
```qml
@@QtQuick.Item {
property int clicks: 0
@@QtQuick.Controls.Button {
text: `clicks: ${clicks}`
onClicked: clicks += 1
}
}
```
In this example, the button's @@QtQuick.Controls.Button.text property is re-evaluated
every time the button is clicked, because the `clicks` property has changed.
###### Avoiding creation
To avoid creating a binding, do not use any other properties in the definition of a property.
You can use the `Component.onCompleted` signal to set a value using a property without creating a binding,
as assignments to properties do not create binding.
```qml
@@QtQuick.Item {
property string theProperty: "initial value"
@@QtQuick.Text {
// text: "Right now, theProperty is: " + theProperty
Component.onCompleted: text = "At creation time, theProperty is: " + theProperty
}
}
```
##### Manual bindings
Occasionally, a binding might need to be created inside of a function, signal, or expression.
If you need to change or attach a binding at runtime, the `Qt.binding` function can be used to
create one.
The `Qt.binding` function takes another function as an argument, and when assigned to a property,
the property will use that function as its binding expression.
```qml
@@QtQuick.Item {
@@QtQuick.Text {
id: boundText
text: "not bound to anything"
}
@@QtQuick.Controls.Button {
text: "bind the above text"
onClicked: {
if (boundText.text == "not bound to anything") {
text = "press me";
boundText.text = Qt.binding(() => `button is pressed: ${this.pressed}`);
}
}
}
}
```
In this example, `boundText`'s `text` property is bound to the button's pressed state
when the button is first clicked. When you press or unpress the button the text will
be updated.
##### Removing bindings
To remove a binding, just assign a new value to the property without using `Qt.binding`.
```qml
@@QtQuick.Item {
@@QtQuick.Text {
id: boundText
text: `button is pressed: ${theButton.pressed}`
}
@@QtQuick.Controls.Button {
id: theButton
text: "break the binding"
onClicked: boundText.text = `button was pressed at the time the binding was broken: ${pressed}`
}
}
```
When the button is first pressed, the text will be updated, but once `onClicked` fires
the text will be unbound, and even though it contains a reference to the `pressed` property,
it will not be updated further by the binding.
### Lazy loading
Often, not all of your interface needs to load immediately. By default, the QML engine
initializes every object in the scene before showing anything onscreen. For parts of
the interface you don't need to be immediately visible, load them asynchronously using
a @@Quickshell.LazyLoader. See its documentation for more information.
#### Components
Another delayed loading mechanism is the @@QtQml.Component type.
This type can be used to create multiple instances of objects or lazily load them. It's used by types such
as @@QtQuick.Repeater and @@Quickshell.Variants to create instances of a component at runtime.

View file

@ -0,0 +1,237 @@
---
title: "Item Size and Position"
index: 2
---
> [!TIP]
> Read the entire page, as understanding this is critical to building a well designed shell.
An @@QtQuick.Item (and its subclasses) has two sets of size properties:
- actual size (@@QtQuick.Item.width and @@QtQuick.Item.height)
- implicit/desired size (@@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. They use their actual size to determine the sizes
of their children.
If an Item is managed by a container, it should not set its own size. Instead, it should
allow the container to determine its size based on its implicit size.
In other words, implicit size should flow from children to parent, while actual size
should flow from parent to children.
In addition to size, Items also have positional properties (@@QtQuick.Item.x and @@QtQuick.Item.y).
Similar to actual size, the actual position should not be set directly if the 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, which is 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 that these exist.
## Container Items
Below is an example container that adds a margin to its child 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 }
}
```
> [!TIP]
> Quickshell has a builtin component @@Quickshell.Widgets.WrapperItem
> that adds margins similar to the behavior above.
### 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. The **`childrenRect` breaks this pattern.**
The `childrenRect` property represents the *actual* geometry of all child items, not their
*implicit* geometry. This results in the container item's size having an indirect dependency
on itself, which 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, which often leads users to use `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 each other. See the linked Qt documentation for more details.
> [!NOTE]
> - Layouts have a default spacing of 5 pixels between items, not zero.
> - Items in a Layout will be pixel-aligned.
### Layouts vs. Row and Column
In addition to the @@QtQuick.Layouts.RowLayout and @@QtQuick.Layouts.ColumnLayout types,
QtQuick has similarly named @@QtQuick.Row and @@QtQuick.Column types.
The Layout types are generally much more flexible and enable the usage of the
@@QtQuick.Layouts.Layout attached object.
In contrast, the @@QtQuick.Row and @@QtQuick.Column types are not part of Qt's Layout
system and do not have access to the @@QtQuick.Layouts.Layout attached object.
In addition, Row and Column do not pixel-align members, meaning if you have a member
with a fractional size, it will break the pixel alignment of members following it.
In general, RowLayout and ColumnLayout should be used over Row and Column outside
of cases where pixel alignment is intentially broken.