2024-10-28 14:45:10 -04:00
|
|
|
import { bind, execAsync, Variable } from 'astal';
|
|
|
|
import { App, Gtk, Widget } from 'astal/gtk3';
|
|
|
|
|
|
|
|
import AstalApps from 'gi://AstalApps';
|
|
|
|
import AstalHyprland from 'gi://AstalHyprland';
|
|
|
|
|
|
|
|
import PopupWindow from '../misc/popup-window';
|
|
|
|
import Separator from '../misc/separator';
|
|
|
|
|
2024-10-28 15:09:10 -04:00
|
|
|
import { hyprMessage } from '../../lib';
|
2024-10-28 14:45:10 -04:00
|
|
|
|
|
|
|
|
|
|
|
const ICON_SEP = 6;
|
2024-11-19 22:05:12 -05:00
|
|
|
const takeScreenshot = (selector: string[], delay = 1000): void => {
|
2024-10-28 14:45:10 -04:00
|
|
|
App.get_window('win-screenshot')?.set_visible(false);
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
execAsync([
|
2024-11-19 22:05:12 -05:00
|
|
|
`${SRC}/widgets/screenshot/capture.sh`,
|
|
|
|
].concat(selector)).catch(console.error);
|
2024-10-28 14:45:10 -04:00
|
|
|
}, delay);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default () => {
|
2024-11-20 23:38:41 -05:00
|
|
|
const hyprland = AstalHyprland.get_default();
|
|
|
|
|
2024-10-28 14:45:10 -04:00
|
|
|
const windowList = <box vertical /> as Widget.Box;
|
|
|
|
|
|
|
|
const updateWindows = async() => {
|
|
|
|
if (!App.get_window('win-screenshot')?.visible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-20 23:38:41 -05:00
|
|
|
const applications = AstalApps.Apps.new();
|
|
|
|
|
2024-10-28 14:45:10 -04:00
|
|
|
windowList.children = (JSON.parse(await hyprMessage('j/clients')) as AstalHyprland.Client[])
|
2024-11-20 23:38:41 -05:00
|
|
|
.filter((client) => client.workspace.id === hyprland.get_focused_workspace().get_id())
|
2024-10-28 14:45:10 -04:00
|
|
|
.map((client) => (
|
|
|
|
<button
|
|
|
|
className="item-btn"
|
|
|
|
cursor="pointer"
|
|
|
|
|
|
|
|
onButtonReleaseEvent={() => {
|
2024-11-19 22:05:12 -05:00
|
|
|
takeScreenshot(['-w', client.address]);
|
2024-10-28 14:45:10 -04:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<box halign={Gtk.Align.CENTER}>
|
2024-11-20 23:38:41 -05:00
|
|
|
<icon icon={applications.fuzzy_query(client.class)[0].iconName} />
|
2024-10-28 14:45:10 -04:00
|
|
|
|
|
|
|
<Separator size={ICON_SEP} />
|
|
|
|
|
|
|
|
<label
|
|
|
|
label={client.title}
|
|
|
|
truncate
|
|
|
|
max_width_chars={50}
|
|
|
|
/>
|
|
|
|
</box>
|
|
|
|
</button>
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
2024-11-20 23:38:41 -05:00
|
|
|
hyprland.connect('notify::clients', updateWindows);
|
|
|
|
hyprland.connect('notify::focused-workspace', updateWindows);
|
2024-10-28 14:45:10 -04:00
|
|
|
|
|
|
|
const Shown = Variable<string>('monitors');
|
|
|
|
|
|
|
|
const stack = (
|
|
|
|
<stack
|
|
|
|
shown={bind(Shown)}
|
|
|
|
transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
|
|
|
|
>
|
|
|
|
<scrollable name="monitors">
|
|
|
|
<box vertical>
|
2024-11-20 23:38:41 -05:00
|
|
|
{bind(hyprland, 'monitors').as((monitors) => monitors.map((monitor) => (
|
2024-10-28 14:45:10 -04:00
|
|
|
<button
|
|
|
|
className="item-btn"
|
|
|
|
cursor="pointer"
|
|
|
|
|
|
|
|
onButtonReleaseEvent={() => {
|
2024-11-19 22:05:12 -05:00
|
|
|
takeScreenshot(['-o', monitor.name]);
|
2024-10-28 14:45:10 -04:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<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;
|
|
|
|
|
2024-11-19 22:05:12 -05:00
|
|
|
let frozen = false;
|
|
|
|
const freezeIcon = <icon icon="checkbox-symbolic" /> as Widget.Icon;
|
|
|
|
const freezeButton = (
|
2024-10-28 14:45:10 -04:00
|
|
|
<button
|
|
|
|
cursor="pointer"
|
|
|
|
className="header-btn"
|
|
|
|
|
|
|
|
onButtonReleaseEvent={() => {
|
2024-11-19 22:05:12 -05:00
|
|
|
frozen = !frozen;
|
|
|
|
freezeIcon.icon = frozen ?
|
|
|
|
'checkbox-checked-symbolic' :
|
|
|
|
'checkbox-symbolic';
|
2024-10-28 14:45:10 -04:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<box halign={Gtk.Align.CENTER}>
|
2024-11-19 22:05:12 -05:00
|
|
|
{freezeIcon}
|
2024-10-28 14:45:10 -04:00
|
|
|
|
|
|
|
<Separator size={ICON_SEP} />
|
|
|
|
|
2024-11-19 22:05:12 -05:00
|
|
|
freeze
|
|
|
|
</box>
|
|
|
|
</button>
|
|
|
|
) as Widget.Button;
|
|
|
|
|
|
|
|
const regionButton = (
|
|
|
|
<button
|
|
|
|
cursor="pointer"
|
|
|
|
className="header-btn"
|
|
|
|
|
|
|
|
onButtonReleaseEvent={() => {
|
|
|
|
takeScreenshot(['region', frozen ? 'true' : 'false'], 0);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<box halign={Gtk.Align.CENTER}>
|
|
|
|
<icon icon="tool-crop-symbolic" />
|
2024-10-28 14:45:10 -04:00
|
|
|
</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" />
|
2024-11-19 22:05:12 -05:00
|
|
|
<box>
|
|
|
|
{regionButton}
|
|
|
|
{freezeButton}
|
|
|
|
</box>
|
2024-10-28 14:45:10 -04:00
|
|
|
</box>
|
|
|
|
|
|
|
|
{stack}
|
|
|
|
</box>
|
|
|
|
</PopupWindow>
|
|
|
|
);
|
|
|
|
};
|