guide page versioning
This commit is contained in:
parent
d23bfbfd00
commit
e7c807ac85
12 changed files with 54 additions and 10 deletions
39
src/guide/v0_1_0/distribution.mdx
Normal file
39
src/guide/v0_1_0/distribution.mdx
Normal 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
172
src/guide/v0_1_0/faq.md
Normal 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
30
src/guide/v0_1_0/index.md
Normal 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
|
||||
204
src/guide/v0_1_0/install-setup.md
Normal file
204
src/guide/v0_1_0/install-setup.md
Normal 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.
|
||||

|
||||
|
||||
[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).
|
||||
802
src/guide/v0_1_0/introduction.md
Normal file
802
src/guide/v0_1_0/introduction.md
Normal 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
|
||||
}
|
||||
}
|
||||
```
|
||||
4
src/guide/v0_1_0/lists-models.mdx
Normal file
4
src/guide/v0_1_0/lists-models.mdx
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: "Lists and Models"
|
||||
index: 2
|
||||
---
|
||||
890
src/guide/v0_1_0/qml-language.md
Normal file
890
src/guide/v0_1_0/qml-language.md
Normal 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.
|
||||
237
src/guide/v0_1_0/size-position.md
Normal file
237
src/guide/v0_1_0/size-position.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue