feat(agsV2): add workspaces and fix types
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
2dcc17cbb2
commit
cb617cbb91
9 changed files with 215 additions and 20 deletions
|
@ -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 = ''
|
||||||
|
|
BIN
lib/mk-types/package-lock.json
generated
BIN
lib/mk-types/package-lock.json
generated
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ts-for-gir/cli": "^3.3.0"
|
"@ts-for-gir/cli": "4.0.0-beta.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
167
nixosModules/ags/v2/widgets/bar/items/workspaces.tsx
Normal file
167
nixosModules/ags/v2/widgets/bar/items/workspaces.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue