feat(agsV2): add workspaces and fix types
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-09-27 21:14:58 -04:00
parent 2dcc17cbb2
commit cb617cbb91
9 changed files with 215 additions and 20 deletions

View file

@ -12,7 +12,7 @@ in
pname = "${pname}-types"; pname = "${pname}-types";
version = "0.0.0"; version = "0.0.0";
npmDepsHash = "sha256-8De8tRUKzRhD1jyx0anYNPMhxZyIr2nI45HdK6nb8jI="; npmDepsHash = "sha256-3ll4Xd5F8ZH/7q/gOF0jgerM6QRk71d93XIwtDssfxU=";
src = ./.; src = ./.;
dontNpmBuild = true; dontNpmBuild = true;
@ -22,8 +22,7 @@ in
${concatMapStringsSep "\n" (p: "-g ${p.package.dev}/share/gir-1.0 \\") withGirNames} ${concatMapStringsSep "\n" (p: "-g ${p.package.dev}/share/gir-1.0 \\") withGirNames}
-g ${ts-for-gir-src}/girs \ -g ${ts-for-gir-src}/girs \
--ignoreVersionConflicts \ --ignoreVersionConflicts \
--package \ -o ./types
-e gjs -o ./types
''; '';
installPhase = '' installPhase = ''

Binary file not shown.

View file

@ -1,5 +1,5 @@
{ {
"dependencies": { "dependencies": {
"@ts-for-gir/cli": "^3.3.0" "@ts-for-gir/cli": "4.0.0-beta.16"
} }
} }

View file

