summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--docs/guide/getting-started/installation.md2
-rw-r--r--examples/js/simple-bar/style.scss50
-rw-r--r--examples/js/simple-bar/widget/Bar.tsx23
-rw-r--r--examples/lua/simple-bar/style.scss50
-rw-r--r--examples/lua/simple-bar/widget/Bar.lua52
-rwxr-xr-xexamples/py/simple-bar/app.py6
-rw-r--r--examples/py/simple-bar/style.scss50
-rw-r--r--examples/py/simple-bar/widget/Bar.py33
-rw-r--r--examples/vala/simple-bar/meson.build19
-rw-r--r--examples/vala/simple-bar/style.scss50
-rw-r--r--examples/vala/simple-bar/widget/Bar.vala32
-rw-r--r--flake.nix2
-rw-r--r--lang/gjs/src/gtk3/astalify.ts12
-rw-r--r--lang/gjs/src/gtk3/jsx-runtime.ts2
-rw-r--r--lang/gjs/src/gtk3/widget.ts9
-rw-r--r--lang/lua/astal/gtk3/astalify.lua2
-rw-r--r--lang/lua/astal/gtk3/widget.lua7
-rw-r--r--lib/astal/io/process.vala13
-rw-r--r--lib/astal/io/variable.vala2
-rw-r--r--lib/tray/meson.build48
-rw-r--r--lib/tray/trayItem.vala219
-rw-r--r--nix/appmenu-glib.nix35
23 files changed, 397 insertions, 323 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7b29bcf..8b4ea71 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,7 +5,7 @@ You can contribute by:
- [Suggesting new features](https://github.com/Aylur/astal/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=)
- [Reporting bugs](https://github.com/Aylur/astal/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=)
- Improving docs with additional contexts and examples
- - adding more distros to sections about installations e.g [bulding from source](https://aylur.github.io/astal/guide/getting-started/installation#bulding-libastal-from-source)
+ - adding more distros to sections about installations e.g [building from source](https://aylur.github.io/astal/guide/getting-started/installation#building-from-source)
- Adding more example projects to [examples](https://github.com/Aylur/astal/tree/main/examples)
- Adding new language support/binding. For these open a PR for discussions.
- Adding new libraries e.g support for more wayland compositors
diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md
index e32b6a9..a942315 100644
--- a/docs/guide/getting-started/installation.md
+++ b/docs/guide/getting-started/installation.md
@@ -22,7 +22,7 @@ maintainer: [@Aylur](https://github.com/Aylur)
Read more about it on the [nix page](./nix#astal)
-## Bulding From Source
+## Building From Source
1. Install the following dependencies
diff --git a/examples/js/simple-bar/style.scss b/examples/js/simple-bar/style.scss
index 1dcf729..f5f771a 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 8a0126e..54a14b0 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 <box>
- {bind(tray, "items").as(items => items.map(item => {
- if (item.iconThemePath)
- App.add_icons(item.iconThemePath)
-
- const menu = item.create_menu()
-
- return <button
+ return <box className="SysTray">
+ {bind(tray, "items").as(items => items.map(item => (
+ <menubutton
tooltipMarkup={bind(item, "tooltipMarkup")}
- onDestroy={() => menu?.destroy()}
- onClickRelease={self => {
- menu?.popup_at_widget(self, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null)
- }}>
+ usePopover={false}
+ actionGroup={bind(item, "action-group").as(ag => ["dbusmenu", ag])}
+ menuModel={bind(item, "menu-model")}>
<icon gIcon={bind(item, "gicon")} />
- </button>
- }))}
+ </menubutton>
+ ))}
</box>
}
@@ -95,6 +89,7 @@ function Workspaces() {
return <box className="Workspaces">
{bind(hypr, "workspaces").as(wss => wss
+ .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
.sort((a, b) => a.id - b.id)
.map(ws => (
<button
diff --git a/examples/lua/simple-bar/style.scss b/examples/lua/simple-bar/style.scss
index 1dcf729..f5f771a 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 bf230bb..5e62253 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"),
}),
@@ -139,17 +127,19 @@ local function Workspaces()
end)
return map(wss, function(ws)
- return Widget.Button({
- class_name = bind(hypr, "focused-workspace"):as(function(fw)
- return fw == ws and "focused" or ""
- end),
- on_clicked = function()
- ws:focus()
- end,
- label = bind(ws, "id"):as(function(v)
- return type(v) == "number" and string.format("%.0f", v) or v
- end),
- })
+ if !(ws.id >= -99 and ws.id <= -2) then -- filter out special workspaces
+ return Widget.Button({
+ class_name = bind(hypr, "focused-workspace"):as(function(fw)
+ return fw == ws and "focused" or ""
+ end),
+ on_clicked = function()
+ ws:focus()
+ end,
+ label = bind(ws, "id"):as(function(v)
+ return type(v) == "number" and string.format("%.0f", v) or v
+ end),
+ })
+ end
end)
end),
})
@@ -189,10 +179,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 17b6782..d95dc0e 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 @@ class App(Astal.Application):
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 @@ app = App(instance_name=instance_name)
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 1dcf729..f5f771a 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 3b09dce..3c5c3f8 100644
--- a/examples/py/simple-bar/widget/Bar.py
+++ b/examples/py/simple-bar/widget/Bar.py
@@ -32,7 +32,8 @@ class Workspaces(Gtk.Box):
child.destroy()
for ws in hypr.get_workspaces():
- self.add(self.button(ws))
+ if not (ws.id >= -99 and ws.id <= -2): # filter out special workspaces
+ self.add(self.button(ws))
def button(self, ws):
hypr = Hyprland.get_default()
@@ -108,6 +109,7 @@ class Media(Gtk.Box):
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 +120,22 @@ class SysTray(Gtk.Box):
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 10f5dd2..5a0ef4c 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 1dcf729..f5f771a 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 ba4062c..8913c95 100644
--- a/examples/vala/simple-bar/widget/Bar.vala
+++ b/examples/vala/simple-bar/widget/Bar.vala
@@ -10,8 +10,12 @@ class Workspaces : Gtk.Box {
foreach (var child in get_children())
child.destroy();
- foreach (var ws in hypr.workspaces)
- add(button(ws));
+ foreach (var ws in hypr.workspaces) {
+ // filter out special workspaces
+ if (!(ws.id >= -99 && ws.id <= -2)) {
+ add(button(ws));
+ }
+ }
}
Gtk.Button button(AstalHyprland.Workspace ws) {
@@ -103,6 +107,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 +117,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 d7c5229..ac1c5e6 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 6973805..9e6f022 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
@@ -165,6 +168,13 @@ export default function astalify<
const { setup, child, children = [], ...props } = config
props.visible ??= true
+ // remove undefined values
+ for (const [key, value] of Object.entries(props)) {
+ if (value === undefined) {
+ delete props[key]
+ }
+ }
+
if (child)
children.unshift(child)
@@ -327,3 +337,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 9da4bb6..19a3b7d 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 b4e8497..b3c4a4d 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<MenuButton, Gtk.MenuButton.ConstructorProps>
+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/astalify.lua b/lang/lua/astal/gtk3/astalify.lua
index 95faa2c..5bf3c1d 100644
--- a/lang/lua/astal/gtk3/astalify.lua
+++ b/lang/lua/astal/gtk3/astalify.lua
@@ -174,7 +174,7 @@ return function(ctor)
end)))
-- default visible to true
- if type(tbl.visible) ~= "boolean" then
+ if tbl.visible == nil then
tbl.visible = true
end
diff --git a/lang/lua/astal/gtk3/widget.lua b/lang/lua/astal/gtk3/widget.lua
index c8857e7..654da29 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 cfd05b9..4b77aee 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 |
@@ -91,13 +91,22 @@ public class AstalIO.Process : Object {
}
/**
+ * 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 [[email protected]_parse_argv].
*/
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 312a27a..e4105f8 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 fbf2f98..139cfb1 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 db0e6d4..991abce 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<ulong> 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 [[email protected]].
*/
public bool is_menu { get { return proxy.ItemIsMenu ;} }
-
- /**
- * the icon theme path, where to look for the [[email protected]:icon-name].
- *
- * It is recommended to use the [[email protected]:gicon] property,
+
+ /**
+ * The icon theme path, where to look for the [[email protected]:icon-name].
+ * It is recommended to use the [[email protected]: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 [[email protected]:icon-theme-path]
- * if set or in the currently used icon theme otherwise.
- *
- * It is recommended to use the [[email protected]:gicon] property,
+
+ /**
+ * The name of the icon. This should be looked up in the [[email protected]:icon-theme-path]
+ * if set or in the currently used icon theme otherwise.
+ * It is recommended to use the [[email protected]: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 [[email protected]:gicon] property,
+
+ /**
+ * A pixbuf containing the icon.
+ * It is recommended to use the [[email protected]: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 [[email protected]:icon-name],
+ * This property unifies the [[email protected]:icon-name],
* [[email protected]:icon-theme-path] and [[email protected]: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 0000000..873fb93
--- /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
+ ];
+ }