Skip to content

Commit

Permalink
feat: gtk4 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Aylur committed Dec 27, 2024
1 parent 1baed2f commit 631f462
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 45 deletions.
166 changes: 157 additions & 9 deletions docs/guide/bundling.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,80 @@ Flags:
Currently there are 3 builtin plugins.

- css: import `.css` will be inlined as a string
- sass: importing `.scss` files will go through the sass transpiler and be inlined as a string that contains valid css
- uses the `sass` executable found on $PATH
- blp: importing `.blp` files will go through [blueprint](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) and be inlined as a string that contains valid xml template definitions
- sass: importing `.scss` files will go through the sass transpiler and be inlined as a string that contains valid css using the `sass` executable found on `$PATH`
:::code-group

```scss [<i class="devicon-sass-plain"></i>style.scss]
$color: white;

selector {
color: $color;
}
```

:::
:::code-group

```ts [<i class="devicon-typescript-plain"></i> app.ts]
import style from "./style.scss"

print(style)
// selector {
// color: white;
// }
```

:::

- blp: importing `.blp` files will go through [blueprint](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) and be inlined as a string that contains xml template definitions
:::code-group

```blp [<i class="devicon-xml-plain"></i> ui.blp]
using Gtk 4.0;
Label {
label: _("hello");
}
```

:::
:::code-group

```ts [<i class="devicon-typescript-plain"></i> app.ts]
import ui from "./ui.blp"

print(ui)
// <?xml version="1.0" encoding="UTF-8"?>
// <interface>
// <requires lib="gtk" version="4.0"/>
// <object class="GtkLabel">
// <property name="label" translatable="yes">hello</property>
// </object>
// </interface>
```

:::

- inline: importing with `inline:/path/to/file` will inline the contents of the file as a string
:::code-group

```txt [data.txt]
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.
```

AGS defines `SRC` by default which by default will point to the directory of `entryfile`.
:::
:::code-group

```ts [<i class="devicon-typescript-plain"></i> app.ts]
import data from "inline:./data.txt"

print(ui)
// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.
```

:::

AGS defines `SRC` which by default will point to the directory of `entryfile`.
It can be overriden with `-d "SRC='/path/to/source'"`

```js
Expand Down Expand Up @@ -118,14 +186,94 @@ and distribute its output JS file as an executable.
Optionally use a build tool like meson to also declare its runtime dependencies.

When you have data files that you cannot inline as a string, for example icons,
a good practice would be to.
a good practice would be to:

1. Install data files to a directory, usually `/usr/share/your-project`
2. Define it as `DATADIR` in `env.d.ts` and at bundle time with `--define`
3. In code you can refer to data files through this `DATADIR` variable

Optionally you should use a build tool like meson or a makefile
to let users decide where to install to.
:::code-group

> [!NOTE]
> On Nix you can use the [lib.bundle](./nix#bundle-and-devshell) function.
```meson [meson.build]
prefix = get_option('prefix')
pkgdatadir = prefix / get_option('datadir') / meson.project_name()
bindir = prefix / get_option('bindir')
install_data(
files('data/data.txt'),
install_dir: pkgdatadir,
)
custom_target(
command: [
find_program('ags'),
'bundle',
'--define', 'DATADIR="' + pkgdatadir + '"',
meson.project_source_root() / 'app.ts',
meson.project_name(),
],
output: [meson.project_name()],
input: files('app.ts'),
install: true,
install_dir: bindir,
)
```

```ts [env.d.ts]
declare const DATADIR: string
```
```ts [app.ts]
const data = `${DATADIR}/data.txt`
```

:::

> [!TIP]
> On Nix you can use the [lib.bundle](./nix#bundle-and-devshell) function as well as meson.
## Notice for Gtk4

[`gtk4-layer-shell` needs to be linked before wayland.](https://github.com/wmww/gtk4-layer-shell/issues/3#issuecomment-1502339477)
When bundling a Gtk4 application you will have to use a wrapper to make it work.

:::code-group

```bash [wrapper.sh]
#!/bin/bash
LD_PRELOAD="@LAYER_SHELL_LIBDIR@/libgtk4-layer-shell.so" @MAIN_PROGRAM@ $@
```

:::
:::code-group

```meson [meson.build]
pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
main = meson.project_name() + '.wrapped'
custom_target(
command: [
find_program('ags'),
'bundle',
meson.project_source_root() / 'app.ts',
main,
],
output: [meson.project_name()],
input: files('app.ts'),
install: true,
install_dir: pkgdatadir,
)
configure_file(
input: files('wrapper.sh'),
output: meson.project_name(),
configuration: {
'MAIN_PROGRAM': pkgdatadir / main,
'LAYER_SHELL_LIBDIR': dependency('gtk4-layer-shell-0').get_variable('libdir'),
},
install: true,
install_dir: get_option('prefix') / get_option('bindir'),
)
```

:::
3 changes: 2 additions & 1 deletion docs/guide/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,6 @@ fi
```

