feat(agsV2): migrate screenshot window
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
c17a45e5d0
commit
653961ada1
15 changed files with 259 additions and 54 deletions
|
@ -12,6 +12,7 @@ import Corners from './widgets/corners/main';
|
||||||
import IconBrowser from './widgets/icon-browser/main';
|
import IconBrowser from './widgets/icon-browser/main';
|
||||||
import { NotifPopups, NotifCenter } from './widgets/notifs/main';
|
import { NotifPopups, NotifCenter } from './widgets/notifs/main';
|
||||||
import PowerMenu from './widgets/powermenu/main';
|
import PowerMenu from './widgets/powermenu/main';
|
||||||
|
import Screenshot from './widgets/screenshot/main';
|
||||||
|
|
||||||
import MonitorClicks from './services/monitor-clicks';
|
import MonitorClicks from './services/monitor-clicks';
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ switch (CONF) {
|
||||||
NotifPopups();
|
NotifPopups();
|
||||||
NotifCenter();
|
NotifCenter();
|
||||||
PowerMenu();
|
PowerMenu();
|
||||||
|
Screenshot();
|
||||||
|
|
||||||
new MonitorClicks();
|
new MonitorClicks();
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,7 +51,7 @@ self: {
|
||||||
})
|
})
|
||||||
// {
|
// {
|
||||||
"${configDir}/node_modules".source =
|
"${configDir}/node_modules".source =
|
||||||
buildNodeModules ./. "sha256-f0hbPvHTqeFM7mfmV+sN4EEuE0F91f5kjJ/EHy0oU+Y=";
|
buildNodeModules ./. "sha256-pK9S6qUjTIL0JDegYJlHSY5XEpLFKfA98MfZ59Q3IL4=";
|
||||||
|
|
||||||
"${configDir}/tsconfig.json".source = pkgs.writers.writeJSON "tsconfig.json" {
|
"${configDir}/tsconfig.json".source = pkgs.writers.writeJSON "tsconfig.json" {
|
||||||
"$schema" = "https://json.schemastore.org/tsconfig";
|
"$schema" = "https://json.schemastore.org/tsconfig";
|
||||||
|
|
|
@ -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';
|
import AstalHyprland from 'gi://AstalHyprland';
|
||||||
const Hyprland = AstalHyprland.get_default();
|
const Hyprland = AstalHyprland.get_default();
|
||||||
|
@ -109,3 +111,16 @@ export const centerCursor = async(): Promise<void> => {
|
||||||
|
|
||||||
await hyprMessage(`dispatch movecursor ${x} ${y}`);
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
BIN
nixosModules/ags/v2/package-lock.json
generated
BIN
nixosModules/ags/v2/package-lock.json
generated
Binary file not shown.
|
@ -6,12 +6,12 @@
|
||||||
"@eslint/js": "9.13.0",
|
"@eslint/js": "9.13.0",
|
||||||
"@stylistic/eslint-plugin": "2.9.0",
|
"@stylistic/eslint-plugin": "2.9.0",
|
||||||
"@types/eslint__js": "8.42.3",
|
"@types/eslint__js": "8.42.3",
|
||||||
"@types/node": "22.7.7",
|
"@types/node": "22.8.1",
|
||||||
"eslint": "9.13.0",
|
"eslint": "9.13.0",
|
||||||
"eslint-plugin-jsdoc": "50.4.3",
|
"eslint-plugin-jsdoc": "50.4.3",
|
||||||
"fzf": "0.5.2",
|
"fzf": "0.5.2",
|
||||||
"jiti": "2.3.3",
|
"jiti": "2.3.3",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.6.3",
|
||||||
"typescript-eslint": "8.10.0"
|
"typescript-eslint": "8.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,8 +149,10 @@ export default class MonitorClicks extends GObject.Object {
|
||||||
return arr;
|
return arr;
|
||||||
};
|
};
|
||||||
const clickIsOnWidget = (w: Layer) => {
|
const clickIsOnWidget = (w: Layer) => {
|
||||||
return pos.x > w.x && pos.x < w.x + w.w &&
|
return (
|
||||||
pos.y > w.y && pos.y < w.y + w.h;
|
pos.x > w.x && pos.x < w.x + w.w &&
|
||||||
|
pos.y > w.y && pos.y < w.y + w.h
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames);
|
const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames);
|
||||||
|
@ -158,8 +160,7 @@ export default class MonitorClicks extends GObject.Object {
|
||||||
const widgets = overlayLayer.filter((n) => {
|
const widgets = overlayLayer.filter((n) => {
|
||||||
let window = null as null | PopupWindow;
|
let window = null as null | PopupWindow;
|
||||||
|
|
||||||
if (App.get_windows().some((win) =>
|
if (App.get_windows().some((win) => win.name === n.namespace)) {
|
||||||
win.name === n.namespace)) {
|
|
||||||
window = (App.get_window(n.namespace) as PopupWindow);
|
window = (App.get_window(n.namespace) as PopupWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,8 +175,10 @@ export default class MonitorClicks extends GObject.Object {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
widgets.forEach((w) => {
|
widgets.forEach((w) => {
|
||||||
if (!(pos.x > w.x && pos.x < w.x + w.w &&
|
if (!(
|
||||||
pos.y > w.y && pos.y < w.y + w.h)) {
|
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);
|
App.get_window(w.namespace)?.set_visible(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,3 +19,4 @@ window, viewport {
|
||||||
@import 'widgets/lockscreen/style.scss';
|
@import 'widgets/lockscreen/style.scss';
|
||||||
@import 'widgets/notifs/style.scss';
|
@import 'widgets/notifs/style.scss';
|
||||||
@import 'widgets/powermenu/style.scss';
|
@import 'widgets/powermenu/style.scss';
|
||||||
|
@import 'widgets/screenshot/style.scss';
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { App, Gtk, Widget } from 'astal/gtk3';
|
import { Gtk, Widget } from 'astal/gtk3';
|
||||||
import { register } from 'astal/gobject';
|
import { register } from 'astal/gobject';
|
||||||
|
|
||||||
|
import { get_app_icon } from '../../lib';
|
||||||
|
|
||||||
/* Types */
|
/* Types */
|
||||||
import AstalApps from 'gi://AstalApps';
|
import AstalApps from 'gi://AstalApps';
|
||||||
type AppItemProps = Widget.BoxProps & {
|
type AppItemProps = Widget.BoxProps & {
|
||||||
|
@ -26,18 +28,11 @@ export class AppItem extends Widget.Box {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|
||||||
const icon = (
|
const icon = (
|
||||||
<icon css="font-size: 42px; margin-right: 25px;" />
|
<icon
|
||||||
) as Widget.Icon;
|
icon={get_app_icon(this.app)}
|
||||||
|
css="font-size: 42px; margin-right: 25px;"
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const textBox = (
|
const textBox = (
|
||||||
<box
|
<box
|
||||||
|
|
|
@ -122,6 +122,7 @@ export default () => {
|
||||||
|
|
||||||
<button
|
<button
|
||||||
css="margin-left: 5px;"
|
css="margin-left: 5px;"
|
||||||
|
cursor="pointer"
|
||||||
onButtonReleaseEvent={refreshApplications}
|
onButtonReleaseEvent={refreshApplications}
|
||||||
>
|
>
|
||||||
<icon icon="view-refresh-symbolic" css="font-size: 26px;" />
|
<icon icon="view-refresh-symbolic" css="font-size: 26px;" />
|
||||||
|
@ -129,6 +130,7 @@ export default () => {
|
||||||
|
|
||||||
</box>
|
</box>
|
||||||
|
|
||||||
|
<eventbox cursor="pointer">
|
||||||
<scrollable
|
<scrollable
|
||||||
className="widget app-list"
|
className="widget app-list"
|
||||||
|
|
||||||
|
@ -141,6 +143,7 @@ export default () => {
|
||||||
{placeholder}
|
{placeholder}
|
||||||
</box>
|
</box>
|
||||||
</scrollable>
|
</scrollable>
|
||||||
|
</eventbox>
|
||||||
</box>
|
</box>
|
||||||
</PopupWindow>
|
</PopupWindow>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
.bar {
|
.bar {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 15px;
|
||||||
margin-bottom: 13px;
|
margin-bottom: 13px;
|
||||||
|
|
||||||
.bar-item {
|
.bar-item {
|
||||||
|
|
|
@ -24,11 +24,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cal-box {
|
.cal-box {
|
||||||
border-radius: 30px;
|
|
||||||
padding: 0 1rem .2rem;
|
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;
|
margin: 0 12px 18px;
|
||||||
|
|
||||||
calendar {
|
calendar {
|
||||||
|
@ -36,7 +32,6 @@
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
padding: .5rem .10rem 0;
|
padding: .5rem .10rem 0;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
border-radius: 30px;
|
|
||||||
|
|
||||||
&>* {
|
&>* {
|
||||||
border: solid 0 transparent;
|
border: solid 0 transparent;
|
||||||
|
|
|
@ -67,8 +67,6 @@
|
||||||
|
|
||||||
.notification-list-box {
|
.notification-list-box {
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
border-radius: 30px;
|
|
||||||
border-top: 2px solid $accent-color;
|
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -10,6 +10,7 @@ const PowermenuWidget = () => (
|
||||||
<centerbox className="powermenu widget">
|
<centerbox className="powermenu widget">
|
||||||
<button
|
<button
|
||||||
className="shutdown button"
|
className="shutdown button"
|
||||||
|
cursor="pointer"
|
||||||
onButtonReleaseEvent={() => execAsync(['systemctl', 'poweroff']).catch(print)}
|
onButtonReleaseEvent={() => execAsync(['systemctl', 'poweroff']).catch(print)}
|
||||||
>
|
>
|
||||||
<icon icon="system-shutdown-symbolic" />
|
<icon icon="system-shutdown-symbolic" />
|
||||||
|
@ -17,6 +18,7 @@ const PowermenuWidget = () => (
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="reboot button"
|
className="reboot button"
|
||||||
|
cursor="pointer"
|
||||||
onButtonReleaseEvent={() => execAsync(['systemctl', 'reboot']).catch(print)}
|
onButtonReleaseEvent={() => execAsync(['systemctl', 'reboot']).catch(print)}
|
||||||
>
|
>
|
||||||
<icon icon="system-restart-symbolic" />
|
<icon icon="system-restart-symbolic" />
|
||||||
|
@ -24,6 +26,7 @@ const PowermenuWidget = () => (
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="logout button"
|
className="logout button"
|
||||||
|
cursor="pointer"
|
||||||
onButtonReleaseEvent={() => hyprMessage('dispatch exit').catch(print)}
|
onButtonReleaseEvent={() => hyprMessage('dispatch exit').catch(print)}
|
||||||
>
|
>
|
||||||
<icon icon="system-log-out-symbolic" />
|
<icon icon="system-log-out-symbolic" />
|
||||||
|
|
163
nixosModules/ags/v2/widgets/screenshot/main.tsx
Normal file
163
nixosModules/ags/v2/widgets/screenshot/main.tsx
Normal file
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
25
nixosModules/ags/v2/widgets/screenshot/style.scss
Normal file
25
nixosModules/ags/v2/widgets/screenshot/style.scss
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue