diff --git a/nixosModules/ags/v2/app.ts b/nixosModules/ags/v2/app.ts index 89a525e5..828e52e3 100644 --- a/nixosModules/ags/v2/app.ts +++ b/nixosModules/ags/v2/app.ts @@ -12,6 +12,7 @@ import Corners from './widgets/corners/main'; import IconBrowser from './widgets/icon-browser/main'; import { NotifPopups, NotifCenter } from './widgets/notifs/main'; import PowerMenu from './widgets/powermenu/main'; +import Screenshot from './widgets/screenshot/main'; import MonitorClicks from './services/monitor-clicks'; @@ -48,6 +49,7 @@ switch (CONF) { NotifPopups(); NotifCenter(); PowerMenu(); + Screenshot(); new MonitorClicks(); }, diff --git a/nixosModules/ags/v2/default.nix b/nixosModules/ags/v2/default.nix index 9eea8d5f..89d69ec9 100644 --- a/nixosModules/ags/v2/default.nix +++ b/nixosModules/ags/v2/default.nix @@ -51,7 +51,7 @@ self: { }) // { "${configDir}/node_modules".source = - buildNodeModules ./. "sha256-f0hbPvHTqeFM7mfmV+sN4EEuE0F91f5kjJ/EHy0oU+Y="; + buildNodeModules ./. "sha256-pK9S6qUjTIL0JDegYJlHSY5XEpLFKfA98MfZ59Q3IL4="; "${configDir}/tsconfig.json".source = pkgs.writers.writeJSON "tsconfig.json" { "$schema" = "https://json.schemastore.org/tsconfig"; diff --git a/nixosModules/ags/v2/lib.ts b/nixosModules/ags/v2/lib.ts index c5a54c71..e058d889 100644 --- a/nixosModules/ags/v2/lib.ts +++ b/nixosModules/ags/v2/lib.ts @@ -1,4 +1,6 @@ -import { Gdk } from 'astal/gtk3'; +import { App, Gdk, Widget } from 'astal/gtk3'; + +import AstalApps from 'gi://AstalApps'; import AstalHyprland from 'gi://AstalHyprland'; const Hyprland = AstalHyprland.get_default(); @@ -109,3 +111,16 @@ export const centerCursor = async(): Promise<void> => { await hyprMessage(`dispatch movecursor ${x} ${y}`); }; + +export const get_app_icon = (app: AstalApps.Application): string => { + if (app.get_icon_name() && Widget.Icon.lookup_icon(app.get_icon_name())) { + return app.get_icon_name(); + } + else { + const iconString = app.get_key('Icon'); + + App.add_icons(iconString); + + return iconString; + } +}; diff --git a/nixosModules/ags/v2/package-lock.json b/nixosModules/ags/v2/package-lock.json index 048cc24b..84fc1355 100644 --- a/nixosModules/ags/v2/package-lock.json +++ b/nixosModules/ags/v2/package-lock.json @@ -11,13 +11,13 @@ "@eslint/js": "9.13.0", "@stylistic/eslint-plugin": "2.9.0", "@types/eslint__js": "8.42.3", - "@types/node": "22.7.7", + "@types/node": "22.8.1", "eslint": "9.13.0", "eslint-plugin-jsdoc": "50.4.3", "fzf": "0.5.2", "jiti": "2.3.3", "typescript": "5.6.3", - "typescript-eslint": "8.10.0" + "typescript-eslint": "8.11.0" } }, "node_modules/@es-joy/jsdoccomment": { @@ -336,25 +336,25 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz", - "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==", + "version": "22.8.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", + "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", - "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", + "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/type-utils": "8.10.0", - "@typescript-eslint/utils": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/type-utils": "8.11.0", + "@typescript-eslint/utils": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -378,15 +378,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", - "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz", + "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==", "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "debug": "^4.3.4" }, "engines": { @@ -406,13 +406,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", - "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", + "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0" + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -423,13 +423,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", - "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz", + "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/utils": "8.11.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -447,9 +447,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", - "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", + "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -460,13 +460,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", - "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", + "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -488,15 +488,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", - "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", + "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0" + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -510,12 +510,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", - "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", + "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/types": "8.11.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1665,14 +1665,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.10.0.tgz", - "integrity": "sha512-YIu230PeN7z9zpu/EtqCIuRVHPs4iSlqW6TEvjbyDAE3MZsSl2RXBo+5ag+lbABCG8sFM1WVKEXhlQ8Ml8A3Fw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.11.0.tgz", + "integrity": "sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.10.0", - "@typescript-eslint/parser": "8.10.0", - "@typescript-eslint/utils": "8.10.0" + "@typescript-eslint/eslint-plugin": "8.11.0", + "@typescript-eslint/parser": "8.11.0", + "@typescript-eslint/utils": "8.11.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/nixosModules/ags/v2/package.json b/nixosModules/ags/v2/package.json index 64ba3347..b42f2c9e 100644 --- a/nixosModules/ags/v2/package.json +++ b/nixosModules/ags/v2/package.json @@ -6,12 +6,12 @@ "@eslint/js": "9.13.0", "@stylistic/eslint-plugin": "2.9.0", "@types/eslint__js": "8.42.3", - "@types/node": "22.7.7", + "@types/node": "22.8.1", "eslint": "9.13.0", "eslint-plugin-jsdoc": "50.4.3", "fzf": "0.5.2", "jiti": "2.3.3", "typescript": "5.6.3", - "typescript-eslint": "8.10.0" + "typescript-eslint": "8.11.0" } } diff --git a/nixosModules/ags/v2/services/monitor-clicks.ts b/nixosModules/ags/v2/services/monitor-clicks.ts index f4b2850f..c207b9f1 100644 --- a/nixosModules/ags/v2/services/monitor-clicks.ts +++ b/nixosModules/ags/v2/services/monitor-clicks.ts @@ -134,23 +134,25 @@ export default class MonitorClicks extends GObject.Object { overlayLayer.find( (n) => n.namespace.startsWith(name), ) || - // Return an empty Layer if widget doesn't exist - { - address: '', - x: 0, - y: 0, - w: 0, - h: 0, - namespace: '', - }, + // Return an empty Layer if widget doesn't exist + { + address: '', + x: 0, + y: 0, + w: 0, + h: 0, + namespace: '', + }, ); }); return arr; }; const clickIsOnWidget = (w: Layer) => { - return pos.x > w.x && pos.x < w.x + w.w && - pos.y > w.y && pos.y < w.y + w.h; + return ( + pos.x > w.x && pos.x < w.x + w.w && + pos.y > w.y && pos.y < w.y + w.h + ); }; const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames); @@ -158,24 +160,25 @@ export default class MonitorClicks extends GObject.Object { const widgets = overlayLayer.filter((n) => { let window = null as null | PopupWindow; - if (App.get_windows().some((win) => - win.name === n.namespace)) { + if (App.get_windows().some((win) => win.name === n.namespace)) { window = (App.get_window(n.namespace) as PopupWindow); } return window && - window.close_on_unfocus && - window.close_on_unfocus === - clickStage; + window.close_on_unfocus && + window.close_on_unfocus === + clickStage; }); if (noCloseWidgets.some(clickIsOnWidget)) { - // Don't handle clicks when on certain widgets + // Don't handle clicks when on certain widgets } else { widgets.forEach((w) => { - if (!(pos.x > w.x && pos.x < w.x + w.w && - pos.y > w.y && pos.y < w.y + w.h)) { + if (!( + pos.x > w.x && pos.x < w.x + w.w && + pos.y > w.y && pos.y < w.y + w.h + )) { App.get_window(w.namespace)?.set_visible(false); } }); diff --git a/nixosModules/ags/v2/style.scss b/nixosModules/ags/v2/style.scss index e8dc409f..fbbbb6e0 100644 --- a/nixosModules/ags/v2/style.scss +++ b/nixosModules/ags/v2/style.scss @@ -19,3 +19,4 @@ window, viewport { @import 'widgets/lockscreen/style.scss'; @import 'widgets/notifs/style.scss'; @import 'widgets/powermenu/style.scss'; +@import 'widgets/screenshot/style.scss'; diff --git a/nixosModules/ags/v2/widgets/applauncher/app-item.tsx b/nixosModules/ags/v2/widgets/applauncher/app-item.tsx index e21141e3..9677ef35 100644 --- a/nixosModules/ags/v2/widgets/applauncher/app-item.tsx +++ b/nixosModules/ags/v2/widgets/applauncher/app-item.tsx @@ -1,6 +1,8 @@ -import { App, Gtk, Widget } from 'astal/gtk3'; +import { Gtk, Widget } from 'astal/gtk3'; import { register } from 'astal/gobject'; +import { get_app_icon } from '../../lib'; + /* Types */ import AstalApps from 'gi://AstalApps'; type AppItemProps = Widget.BoxProps & { @@ -26,18 +28,11 @@ export class AppItem extends Widget.Box { this.app = app; const icon = ( - <icon css="font-size: 42px; margin-right: 25px;" /> - ) as Widget.Icon; - - if (app.get_icon_name() && Widget.Icon.lookup_icon(app.get_icon_name())) { - icon.icon = this.app.get_icon_name(); - } - else { - const iconString = this.app.get_key('Icon'); - - App.add_icons(iconString); - icon.icon = iconString; - } + <icon + icon={get_app_icon(this.app)} + css="font-size: 42px; margin-right: 25px;" + /> + ); const textBox = ( <box diff --git a/nixosModules/ags/v2/widgets/applauncher/main.tsx b/nixosModules/ags/v2/widgets/applauncher/main.tsx index 60027951..89e7c812 100644 --- a/nixosModules/ags/v2/widgets/applauncher/main.tsx +++ b/nixosModules/ags/v2/widgets/applauncher/main.tsx @@ -122,6 +122,7 @@ export default () => { <button css="margin-left: 5px;" + cursor="pointer" onButtonReleaseEvent={refreshApplications} > <icon icon="view-refresh-symbolic" css="font-size: 26px;" /> @@ -129,18 +130,20 @@ export default () => { </box> - <scrollable - className="widget app-list" + <eventbox cursor="pointer"> + <scrollable + className="widget app-list" - css="min-height: 600px; min-width: 600px;" - hscroll={Gtk.PolicyType.NEVER} - vscroll={Gtk.PolicyType.AUTOMATIC} - > - <box vertical> - {list} - {placeholder} - </box> - </scrollable> + css="min-height: 600px; min-width: 600px;" + hscroll={Gtk.PolicyType.NEVER} + vscroll={Gtk.PolicyType.AUTOMATIC} + > + <box vertical> + {list} + {placeholder} + </box> + </scrollable> + </eventbox> </box> </PopupWindow> ); diff --git a/nixosModules/ags/v2/widgets/bar/style.scss b/nixosModules/ags/v2/widgets/bar/style.scss index d9029bb1..7fe7a600 100644 --- a/nixosModules/ags/v2/widgets/bar/style.scss +++ b/nixosModules/ags/v2/widgets/bar/style.scss @@ -1,4 +1,6 @@ .bar { + margin-left: 5px; + margin-right: 15px; margin-bottom: 13px; .bar-item { diff --git a/nixosModules/ags/v2/widgets/date/style.scss b/nixosModules/ags/v2/widgets/date/style.scss index 893dffcf..3003419d 100644 --- a/nixosModules/ags/v2/widgets/date/style.scss +++ b/nixosModules/ags/v2/widgets/date/style.scss @@ -24,11 +24,7 @@ } .cal-box { - border-radius: 30px; padding: 0 1rem .2rem; - background-color: $window_bg_color; - border-bottom: 2px solid $accent_color; - border-top: 2px solid $accent_color; margin: 0 12px 18px; calendar { @@ -36,7 +32,6 @@ background-color: inherit; padding: .5rem .10rem 0; margin-left: 10px; - border-radius: 30px; &>* { border: solid 0 transparent; diff --git a/nixosModules/ags/v2/widgets/notifs/style.scss b/nixosModules/ags/v2/widgets/notifs/style.scss index 16efc182..acb22816 100644 --- a/nixosModules/ags/v2/widgets/notifs/style.scss +++ b/nixosModules/ags/v2/widgets/notifs/style.scss @@ -67,8 +67,6 @@ .notification-list-box { padding: 0 12px; - border-radius: 30px; - border-top: 2px solid $accent-color; .notification { box-shadow: none; diff --git a/nixosModules/ags/v2/widgets/powermenu/main.tsx b/nixosModules/ags/v2/widgets/powermenu/main.tsx index 8f4d10bf..fd4a10a5 100644 --- a/nixosModules/ags/v2/widgets/powermenu/main.tsx +++ b/nixosModules/ags/v2/widgets/powermenu/main.tsx @@ -10,6 +10,7 @@ const PowermenuWidget = () => ( <centerbox className="powermenu widget"> <button className="shutdown button" + cursor="pointer" onButtonReleaseEvent={() => execAsync(['systemctl', 'poweroff']).catch(print)} > <icon icon="system-shutdown-symbolic" /> @@ -17,6 +18,7 @@ const PowermenuWidget = () => ( <button className="reboot button" + cursor="pointer" onButtonReleaseEvent={() => execAsync(['systemctl', 'reboot']).catch(print)} > <icon icon="system-restart-symbolic" /> @@ -24,6 +26,7 @@ const PowermenuWidget = () => ( <button className="logout button" + cursor="pointer" onButtonReleaseEvent={() => hyprMessage('dispatch exit').catch(print)} > <icon icon="system-log-out-symbolic" /> diff --git a/nixosModules/ags/v2/widgets/screenshot/main.tsx b/nixosModules/ags/v2/widgets/screenshot/main.tsx new file mode 100644 index 00000000..816e176b --- /dev/null +++ b/nixosModules/ags/v2/widgets/screenshot/main.tsx @@ -0,0 +1,163 @@ +import { bind, execAsync, Variable } from 'astal'; +import { App, Gtk, Widget } from 'astal/gtk3'; + +import AstalApps from 'gi://AstalApps'; +const Applications = AstalApps.Apps.new(); + +import AstalHyprland from 'gi://AstalHyprland'; +const Hyprland = AstalHyprland.get_default(); + +import PopupWindow from '../misc/popup-window'; +import Separator from '../misc/separator'; + +import { get_app_icon, hyprMessage } from '../../lib'; + + +const ICON_SEP = 6; +const takeScreenshot = (selector: string, delay = 1000): void => { + App.get_window('win-screenshot')?.set_visible(false); + + setTimeout(() => { + execAsync([ + 'bash', + '-c', + `grim ${selector} - | satty -f - || true`, + ]).catch(console.error); + }, delay); +}; + +export default () => { + const windowList = <box vertical /> as Widget.Box; + + const updateWindows = async() => { + if (!App.get_window('win-screenshot')?.visible) { + return; + } + + windowList.children = (JSON.parse(await hyprMessage('j/clients')) as AstalHyprland.Client[]) + .filter((client) => client.workspace.id === Hyprland.get_focused_workspace().get_id()) + .map((client) => ( + <button + className="item-btn" + cursor="pointer" + + onButtonReleaseEvent={() => { + takeScreenshot(`-w ${client.address}`); + }} + > + <box halign={Gtk.Align.CENTER}> + <icon icon={get_app_icon(Applications.fuzzy_query(client.class)[0])} /> + + <Separator size={ICON_SEP} /> + + <label + label={client.title} + truncate + max_width_chars={50} + /> + </box> + </button> + )); + }; + + Hyprland.connect('notify::clients', updateWindows); + Hyprland.connect('notify::focused-workspace', updateWindows); + + const Shown = Variable<string>('monitors'); + + const stack = ( + <stack + shown={bind(Shown)} + transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT} + > + <scrollable name="monitors"> + <box vertical> + {bind(Hyprland, 'monitors').as((monitors) => monitors.map((monitor) => ( + <button + className="item-btn" + cursor="pointer" + + onButtonReleaseEvent={() => { + takeScreenshot(`-o ${monitor.name}`); + }} + > + <label + label={`${monitor.name}: ${monitor.description}`} + truncate + maxWidthChars={50} + /> + </button> + )))} + </box> + </scrollable> + + <scrollable name="windows"> + {windowList} + </scrollable> + </stack> + ) as Widget.Stack; + + const StackButton = ({ label = '', iconName = '' }) => ( + <button + cursor="pointer" + className={bind(Shown).as((shown) => + `header-btn${shown === label ? ' active' : ''}`)} + + onButtonReleaseEvent={() => { + Shown.set(label); + }} + > + <box halign={Gtk.Align.CENTER}> + <icon icon={iconName} /> + + <Separator size={ICON_SEP} /> + + {label} + </box> + </button> + ) as Widget.Button; + + const regionButton = ( + <button + cursor="pointer" + className="header-btn" + + onButtonReleaseEvent={() => { + takeScreenshot('-g "$(slurp)"', 0); + }} + > + <box halign={Gtk.Align.CENTER}> + <icon icon="tool-pencil-symbolic" /> + + <Separator size={ICON_SEP} /> + + region + </box> + </button> + ) as Widget.Button; + + return ( + <PopupWindow + name="screenshot" + on_open={() => { + updateWindows(); + }} + > + <box + className="screenshot widget" + vertical + > + <box + className="header" + homogeneous + > + <StackButton label="monitors" iconName="display-symbolic" /> + <StackButton label="windows" iconName="window-symbolic" /> + {regionButton} + </box> + + {stack} + </box> + </PopupWindow> + ); +}; diff --git a/nixosModules/ags/v2/widgets/screenshot/style.scss b/nixosModules/ags/v2/widgets/screenshot/style.scss new file mode 100644 index 00000000..735dcfab --- /dev/null +++ b/nixosModules/ags/v2/widgets/screenshot/style.scss @@ -0,0 +1,25 @@ +.screenshot { + font-size: 30px; + + .header { + .header-btn { + margin: 5px; + transition: background 400ms; + + &.active { + background: $window_bg_color; + } + } + } + + scrollable { + margin: 5px; + min-height: 400px; + + box { + .item-btn { + margin: 3px; + } + } + } +}