> [!TIP]
> If you are happy with the script and don't plan to change it anymore [bundle](./bundling.md) it,
> If you are happy with the script and don't
> plan to change it anymore [bundle](./bundling.md) it,
> which will remove the dependency on AGS.
60 changes: 29 additions & 31 deletions docs/guide/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,59 +26,57 @@ It will generate the following files:

```txt
.
├── .gitignore
├── @girs/ # generated types
├── @girs/
├── node_modules/
│ └── astal
├── widget/
│ └── Bar.tsx
├── app.ts # entry proint
├── env.d.ts # additional types
├── app.ts
├── env.d.ts
├── style.scss
└── tsconfig.json # needed by LSPs
├── package.json
└── tsconfig.json
```

The `@girs` directory contains the generated types, which are
created when running the `init` command or the `types` command.

Assuming this directory will be tracked with git,
it generates a `.gitignore` file which is set to ignore `@girs` and `node_modules`.
Initially `node_modules` doesn't exist, but if you decide to install any `npm`
package it is not needed to track them with git. You can also add `tsconfig.json`
and `env.d.ts` to this list, as they are only used for developing and can be
regenerated with the `types` command. Only track `tsconfig.json` if you add
anything additional to `compilerOptions.paths`.

> [!NOTE]
> Since the runtime is `gjs`, very few packages will run from `npm`.
:::details Details on TypeScript.
While `gjs` does not currently support Node.js project structures and its ecosystem
the JavaScript tooling we are using relies on it. The `node_modules` directory
contains the `astal` package, but its purpose is only to provide type information.
The `package.json` is a file describing the project and `tsconfig.json` is a file
containing settings for TypeScript.
:::

The `env.d.ts` will tell the LSP that `.css`, `.scss` and `.blp` files can be
imported and will be inlined as a string. It also tells it that imports
prefixed with `inline:` will be inlined as well as that a global `SRC` variable
is available.
> [!WARNING]
> Since the runtime is `gjs`, very few packages will run from `npm`,
since most depends on `node` features.

The `tsconfig.json` file tells information to the LSP so that
intellisense can do its thing and provide great DX.
The `env.d.ts` let's the type checker in your editor know about additional
[features](./bundling) the `ags` bundler provides, for example the ability to inline files.
This can be also be expanded for variables defined with the `--define` flag at bundling.

