Reworked toggle buttons and added power menu
This commit is contained in:
@@ -9,6 +9,7 @@ import client from "./client"
|
||||
import player from "./player"
|
||||
import bluetooth from "./bluetooth"
|
||||
import notifs from "./notif"
|
||||
import power from "./power"
|
||||
|
||||
const time = Variable("").poll(1000, "date +'%a %b %d · %H:%M'")
|
||||
|
||||
@@ -21,11 +22,13 @@ export default function Bar(monitor: Hyprland.Monitor) {
|
||||
monitor={monitor.id}
|
||||
exclusivity={Astal.Exclusivity.IGNORE}
|
||||
anchor={TOP | LEFT | RIGHT}
|
||||
heightRequest={36}
|
||||
layer={Astal.Layer.TOP}
|
||||
application={App}>
|
||||
<centerbox>
|
||||
<eventbox
|
||||
onHover={self => self.css="box-shadow: 0 0 16px 4px black;"}
|
||||
><centerbox heightRequest={36}>
|
||||
<box>
|
||||
{power()}
|
||||
{battery()}
|
||||
{volume()}
|
||||
{brightness()}
|
||||
@@ -43,6 +46,6 @@ export default function Bar(monitor: Hyprland.Monitor) {
|
||||
</box></button>
|
||||
{notifs()}
|
||||
</box>
|
||||
</centerbox>
|
||||
</centerbox></eventbox>
|
||||
</window>
|
||||
}
|
||||
|
||||
@@ -19,17 +19,19 @@ function profileButton(label: string, profile: string) {
|
||||
return <button className="toggleButton" onClicked={(self) => {
|
||||
powerProfiles.activeProfile = profile
|
||||
}}>
|
||||
<overlay overlay={new Widget.Box({
|
||||
halign: Gtk.Align.END,
|
||||
valign: Gtk.Align.CENTER,
|
||||
className: bind(powerProfiles, "activeProfile").as((p) =>
|
||||
<box halign={Gtk.Align.FILL}>
|
||||
<label halign={Gtk.Align.START} hexpand>{label}</label>
|
||||
<box
|
||||
halign={Gtk.Align.END}
|
||||
valign={Gtk.Align.CENTER}
|
||||
className={bind(powerProfiles, "activeProfile").as((p) =>
|
||||
(p === profile)
|
||||
? "toggleIndicator active"
|
||||
: "toggleIndicator inactive"
|
||||
)
|
||||
})}>
|
||||
<label halign={Gtk.Align.START}>{label}</label>
|
||||
</overlay>
|
||||
}
|
||||
/>
|
||||
</box>
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -63,47 +65,40 @@ function BattWindow() {
|
||||
/>
|
||||
</overlay>
|
||||
<box vertical={true} spacing={10}>
|
||||
<overlay overlay={new Widget.Box({
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.CENTER,
|
||||
vertical: true,
|
||||
children: [
|
||||
new Widget.Label({
|
||||
className: "bigText",
|
||||
label: bind(batt, "energyRate").as((w) => `${w}W`),
|
||||
}),
|
||||
new Widget.Label({
|
||||
className: "smallText",
|
||||
label: bind(batt, "charging").as(
|
||||
(c) => c ? "Charging" : "Discharging"
|
||||
),
|
||||
}),
|
||||
]
|
||||
})}>
|
||||
<box className="info" />
|
||||
</overlay>
|
||||
<overlay overlay={new Widget.Box({
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.CENTER,
|
||||
vertical: true,
|
||||
children: [
|
||||
new Widget.Label({
|
||||
className: "status",
|
||||
label: mergeBindings([charging, toFull, toEmpty]).as(
|
||||
<box className="info" vertical>
|
||||
<label
|
||||
css="font-size: 16px;"
|
||||
vexpand
|
||||
valign={Gtk.Align.END}>
|
||||
{bind(batt, "energyRate").as(w => `${w}W`)}
|
||||
</label>
|
||||
<label
|
||||
vexpand
|
||||
valign={Gtk.Align.START}>
|
||||
{bind(batt, "charging").as(c =>
|
||||
c ? "Charging" : "Discharging"
|
||||
)}
|
||||
</label>
|
||||
</box>
|
||||
<box className="info" vertical>
|
||||
<label
|
||||
css="font-size: 16px;"
|
||||
vexpand
|
||||
valign={Gtk.Align.END}>
|
||||
{mergeBindings([charging, toFull, toEmpty]).as(
|
||||
(b) => {
|
||||
let s = (b[0] ? b[1] : b[2])
|
||||
return `${Math.floor(s/3600)}H ${Math.floor((s%3600)/60)}M`
|
||||
}
|
||||
),
|
||||
}),
|
||||
new Widget.Label({
|
||||
className: "title",
|
||||
label: charging.as(c => c ? "To full" : "To empty"),
|
||||
}),
|
||||
]
|
||||
})}>
|
||||
<box className="info" />
|
||||
</overlay>
|
||||
)
|
||||
}
|
||||
</label>
|
||||
<label
|
||||
vexpand
|
||||
valign={Gtk.Align.START}>
|
||||
{charging.as(c => c ? "To full" : "To empty")}
|
||||
</label>
|
||||
</box>
|
||||
</box>
|
||||
</box>
|
||||
<box
|
||||
|
||||
@@ -13,18 +13,13 @@ function bluetoothItem(device: Bluetooth.Device) {
|
||||
onClicked={() => {
|
||||
execAsync(["bluetoothctl", device.connected ? "disconnect" : "connect", device.address]).then(out => console.log(out)).catch(out => console.log(out))
|
||||
}}>
|
||||
<overlay overlay={new Widget.Box({
|
||||
className: bind(device, "connected").as(c =>
|
||||
"toggleIndicator ".concat(c ? "active" : "inactive")
|
||||
),
|
||||
halign: Gtk.Align.END,
|
||||
valign: Gtk.Align.CENTER,
|
||||
})}>
|
||||
<box>
|
||||
<box>
|
||||
<box hexpand>
|
||||
<icon icon={device.icon ?? "bluetooth"} css="font-size: 32px;" />
|
||||
<label valign={Gtk.Align.CENTER}>{device.alias ?? device.name}</label>
|
||||
</box>
|
||||
</overlay>
|
||||
<box className={bind(device, "connected").as(c => "toggleIndicator ".concat(c ? "active" : "inactive"))} halign={Gtk.Align.END} valign={Gtk.Align.CENTER}/>
|
||||
</box>
|
||||
</button>
|
||||
}
|
||||
|
||||
|
||||
@@ -68,9 +68,12 @@ function BrightWindow() {
|
||||
.then((out) => (console.log(out)))
|
||||
.catch((out) => (console.log(out)))
|
||||
}}
|
||||
><overlay overlay={toggleIndicator}>
|
||||
<label halign={Gtk.Align.START}>Night Light</label>
|
||||
</overlay></button>
|
||||
>
|
||||
<box>
|
||||
<label hexpand halign={Gtk.Align.START}>Night Light</label>
|
||||
{toggleIndicator}
|
||||
</box>
|
||||
</button>
|
||||
</box>
|
||||
</window>
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ function NewClient(client: Hyprland.Client) {
|
||||
}
|
||||
let b = <box halign={Gtk.Align.END} className="clientLabel" visible={true}>
|
||||
<icon
|
||||
icon={(appsClass) ? appsClass.iconName : "archlinux"}
|
||||
icon={(appsClass) ? appsClass.iconName : "hyprland"}
|
||||
css="font-size: 24px; margin-right: 4px;"
|
||||
/>
|
||||
<label css="font-family: comfortaa; font-size: 14px;">{
|
||||
(appsClass) ? appsClass.name : "Hummingbird"
|
||||
(appsClass) ? appsClass.name : "Hyprland"
|
||||
}</label>
|
||||
</box>
|
||||
return b
|
||||
|
||||
@@ -4,16 +4,16 @@ import Notif from "gi://AstalNotifd"
|
||||
import Pango from "gi://Pango?version=1.0"
|
||||
const notifs = Notif.get_default()
|
||||
|
||||
const { TOP, RIGHT } = Astal.WindowAnchor
|
||||
const { TOP, RIGHT, BOTTOM } = Astal.WindowAnchor
|
||||
const notifPlaceholder = "/home/protoshark/.config/ags/assets/notif.svg"
|
||||
let notifWindow: Widget.Window
|
||||
|
||||
function NewNotif(notif: Notif.Notification) {
|
||||
|
||||
function NewNotif(notif: Notif.Notification, inPanel: boolean = true) {
|
||||
return <box
|
||||
className="notifBox"
|
||||
className={inPanel ? "notifBox" : "panelNotifBox"}
|
||||
halign={Gtk.Align.END}
|
||||
valign={Gtk.Align.START} >
|
||||
<box
|
||||
valign={Gtk.Align.START}
|
||||
spacing={8}>
|
||||
<box
|
||||
halign={Gtk.Align.CENTER}
|
||||
@@ -21,7 +21,11 @@ function NewNotif(notif: Notif.Notification) {
|
||||
className="notifIcon"
|
||||
css={`background-image: url("${notif.image ?? notifPlaceholder}")`}
|
||||
/>
|
||||
<box vertical={true} valign={Gtk.Align.CENTER} spacing={6}>
|
||||
<box
|
||||
vertical
|
||||
valign={Gtk.Align.CENTER}
|
||||
spacing={6}
|
||||
hexpand>
|
||||
<label
|
||||
halign={Gtk.Align.START}
|
||||
css="font-size: 16px;"
|
||||
@@ -39,7 +43,45 @@ function NewNotif(notif: Notif.Notification) {
|
||||
{notif.body.substring(0, 88)}
|
||||
</label>
|
||||
</box>
|
||||
</box></box>
|
||||
<box vertical valign={Gtk.Align.CENTER}>
|
||||
{notif.actions.map(action => <button onClicked={() => {
|
||||
notif.invoke(action.id)
|
||||
}}>
|
||||
<box><icon icon={action.label.toLowerCase()} css="margin: 4px -2px;"/></box>
|
||||
</button>)}
|
||||
</box>
|
||||
</box>
|
||||
}
|
||||
|
||||
function NotifPanel() {
|
||||
return <window
|
||||
anchor={ TOP | RIGHT | BOTTOM }
|
||||
marginTop={36}
|
||||
namespace="notifpanel" >
|
||||
<box vertical={true}>
|
||||
<box>
|
||||
<button
|
||||
hexpand
|
||||
heightRequest={36}
|
||||
onClicked={() => {
|
||||
for (let n of notifs.notifications) {
|
||||
n.dismiss()
|
||||
}
|
||||
}}>Clear notifications</button>
|
||||
<button
|
||||
hexpand
|
||||
heightRequest={36}
|
||||
onClicked={() => {
|
||||
notifs.dontDisturb = !notifs.dontDisturb
|
||||
}}>Do not disturb</button>
|
||||
</box>
|
||||
<scrollable widthRequest={416} vexpand>
|
||||
<box vertical={true}>
|
||||
{notifs.notifications.map(n => NewNotif(n, false))}
|
||||
</box>
|
||||
</scrollable>
|
||||
</box>
|
||||
</window>
|
||||
}
|
||||
|
||||
function notify(notif: Notif.Notification) {
|
||||
@@ -59,6 +101,7 @@ function notify(notif: Notif.Notification) {
|
||||
notifWindow = <window
|
||||
anchor={ TOP | RIGHT }
|
||||
marginTop={36}
|
||||
layer={Astal.Layer.OVERLAY}
|
||||
css="background: transparent;"
|
||||
namespace="notification">
|
||||
<overlay overlay={thisNotif}>
|
||||
@@ -93,7 +136,14 @@ function notifCount() {
|
||||
|
||||
export default function notifWidget() {
|
||||
return <button
|
||||
onClicked={() => {}}
|
||||
onClicked={() => {
|
||||
if (notifWindow) {
|
||||
notifWindow.destroy()
|
||||
notifWindow = null
|
||||
} else {
|
||||
notifWindow = NotifPanel()
|
||||
}
|
||||
}}
|
||||
setup={(self) => self.hook(notifs, "notified", (_, id) => {
|
||||
notify(notifs.get_notification(id))
|
||||
})}>
|
||||
|
||||
@@ -44,16 +44,6 @@ function cover(player: Mpris.Player) {
|
||||
return coverOverlay
|
||||
}
|
||||
|
||||
function wrap(str: string) {
|
||||
if (str.length > 40) {
|
||||
str = str.substring(0, 40)
|
||||
}
|
||||
if (str.length > 20) {
|
||||
return str.substring(0, 20) + "\n" + str.substring(20)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function playerBox(player: Mpris.Player) {
|
||||
return <box
|
||||
vertical={true}
|
||||
@@ -92,12 +82,16 @@ function playerBox(player: Mpris.Player) {
|
||||
new Widget.Label({
|
||||
className: "playerTitle",
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: bind(player, "title").as(str => wrap(str))
|
||||
wrap: true,
|
||||
width_chars: 20,
|
||||
label: bind(player, "title").as(str => str.substring(0, 60))
|
||||
}),
|
||||
new Widget.Label({
|
||||
className: "playerArtist",
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: bind(player, "artist").as(str => wrap(str))
|
||||
wrap: true,
|
||||
width_chars: 20,
|
||||
label: bind(player, "artist").as(str => str.substring(0, 60))
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
61
widget/power.tsx
Normal file
61
widget/power.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Astal } from "astal/gtk3"
|
||||
import { popup } from "./popup"
|
||||
import { execAsync } from "astal"
|
||||
|
||||
const { TOP, LEFT } = Astal.WindowAnchor
|
||||
|
||||
function PowerWindow() {
|
||||
return <window
|
||||
className="popupWindow"
|
||||
anchor={ TOP | LEFT }
|
||||
namespace="lazerpopup">
|
||||
<box vertical css="padding: 6px 0;">
|
||||
{[
|
||||
["Lock", "bash -c 'pgrep -x hyprlock || hyprlock &'" ],
|
||||
["Suspend", "/home/protoshark/.config/scripts/lock.sh" ],
|
||||
["Logout", "hyprctl dispatch exit" ],
|
||||
["Shutdown", "systemctl poweroff" ],
|
||||
["Reboot", "systemctl reboot" ],
|
||||
].map(action => <button onClicked={() => {
|
||||
execAsync(action[1])
|
||||
.then(out => print(out))
|
||||
.catch(out => print(out))
|
||||
popup.window.destroy()
|
||||
popup.window = null
|
||||
popup.state = ""
|
||||
}}>
|
||||
<box hexpand css="padding: 4px 8px 4px 0;">
|
||||
<icon icon={action[0].toLowerCase()} css="margin: 0 6px;"/>
|
||||
<label>{action[0]}</label>
|
||||
</box>
|
||||
</button>)}
|
||||
</box>
|
||||
</window>
|
||||
}
|
||||
|
||||
export default function power() {
|
||||
return <button onClicked={(self) => {
|
||||
if (popup.state !== "power") {
|
||||
if (popup.window !== null) {
|
||||
popup.window.destroy()
|
||||
popup.window = null
|
||||
}
|
||||
popup.window = PowerWindow();
|
||||
popup.state = "power"
|
||||
self.toggleClassName("selected", true)
|
||||
self.hook(popup.window, "destroy", () => {
|
||||
self.toggleClassName("selected", false)
|
||||
popup.window = null
|
||||
popup.state = ""
|
||||
})
|
||||
} else {
|
||||
popup.window.destroy()
|
||||
popup.window = null
|
||||
popup.state = ""
|
||||
}
|
||||
}}>
|
||||
<box>
|
||||
<icon icon="archlinux" />
|
||||
</box>
|
||||
</button>
|
||||
}
|
||||
Reference in New Issue
Block a user