【问题标题】:Sharing code between a GTK/GJS App and a Gnome Shell Extension在 GTK/GJS 应用程序和 Gnome Shell 扩展之间共享代码
【发布时间】:2021-07-06 17:39:33
【问题描述】:

我正在用 GJS 开发一个 GTK 应用程序,并且喜欢重用部分 GTK Gnome Shell 扩展中的代码。但是,我没有找到添加方法 Gtk.Widget 到我的 Gnome Shell 面板图标的菜单。

我尝试使用来自clutter-gtkGtkClutter.Actor,但图书馆似乎 已经过时并且在 Wayland 或 X11 Gnome Shell 中都不起作用,因为它 需要杂波1.0,但看到7 已加载。导入时 imports.gi.GtkClutter 在扩展中,Gnome Shell 产生此错误:

Requiring GtkClutter, version none: Requiring namespace 'Clutter' version '1.0', but '7' is already loaded.

这里有一些代码来证明 clutter-gtk 确实有效,如果直接 通过gjs 运行它;可能是因为我可以在这里强制执行 GTK 3.0。

gtkclutter.js:

imports.gi.versions.Gtk = '3.0'  // fails if set to 4.0

const { Gtk, GLib, Clutter, GtkClutter } = imports.gi

// gtkUI returns a Gtk.Widget tree. This should be the reusable code.
function gtkUI() {
    return new Gtk.Label({
        label: '<span size="100000">????</span>',
        use_markup: true,
    })
}

// embedClutterActor returns a Gtk.Widget with an embedded Clutter.Actor.
function embedClutterActor(clutter_actor) {
    let embed = new GtkClutter.Embed()
    embed.get_stage().add_child(clutter_actor)
    return embed
}

// embedGtkWidget returns a Clutter.Actor with an embedded Gtk.Widget.
function embedGtkWidget(gtk_widget) {
    return new GtkClutter.Actor({ contents: gtk_widget })
}

class App {
    constructor() {
        this.title = 'GtkClutter'
        GLib.set_prgname(this.title)
    }
    onActivate() { this.window.show_all() }
    onStartup()  { this.buildUI() }

    run(ARGV=[]) {
        this.app = new Gtk.Application()
        this.app.connect('activate', () => this.onActivate())
        this.app.connect('startup',  () => this.onStartup())
        this.app.run(ARGV)
    }

    buildUI() {
        let w = this.window = new Gtk.ApplicationWindow({
            application: this.app, title: this.title, icon_name: 'face-smile',
            default_height: 160, default_width: 160, window_position: Gtk.WindowPosition.CENTER,
        })

        // Just to demonstrate that GtkClutter embedding works, we use both embeds here to create
        // a Gtk.Widget from a Clutter.Actor from the actual Gtk.Widget that we want to show.
        GtkClutter.init(null)
        Clutter.init(null)
        w.add(embedClutterActor(embedGtkWidget(gtkUI())))

        // In the actual GTK App, we would just have used `w.add(gtkUI())`
        // and not imported Clutter and GtkClutter at all.
    }
}

new App().run(ARGV)

这是 GTK 应用程序的配套扩展,正在尝试(但失败) 将 GTK 代码重用为 contentsGtkClutter.Actor

extension.js:

const { Clutter, Gtk, Gio, St } = imports.gi

let GtkClutter = null  // lazy import for debugging

const Main = imports.ui.main
const PanelMenu = imports.ui.panelMenu
const PopupMenu = imports.ui.popupMenu

const Me = imports.misc.extensionUtils.getCurrentExtension()
const VERSION = 'dev-version' // filled during install
const NAME = 'GtkClutterExt'

// gtkUI returns a Gtk.Widget tree. This should be the reusable code.
function gtkUI() {
    return new Gtk.Button({ child: Gtk.Label({
        label: `<span size="100000">????</span>`,
        use_markup: true,
    })})
}

// stUI returns an Gnome Shell widget tree that works only in Gnome Shell.
function stUI(icon_name='face-sad') {
    return new St.Icon({ icon_name })
}