`app.ts` is the entry point of the project which usually contains only
an `App.start` call where you define [main](https://aylur.github.io/astal/guide/typescript/cli-app#entry-point) and [requestHandler](https://aylur.github.io/astal/guide/typescript/cli-app#messaging-from-cli),
`app.ts` is the entry point of the project which usually
contains only an `App.start` call where you define [main](https://aylur.github.io/astal/guide/typescript/cli-app#entry-point) and [requestHandler](https://aylur.github.io/astal/guide/typescript/cli-app#messaging-from-cli),
but can contain any other code.

> [!NOTE]
> [!TIP]
> You could also name the entry file `app.tsx` and write any JSX there.
> [!TIP]
> You are not forced to use TypeScript. Adding `"allowJs": true`
> in `tsconfig.json` and optionally `"checkJs": true` will allow
> JavaScript, although it is very much recommended to TypeScript.
You are not forced into a project structure. You can put
`style.scss` and `widget/Bar.ts` anywhere you like, only the entry file matters.

## Running projects

`tsconfig.json`, `env.d.ts` and `@girs` are only significant for the LSP,
they are not needed to run the project.

:::tip
You can also use `ags run` as a shebang line for simple scripts.
See an [simple dialog example](./example.md)
:::
> [!TIP]
> You can also use `ags run` as a shebang line for simple scripts.
> See an [simple dialog example](./example.md).
> [!IMPORTANT]
> When using Gtk4 you have to use `--gtk4` flag for [gtk4-layer-shell](https://github.com/wmww/gtk4-layer-shell/issues/3#issuecomment-1502339477).
1 change: 1 addition & 0 deletions docs/guide/nix.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Using nix, you'll technically never have to use the `ags` cli.
src = ./.;
name = "my-shell"; # name of executable
entry = "app.ts";
gtk4 = false;
# additional libraries and executables to add to gjs' runtime
extraPackages = [
Expand Down
22 changes: 19 additions & 3 deletions docs/guide/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,32 @@ nix shell github:aylur/ags # ags in a temporary shell

2. Initialize a project

```sh
ags init
:::code-group

```sh [Gtk3]
ags init --gtk 3
```

```sh [Gtk4]
ags init --gtk 3
```

:::

3. Run the project

```sh
:::code-group

```sh [Gtk3]
ags run
```

```sh [Gtk4]
ags run --gtk4
```

:::

4. Learn [TypeScript in Y minutes](https://learnxinyminutes.com/docs/typescript/)

5. Read the [Astal Documentation](https://aylur.github.io/astal/guide/typescript/first-widgets) to start developing
7 changes: 7 additions & 0 deletions docs/vitepress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ export default defineConfig({
["link", { rel: "icon", href: "https://aylur.github.io/astal/icon.svg" }],
],

markdown: {
languageAlias: {
meson: "ruby",
blp: "c", // TODO: find a better alternative
},
},

themeConfig: {
outline: "deep",

Expand Down
11 changes: 10 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@
};

devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [go gopls gotools go-tools];
packages = with pkgs; [
markdownlint-cli2
marksman
vtsls
vscode-langservers-extracted
go
gopls
gotools
go-tools
];
};
};
}
5 changes: 5 additions & 0 deletions lib/esbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ func Bundle(opts BundleOpts) {
Platform: api.PlatformNeutral,
TsconfigRaw: tsconfig,
Define: defines,
Target: api.ES2022,
Sourcemap: api.SourceMapInline,
Engines: []api.Engine{
{Name: api.EngineFirefox, Version: "115"},
},
Loader: map[string]api.Loader{
".js": api.LoaderJSX,
".css": api.LoaderText,
Expand Down
2 changes: 2 additions & 0 deletions nix/bundle.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
src,
name,
extraPackages ? [],
gtk4 ? false,
}:
pkgs.stdenvNoCC.mkDerivation {
inherit src name;
Expand All @@ -29,6 +30,7 @@ pkgs.stdenvNoCC.mkDerivation {

preFixup = ''
gappsWrapperArgs+=(
--set LD_PRELOAD "${pkgs.gtk4-layer-shell}/lib/libgtk4-layer-shell.so"
--prefix PATH : ${with pkgs;
lib.makeBinPath (extraPackages
++ [
Expand Down

0 comments on commit 631f462

Please sign in to comment.