diff --git a/examples/js/simple-bar/style.scss b/examples/js/simple-bar/style.scss
index 1dcf7293..f5f771a0 100644
--- a/examples/js/simple-bar/style.scss
+++ b/examples/js/simple-bar/style.scss
@@ -13,31 +13,45 @@ window.Bar {
font-size: 1.1em;
font-weight: bold;
- button {
- all: unset;
- background-color: transparent;
+ label {
+ margin: 0 8px;
+ }
+
+ .Workspaces {
+ button {
+ all: unset;
+ background-color: transparent;
- &:hover label {
- background-color: color.adjust($fg, $alpha: -0.84);
- border-color: color.adjust($accent, $alpha: -0.8);
+ &:hover label {
+ background-color: color.adjust($fg, $alpha: -0.84);
+ border-color: color.adjust($accent, $alpha: -0.8);
+ }
+
+ &:active label {
+ background-color: color.adjust($fg, $alpha: -0.8)
+ }
}
- &:active label {
- background-color: color.adjust($fg, $alpha: -0.8)
+ label {
+ transition: 200ms;
+ padding: 0 8px;
+ margin: 2px;
+ border-radius: $radius;
+ border: 1pt solid transparent;
}
- }
- label {
- transition: 200ms;
- padding: 0 8px;
- margin: 2px;
- border-radius: $radius;
- border: 1pt solid transparent;
+ .focused label {
+ color: $accent;
+ border-color: $accent;
+ }
}
- .Workspaces .focused label {
- color: $accent;
- border-color: $accent;
+ .SysTray {
+ margin-right: 8px;
+
+ button {
+ padding: 0 4px;
+ }
}
.FocusedClient {
diff --git a/examples/js/simple-bar/widget/Bar.tsx b/examples/js/simple-bar/widget/Bar.tsx
index 8a0126e5..87cc20da 100644
--- a/examples/js/simple-bar/widget/Bar.tsx
+++ b/examples/js/simple-bar/widget/Bar.tsx
@@ -11,22 +11,16 @@ import Tray from "gi://AstalTray"
function SysTray() {
const tray = Tray.get_default()
- return
- {bind(tray, "items").as(items => items.map(item => {
- if (item.iconThemePath)
- App.add_icons(item.iconThemePath)
-
- const menu = item.create_menu()
-
- return
- }))}
+
+ ))}
}
diff --git a/examples/lua/simple-bar/style.scss b/examples/lua/simple-bar/style.scss
index 1dcf7293..f5f771a0 100644
--- a/examples/lua/simple-bar/style.scss
+++ b/examples/lua/simple-bar/style.scss
@@ -13,31 +13,45 @@ window.Bar {
font-size: 1.1em;
font-weight: bold;
- button {
- all: unset;
- background-color: transparent;
+ label {
+ margin: 0 8px;
+ }
+
+ .Workspaces {
+ button {
+ all: unset;
+ background-color: transparent;
- &:hover label {
- background-color: color.adjust($fg, $alpha: -0.84);
- border-color: color.adjust($accent, $alpha: -0.8);
+ &:hover label {
+ background-color: color.adjust($fg, $alpha: -0.84);
+ border-color: color.adjust($accent, $alpha: -0.8);
+ }
+
+ &:active label {
+ background-color: color.adjust($fg, $alpha: -0.8)
+ }
}
- &:active label {
- background-color: color.adjust($fg, $alpha: -0.8)
+ label {
+ transition: 200ms;
+ padding: 0 8px;
+ margin: 2px;
+ border-radius: $radius;
+ border: 1pt solid transparent;
}
- }
- label {
- transition: 200ms;
- padding: 0 8px;
- margin: 2px;
- border-radius: $radius;
- border: 1pt solid transparent;
+ .focused label {
+ color: $accent;
+ border-color: $accent;
+ }
}
- .Workspaces .focused label {
- color: $accent;
- border-color: $accent;
+ .SysTray {
+ margin-right: 8px;
+
+ button {
+ padding: 0 4px;
+ }
}
.FocusedClient {
diff --git a/examples/lua/simple-bar/widget/Bar.lua b/examples/lua/simple-bar/widget/Bar.lua
index bf230bb4..e7bd4ff7 100644
--- a/examples/lua/simple-bar/widget/Bar.lua
+++ b/examples/lua/simple-bar/widget/Bar.lua
@@ -1,8 +1,6 @@
local astal = require("astal")
-local App = require("astal.gtk3.app")
local Widget = require("astal.gtk3.widget")
local Variable = astal.Variable
-local Gdk = astal.require("Gdk", "3.0")
local GLib = astal.require("GLib")
local bind = astal.bind
local Mpris = astal.require("AstalMpris")
@@ -17,26 +15,16 @@ local function SysTray()
local tray = Tray.get_default()
return Widget.Box({
+ class_name = "SysTray",
bind(tray, "items"):as(function(items)
return map(items, function(item)
- if item.icon_theme_path ~= nil then
- App:add_icons(item.icon_theme_path)
- end
-
- local menu = item:create_menu()
-
- return Widget.Button({
+ return Widget.MenuButton({
tooltip_markup = bind(item, "tooltip_markup"),
- on_destroy = function()
- if menu ~= nil then
- menu:destroy()
- end
- end,
- on_click_release = function(self)
- if menu ~= nil then
- menu:popup_at_widget(self, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, nil)
- end
- end,
+ use_popover = false,
+ menu_model = bind(item, "menu-model"),
+ action_group = bind(item, "action-group"):as(function(ag)
+ return { "dbusmenu", ag }
+ end),
Widget.Icon({
g_icon = bind(item, "gicon"),
}),
@@ -189,10 +177,10 @@ return function(gdkmonitor)
}),
Widget.Box({
halign = "END",
+ SysTray(),
Wifi(),
AudioSlider(),
BatteryLevel(),
- SysTray(),
Time("%H:%M - %A %e."),
}),
}),
diff --git a/examples/py/simple-bar/app.py b/examples/py/simple-bar/app.py
index 17b67821..d95dc0e6 100755
--- a/examples/py/simple-bar/app.py
+++ b/examples/py/simple-bar/app.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import sys
import versions
+import subprocess
from gi.repository import AstalIO, Astal, Gio
from widget.Bar import Bar
from pathlib import Path
@@ -18,9 +19,8 @@ def do_astal_application_request(
def do_activate(self) -> None:
self.hold()
- AstalIO.Process.execv(["sass", scss, css])
+ subprocess.run(["sass", scss, css])
self.apply_css(css, True)
- print("hello")
for mon in self.get_monitors():
self.add_window(Bar(mon))
@@ -30,7 +30,7 @@ def do_activate(self) -> None:
if __name__ == "__main__":
try:
- print(app.acquire_socket())
+ app.acquire_socket()
app.run(None)
except Exception as e:
print(AstalIO.send_message(instance_name, "".join(sys.argv[1:])))
diff --git a/examples/py/simple-bar/style.scss b/examples/py/simple-bar/style.scss
index 1dcf7293..f5f771a0 100644
--- a/examples/py/simple-bar/style.scss
+++ b/examples/py/simple-bar/style.scss
@@ -13,31 +13,45 @@ window.Bar {
font-size: 1.1em;
font-weight: bold;
- button {
- all: unset;
- background-color: transparent;
+ label {
+ margin: 0 8px;
+ }
+
+ .Workspaces {
+ button {
+ all: unset;
+ background-color: transparent;
- &:hover label {
- background-color: color.adjust($fg, $alpha: -0.84);
- border-color: color.adjust($accent, $alpha: -0.8);
+ &:hover label {
+ background-color: color.adjust($fg, $alpha: -0.84);
+ border-color: color.adjust($accent, $alpha: -0.8);
+ }
+
+ &:active label {
+ background-color: color.adjust($fg, $alpha: -0.8)
+ }
}
- &:active label {
- background-color: color.adjust($fg, $alpha: -0.8)
+ label {
+ transition: 200ms;
+ padding: 0 8px;
+ margin: 2px;
+ border-radius: $radius;
+ border: 1pt solid transparent;
}
- }
- label {
- transition: 200ms;
- padding: 0 8px;
- margin: 2px;
- border-radius: $radius;
- border: 1pt solid transparent;
+ .focused label {
+ color: $accent;
+ border-color: $accent;
+ }
}
- .Workspaces .focused label {
- color: $accent;
- border-color: $accent;
+ .SysTray {
+ margin-right: 8px;
+
+ button {
+ padding: 0 4px;
+ }
}
.FocusedClient {
diff --git a/examples/py/simple-bar/widget/Bar.py b/examples/py/simple-bar/widget/Bar.py
index 3b09dceb..ca02dd6b 100644
--- a/examples/py/simple-bar/widget/Bar.py
+++ b/examples/py/simple-bar/widget/Bar.py
@@ -108,6 +108,7 @@ def on_cover_art(*_):
class SysTray(Gtk.Box):
def __init__(self) -> None:
super().__init__()
+ Astal.widget_set_class_names(self, ["SysTray"])
self.items = {}
tray = Tray.get_default()
tray.connect("item_added", self.add_item)
@@ -118,33 +119,22 @@ def add_item(self, _: Tray.Tray, id: str):
return
item = Tray.get_default().get_item(id)
- theme = item.get_icon_theme_path()
-
- if theme is not None:
- from app import app
-
- app.add_icons(theme)
-
- menu = item.create_menu()
- btn = Astal.Button(visible=True)
+ btn = Gtk.MenuButton(use_popover=False, visible=True)
icon = Astal.Icon(visible=True)
- def on_clicked(btn):
- if menu:
- menu.popup_at_widget(btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, None)
+ item.bind_property("tooltip-markup", btn, "tooltip-markup", SYNC)
+ item.bind_property("gicon", icon, "g-icon", SYNC)
+ item.bind_property("menu-model", btn, "menu-model", SYNC)
+ btn.insert_action_group("dbusmenu", item.get_action_group())
- def on_destroy(btn):
- if menu:
- menu.destroy()
+ def on_action_group(*args):
+ btn.insert_action_group("dbusmenu", item.get_action_group())
- btn.connect("clicked", on_clicked)
- btn.connect("destroy", on_destroy)
+ item.connect("notify::action-group", on_action_group)
- item.bind_property("tooltip-markup", btn, "tooltip-markup", SYNC)
- item.bind_property("gicon", icon, "gicon", SYNC)
+ btn.add(icon)
self.add(btn)
self.items[id] = btn
- self.show_all()
def remove_item(self, _: Tray.Tray, id: str):
if id in self.items:
diff --git a/examples/vala/simple-bar/meson.build b/examples/vala/simple-bar/meson.build
index 10f5dd2c..5a0ef4c6 100644
--- a/examples/vala/simple-bar/meson.build
+++ b/examples/vala/simple-bar/meson.build
@@ -21,22 +21,15 @@ pkgconfig_deps = [
# needed for GLib.Math
deps = pkgconfig_deps + meson.get_compiler('c').find_library('m')
-custom_target(
- 'style.css',
- command: [
- find_program('sass'),
- meson.project_source_root() / 'style.scss',
- '@OUTPUT@',
- ],
- output: 'style.css',
- install: true,
- install_dir: libdir,
-)
-
main = configure_file(
input: 'app.in.vala',
output: 'app.vala',
- configuration: {'STYLE': libdir / 'style.css'},
+ configuration: {
+ 'STYLE': run_command(
+ find_program('sass'),
+ meson.project_source_root() / 'style.scss',
+ ).stdout(),
+ },
)
sources = files(
diff --git a/examples/vala/simple-bar/style.scss b/examples/vala/simple-bar/style.scss
index 1dcf7293..f5f771a0 100644
--- a/examples/vala/simple-bar/style.scss
+++ b/examples/vala/simple-bar/style.scss
@@ -13,31 +13,45 @@ window.Bar {
font-size: 1.1em;
font-weight: bold;
- button {
- all: unset;
- background-color: transparent;
+ label {
+ margin: 0 8px;
+ }
+
+ .Workspaces {
+ button {
+ all: unset;
+ background-color: transparent;
- &:hover label {
- background-color: color.adjust($fg, $alpha: -0.84);
- border-color: color.adjust($accent, $alpha: -0.8);
+ &:hover label {
+ background-color: color.adjust($fg, $alpha: -0.84);
+ border-color: color.adjust($accent, $alpha: -0.8);
+ }
+
+ &:active label {
+ background-color: color.adjust($fg, $alpha: -0.8)
+ }
}
- &:active label {
- background-color: color.adjust($fg, $alpha: -0.8)
+ label {
+ transition: 200ms;
+ padding: 0 8px;
+ margin: 2px;
+ border-radius: $radius;
+ border: 1pt solid transparent;
}
- }
- label {
- transition: 200ms;
- padding: 0 8px;
- margin: 2px;
- border-radius: $radius;
- border: 1pt solid transparent;
+ .focused label {
+ color: $accent;
+ border-color: $accent;
+ }
}
- .Workspaces .focused label {
- color: $accent;
- border-color: $accent;
+ .SysTray {
+ margin-right: 8px;
+
+ button {
+ padding: 0 4px;
+ }
}
.FocusedClient {
diff --git a/examples/vala/simple-bar/widget/Bar.vala b/examples/vala/simple-bar/widget/Bar.vala
index ba4062c7..ba9f06c8 100644
--- a/examples/vala/simple-bar/widget/Bar.vala
+++ b/examples/vala/simple-bar/widget/Bar.vala
@@ -103,6 +103,7 @@ class SysTray : Gtk.Box {
AstalTray.Tray tray = AstalTray.get_default();
public SysTray() {
+ Astal.widget_set_class_names(this, { "SysTray" });
tray.item_added.connect(add_item);
tray.item_removed.connect(remove_item);
}
@@ -112,26 +113,19 @@ class SysTray : Gtk.Box {
return;
var item = tray.get_item(id);
-
- var menu = item.create_menu();
- var btn = new Astal.Button();
- var icon = new Astal.Icon();
-
- btn.clicked.connect(() => {
- if (menu != null)
- menu.popup_at_widget(this, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null);
- });
-
- btn.destroy.connect(() => {
- if (menu != null)
- menu.destroy();
- });
+ var btn = new Gtk.MenuButton() { use_popover = false, visible = true };
+ var icon = new Astal.Icon() { visible = true };
item.bind_property("tooltip-markup", btn, "tooltip-markup", BindingFlags.SYNC_CREATE);
item.bind_property("gicon", icon, "g-icon", BindingFlags.SYNC_CREATE);
+ item.bind_property("menu-model", btn, "menu-model", BindingFlags.SYNC_CREATE);
+ btn.insert_action_group("dbusmenu", item.action_group);
+ item.notify["action-group"].connect(() => {
+ btn.insert_action_group("dbusmenu", item.action_group);
+ });
+
btn.add(icon);
add(btn);
- btn.show_all();
items.set(id, btn);
}
diff --git a/flake.nix b/flake.nix
index d7c52298..ac1c5e68 100644
--- a/flake.nix
+++ b/flake.nix
@@ -62,7 +62,7 @@
notifd = mkPkg "astal-notifd" ./lib/notifd [json-glib gdk-pixbuf];
powerprofiles = mkPkg "astal-power-profiles" ./lib/powerprofiles [json-glib];
river = mkPkg "astal-river" ./lib/river [json-glib];
- tray = mkPkg "astal-tray" ./lib/tray [gtk3 gdk-pixbuf libdbusmenu-gtk3 json-glib];
+ tray = mkPkg "astal-tray" ./lib/tray [json-glib (pkgs.callPackage ./nix/appmenu-glib.nix {})];
wireplumber = mkPkg "astal-wireplumber" ./lib/wireplumber [wireplumber];
gjs = pkgs.stdenvNoCC.mkDerivation {
diff --git a/lang/gjs/src/gtk3/astalify.ts b/lang/gjs/src/gtk3/astalify.ts
index 69738054..18df132f 100644
--- a/lang/gjs/src/gtk3/astalify.ts
+++ b/lang/gjs/src/gtk3/astalify.ts
@@ -69,6 +69,9 @@ export default function astalify<
get noImplicitDestroy(): boolean { return this.__no_implicit_destroy }
set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value }
+ set actionGroup([prefix, group]: ActionGroup) { this.insert_action_group(prefix, group) }
+ set_action_group(actionGroup: ActionGroup) { this.actionGroup = actionGroup }
+
_setChildren(children: Gtk.Widget[]) {
children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget
? ch
@@ -327,3 +330,5 @@ type Cursor =
| "nwse-resize"
| "zoom-in"
| "zoom-out"
+
+type ActionGroup = [prefix: string, actionGroup: Gtk.ActionGroup]
diff --git a/lang/gjs/src/gtk3/jsx-runtime.ts b/lang/gjs/src/gtk3/jsx-runtime.ts
index 9da4bb68..19a3b7d2 100644
--- a/lang/gjs/src/gtk3/jsx-runtime.ts
+++ b/lang/gjs/src/gtk3/jsx-runtime.ts
@@ -55,6 +55,7 @@ const ctors = {
label: Widget.Label,
levelbar: Widget.LevelBar,
// TODO: listbox
+ menubutton: Widget.MenuButton,
overlay: Widget.Overlay,
revealer: Widget.Revealer,
scrollable: Widget.Scrollable,
@@ -83,6 +84,7 @@ declare global {
label: Widget.LabelProps
levelbar: Widget.LevelBarProps
// TODO: listbox
+ menubutton: Widget.MenuButton,
overlay: Widget.OverlayProps
revealer: Widget.RevealerProps
scrollable: Widget.ScrollableProps
diff --git a/lang/gjs/src/gtk3/widget.ts b/lang/gjs/src/gtk3/widget.ts
index b4e8497f..b3c4a4dd 100644
--- a/lang/gjs/src/gtk3/widget.ts
+++ b/lang/gjs/src/gtk3/widget.ts
@@ -102,6 +102,15 @@ export class LevelBar extends astalify(Astal.LevelBar) {
// TODO: ListBox
+
+// MenuButton
+export type MenuButtonProps = ConstructProps
+export class MenuButton extends astalify(Gtk.MenuButton) {
+ static { GObject.registerClass({ GTypeName: "MenuButton" }, this) }
+ constructor(props?: MenuButtonProps, child?: BindableChild) { super({ child, ...props } as any) }
+}
+
+
// Overlay
Object.defineProperty(Astal.Overlay.prototype, "overlays", {
get() { return this.get_overlays() },
diff --git a/lang/lua/astal/gtk3/widget.lua b/lang/lua/astal/gtk3/widget.lua
index c8857e7a..654da296 100644
--- a/lang/lua/astal/gtk3/widget.lua
+++ b/lang/lua/astal/gtk3/widget.lua
@@ -19,6 +19,7 @@ local Widget = {
Label = astalify(Gtk.Label),
LevelBar = astalify(Astal.LevelBar),
-- TODO: ListBox
+ MenuButton = astalify(Gtk.MenuButton),
Overlay = astalify(Astal.Overlay),
Revealer = astalify(Gtk.Revealer),
Scrollable = astalify(Astal.Scrollable),
@@ -64,6 +65,12 @@ Gtk.Widget._attribute.click_through = {
set = Astal.widget_set_click_through,
}
+Gtk.Widget._attribute.action_group = {
+ set = function (self, v)
+ self:insert_action_group(v[1], v[2])
+ end
+}
+
local no_implicit_destroy = {}
Gtk.Widget._attribute.no_implicit_destroy = {
get = function(self)
diff --git a/lib/astal/io/process.vala b/lib/astal/io/process.vala
index cfd05b9a..4b77aee1 100644
--- a/lib/astal/io/process.vala
+++ b/lib/astal/io/process.vala
@@ -76,7 +76,7 @@ public class AstalIO.Process : Object {
*
* The first element of the vector is executed with the remaining elements as the argument list.
*/
- public Process.subprocessv(string[] cmd) throws Error {
+ public Process(string[] cmd) throws Error {
Object(argv: cmd);
process = new Subprocess.newv(cmd,
SubprocessFlags.STDIN_PIPE |
@@ -90,6 +90,15 @@ public class AstalIO.Process : Object {
read_stream(err_stream, false);
}
+ /**
+ * Start a new subprocess with the given command.
+ *
+ * The first element of the vector is executed with the remaining elements as the argument list.
+ */
+ public static Process subprocessv(string[] cmd) throws Error {
+ return new Process(cmd);
+ }
+
/**
* Start a new subprocess with the given command
* which is parsed using [func@GLib.shell_parse_argv].
@@ -97,7 +106,7 @@ public class AstalIO.Process : Object {
public static Process subprocess(string cmd) throws Error {
string[] argv;
Shell.parse_argv(cmd, out argv);
- return new Process.subprocessv(argv);
+ return Process.subprocessv(argv);
}
/**
diff --git a/lib/astal/io/variable.vala b/lib/astal/io/variable.vala
index 312a27af..e4105f8a 100644
--- a/lib/astal/io/variable.vala
+++ b/lib/astal/io/variable.vala
@@ -172,7 +172,7 @@ public class AstalIO.Variable : VariableBase {
return_if_fail(watch_proc == null);
return_if_fail(watch_exec != null);
- watch_proc = new Process.subprocessv(watch_exec);
+ watch_proc = Process.subprocessv(watch_exec);
watch_proc.stdout.connect((str) => set_closure(str, watch_transform));
watch_proc.stderr.connect((str) => this.error(str));
}
diff --git a/lib/tray/meson.build b/lib/tray/meson.build
index fbf2f98b..139cfb1c 100644
--- a/lib/tray/meson.build
+++ b/lib/tray/meson.build
@@ -39,29 +39,9 @@ deps = [
dependency('gio-2.0'),
dependency('json-glib-1.0'),
dependency('gdk-pixbuf-2.0'),
- dependency('gtk+-3.0'),
+ dependency('appmenu-glib-translator')
]
-dbusmenu_cflags = run_command(
- find_program('pkg-config', required: true),
- '--cflags', 'dbusmenu-gtk3-0.4',
- 'gobject-introspection-1.0',
- 'gobject-2.0',
- 'glib-2.0',
- capture: true,
- check: true,
-).stdout().strip()
-
-dbusmenu_libs = run_command(
- find_program('pkg-config', required: true),
- '--libs', 'dbusmenu-gtk3-0.4',
- 'gobject-introspection-1.0',
- 'gobject-2.0',
- 'glib-2.0',
- capture: true,
- check: true,
-).stdout().strip()
-
sources = [config] + files('tray.vala', 'watcher.vala', 'trayItem.vala')
if get_option('lib')
@@ -71,15 +51,13 @@ if get_option('lib')
dependencies: deps,
vala_header: meson.project_name() + '.h',
vala_vapi: meson.project_name() + '-' + api_version + '.vapi',
- vala_args: ['--vapi-comments', '--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'],
+ vala_args: ['--vapi-comments'],
version: meson.project_version(),
- c_args: dbusmenu_cflags.split(' '),
- link_args: dbusmenu_libs.split(' '),
install: true,
install_dir: [true, true, true]
)
- pkgs = ['--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4']
+ pkgs = []
foreach dep : deps
pkgs += ['--pkg=' + dep.name()]
endforeach
@@ -120,31 +98,13 @@ if get_option('lib')
requires: deps,
install_dir: get_option('libdir') / 'pkgconfig',
)
- #
- # custom_target(
- # typelib,
- # command: [
- # find_program('g-ir-compiler'),
- # '--output', '@OUTPUT@',
- # '--shared-library', get_option('prefix') / get_option('libdir') / '@PLAINNAME@',
- # meson.current_build_dir() / gir,
- # ],
- # input: lib,
- # output: typelib,
- # depends: lib,
- # install: true,
- # install_dir: get_option('libdir') / 'girepository-1.0',
- # )
-endif
+ endif
if get_option('cli')
executable(
meson.project_name(),
['cli.vala', sources],
dependencies: deps,
- vala_args: ['--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'],
- c_args: dbusmenu_cflags.split(' '),
- link_args: dbusmenu_libs.split(' '),
install: true,
)
endif
diff --git a/lib/tray/trayItem.vala b/lib/tray/trayItem.vala
index db0e6d46..991abce6 100644
--- a/lib/tray/trayItem.vala
+++ b/lib/tray/trayItem.vala
@@ -1,5 +1,3 @@
-using DbusmenuGtk;
-
namespace AstalTray {
public struct Pixmap {
int width;
@@ -88,21 +86,20 @@ public class TrayItem : Object {
private IItem proxy;
private List connection_ids;
-
/** The Title of the TrayItem */
public string title { owned get { return proxy.Title; } }
-
+
/** The category this item belongs to */
public Category category { get { return proxy.Category; } }
-
- /** the current status of this item */
+
+ /** The current status of this item */
public Status status { get { return proxy.Status; } }
-
- /** the tooltip of this item */
+
+ /** The tooltip of this item */
public Tooltip? tooltip { owned get { return proxy.ToolTip; } }
-
- /**
- * a markup representation of the tooltip. This is basically equvivalent
+
+ /**
+ * A markup representation of the tooltip. This is basically equvivalent
* to `tooltip.title \n tooltip.description`
*/
public string tooltip_markup {
@@ -120,26 +117,24 @@ public class TrayItem : Object {
/** the id of the item. This id is specified by the tray app.*/
public string id { owned get { return proxy.Id ;} }
-
- /**
- * If set, this only supports the menu, so showing the menu should be prefered
+
+ /**
+ * If set, this only supports the menu, so showing the menu should be prefered
* over calling [method@AstalTray.TrayItem.activate].
*/
public bool is_menu { get { return proxy.ItemIsMenu ;} }
-
- /**
- * the icon theme path, where to look for the [property@AstalTray.TrayItem:icon-name].
- *
- * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
+
+ /**
+ * The icon theme path, where to look for the [property@AstalTray.TrayItem:icon-name].
+ * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
* which does the icon lookups for you.
*/
public string icon_theme_path { owned get { return proxy.IconThemePath ;} }
-
- /**
- * the name of the icon. This should be looked up in the [property@AstalTray.TrayItem:icon-theme-path]
- * if set or in the currently used icon theme otherwise.
- *
- * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
+
+ /**
+ * The name of the icon. This should be looked up in the [property@AstalTray.TrayItem:icon-theme-path]
+ * if set or in the currently used icon theme otherwise.
+ * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
* which does the icon lookups for you.
*/
public string icon_name {
@@ -149,26 +144,41 @@ public class TrayItem : Object {
: proxy.IconName;
}
}
-
- /**
- * a pixbuf containing the icon.
- *
- * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
+
+ /**
+ * A pixbuf containing the icon.
+ * It is recommended to use the [property@AstalTray.TrayItem:gicon] property,
* which does the icon lookups for you.
*/
public Gdk.Pixbuf icon_pixbuf { owned get { return _get_icon_pixbuf(); } }
/**
- * contains the items icon. This property is intended to be used with the gicon property
+ * Contains the items icon. This property is intended to be used with the gicon property
* of the Icon widget and the recommended way to display the icon.
- * This property unifies the [property@AstalTray.TrayItem:icon-name],
+ * This property unifies the [property@AstalTray.TrayItem:icon-name],
* [property@AstalTray.TrayItem:icon-theme-path] and [property@AstalTray.TrayItem:icon-pixbuf] properties.
*/
public GLib.Icon gicon { get; private set; }
-
- /** the id of the item used to uniquely identify the TrayItems by this lib.*/
+
+ /** The id of the item used to uniquely identify the TrayItems by this lib.*/
public string item_id { get; private set; }
+ private DBusMenu.Importer menu_importer;
+
+ public MenuModel menu_model {
+ owned get {
+ if (menu_importer == null) return null;
+ return menu_importer.model;
+ }
+ }
+
+ public ActionGroup action_group {
+ owned get {
+ if (menu_importer == null) return null;
+ return menu_importer.action_group;
+ }
+ }
+
public signal void changed();
public signal void ready();
@@ -183,7 +193,8 @@ public class TrayItem : Object {
proxy = yield Bus.get_proxy(
BusType.SESSION,
service,
- path);
+ path
+ );
connection_ids.append(proxy.NewStatus.connect(refresh_all_properties));
connection_ids.append(proxy.NewToolTip.connect(refresh_all_properties));
@@ -196,9 +207,16 @@ public class TrayItem : Object {
SignalHandler.disconnect(proxy, id);
}
});
-
- update_gicon();
+ if (proxy.Menu != null) {
+ this.menu_importer = new DBusMenu.Importer(proxy.get_name_owner(), proxy.Menu);
+ this.menu_importer.notify["model"].connect(() => {
+ notify_property("menu-model");
+ notify_property("action-group");
+ });
+ }
+
+ update_gicon();
ready();
} catch (Error err) {
critical(err.message);
@@ -206,28 +224,33 @@ public class TrayItem : Object {
}
private void _notify() {
- string[] props = { "category", "id", "title", "status", "is-menu", "tooltip-markup", "icon-name", "icon-pixbuf" };
-
- foreach (string prop in props)
+ string[] props = {
+ "category",
+ "id",
+ "title",
+ "status",
+ "is-menu",
+ "tooltip-markup",
+ "icon-name",
+ "icon-pixbuf"
+ };
+
+ foreach (string prop in props) {
notify_property(prop);
+ }
changed();
}
private void update_gicon() {
- if(icon_name != null && icon_name != "") {
- if(icon_theme_path != null && icon_theme_path != "") {
-
- Gtk.IconTheme icon_theme = new Gtk.IconTheme();
- string[] paths = {icon_theme_path};
- icon_theme.set_search_path(paths);
-
- int size = icon_theme.get_icon_sizes(icon_name)[0];
- Gtk.IconInfo icon_info = icon_theme.lookup_icon(
- icon_name, size, Gtk.IconLookupFlags.FORCE_SIZE);
-
- if (icon_info != null)
- gicon = new GLib.FileIcon(GLib.File.new_for_path(icon_info.get_filename()));
+ if (icon_name != null && icon_name != "") {
+ if (GLib.FileUtils.test(icon_name, GLib.FileTest.EXISTS)) {
+ gicon = new GLib.FileIcon(GLib.File.new_for_path(icon_name));
+ }
+ else if(icon_theme_path != null && icon_theme_path != "") {
+ gicon = new GLib.FileIcon(GLib.File.new_for_path(
+ find_icon_in_theme(icon_name, icon_theme_path)
+ ));
} else {
gicon = new GLib.ThemedIcon(icon_name);
}
@@ -274,33 +297,35 @@ public class TrayItem : Object {
}
);
}
-
+
/**
- * send an activate request to the tray app.
+ * Send an activate request to the tray app.
*/
public void activate(int x, int y) {
try {
proxy.Activate(x, y);
} catch (Error e) {
- if(e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD)
+ if (e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) {
warning(e.message);
+ }
}
}
/**
- * send a secondary activate request to the tray app.
+ * Send a secondary activate request to the tray app.
*/
public void secondary_activate(int x, int y) {
try {
proxy.SecondaryActivate(x, y);
} catch (Error e) {
- if(e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD)
+ if (e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) {
warning(e.message);
+ }
}
}
/**
- * send a scroll request to the tray app.
+ * Send a scroll request to the tray app.
* valid values for the orientation are "horizontal" and "vertical".
*/
public void scroll(int delta, string orientation) {
@@ -312,58 +337,44 @@ public class TrayItem : Object {
}
}
- /**
- * creates a new Gtk Menu for this item.
- */
- public Gtk.Menu? create_menu() {
- if (proxy.Menu == null)
+ private string? find_icon_in_theme(string icon_name, string theme_path){
+ if (icon_name == null || theme_path == null || icon_name == "" || theme_path == "") {
return null;
+ }
- return new DbusmenuGtk.Menu(
- proxy.get_name_owner(),
- proxy.Menu);
- }
-
- private Gdk.Pixbuf? _get_icon_pixbuf() {
- Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION
- ? proxy.AttentionIconPixmap
- : proxy.IconPixmap;
-
-
- string icon_name = proxy.Status == Status.NEEDS_ATTENTION
- ? proxy.AttentionIconName
- : proxy.IconName;
-
- Gdk.Pixbuf pixbuf = null;
-
- if (icon_name != null && proxy.IconThemePath != null)
- pixbuf = load_from_theme(icon_name, proxy.IconThemePath);
+ try {
+ Dir dir = Dir.open(theme_path, 0);
+ string? name = null;
- if (pixbuf == null)
- pixbuf = pixmap_to_pixbuf(pixmaps);
+ while ((name = dir.read_name ()) != null) {
+ var path = Path.build_filename(theme_path, name);
- return pixbuf;
- }
+ if (FileUtils.test(path, FileTest.IS_DIR)) {
+ string? icon = find_icon_in_theme(icon_name, path);
+ if (icon != null) return icon;
+ else continue;
+ }
- private Gdk.Pixbuf? load_from_theme(string icon_name, string theme_path) {
- if (theme_path == "" || theme_path == null)
- return null;
+ int dot_index = name.last_index_of(".");
+ if (dot_index != -1) {
+ name = name.substring(0, dot_index);
+ }
- if (icon_name == "" || icon_name == null)
+ if (name == icon_name) return path;
+ }
+ } catch (FileError err) {
return null;
+ }
+ return null;
- Gtk.IconTheme icon_theme = new Gtk.IconTheme();
- string[] paths = {theme_path};
- icon_theme.set_search_path(paths);
-
- int size = icon_theme.get_icon_sizes(icon_name)[0];
- Gtk.IconInfo icon_info = icon_theme.lookup_icon(
- icon_name, size, Gtk.IconLookupFlags.FORCE_SIZE);
+ }
- if (icon_info != null)
- return icon_info.load_icon();
+ private Gdk.Pixbuf? _get_icon_pixbuf() {
+ Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION
+ ? proxy.AttentionIconPixmap
+ : proxy.IconPixmap;
- return null;
+ return pixmap_to_pixbuf(pixmaps);
}
private Gdk.Pixbuf? pixmap_to_pixbuf(Pixmap[] pixmaps) {
@@ -371,6 +382,12 @@ public class TrayItem : Object {
return null;
Pixmap pixmap = pixmaps[0];
+
+ for (int i = 0; i < pixmaps.length; i++){
+ if(pixmap.width < pixmaps[i].width)
+ pixmap = pixmaps[i];
+ };
+
uint8[] image_data = pixmap.bytes.copy();
for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 4) {
diff --git a/nix/appmenu-glib.nix b/nix/appmenu-glib.nix
new file mode 100644
index 00000000..873fb93d
--- /dev/null
+++ b/nix/appmenu-glib.nix
@@ -0,0 +1,35 @@
+{
+ stdenv,
+ fetchFromGitLab,
+ pkg-config,
+ meson,
+ ninja,
+ gobject-introspection,
+ vala,
+ glib,
+}: let
+ vala-panel-appmenu = fetchFromGitLab {
+ owner = "vala-panel-project";
+ repo = "vala-panel-appmenu";
+ rev = "24.05";
+ hash = "sha256-8GWauw7r3zKhvGF2TNOI8GDVctUFDhtG/Vy1cNUpsVo=";
+ };
+in
+ stdenv.mkDerivation {
+ pname = "appmenu-glib-translator";
+ version = "24.05";
+
+ src = "${vala-panel-appmenu}/subprojects/appmenu-glib-translator";
+
+ buildInputs = [
+ glib
+ ];
+
+ nativeBuildInputs = [
+ gobject-introspection
+ meson
+ pkg-config
+ ninja
+ vala
+ ];
+ }