feat: polish base bar

Simplify the example design and popovers.
This commit is contained in:
Anthony Rodriguez 2025-02-12 17:02:27 +01:00
parent af86cc7beb
commit 1a23a45283
Signed by: nezia
SSH key fingerprint: SHA256:R/ue1eTzTHUoo77lJD/3fSUsyL4AwvcHImU5BAZai+8
4 changed files with 169 additions and 53 deletions

View file

@ -9,34 +9,25 @@
$bg: #{"@window_bg_color"};
$fg: #{"@window_fg_color"};
$accent: #{"@accent_color"};
$radius: 7px;
window.Bar {
.Bar {
border: none;
box-shadow: none;
background-color: $bg;
color: $fg;
font-size: 1.2em;
padding: 0.5em 0;
font-size: 16px;
padding: 0.5rem;
centerbox {
min-height: 2.5em;
}
.Workspaces {
button {
all: unset;
min-width: 1.1rem;
min-height: 1.1rem;
background-color: transparent;
border-radius: 50%;
min-width: 0.9rem;
min-height: 0.9rem;
border: 2px solid $fg;
margin: 0.25rem 0.3rem;
border-radius: 50%;
margin: 0em 0.3rem;
}
&:hover {
border-color: gtkalpha($accent, 0.6);
}
}
.focused {
border-color: $accent;
background-color: $accent;
@ -44,13 +35,30 @@ window.Bar {
}
.Battery label {
padding-left: 0;
margin-left: 0;
}
.NotificationCenter {
&:hover {
background-color: gtkalpha($fg, 0.2);
.QuickSettings {
image {
-gtk-icon-size: 1.2em;
}
menubutton box {
margin-right: 0.5em;
&:last-child {
margin-right: 0;
}
}
}
menubutton button {
background: transparent;
border-radius: 1em;
&:hover {
background-color: gtkalpha($fg, 0.1);
}
}
popover {
margin-top: 0.5em;
}
}

View file

@ -1,9 +1,9 @@
import { App, Astal, Gtk, Gdk } from "astal/gtk4";
import { Astal, Gtk, Gdk } from "astal/gtk4";
import { Variable, GLib, bind } from "astal";
import Battery from "gi://AstalBattery";
import Wp from "gi://AstalWp";
import Network from "gi://AstalNetwork";
import { Workspaces } from "./Workspaces";
import Hyprland from "gi://AstalHyprland";
function Wifi() {
const network = Network.get_default();
@ -25,7 +25,7 @@ function Audio() {
const speaker = Wp.get_default()?.audio.defaultSpeaker!;
return (
<box cssClasses={["AudioSlider"]}>
<box cssClasses={["Audio"]}>
<image iconName={bind(speaker, "volumeIcon")} />
</box>
);
@ -51,10 +51,54 @@ function Time({ format = "%H:%M - %A %e." }) {
);
return (
<label cssClasses={["Time"]} onDestroy={() => time.drop()} label={time()} />
<box cssClasses={["Time"]}>
<menubutton>
<label label={time()} />
<popover hasArrow={false}>
<Gtk.Calendar />
</popover>
</menubutton>
</box>
);
}
function QuickSettings() {
return (
<box cssClasses={["QuickSettings"]}>
<menubutton>
<box>
<Audio />
<Wifi />
<BatteryLevel />
</box>
<popover hasArrow={false}></popover>
</menubutton>
</box>
);
}
function Workspaces() {
const hypr = Hyprland.get_default();
return (
<box cssClasses={["Workspaces"]}>
{bind(hypr, "workspaces").as((wss) =>
wss
.filter((ws) => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
.sort((a, b) => a.id - b.id)
.map((ws) => (
<button
valign={Gtk.Align.CENTER}
cssClasses={bind(hypr, "focusedWorkspace").as((fw) =>
ws === fw ? ["focused"] : [""],
)}
onClicked={() => ws.focus()}
></button>
)),
)}
</box>
);
}
export default function Bar(monitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
@ -67,17 +111,15 @@ export default function Bar(monitor: Gdk.Monitor) {
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
>
<centerbox>
<centerbox shrinkCenterLast>
<box hexpand halign={Gtk.Align.START}>
<Workspaces />
</box>
<box>
<box halign={Gtk.Align.CENTER}>
<Time />
</box>
<box cssClasses={["NotificationCenter"]} hexpand halign={Gtk.Align.END}>
<Audio />
<Wifi />
<BatteryLevel />
<box hexpand halign={Gtk.Align.END}>
<QuickSettings />
</box>
</centerbox>
</window>

88
widget/Popover.tsx Normal file
View file

@ -0,0 +1,88 @@
import { Astal, Gdk, Gtk, Widget } from "astal/gtk4";
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
type PopoverProps = Pick<
Widget.WindowProps,
| "name"
| "namespace"
| "className"
| "visible"
| "child"
| "marginBottom"
| "marginTop"
| "marginLeft"
| "marginRight"
| "halign"
| "valign"
> & {
onClose?(self): void;
};
/**
* Full screen window widget where you can space the child widget
* using margins and alignment properties.
*
* NOTE: Child widgets will assume they can span across the full window width
* this means that setting `wrap` or `ellipsize` on labels for example will not work
* without explicitly setting its `max_width_chars` property.
* For a workaround see Popover2.tsx
*/
export default function Popover({
child,
marginBottom,
marginTop,
marginLeft,
marginRight,
halign = Gtk.Align.CENTER,
valign = Gtk.Align.CENTER,
onClose,
...props
}: PopoverProps) {
return (
<window
{...props}
css="background-color: transparent"
keymode={Astal.Keymode.EXCLUSIVE}
anchor={TOP | BOTTOM | LEFT | RIGHT}
exclusivity={Astal.Exclusivity.IGNORE}
onNotifyVisible={(self) => {
if (!self.visible) onClose?.(self);
}}
// close when click occurs otside of child
onButtonPressEvent={(self, event) => {
const [, _x, _y] = event.get_coords();
const { x, y, width, height } = self.get_child()!.get_allocation();
const xOut = _x < x || _x > x + width;
const yOut = _y < y || _y > y + height;
// clicked outside
if (xOut || yOut) {
self.visible = false;
}
}}
// close when hitting Escape
onKeyPressEvent={(self, event: Gdk.Event) => {
if (event.get_keyval()[1] === Gdk.KEY_Escape) {
self.visible = false;
}
}}
>
<box
// make sure click event does not bubble up
onButtonPressEvent={() => true}
// child can be positioned with `halign` `valign` and margins
expand
halign={halign}
valign={valign}
marginBottom={marginBottom}
marginTop={marginTop}
marginStart={marginLeft}
marginEnd={marginRight}
>
{child}
</box>
</window>
);
}

View file

@ -1,24 +1,2 @@
import { bind } from "astal";
import Hyprland from "gi://AstalHyprland";
export const Workspaces = () => {
const hypr = Hyprland.get_default();
return (
<box cssClasses={["Workspaces"]}>
{bind(hypr, "workspaces").as((wss) =>
wss
.filter((ws) => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
.sort((a, b) => a.id - b.id)
.map((ws) => (
<button
cssClasses={bind(hypr, "focusedWorkspace").as((fw) =>
ws === fw ? ["focused"] : [""],
)}
onClicked={() => ws.focus()}
></button>
)),
)}
</box>
);
};