@ -51,7 +51,10 @@ export default ({
gdkmonitor = Gdk.Display.get_default()?.get_monitor(0) as Gdk.Monitor, gdkmonitor = Gdk.Display.get_default()?.get_monitor(0) as Gdk.Monitor,
child, child,
...rest ...rest
}: Widget.WindowProps) => { }: {
anchor: Astal.WindowAnchor
gdkmonitor?: Gdk.Monitor
} & Widget.WindowProps) => {
const monitor = get_hyprland_monitor_desc(gdkmonitor); const monitor = get_hyprland_monitor_desc(gdkmonitor);
const BarVisible = Variable(true); const BarVisible = Variable(true);
@ -143,7 +146,6 @@ export default ({
name={`bar-${monitor}`} name={`bar-${monitor}`}
layer={Astal.Layer.OVERLAY} layer={Astal.Layer.OVERLAY}
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
margins={[-1, -1, -1, -1]}
anchor={anchor} anchor={anchor}
{...rest} {...rest}
> >

View file

@ -1,4 +1,4 @@
import { bind, Widget } from 'astal'; import { bind } from 'astal';
import AstalBattery from 'gi://AstalBattery'; import AstalBattery from 'gi://AstalBattery';
const Battery = AstalBattery.get_default(); const Battery = AstalBattery.get_default();
@ -7,12 +7,11 @@ import Separator from '../../misc/separator';
const LOW_BATT = 20; const LOW_BATT = 20;
const SPACING = 8;
export default () => ( export default () => (
<box className="bar-item battery"> <box className="bar-item battery">
<icon <icon
setup={(self: Widget.Icon) => { setup={(self) => {
const update = () => { const update = () => {
const percent = Math.round(Battery.get_percentage() * 100); const percent = Math.round(Battery.get_percentage() * 100);
const level = Math.floor(percent / 10) * 10; const level = Math.floor(percent / 10) * 10;
@ -37,7 +36,7 @@ export default () => (
}} }}
/> />
<Separator size={SPACING} /> <Separator size={8} />
<label label={bind(Battery, 'percentage').as((v) => `${Math.round(v * 100)}%`)} /> <label label={bind(Battery, 'percentage').as((v) => `${Math.round(v * 100)}%`)} />
</box> </box>

View file

@ -1,6 +1,4 @@
import { bind, Widget } from 'astal'; import { bind } from 'astal';
import Pango from 'gi://Pango?version=1.0';
import AstalApps from 'gi://AstalApps?version=0.1'; import AstalApps from 'gi://AstalApps?version=0.1';
const Applications = AstalApps.Apps.new(); const Applications = AstalApps.Apps.new();
@ -11,8 +9,6 @@ const Hyprland = AstalHyprland.get_default();
import Separator from '../../misc/separator'; import Separator from '../../misc/separator';
const SPACING = 8;
export default () => { export default () => {
const focused = bind(Hyprland, 'focusedClient'); const focused = bind(Hyprland, 'focusedClient');
@ -23,10 +19,10 @@ export default () => {
> >
<icon <icon
css="font-size: 32px;" css="font-size: 32px;"
setup={(self: Widget.Icon) => { setup={(self) => {
self.hook(Hyprland, 'notify::focused-client', () => { self.hook(Hyprland, 'notify::focused-client', () => {
const app = Applications.query( const app = Applications.query(
Hyprland.get_focused_client().get_class(), Hyprland.get_focused_client()?.get_class() ?? '',
false, false,
)[0]; )[0];
@ -35,12 +31,12 @@ export default () => {
}} }}
/> />
<Separator size={SPACING} /> <Separator size={8} />
{focused.as((client) => (client && ( {focused.as((client) => (client && (
<label <label
label={bind(client, 'title').as(String)} label={bind(client, 'title').as(String)}
truncate={Pango.EllipsizeMode.END} truncate
/> />
)))} )))}
</box> </box>

View file

@ -0,0 +1,167 @@
import { Gtk, idle, timeout, Widget } from 'astal';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const Hyprland = AstalHyprland.get_default();
const URGENT_DURATION = 1000;
const Workspace = ({ id = 0 }) => (
<revealer
name={id.toString()}
transitionType={Gtk.RevealerTransitionType.SLIDE_RIGHT}
>
<eventbox
cursor="pointer"
tooltip_text={id.toString()}
onClickRelease={() => {
Hyprland.message_async(`dispatch workspace ${id}`, () => { /**/ });
}}
>
<box
valign={Gtk.Align.CENTER}
className="button"
setup={(self) => idle(() => {
const update = (
_: Widget.Box,
addr?: string,
) => {
const workspace = Hyprland.get_workspace(id);
const occupied = workspace && workspace.get_clients().length > 0;
self.toggleClassName('occupied', occupied);
if (!addr) {
return;
}
// Deal with urgent windows
const client = Hyprland.get_client(addr);
const isThisUrgent = client &&
client.workspace.id === id;
if (isThisUrgent) {
self.toggleClassName('urgent', true);
// Only show for a sec when urgent is current workspace
if (Hyprland.get_focused_workspace().get_id() === id) {
timeout(URGENT_DURATION, () => {
self.toggleClassName('urgent', false);
});
}
}
};
self
.hook(Hyprland, 'event', update)
// Deal with urgent windows
.hook(Hyprland, 'urgent', update)
.hook(Hyprland, 'notify::focused-workspace', () => {
if (Hyprland.get_focused_workspace().get_id() === id) {
self.toggleClassName('urgent', false);
}
});
})}
/>
</eventbox>
</revealer>
);
export default () => {
const L_PADDING = 2;
const WS_WIDTH = 30;
const updateHighlight = (self: Widget.Box) => {
const currentId = Hyprland.get_focused_workspace().get_id().toString();
const indicators = ((self.get_parent() as Widget.Overlay)
.child as Widget.Box)
.children as Widget.Revealer[];
const currentIndex = indicators.findIndex((w) => w.name === currentId);
if (currentIndex >= 0) {
self.css = `margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`;
}
};
const highlight = (
<box
className="button active"
valign={Gtk.Align.CENTER}
halign={Gtk.Align.START}
setup={(self) => {
self.hook(Hyprland, 'notify::focused-workspace', updateHighlight);
}}
/>
) as Widget.Box;
let workspaces: Widget.Revealer[] = [];
return (
<box
className="bar-item"
>
<overlay
className="workspaces"
passThrough
overlay={highlight}
>
<box
setup={(self) => {
const refresh = () => {
(self.children as Widget.Revealer[]).forEach((rev) => {
rev.reveal_child = false;
});
workspaces.forEach((ws) => {
ws.reveal_child = true;
});
};
const updateWorkspaces = () => {
Hyprland.get_workspaces().forEach((ws) => {
const currentWs = (self.children as Widget.Revealer[])
.find((ch) => ch.name === ws.id.toString());
if (!currentWs && ws.id > 0) {
self.add(Workspace({ id: ws.id }));
}
});
// Make sure the order is correct
workspaces.forEach((workspace, i) => {
(workspace.get_parent() as Widget.Box)
.reorder_child(workspace, i);
});
};
self.hook(Hyprland, 'event', () => {
workspaces = (self.children as Widget.Revealer[])
.filter((ch) => {
return Hyprland.get_workspaces().find((ws) => {
return ws.id.toString() === ch.name;
});
})
.sort((a, b) => parseInt(a.name ?? '0') - parseInt(b.name ?? '0'));
updateWorkspaces();
refresh();
// Make sure the highlight doesn't go too far
const TEMP_TIMEOUT = 100;
timeout(TEMP_TIMEOUT, () => updateHighlight(highlight));
});
}}
/>
</overlay>
</box>
);
};

View file

@ -4,7 +4,7 @@
border-radius: 7px; border-radius: 7px;
background-color: darken($window_bg_color, 3%); background-color: darken($window_bg_color, 3%);
font-size: 20px; font-size: 20px;
min-height: 30px; min-height: 35px;
.battery icon { .battery icon {
&.charging { &.charging {
@ -15,5 +15,32 @@
color: red; color: red;
} }
} }
.workspaces {
.button {
margin: 0 2.5px;
min-height: 22px;
min-width: 22px;
border-radius: 100%;
border: 2px solid transparent;
}
.occupied {
border: 2px solid $window_bg_color;
background: $accent_color;
transition: background-color 0.6s ease-in-out;
}
.urgent {
border: 2px solid $window_bg_color;
background: red;
transition: background-color 0.6s ease-in-out;
}
.active {
border: 2px solid #50fa7b;
transition: margin-left 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
}
} }
} }

View file

@ -3,6 +3,7 @@ import { Astal, Gtk } from 'astal';
import Battery from './items/battery'; import Battery from './items/battery';
import Clock from './items/clock'; import Clock from './items/clock';
import CurrentClient from './items/current-client'; import CurrentClient from './items/current-client';
import Workspaces from './items/workspaces';
import BarRevealer from './fullscreen'; import BarRevealer from './fullscreen';
import Separator from '../misc/separator'; import Separator from '../misc/separator';
@ -20,6 +21,10 @@ export default () => (
<centerbox className="bar widget"> <centerbox className="bar widget">
<box hexpand halign={Gtk.Align.START}> <box hexpand halign={Gtk.Align.START}>
<Workspaces />
<Separator size={8} />
<CurrentClient /> <CurrentClient />
</box> </box>