js-gnome-apps

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GNOME JavaScript (GJS) Application Development

GNOME JavaScript(GJS)应用开发

Technology Stack

技术栈

ComponentPurpose
GJSJavaScript runtime (SpiderMonkey) with GObject Introspection bindings
GTK 4UI toolkit
Libadwaita (Adw) 1.xGNOME-specific widgets, adaptive layouts, styling
GLib / GIOCore utilities, async I/O, settings, D-Bus, file operations
MesonBuild system
FlatpakApp packaging and distribution
Blueprint (optional)Declarative UI markup that compiles to GTK XML
组件用途
GJS带有GObject自省绑定的JavaScript运行时(SpiderMonkey)
GTK 4UI工具包
Libadwaita (Adw) 1.xGNOME专用组件、自适应布局、样式设置
GLib / GIO核心工具、异步I/O、设置、D-Bus、文件操作
Meson构建系统
Flatpak应用打包与分发
Blueprint(可选)可编译为GTK XML的声明式UI标记语言

Imports (ES Modules — required for new code)

导入(ES Modules — 新代码必填)

js
// Built-in GJS modules
import Cairo from "cairo";
import Gettext from "gettext";
import System from "system";

// Platform libraries via gi:// URI
import GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";

// Versioned imports (required when multiple API versions exist)
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

// Relative user modules (include .js extension)
import * as Utils from "./lib/utils.js";
Keep imports grouped: built-in → platform (
gi://
) → local, separated by blank lines. Use
PascalCase
for imported modules.
js
// Built-in GJS modules
import Cairo from "cairo";
import Gettext from "gettext";
import System from "system";

// Platform libraries via gi:// URI
import GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";

// Versioned imports (required when multiple API versions exist)
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

// Relative user modules (include .js extension)
import * as Utils from "./lib/utils.js";
导入分组规则:内置模块 → 平台模块(
gi://
)→ 本地模块,各组之间用空行分隔。导入的模块使用
PascalCase
命名规范。

GObject Subclassing

GObject子类化

Register every GObject subclass with
GObject.registerClass()
. Use
constructor()
(not
_init()
, which is legacy pre-GNOME 42).
js
const MyWidget = GObject.registerClass(
  {
    GTypeName: "MyWidget",
    Template: "resource:///com/example/MyApp/my-widget.ui",
    InternalChildren: ["title_label", "action_button"],
    Properties: {
      "example-prop": GObject.ParamSpec.string(
        "example-prop",
        "",
        "",
        GObject.ParamFlags.READWRITE,
        null,
      ),
    },
    Signals: {
      "item-selected": {
        param_types: [GObject.TYPE_STRING],
      },
    },
  },
  class MyWidget extends Gtk.Box {
    constructor(params = {}) {
      super(params);
      // Template children: this._title_label, this._action_button
    }

    get example_prop() {
      return this._example_prop ?? null;
    }

    set example_prop(value) {
      if (this.example_prop === value) return;
      this._example_prop = value;
      this.notify("example-prop");
    }
  },
);
Key rules:
  • Property names:
    kebab-case
    in GObject declarations,
    snake_case
    in JS accessors
  • Always call
    this.notify('prop-name')
    in setters to emit change notifications
  • GTypeName
    is optional unless the type is referenced in UI XML
    <template class="...">
  • InternalChildren
    maps
    id
    attrs in UI XML →
    this._childId
    ;
    Children
    this.childId
使用
GObject.registerClass()
注册所有GObject子类。使用
constructor()
(而非
_init()
,后者是GNOME 42之前的遗留写法)。
js
const MyWidget = GObject.registerClass(
  {
    GTypeName: "MyWidget",
    Template: "resource:///com/example/MyApp/my-widget.ui",
    InternalChildren: ["title_label", "action_button"],
    Properties: {
      "example-prop": GObject.ParamSpec.string(
        "example-prop",
        "",
        "",
        GObject.ParamFlags.READWRITE,
        null,
      ),
    },
    Signals: {
      "item-selected": {
        param_types: [GObject.TYPE_STRING],
      },
    },
  },
  class MyWidget extends Gtk.Box {
    constructor(params = {}) {
      super(params);
      // Template children: this._title_label, this._action_button
    }

    get example_prop() {
      return this._example_prop ?? null;
    }

    set example_prop(value) {
      if (this.example_prop === value) return;
      this._example_prop = value;
      this.notify("example-prop");
    }
  },
);
核心规则:
  • 属性名称:在GObject声明中使用
    kebab-case
    ,在JS访问器中使用
    snake_case
  • 在setter中必须调用
    this.notify('prop-name')
    来触发变更通知
  • 除非在UI XML的
    <template class="...">
    中引用该类型,否则
    GTypeName
    是可选的
  • InternalChildren
    将UI XML中的
    id
    属性映射为
    this._childId
    Children
    则映射为
    this.childId