function statusIcon(icon_name) {
    let box = new St.BoxLayout()
    let icon = new St.Icon({ icon_name, style_class: 'system-status-icon emotes-icon' })
    box.add_child(icon)
    box.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM))
    return box
}

class Ext {
    constructor() { this.panel_widget = null }

    enable() {
        log(`enabling extension ${Me.uuid}`)
        try {
            // Use St only for the status icon and the menu container (not the menu content).
            let btn = this.panel_widget = new PanelMenu.Button(0.0, NAME, false)
            let item = new PopupMenu.PopupBaseMenuItem({ reactive: false, can_focus: false })
            btn.menu.addMenuItem(item)
            Main.panel.addToStatusArea(NAME, btn)

            try       { GtkClutter = imports.gi.GtkClutter }
            catch (e) { log(`failed to load clutter-gtk, err=${e.message}`) }

            if (GtkClutter) {
                // Using St for the status icon is OK, since it is only used by the extension.
                btn.add_child(statusIcon('face-happy'))
                // But for the menu, I like to reuse my GTK code from the GTK app.
                // That is what GtkClutter was designed for, I believe.
                item.actor.add_child(new GtkClutter.Actor({ contents: gtkUI() }))
            } else {
                // As fallback we show our mood regarding GtkClutter support in Gnome Shell ;)
                btn.add_child(statusIcon('face-sad'))
                item.actor.add_child(stUI('face-angry'))
            }
        } catch (e) {
            log(`failed to enable ${Me.uuid}, err=${e.message}`)
        }
    }

    disable() {
        debug(`disabling extension ${Me.uuid}`)
        if (this.panel_widget == null) return
        this.panel_widget.destroy()
        this.panel_widget = null
    }
}

function init() { return new Ext() }

我知道clutter-gtk 已经过时了(参见https://gitlab.gnome.org/GNOME/clutter-gtk), 但我没有找到更好的方法将 GTK 提升到我的扩展程序中。

问题

  1. Gnome Shell 是否提供类似于GtkClutter.Actor 的功能,允许 扩展程序员重用他们的 GJS/GTK 代码?
  2. 您认为重用 GTK/GJS 代码的替代方法有哪些?
  3. 如果 GTK 是这样一个通用和跨平台的库,为什么 Gnome Shell 不 支持开箱即用? (额外问题 ??????,更多是出于好奇)

【问题讨论】:

    标签: gtk code-reuse gnome-shell gnome-shell-extensions gjs


    【解决方案1】:

    TL;DR您不能在 GNOME Shell 扩展中使用 GTK 小部件。

    GNOME Shell 中使用的工具包是 Clutter,而不是 GTK。 Clutter 是 Mutter 的内部库,而 GTK3 仅在 GNOME Shell 中用于少数实用程序。

    Clutter 曾经是一个独立的库,但现在专门开发为 Mutter 的合成器工具包。 GTK 是一个应用程序工具包,不适合在合成器中使用。

    独立的 Clutter 项目现在实际上没有维护,使得 GtkClutter 几乎相同。

    【讨论】:

    • 好的,谢谢!这是否也意味着现在可以在 GJS 应用程序和 Gnome Shell 扩展之间共享来自任何成熟 UI 工具包的 JS UI 代码?
    • 差不多。我可以推测一些非常糟糕的想法,但我认为您实际上只能在一个进程中拥有一个基于 GLib 的工具包,因为它们绘制单线程并且需要管理该线程中的事件循环。
    • 我明白了。然后 Gnome Shell UI 技术 100% 固定在(内部)Clutter + St 上,GJS 代码共享仅适用于非 UI 代码。仍然应该有可能的是共享 Gio 和 GLib 代码来处理数据,并且可能使用首选项(从未尝试过,仍在学习)。这澄清了一切,我会接受这个答案。谢谢!
    猜你喜欢
    • 2021-05-25
    • 2015-11-20
    • 2018-07-12
    • 2019-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多