Expand description
§Spell
§Introduction
This crate provides the necessary abstractions for wlr_layer_shell protocols with an implementation of slint platform backend for creating any and every kind of widget in slint. So, by the dark arts of spell, one can program their widgets creation in every kind of way suitable to their specific needs. Internally, spell provides a slint Platform implementation combined with necessary wayland counterparts using Smithay client toolkit’s Wayland bindings in rust. Apart from that, spell also provides convenience functions and method implementations for various common tasks (like Apps search backend, mpris backend etc) according to freedesktop specs standards. Though a lot of them are still partially complete, incomplete or not even started.
§Why use Spell and Slint?
It is a necessary question to answer. Spell was created as a personal project to fill a gap, i.e. absence of proper widget making toolkits and frameworks in rust. Moreover, I didn’t want to make yet another abstraction over gtk_layer_shell and call it a day. Slint is simple yet powerful declarative language which provides excellent support for rust as backend. Spell fills this gap, it implements slint’s backend for usage in making widgets. This was rather than bending a verbose language(like rust) to write widgets, people can use slint which was made for it, and still get the excellent support of rust in backend. Another reason was to inspect the inner workings of wayland and how display is managed in linux systems in modern days.
§Panics and Errors
Before starting further, it is important to note that due to nature of rust, some sections become more complicated than they need to be, especially in case of UI related components and libraries. Thus, it is important to read the panic sections (if there is any) of spell objects which you are using, to get a sense of cases in which the code will panic.
§Examples and Basic Usage
Let’s first install the CLI of Spell.
cargo install spell-cli
sp new project-namesp CLI offers this clean way to create a project so you wouldn’t have to hustle with
the initial setup. Under the hood, it uses cargo new command paired
with filling the files with appropriate content.
Now the main code, following code in you slint file create a counter and initialises it from default value of 42.
// In path and file name `ui/app-window.slint`
import { VerticalBox, Button } from "std-widgets.slint";
export component AppWindow inherits Window {
in-out property <int> counter: 42;
callback request-increase-value();
VerticalBox {
Text {
text: "Counter: \{root.counter}";
}
Button {
text: "Increase value";
clicked => {
root.request-increase-value();
}
}
}
}Now, to increment the data and specify the dimensions of widget add the following to your src/main.rs file.
use slint::ComponentHandle;
use spell_framework::{
self, cast_spell,
layer_properties::{LayerAnchor, LayerType, WindowConf},
};
use std::{env, error::Error};
slint::include_modules!();
// Generating Spell widgets/windows from slint windows.
spell_framework::generate_widgets![AppWindow];
fn main() -> Result<(), Box<dyn Error>> {
// Setting configurations for the window.
let window_conf = WindowConf::builder()
.width(376_u32)
.height(576_u32)
.anchor_1(LayerAnchor::TOP)
.margins(5, 0, 0, 10)
.layer_type(LayerType::Top)
.build()
.unwrap();
// Initialising Slint Window and corresponding wayland part.
let ui = AppWindowSpell::invoke_spell("counter-widget", window_conf);
// Setting the callback closure value which will be called on when the button is clicked.
ui.on_request_increase_value({
let ui_handle = ui.as_weak();
move || {
let ui = ui_handle.unwrap();
ui.set_counter(ui.get_counter() + 1);
}
});
// Calling the event loop function for running the window
cast_spell!(ui)
}Running this code with cargo will display a widget in your wayland compositor. It is important to
mention that if you have defined width and height in both your window and in the rust
code,then the renderer will manage the more or less dimensions accordingly, which may lead to undefined behaviour. For details of arguments and use of layer_properties::WindowConf and cast_spell, head to their respective attached docs.
ehe frontend code for this example is adopted from.slint-rust-template
§Spell CLI
Spell CLI is a utility for managing and handling remotely running windows/widgets. It provides various features like hiding/opening the widget, toggling the widget, remotely setting variables with new/changed values, seeing logs etc. CLI’s commands can be combined with your wayland compositor’s configuration file to make keybind for your windows. Read more about it in its documentation. You can install the command sp by the following command
cargo install spell-cli§Inspiration
The inspiration for making spell came from various weird places. First and foremost was my inability to understand the workings of GTK (I didn’t spend much time on it), which drifted me away to better alternative,slint. Another reason was to answer the question, “how does wayland actually works?”. Moreover, outfoxxed made quickshell around the same time, which does a similar thing with Qt and C++, this gave me confidence that a non-gtk framework for widget creation is possible.
§Slint part of Spell
This is future work and here is a note to it. As some more base functionalities will be complete I will also create a slint counterpart of “components” which are most commonly used in the process of widget creation.
Modules§
- forge
forgeis a mini submodule which provides alternative method to create and run events after a certain duration of time. Obvious approach to tackle such events is to use Timer. Alternatively, if you want a more rust facing interface (where timed events are not managed inside.slintfiles and rather directly created in rust code), you can useForge- layer_
properties - It contains related enums and struct which are used to manage, define and update various properties of a widget(viz a viz layer). You can import necessary types from this module to implement relevant features. See docs of related objects for their overview.
- macro_
internal - Components of this module are not be used by end user directly. This module contains certain reexports used by public facing macros like cast_spell and generate_widgets internally.
- slint_
adapter - This module contains relevent structs for slint side backend configurations. All structs mentioned are either internal or not used anymore. Still their implementation is public because they had to be set by the user of library in intial iterations of spell_framework.
- vault
vaultcontains the necessary utilities/APTs for various common tasks required when creating a custom shell. This includes apps, pipewire, PAM, Mpris etc among other things.- wayland_
adapter - It provides various widget types for implementing properties
across various functionalities for your shell. The most common widget (or
window as called by many) is SpellWin. You can also implement a lock screen
with
SpellLock.
Macros§
- cast_
spell - It is a macro which builds upon the internal functions of spell to create and start an event loop. This macro is responsible for the creation of necessary bindings before running the code.
- generate_
widgets - This macro is responsible for generating Spell compatible types from Slint compatible types.
Traits§
- IpcController
- This trait is implemented upon slint generated windows to enable IPC handling
- Spell
Associated New - This is an internal trait implemented by objects generated from
generate_widgets. It helps in running every SpellWidget (like SpellWin, SpellLock) through the same event_loop function.
Functions§
- cast_
spell_ inner - event loop function internally used by
cast_spellfor single widget setups. Not to be used by end user, - cast_
spells_ new - event loop function internally used by
cast_spellfor multiple widget setups. Not to be used by end user.