Application Entry Point

应用入口

js
import GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

import { MyAppWindow } from "./window.js";

const MyApp = GObject.registerClass(
  class MyApp extends Adw.Application {
    constructor() {
      super({
        application_id: "com.example.MyApp",
        flags: Gio.ApplicationFlags.DEFAULT_FLAGS,
      });

      const quitAction = new Gio.SimpleAction({ name: "quit" });
      quitAction.connect("activate", () => this.quit());
      this.add_action(quitAction);
      this.set_accels_for_action("app.quit", ["<Primary>q"]);
    }

    vfunc_activate() {
      let win = this.active_window;
      if (!win) win = new MyAppWindow(this);
      win.present();
    }
  },
);

const app = new MyApp();
app.run([System.programInvocationName, ...ARGV]);
js
import GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

import { MyAppWindow } from "./window.js";

const MyApp = GObject.registerClass(
  class MyApp extends Adw.Application {
    constructor() {
      super({
        application_id: "com.example.MyApp",
        flags: Gio.ApplicationFlags.DEFAULT_FLAGS,
      });

      const quitAction = new Gio.SimpleAction({ name: "quit" });
      quitAction.connect("activate", () => this.quit());
      this.add_action(quitAction);
      this.set_accels_for_action("app.quit", ["<Primary>q"]);
    }

    vfunc_activate() {
      let win = this.active_window;
      if (!win) win = new MyAppWindow(this);
      win.present();
    }
  },
);

const app = new MyApp();
app.run([System.programInvocationName, ...ARGV]);

Async with Gio._promisify

使用Gio._promisify实现异步

Wrap
*_async/*_finish
pairs once at module level for
async/await
usage:
js
Gio._promisify(
  Gio.File.prototype,
  "load_contents_async",
  "load_contents_finish",
);

const file = Gio.File.new_for_path("/tmp/example.txt");
try {
  const [contents] = await file.load_contents_async(null);
  const text = new TextDecoder().decode(contents);
} catch (e) {
  logError(e, "Failed to load file");
}
在模块级别一次性包装
*_async/*_finish
方法对,以支持
async/await
语法:
js
Gio._promisify(
  Gio.File.prototype,
  "load_contents_async",
  "load_contents_finish",
);

const file = Gio.File.new_for_path("/tmp/example.txt");
try {
  const [contents] = await file.load_contents_async(null);
  const text = new TextDecoder().decode(contents);
} catch (e) {
  logError(e, "Failed to load file");
}

Code Style (GJS conventions)

代码风格(GJS规范)

  • 4-space indentation, single quotes, semicolons
  • const
    by default,
    let
    when mutation needed, never
    var
  • File names:
    lowerCamelCase.js
    ; directories:
    lowercase
  • Arrow functions for inline callbacks;
    .bind(this)
    for method references
  • export
    for public API, never
    var
  • 使用4空格缩进,单引号,末尾加分号
  • 优先使用
    const
    ,需要修改变量时使用
    let
    ,禁止使用
    var
  • 文件名:
    lowerCamelCase.js
    ;目录名:全小写
  • 内联回调使用箭头函数;方法引用使用
    .bind(this)
  • 公开API使用
    export
    ,禁止使用
    var

Reference Files

参考文档

Consult these based on the task at hand:
  • Project setup & packaging: references/project-setup.md — Meson build, Flatpak manifests, GResource, application ID, desktop files, GSettings schemas, directory layout
  • UI design (GTK4 + Libadwaita): references/ui-patterns.md — Widget templates (UI XML), adaptive layouts (breakpoints, split views, view switcher), boxed lists, style classes, header bars, GNOME HIG patterns
  • GIO & platform patterns: references/gio-patterns.md — File I/O, GSettings, actions & menus, D-Bus, subprocesses, list models, GVariant
根据任务需求参考以下文档:
  • 项目配置与打包references/project-setup.md — Meson构建、Flatpak清单、GResource、应用ID、桌面文件、GSettings模式、目录结构
  • UI设计(GTK4 + Libadwaita)references/ui-patterns.md — 组件模板(UI XML)、自适应布局(断点、拆分视图、视图切换器)、列表框、样式类、标题栏、GNOME HIG设计模式
  • GIO与平台模式references/gio-patterns.md — 文件I/O、GSettings、动作与菜单、D-Bus、子进程、列表模型、GVariant",