在 GTK/GJS 应用程序和 Gnome Shell 扩展程序之间共享代码
Sharing code between a GTK/GJS App and a Gnome Shell Extension
我正在使用 GJS 开发 GTK 应用程序,并且喜欢重用 GTK 的部分内容
Gnome Shell 扩展中的代码。但是,我没有找到添加的方法
Gtk.Widget
到我的 Gnome Shell 面板图标的菜单。
我尝试使用 clutter-gtk
中的 GtkClutter.Actor
,但图书馆似乎
已经过时并且在 Wayland 或 X11 Gnome Shell 中都不起作用,因为它
需要 Clutter 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 代码作为 GtkClutter.Actor
.
的 contents
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 提升到我的扩展中。
问题
- Gnome Shell 是否提供类似于
GtkClutter.Actor
的东西允许
扩展程序员重用他们的 GJS/GTK 代码?
- 您看到哪种重用 GTK/GJS 代码的替代方法?
- 如果 GTK 是这样一个通用的跨平台库,为什么 Gnome Shell 不是
支持开箱即用? (奖金问题,更多是出于好奇)
TL;DR 您不能在 GNOME Shell 扩展中使用 GTK 小部件。
GNOME Shell 中使用的工具包是 Clutter,而不是 GTK。 Clutter 是 Mutter 的内部库,而 GTK3 仅在 GNOME Shell 中用于少数实用程序。
Clutter 曾经是一个独立的库,但现在专门开发为 Mutter 的合成器工具包。 GTK 是一个应用程序工具包,不适合在合成器中使用。
独立的 Clutter 项目现在实际上已经无人维护,使得 GtkClutter 几乎相同。
我正在使用 GJS 开发 GTK 应用程序,并且喜欢重用 GTK 的部分内容
Gnome Shell 扩展中的代码。但是,我没有找到添加的方法
Gtk.Widget
到我的 Gnome Shell 面板图标的菜单。
我尝试使用 clutter-gtk
中的 GtkClutter.Actor
,但图书馆似乎
已经过时并且在 Wayland 或 X11 Gnome Shell 中都不起作用,因为它
需要 Clutter 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 代码作为 GtkClutter.Actor
.
contents
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 提升到我的扩展中。
问题
- Gnome Shell 是否提供类似于
GtkClutter.Actor
的东西允许 扩展程序员重用他们的 GJS/GTK 代码? - 您看到哪种重用 GTK/GJS 代码的替代方法?
- 如果 GTK 是这样一个通用的跨平台库,为什么 Gnome Shell 不是 支持开箱即用? (奖金问题,更多是出于好奇)
TL;DR 您不能在 GNOME Shell 扩展中使用 GTK 小部件。
GNOME Shell 中使用的工具包是 Clutter,而不是 GTK。 Clutter 是 Mutter 的内部库,而 GTK3 仅在 GNOME Shell 中用于少数实用程序。
Clutter 曾经是一个独立的库,但现在专门开发为 Mutter 的合成器工具包。 GTK 是一个应用程序工具包,不适合在合成器中使用。
独立的 Clutter 项目现在实际上已经无人维护,使得 GtkClutter 几乎相同。