bar: vertical mode

This commit is contained in:
2 * r + 2 * t 2025-02-18 22:34:13 +11:00
parent f7077ac51e
commit 0b771cbd1b
5 changed files with 164 additions and 68 deletions

View file

@ -2,7 +2,8 @@ import { Astal } from "astal/gtk3";
// Modules // Modules
export const bar = { export const bar = {
wsPerGroup: 10, vertical: true,
wsPerGroup: 5,
dateTimeFormat: "%d/%m/%y %R", dateTimeFormat: "%d/%m/%y %R",
}; };

View file

@ -6,22 +6,12 @@
.bar { .bar {
@include font.mono; @include font.mono;
margin: 10px 10px 0 10px;
font-size: lib.s(14); font-size: lib.s(14);
@include lib.spacing(10);
& > * {
@include lib.spacing(10);
}
.module { .module {
@include lib.rounded(8); @include lib.rounded(8);
padding: lib.s(3) lib.s(8);
background-color: scheme.$base; background-color: scheme.$base;
@include lib.spacing;
} }
label.icon { label.icon {
@ -33,7 +23,6 @@
color: scheme.$yellow; color: scheme.$yellow;
font-size: lib.s(14); font-size: lib.s(14);
padding-right: lib.s(12);
} }
.active-window { .active-window {
@ -43,55 +32,35 @@
.media-playing { .media-playing {
color: scheme.$lavender; color: scheme.$lavender;
@include lib.spacing(8);
icon { icon {
font-size: lib.s(16); font-size: lib.s(16);
} }
} }
.workspaces { .workspaces > * {
padding: lib.s(3) lib.s(18); @include lib.rounded(100);
@include lib.element-decel;
@include lib.spacing(10); min-width: lib.s(8);
min-height: lib.s(8);
background-color: scheme.$surface1;
& > * { &.occupied {
@include lib.rounded(100); background-color: scheme.$overlay1;
@include lib.element-decel; }
min-width: lib.s(8); &.focused {
min-height: lib.s(8); background-color: scheme.$mauve;
background-color: scheme.$surface1;
&.occupied {
background-color: scheme.$overlay1;
}
&.focused {
min-width: lib.s(30);
background-color: scheme.$mauve;
}
} }
} }
.tray { .tray {
font-size: lib.s(15); font-size: lib.s(15);
color: scheme.$text; color: scheme.$text;
@include lib.spacing(10);
} }
.status-icons { .status-icons {
color: scheme.$rosewater; color: scheme.$rosewater;
.bluetooth {
@include lib.spacing(10);
// The spacing doesn't look right for some reason so this
& > :first-child:not(:last-child) {
margin-right: lib.s(5);
}
}
} }
.pkg-updates { .pkg-updates {
@ -123,4 +92,98 @@
font-weight: bold; font-weight: bold;
font-size: lib.s(16); font-size: lib.s(16);
} }
&.horizontal {
margin: 10px 10px 0 10px;
@include lib.spacing(10);
& > * {
@include lib.spacing(10);
}
.module {
padding: lib.s(3) lib.s(8);
@include lib.spacing;
}
.os-icon {
padding-right: lib.s(12);
}
.media-playing {
@include lib.spacing(8);
}
.workspaces {
padding: lib.s(3) lib.s(18);
@include lib.spacing(10);
& > .focused {
min-width: lib.s(30);
}
}
.tray {
@include lib.spacing(10);
}
.status-icons .bluetooth {
@include lib.spacing(10);
// The spacing doesn't look right for some reason so this
& > :first-child:not(:last-child) {
margin-right: lib.s(5);
}
}
}
&.vertical {
margin: 10px 0 10px 10px;
@include lib.spacing(10, true);
& > * {
@include lib.spacing(10, true);
}
.module {
padding: lib.s(8);
@include lib.spacing($vertical: true);
}
.os-icon > * {
margin-left: lib.s(-5);
}
.media-playing {
@include lib.spacing(8, true);
}
.workspaces {
padding: lib.s(18) lib.s(3);
@include lib.spacing(10, true);
& > .focused {
min-height: lib.s(30);
}
}
.tray {
@include lib.spacing(10, true);
}
.status-icons .bluetooth {
@include lib.spacing(10, true);
// The spacing doesn't look right for some reason so this
& > :first-child:not(:last-child) {
margin-bottom: lib.s(5);
}
}
}
} }

View file

@ -68,14 +68,15 @@ const togglePopup = (self: JSX.Element, event: Astal.ClickEvent, name: string) =
const OSIcon = () => ( const OSIcon = () => (
<button <button
className="module os-icon" className="module os-icon"
label={osIcon}
onClick={(self, event) => event.button === Astal.MouseButton.PRIMARY && togglePopup(self, event, "sideleft")} onClick={(self, event) => event.button === Astal.MouseButton.PRIMARY && togglePopup(self, event, "sideleft")}
/> >
{osIcon}
</button>
); );
const ActiveWindow = () => ( const ActiveWindow = () => (
<box <box
hasTooltip vertical={config.vertical}
className="module active-window" className="module active-window"
setup={self => { setup={self => {
const title = Variable(""); const title = Variable("");
@ -101,8 +102,13 @@ const ActiveWindow = () => (
} }
/> />
<label <label
angle={config.vertical ? 270 : 0}
setup={self => setup={self =>
hookFocusedClientProp(self, "title", c => (self.label = c?.title ? ellipsize(c.title) : "Desktop")) hookFocusedClientProp(
self,
"title",
c => (self.label = c?.title ? ellipsize(c.title, config.vertical ? 30 : 40) : "Desktop")
)
} }
/> />
</box> </box>
@ -126,7 +132,7 @@ const MediaPlaying = () => {
setupCustomTooltip(self, bind(label)); setupCustomTooltip(self, bind(label));
}} }}
> >
<box className="module media-playing"> <box vertical={config.vertical} className="module media-playing">
<icon <icon
setup={self => setup={self =>
players.hookLastPlayer(self, "notify::identity", () => { players.hookLastPlayer(self, "notify::identity", () => {
@ -142,6 +148,7 @@ const MediaPlaying = () => {
} }
/> />
<label <label
angle={config.vertical ? 270 : 0}
setup={self => setup={self =>
players.hookLastPlayer(self, ["notify::title", "notify::artist"], () => { players.hookLastPlayer(self, ["notify::title", "notify::artist"], () => {
self.label = ellipsize(getLabel("No media")); // TODO: scroll text self.label = ellipsize(getLabel("No media")); // TODO: scroll text
@ -195,7 +202,7 @@ const Workspaces = () => (
hyprland.dispatch("workspace", (event.delta_y < 0 ? "-" : "+") + 1); hyprland.dispatch("workspace", (event.delta_y < 0 ? "-" : "+") + 1);
}} }}
> >
<box className="module workspaces"> <box vertical={config.vertical} className="module workspaces">
{Array.from({ length: config.wsPerGroup }).map((_, idx) => ( {Array.from({ length: config.wsPerGroup }).map((_, idx) => (
<Workspace idx={idx + 1} /> // Start from 1 <Workspace idx={idx + 1} /> // Start from 1
))} ))}
@ -248,7 +255,11 @@ const TrayItem = (item: AstalTray.TrayItem) => {
}; };
const Tray = () => ( const Tray = () => (
<box className="module tray" visible={bind(AstalTray.get_default(), "items").as(i => i.length > 0)}> <box
vertical={config.vertical}
className="module tray"
visible={bind(AstalTray.get_default(), "items").as(i => i.length > 0)}
>
{bind(AstalTray.get_default(), "items").as(i => i.map(TrayItem))} {bind(AstalTray.get_default(), "items").as(i => i.map(TrayItem))}
</box> </box>
); );
@ -378,7 +389,7 @@ const BluetoothDevice = (device: AstalBluetooth.Device) => (
); );
const Bluetooth = () => ( const Bluetooth = () => (
<box className="bluetooth"> <box vertical={config.vertical} className="bluetooth">
<button <button
onClick={(self, event) => { onClick={(self, event) => {
if (event.button === Astal.MouseButton.PRIMARY) togglePopup(self, event, "bluetooth-devices"); if (event.button === Astal.MouseButton.PRIMARY) togglePopup(self, event, "bluetooth-devices");
@ -424,7 +435,7 @@ const Bluetooth = () => (
); );
const StatusIcons = () => ( const StatusIcons = () => (
<box className="module status-icons"> <box vertical={config.vertical} className="module status-icons">
<Network /> <Network />
<Bluetooth /> <Bluetooth />
</box> </box>
@ -440,7 +451,7 @@ const PkgUpdates = () => (
) )
} }
> >
<box className="module pkg-updates"> <box vertical={config.vertical} className="module pkg-updates">
<label className="icon" label="download" /> <label className="icon" label="download" />
<label label={bind(Updates.get_default(), "numUpdates").as(String)} /> <label label={bind(Updates.get_default(), "numUpdates").as(String)} />
</box> </box>
@ -461,7 +472,7 @@ const NotifCount = () => (
) )
} }
> >
<box className="module notif-count"> <box vertical={config.vertical} className="module notif-count">
<label className="icon" label="info" /> <label className="icon" label="info" />
<label label={bind(AstalNotifd.get_default(), "notifications").as(n => String(n.length))} /> <label label={bind(AstalNotifd.get_default(), "notifications").as(n => String(n.length))} />
</box> </box>
@ -476,6 +487,7 @@ const Battery = () => {
return ( return (
<box <box
vertical={config.vertical}
className={bind(className)} className={bind(className)}
setup={self => setup={self =>
setupCustomTooltip( setupCustomTooltip(
@ -486,7 +498,9 @@ const Battery = () => {
onDestroy={() => className.drop()} onDestroy={() => className.drop()}
> >
<revealer <revealer
transitionType={Gtk.RevealerTransitionType.SLIDE_RIGHT} transitionType={
config.vertical ? Gtk.RevealerTransitionType.SLIDE_UP : Gtk.RevealerTransitionType.SLIDE_LEFT
}
transitionDuration={150} transitionDuration={150}
revealChild={bind(AstalBattery.get_default(), "charging")} revealChild={bind(AstalBattery.get_default(), "charging")}
> >
@ -502,9 +516,9 @@ const DateTime = () => (
<button <button
onClick={(self, event) => event.button === Astal.MouseButton.PRIMARY && togglePopup(self, event, "sideright")} onClick={(self, event) => event.button === Astal.MouseButton.PRIMARY && togglePopup(self, event, "sideright")}
> >
<box className="module date-time"> <box vertical={config.vertical} className="module date-time">
<label className="icon" label="calendar_month" /> <label className="icon" label="calendar_month" />
<label label={bindCurrentTime(config.dateTimeFormat)} /> <label angle={config.vertical ? 270 : 0} label={bindCurrentTime(config.dateTimeFormat)} />
</box> </box>
</button> </button>
); );
@ -521,25 +535,29 @@ export default ({ monitor }: { monitor: Monitor }) => (
<window <window
namespace="caelestia-bar" namespace="caelestia-bar"
monitor={monitor.id} monitor={monitor.id}
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT} anchor={
Astal.WindowAnchor.TOP |
Astal.WindowAnchor.LEFT |
(config.vertical ? Astal.WindowAnchor.BOTTOM : Astal.WindowAnchor.RIGHT)
}
exclusivity={Astal.Exclusivity.EXCLUSIVE} exclusivity={Astal.Exclusivity.EXCLUSIVE}
> >
<centerbox className="bar"> <centerbox vertical={config.vertical} className={`bar ${config.vertical ? "vertical" : " horizontal"}`}>
<box> <box vertical={config.vertical}>
<OSIcon /> <OSIcon />
<ActiveWindow /> <ActiveWindow />
<MediaPlaying /> <MediaPlaying />
<button <button
hexpand expand
onScroll={(_, event) => onScroll={(_, event) =>
event.delta_y > 0 ? (monitor.brightness -= 0.1) : (monitor.brightness += 0.1) event.delta_y > 0 ? (monitor.brightness -= 0.1) : (monitor.brightness += 0.1)
} }
/> />
</box> </box>
<Workspaces /> <Workspaces />
<box> <box vertical={config.vertical}>
<button <button
hexpand expand
onScroll={(_, event) => { onScroll={(_, event) => {
const speaker = AstalWp01.get_default()?.audio.defaultSpeaker; const speaker = AstalWp01.get_default()?.audio.defaultSpeaker;
if (!speaker) return; if (!speaker) return;

View file

@ -6,6 +6,8 @@ import type { AstalWidget } from "./types";
export const setupCustomTooltip = (self: AstalWidget, text: string | Binding<string>) => { export const setupCustomTooltip = (self: AstalWidget, text: string | Binding<string>) => {
if (!text) return null; if (!text) return null;
self.set_has_tooltip(true);
const window = new Widget.Window({ const window = new Widget.Window({
visible: false, visible: false,
namespace: "caelestia-tooltip", namespace: "caelestia-tooltip",

View file

@ -1,5 +1,6 @@
import { Binding, register } from "astal"; import { Binding, register } from "astal";
import { App, Astal, Gdk, Widget } from "astal/gtk3"; import { App, Astal, Gdk, Widget } from "astal/gtk3";
import { bar } from "config";
import AstalHyprland from "gi://AstalHyprland?version=0.1"; import AstalHyprland from "gi://AstalHyprland?version=0.1";
const extendProp = <T>( const extendProp = <T>(
@ -29,19 +30,30 @@ export default class PopupWindow extends Widget.Window {
popup_at_widget(widget: JSX.Element, event: Gdk.Event | Astal.ClickEvent) { popup_at_widget(widget: JSX.Element, event: Gdk.Event | Astal.ClickEvent) {
const { width, height } = widget.get_allocation(); const { width, height } = widget.get_allocation();
const mWidth = AstalHyprland.get_default().get_focused_monitor().get_width(); const { width: mWidth, height: mHeight } = AstalHyprland.get_default().get_focused_monitor();
const pWidth = this.get_preferred_width()[1]; const pWidth = this.get_preferred_width()[1];
const pHeight = this.get_preferred_height()[1];
const [, x, y] = event instanceof Gdk.Event ? event.get_coords() : [null, event.x, event.y]; const [, x, y] = event instanceof Gdk.Event ? event.get_coords() : [null, event.x, event.y];
const { x: cx, y: cy } = AstalHyprland.get_default().get_cursor_position(); const { x: cx, y: cy } = AstalHyprland.get_default().get_cursor_position();
let marginLeft = cx + ((width - pWidth) / 2 - x); let marginLeft = 0;
if (marginLeft < 0) marginLeft = 0; let marginTop = 0;
else if (marginLeft + pWidth > mWidth) marginLeft = mWidth - pWidth; if (bar.vertical) {
marginLeft = cx + (width - x);
marginTop = cy + ((height - pHeight) / 2 - y);
if (marginTop < 0) marginTop = 0;
else if (marginTop + pHeight > mHeight) marginTop = mHeight - pHeight;
} else {
marginLeft = cx + ((width - pWidth) / 2 - x);
if (marginLeft < 0) marginLeft = 0;
else if (marginLeft + pWidth > mWidth) marginLeft = mWidth - pWidth;
marginTop = cy + (height - y);
}
this.anchor = Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT; this.anchor = Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT;
this.exclusivity = Astal.Exclusivity.IGNORE; this.exclusivity = Astal.Exclusivity.IGNORE;
this.marginLeft = marginLeft; this.marginLeft = marginLeft;
this.marginTop = cy + (height - y); this.marginTop = marginTop;
this.show(); this.show();
} }