feat: use gtk icons instead of nerdfonts across the shell

also removed hour count from recording widgets
This commit is contained in:
retrozinndev
2025-06-04 17:29:36 -03:00
parent a17fc3c127
commit b1db748351
32 changed files with 375 additions and 241 deletions
+1
View File
@@ -38,6 +38,7 @@ const runnerPlugins: Array<Runner.Plugin> = [
App.start({ App.start({
instanceName: "astal", instanceName: "astal",
icons: "icons/",
requestHandler: (request: string, response: (result: any) => void): void => { requestHandler: (request: string, response: (result: any) => void): void => {
response(handleArguments(request)); response(handleArguments(request));
}, },
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 7.40625 1 c -0.613281 0.007812 -1.234375 0.089844 -1.847656 0.253906 c -3.273438 0.878906 -5.558594 3.855469 -5.558594 7.246094 s 2.285156 6.367188 5.558594 7.242188 c 3.273437 0.878906 6.742187 -0.558594 8.4375 -3.492188 c 0.273437 -0.480469 0.109375 -1.089844 -0.367188 -1.367188 c -0.476562 -0.273437 -1.089844 -0.109374 -1.367187 0.367188 c -1.246094 2.160156 -3.777344 3.207031 -6.1875 2.5625 c -2.40625 -0.644531 -4.074219 -2.820312 -4.074219 -5.3125 c 0 -2.496094 1.667969 -4.667969 4.074219 -5.3125 c 2.410156 -0.644531 4.941406 0.402344 6.1875 2.5625 c 0.058593 0.085938 0.125 0.164062 0.203125 0.226562 l -0.019532 0.015626 l -0.007812 0.007812 h -1.4375 c -0.550781 0 -1 0.449219 -1 1 c 0 0 0 1 1 1 h 5 v -5 s 0.003906 -1 -1 -1 c -0.550781 0 -1 0.449219 -1 1 v 1.6875 l -0.015625 0.011719 l -0.011719 0.011719 c -1.277344 -2.179688 -3.53125 -3.519532 -5.953125 -3.691407 c -0.203125 -0.015625 -0.40625 -0.019531 -0.613281 -0.019531 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 1 c -3.855469 0 -7 3.144531 -7 7 s 3.144531 7 7 7 s 7 -3.144531 7 -7 s -3.144531 -7 -7 -7 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 261 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 5.019531 -0.00390625 c -0.96875 0 -2 1.05078125 -2 2.00000025 v 2.988281 c 0 0.429687 0.222657 0.675781 0.554688 1.007813 l 2.023437 2.003906 l -2.007812 1.992187 c -0.367188 0.363281 -0.570313 0.6875 -0.570313 1 v 3.007813 c 0 1.011718 0.988281 2 2 2 h 6 c 1.007813 0 2 -1.011719 2 -2.003906 v -3.003907 c 0 -0.3125 -0.222656 -0.628906 -0.570312 -0.976562 l -2.015625 -2.015625 l 1.988281 -1.988282 c 0.261719 -0.261718 0.585937 -0.6875 0.597656 -1.015624 v -2.996094 c 0 -1.003906 -1.007812 -2.00000025 -2 -2.00000025 z m 6 5.00000025 h -6 v -3 h 6 m -3.589843 7 h 1.175781 l 2.414062 2.414062 v 1.585938 l -3 -2 l -3 2 v -1.613282 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 803 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 4 3 c -2.199219 0 -4 1.800781 -4 4 v 2 c 0 2.199219 1.800781 4 4 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 c -1.125 0 -2 -0.875 -2 -2 v -2 c 0 -1.125 0.875 -2 2 -2 h 8 c 1.125 0 2 0.875 2 2 v 2 c 0 1.125 -0.875 2 -2 2 h -4 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 4 c 2.199219 0 4 -1.800781 4 -4 v -2 c 0 -2.199219 -1.800781 -4 -4 -4 z m 0 0"/><path d="m 10 10.996094 v -2.003906 h -1 v 0.007812 c -0.265625 0 -0.519531 0.105469 -0.707031 0.289062 l -2 2 c -0.390625 0.390626 -0.390625 1.023438 0 1.414063 l 2 2 c 0.1875 0.183594 0.441406 0.289063 0.707031 0.285156 v 0.011719 h 1 v -1.992188"/></g></svg>

After

Width:  |  Height:  |  Size: 781 B

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 1.03125 c -3.871094 0 -7 3.128906 -7 7 s 3.128906 7 7 7 s 7 -3.128906 7 -7 s -3.128906 -7 -7 -7 z m -4 6 h 8 v 2 h -8 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 289 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 0 2.316406 v 5.507813 c 0 2.214843 1.1875 4.257812 3.109375 5.355469 l 4.890625 2.796874 l 4.890625 -2.796874 c 1.921875 -1.097657 3.109375 -3.140626 3.109375 -5.355469 v -5.507813 l -8 -2.285156 z m 14.726562 1.71875 l -0.726562 -0.964844 v 4.753907 c 0 1.496093 -0.800781 2.875 -2.101562 3.617187 l -4.394532 2.511719 h 0.992188 l -4.394532 -2.511719 c -1.300781 -0.742187 -2.101562 -2.121094 -2.101562 -3.617187 v -4.753907 l -0.726562 0.964844 l 7 -2 h -0.546876 z m 0 0"/><path d="m 5.941406 6.957031 l 3.058594 3.058594 c 0.292969 0.292969 0.765625 0.292969 1.058594 0 c 0.292968 -0.292969 0.292968 -0.765625 0 -1.058594 l -3.058594 -3.058593 c -0.292969 -0.292969 -0.765625 -0.292969 -1.058594 0 c -0.292968 0.292968 -0.292968 0.765624 0 1.058593 z m 0 0"/><path d="m 9 5.898438 l -3.058594 3.058593 c -0.292968 0.292969 -0.292968 0.765625 0 1.058594 c 0.292969 0.292969 0.765625 0.292969 1.058594 0 l 3.058594 -3.058594 c 0.292968 -0.292969 0.292968 -0.765625 0 -1.058593 c -0.292969 -0.292969 -0.765625 -0.292969 -1.058594 0 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 0 2.316406 v 5.507813 c 0 2.214843 1.1875 4.257812 3.109375 5.355469 l 4.890625 2.796874 l 4.890625 -2.796874 c 1.921875 -1.097657 3.109375 -3.140626 3.109375 -5.355469 v -5.507813 l -8 -2.285156 z m 14.726562 1.71875 l -0.726562 -0.964844 v 4.753907 c 0 1.496093 -0.800781 2.875 -2.101562 3.617187 l -4.394532 2.511719 h 0.992188 l -4.394532 -2.511719 c -1.300781 -0.742187 -2.101562 -2.121094 -2.101562 -3.617187 v -4.753907 l -0.726562 0.964844 l 7 -2 h -0.546876 z m 0 0"/><path d="m 5.46875 7.78125 l 2 2 c 0.292969 0.292969 0.769531 0.292969 1.0625 0 l 3 -3 c 0.292969 -0.292969 0.292969 -0.769531 0 -1.0625 s -0.769531 -0.292969 -1.0625 0 l -3 3 h 1.0625 l -2 -2 c -0.292969 -0.292969 -0.769531 -0.292969 -1.0625 0 s -0.292969 0.769531 0 1.0625 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 928 B

+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<g fill="#2e3436">
<path d="m 1 3 h 14 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 h -14 c -0.550781 0 -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0"/>
<path d="m 4 4 v -1.5 c 0 -1.386719 1.113281 -2.5 2.5 -2.5 h 2.980469 c 1.382812 0 2.5 1.113281 2.5 2.5 v 1.5 h -2 v -1.5 c 0 -0.269531 -0.230469 -0.5 -0.5 -0.5 h -2.980469 c -0.269531 0 -0.5 0.230469 -0.5 0.5 v 1.5 z m 0 0"/>
<path d="m 4 4 v 9 c 0 0.546875 0.453125 1 1 1 h 6 c 0.546875 0 1 -0.453125 1 -1 v -9 h 2 v 9 c 0 1.660156 -1.339844 3 -3 3 h -6 c -1.660156 0 -3 -1.339844 -3 -3 v -9 z m 0 0"/>
<path d="m 7 7 v 5 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -5 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 s 0.5 0.222656 0.5 0.5 z m 0 0"/>
<path d="m 10 7 v 5 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -5 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 s 0.5 0.222656 0.5 0.5 z m 0 0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

-9
View File
@@ -242,15 +242,6 @@ selection {
background: colors.$bg-tertiary; background: colors.$bg-tertiary;
} }
label.nf,
button.nf label {
font-size: 12px;
font-family: "Symbols Nerd Font Mono", "Noto Sans Nerd Font Mono",
"0xProto Nerd Font Mono", "Fira Code Nerd Font Mono",
"Symbols Nerd Font", "Noto Sans Nerd Font", "Fira Code Nerd Font",
"Font Awesome";
}
trough { trough {
background: functions.toRGB(color.adjust($color: wal.$color1, $lightness: -20%)); background: functions.toRGB(color.adjust($color: wal.$color1, $lightness: -20%));
border-radius: 8px; border-radius: 8px;
+15 -15
View File
@@ -149,7 +149,7 @@
box-shadow: inset 0 0 0 300px rgba(colors.$fg-primary, .2); box-shadow: inset 0 0 0 300px rgba(colors.$fg-primary, .2);
} }
& .icon { & icon {
font-size: 14px; font-size: 14px;
} }
@@ -162,8 +162,8 @@
& > button { & > button {
margin: 4px 1px; margin: 4px 1px;
& label { & icon {
font-size: 8px; font-size: 10px;
} }
} }
} }
@@ -211,23 +211,23 @@
& > box { & > box {
padding: 0 8px; padding: 0 8px;
& icon {
& .nf { font-size: 14px;
margin: {
right: 3px;
left: 2px;
};
font-size: 12px;
} }
& .status-icons { & .status-icons {
padding-left: 4px; padding-left: 4px;
& .notification-count { & revealer > eventbox > box {
font-size: 5px; background: rgba($color: colors.$bg-tertiary, $alpha: .7);
margin-left: -3px; border-radius: 12px;
margin-top: 1px; margin: 4px 0;
padding: 2px 6px;
}
& icon.notification-count {
font-size: 6px;
margin-top: -14px;
} }
} }
} }
+2 -2
View File
@@ -51,11 +51,11 @@
& .bottom { & .bottom {
& .controls { & .controls {
margin-top: 5px; margin-top: 9px;
& button { & button {
padding: 7px; padding: 7px;
& label { & label {
font-size: 10px; font-size: 9px;
} }
} }
} }
+29 -22
View File
@@ -23,11 +23,19 @@
} }
& .quickactions { & .quickactions {
margin-bottom: .8em;
& .hostname { & .hostname {
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
} }
& > box:not(.button-row) icon {
font-size: 12px;
color: colors.$fg-disabled;
margin-right: 3px;
}
& .uptime { & .uptime {
font-size: 10.1px; font-size: 10.1px;
font-family: "Symbols Nerd Font Mono"; font-family: "Symbols Nerd Font Mono";
@@ -46,31 +54,19 @@
} }
& .sliders { & .sliders {
padding: 0; icon {
& > box { font-size: 16px;
margin: 8px 0;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
} }
label.nf, button {
button.nf label { @include mixins.hover-shadow2;
margin-right: 8px;
font-size: 15px;
}
button.more {
@include mixins.hover-shadow;
padding: 4px; padding: 4px;
border-radius: 16px; border-radius: 16px;
margin-left: 6px;
icon {
font-size: 14px;
}
} }
& .page .content { & .page .content {
@@ -133,6 +129,16 @@
& label { & label {
font-size: 14px; font-size: 14px;
} }
& label.description {
font-size: 10px;
font-weight: 400;
color: colors.$fg-disabled;
}
& icon {
font-size: 16px;
}
} }
& .bottom-buttons button { & .bottom-buttons button {
@@ -176,14 +182,15 @@ box.history {
& > .button-row { & > .button-row {
margin-top: 12px; margin-top: 12px;
& button { & button {
padding: 6px; padding: 6px;
& label.nf { & icon {
font-size: 16px; font-size: 16px;
} }
& label:not(.nf) { & label {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
+1 -1
View File
@@ -2,7 +2,7 @@
/** /**
* GTK3 only supports sRGB color space, unfortunatly * GTK3 only supports sRGB color space, unfortunately
*/ */
@function toRGB($color) { @function toRGB($color) {
@return rgba( @return rgba(
+2 -2
View File
@@ -17,8 +17,8 @@
margin: 0 150px; margin: 0 150px;
& > button { & > button {
& label { & icon {
font-size: 96px; font-size: 128px;
} }
margin: { margin: {
+1 -1
View File
@@ -92,7 +92,7 @@ export function NotificationWidget(notification: AstalNotifd.Notification|number
label: GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M"), label: GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M"),
} as Widget.LabelProps), } as Widget.LabelProps),
new Widget.Button({ new Widget.Button({
className: "close nf", className: "close",
onClick: () => onClose && onClose(notification), onClick: () => onClose && onClose(notification),
image: new Widget.Icon({ image: new Widget.Icon({
className: "close icon", className: "close icon",
+46 -32
View File
@@ -1,16 +1,15 @@
import { bind, execAsync, GLib } from "astal"; import { bind } from "astal";
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
import AstalMpris from "gi://AstalMpris"; import AstalMpris from "gi://AstalMpris";
import { Separator, SeparatorProps } from "../Separator"; import { Separator, SeparatorProps } from "../Separator";
import { Windows } from "../../windows"; import { Windows } from "../../windows";
import { Clipboard } from "../../scripts/clipboard";
const playerIcons = { const playerIcons = {
spotify: '󰓇', spotify: "spotify-symbolic",
clapper: '󰿎', mpv: "mpv-symbolic",
mpv: '', Clapper: "com.github.rafostar.Clapper-symbolic"
spotube: '󰋋',
firefox: '󰈹'
} }
export function Media(): Gtk.Widget { export function Media(): Gtk.Widget {
@@ -28,35 +27,46 @@ export function Media(): Gtk.Widget {
children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) => children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
players[0] ? [ players[0] ? [
new Widget.Button({ new Widget.Button({
className: "link nf", className: "link",
label: "󰌹", image: new Widget.Icon({
icon: "edit-paste-symbolic"
} as Widget.IconProps),
tooltipText: "Copy link to Clipboard", tooltipText: "Copy link to Clipboard",
visible: bind(players[0], "metadata").as((_metadata: GLib.HashTable) => visible: bind(players[0], "metadata").as((meta) =>
players[0].get_meta("xesam:url") === null), Boolean(meta["xesam:url"]?.get_string()[0])),
onClick: () => execAsync(`sh -c "wl-copy \\"$(playerctl metadata 'xesam:url')\\""`) onClick: () => Clipboard.getDefault().copyAsync(
players[0].metadata["xesam:url"].get_string()[0]
)
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "previous nf", className: "previous",
label: "󰒮", image: new Widget.Icon({
icon: "media-skip-backward-symbolic"
} as Widget.IconProps),
tooltipText: "Previous", tooltipText: "Previous",
onClick: () => players[0].canGoPrevious && players[0].previous() onClick: () => players[0].canGoPrevious && players[0].previous()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "pause nf", className: "play-pause",
tooltipText: bind(players[0], "playback_status").as((status: AstalMpris.PlaybackStatus) => tooltipText: bind(players[0], "playback_status").as((status) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"), status === AstalMpris.PlaybackStatus.PLAYING ?
label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) => "Pause"
status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊"), : "Play"),
onClick: () => { image: new Widget.Icon({
players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ? icon: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
players[0].play() status === AstalMpris.PlaybackStatus.PLAYING ?
: "media-playback-pause-symbolic"
players[0].pause() : "media-playback-start-symbolic")
} } as Widget.IconProps),
onClick: () => players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
players[0].play()
: players[0].pause()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "next nf", className: "next",
label: "󰒭", image: new Widget.Icon({
icon: "media-skip-forward-symbolic"
} as Widget.IconProps),
tooltipText: "Next", tooltipText: "Next",
onClick: () => players[0].canGoNext && players[0].next() onClick: () => players[0].canGoNext && players[0].next()
} as Widget.ButtonProps) } as Widget.ButtonProps)
@@ -80,13 +90,17 @@ export function Media(): Gtk.Widget {
spacing: 4, spacing: 4,
children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) => children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
players[0] ? [ players[0] ? [
new Widget.Label({ new Widget.Icon({
className: "icon nf", icon: bind(players[0], "busName").as((busName: string) => {
label: bind(players[0], "busName").as((busName: string) => { const splitName = busName.split('.').filter(str => str !== "");
const playerName: string = busName.split('.')[busName.split('.').length-1];
return playerIcons[playerName.toLowerCase() as keyof typeof playerIcons] || "󰎇"; return playerIcons[
Object.keys(playerIcons).filter(icon =>
splitName[splitName.length - 1].includes(icon))?.[0] as keyof typeof playerIcons
]
?? "folder-music-symbolic";
}) })
} as Widget.LabelProps), } as Widget.IconProps),
new Widget.Label({ new Widget.Label({
className: "title", className: "title",
label: bind(players[0], "title").as((title: string) => title || "No Title"), label: bind(players[0], "title").as((title: string) => title || "No Title"),
+38 -38
View File
@@ -24,13 +24,15 @@ export function Status(): Gtk.Widget {
className: "sink", className: "sink",
endpoint: Wireplumber.getDefault().getDefaultSink(), endpoint: Wireplumber.getDefault().getDefaultSink(),
icon: bind(Wireplumber.getDefault().getDefaultSink(), "volumeIcon").as(icon => icon: bind(Wireplumber.getDefault().getDefaultSink(), "volumeIcon").as(icon =>
!Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ? icon : "audio-volume-muted-symbolic"), !Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ?
icon : "audio-volume-muted-symbolic"),
}), }),
volumeStatus({ volumeStatus({
className: "source", className: "source",
endpoint: Wireplumber.getDefault().getDefaultSource(), endpoint: Wireplumber.getDefault().getDefaultSource(),
icon: bind(Wireplumber.getDefault().getDefaultSource(), "volumeIcon").as(icon => icon: bind(Wireplumber.getDefault().getDefaultSource(), "volumeIcon").as(icon =>
!Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ? icon : "microphone-sensitivity-muted-symbolic"), !Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ?
icon : "microphone-sensitivity-muted-symbolic"),
}), }),
StatusIcons() StatusIcons()
] ]
@@ -69,9 +71,10 @@ function StatusIcons(): Gtk.Widget {
bind(AstalBluetooth.get_default(), "isConnected") bind(AstalBluetooth.get_default(), "isConnected")
], (powered, connected) => { ], (powered, connected) => {
return powered ? ( return powered ? (
connected ? "󰂱" connected ?
: "󰂯" "bluetooth-active-symbolic"
) : "󰂲" : "bluetooth-symbolic"
) : "bluetooth-disabled-symbolic"
}); });
const networkIcon: Variable<string> = Variable.derive([ const networkIcon: Variable<string> = Variable.derive([
@@ -82,15 +85,15 @@ function StatusIcons(): Gtk.Widget {
(primary, wired, wifi) => { (primary, wired, wifi) => {
switch(primary) { switch(primary) {
case AstalNetwork.Primary.WIRED: return wired ? case AstalNetwork.Primary.WIRED: return wired ?
"󰛳" "network-wired-symbolic"
: "󰛵"; : "network-wired-no-route-symbolic";
case AstalNetwork.Primary.WIFI: return wifi ? case AstalNetwork.Primary.WIFI: return wifi ?
"󰤨" "network-wireless-signal-excellent-symbolic"
: "󰤭"; : "network-wireless-offline-symbolic";
} }
return "󰲊"; return "network-no-route-symbolic";
}); });
const recordingTimer: Variable<string> = Variable.derive([ const recordingTimer: Variable<string> = Variable.derive([
@@ -103,18 +106,15 @@ function StatusIcons(): Gtk.Widget {
const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix(); const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix();
if(startedAtSeconds <= 0) return "00:00"; if(startedAtSeconds <= 0) return "00:00";
const hours = Math.floor(startedAtSeconds / 120);
const minutes = Math.floor(startedAtSeconds / 60); const minutes = Math.floor(startedAtSeconds / 60);
const seconds = Math.floor(startedAtSeconds % 60); const seconds = Math.floor(startedAtSeconds % 60);
return `${ hours > 0 ? `${hours < 10 ? `0${hours}` : hours }:` : "" return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
}${ minutes < 10 ? `0${minutes}` : minutes
}:${ seconds < 10 ? `0${seconds}` : seconds }`;
}); });
return new Widget.Box({ return new Widget.Box({
className: "status-icons", className: "status-icons",
spacing: 3, spacing: 8,
children: [ children: [
new Widget.Revealer({ new Widget.Revealer({
revealChild: bind(Recording.getDefault(), "recording"), revealChild: bind(Recording.getDefault(), "recording"),
@@ -127,10 +127,11 @@ function StatusIcons(): Gtk.Widget {
tooltipText: tr("control_center.tiles.recording.enabled_desc"), tooltipText: tr("control_center.tiles.recording.enabled_desc"),
child: new Widget.Box({ child: new Widget.Box({
children: [ children: [
new Widget.Label({ new Widget.Icon({
className: "recording nf state", className: "recording state",
label: '󰻃' icon: "media-record-symbolic",
} as Widget.LabelProps), css: "margin-right: 4px;"
} as Widget.IconProps),
new Widget.Label({ new Widget.Label({
className: "rec-time", className: "rec-time",
label: recordingTimer() label: recordingTimer()
@@ -139,32 +140,31 @@ function StatusIcons(): Gtk.Widget {
} as Widget.BoxProps) } as Widget.BoxProps)
} as Widget.EventBoxProps) } as Widget.EventBoxProps)
} as Widget.RevealerProps), } as Widget.RevealerProps),
new Widget.Label({ new Widget.Icon({
className: "bluetooth nf state", className: "bluetooth state",
visible: bind(AstalBluetooth.get_default(), "adapter").as(Boolean), visible: bind(AstalBluetooth.get_default(), "adapter").as(Boolean),
label: bluetoothIcon(), icon: bluetoothIcon(),
onDestroy: () => bluetoothIcon.drop() onDestroy: () => bluetoothIcon.drop()
} as Widget.LabelProps), } as Widget.IconProps),
new Widget.Label({ new Widget.Icon({
className: "network nf state", className: "network state",
label: networkIcon(), icon: networkIcon(),
onDestroy: () => networkIcon.drop() onDestroy: () => networkIcon.drop()
} as Widget.LabelProps), } as Widget.IconProps),
new Widget.Box({ new Widget.Box({
children: [ children: [
new Widget.Label({ new Widget.Icon({
className: "bell nf state", className: "bell state",
label: bind(Notifications.getDefault().getNotifd(), "dontDisturb").as((dnd: boolean) => icon: bind(Notifications.getDefault().getNotifd(), "dontDisturb").as((dnd) =>
dnd ? "󰂠" : "󰂚") dnd ? "minus-circle-filled-symbolic"
} as Widget.LabelProps), : "preferences-system-notifications-symbolic")
new Widget.Label({ } as Widget.IconProps),
className: "notification-count nf", new Widget.Icon({
xalign: 0, className: "notification-count",
yalign: 0.25,
visible: bind(Notifications.getDefault(), "history").as(history => visible: bind(Notifications.getDefault(), "history").as(history =>
history.length > 0), history.length > 0),
label: '󰧞' icon: "circle-filled-symbolic"
} as Widget.LabelProps) } as Widget.IconProps)
] ]
} as Widget.BoxProps) } as Widget.BoxProps)
] ]
+58 -28
View File
@@ -96,59 +96,89 @@ export function BigMedia(): Gtk.Widget {
className: "controls button-row", className: "controls button-row",
children: [ children: [
new Widget.Button({ new Widget.Button({
className: "link nf", className: "link",
label: "󰌹", image: new Widget.Icon({
icon: "edit-paste-symbolic"
} as Widget.IconProps),
tooltipText: "Copy link to Clipboard", tooltipText: "Copy link to Clipboard",
visible: bind(players[0], "metadata").as((_meta: GLib.HashTable) => visible: bind(players[0], "metadata").as((_meta: GLib.HashTable) =>
players[0].get_meta("xesam:url") === null), players[0].get_meta("xesam:url") === null),
onClick: () => execAsync(`sh -c "wl-copy \\"$(playerctl metadata 'xesam:url')\\""`) onClick: () => execAsync(`sh -c "wl-copy \\"$(playerctl metadata 'xesam:url')\\""`)
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "shuffle nf", className: "shuffle",
visible: bind(players[0], "shuffleStatus").as((shuffleStatus: AstalMpris.Shuffle) => visible: bind(players[0], "shuffleStatus").as((shuffleStatus) =>
shuffleStatus !== AstalMpris.Shuffle.UNSUPPORTED), shuffleStatus !== AstalMpris.Shuffle.UNSUPPORTED),
label: bind(players[0], "shuffleStatus").as((shuffleStatus: AstalMpris.Shuffle) => image: new Widget.Icon({
shuffleStatus === AstalMpris.Shuffle.ON ? "󰒝" : "󰒞"), icon: bind(players[0], "shuffleStatus").as((shuffleStatus) =>
tooltipText: "Toggle Shuffle", shuffleStatus === AstalMpris.Shuffle.ON ?
"media-playlist-shuffle-symbolic"
: "media-playlist-consecutive-symbolic")
} as Widget.IconProps),
tooltipText: bind(players[0], "shuffleStatus").as((shuffleStatus) =>
shuffleStatus === AstalMpris.Shuffle.ON ?
"Shuffle"
: "No shuffle"),
onClick: () => players[0].shuffle() onClick: () => players[0].shuffle()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "previous nf", className: "previous",
label: "󰒮", image: new Widget.Icon({
icon: "media-skip-backward-symbolic"
} as Widget.IconProps),
tooltipText: "Previous", tooltipText: "Previous",
onClick: () => players[0].canGoPrevious && players[0].previous() onClick: () => players[0].canGoPrevious && players[0].previous()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "pause nf", className: "pause",
tooltipText: bind(players[0], "playback_status").as((status: AstalMpris.PlaybackStatus) => tooltipText: bind(players[0], "playback_status").as((status) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"), status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"),
label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) => image: new Widget.Icon({
status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊"), icon: bind(players[0], "playbackStatus").as((status) =>
onClick: () => { status === AstalMpris.PlaybackStatus.PLAYING ?
players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ? "media-playback-pause-symbolic"
players[0].play() : "media-playback-start-symbolic"),
: } as Widget.IconProps),
players[0].pause() onClick: () => players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
} players[0].play()
: players[0].pause()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "next nf", className: "next",
label: "󰒭", image: new Widget.Icon({
icon: "media-skip-forward-symbolic"
} as Widget.IconProps),
tooltipText: "Next", tooltipText: "Next",
onClick: () => players[0].canGoNext && players[0].next() onClick: () => players[0].canGoNext && players[0].next()
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "repeat nf", className: "repeat",
visible: bind(players[0], "loopStatus").as((loopStatus: AstalMpris.Loop) => visible: bind(players[0], "loopStatus").as((loopStatus) =>
loopStatus !== AstalMpris.Loop.UNSUPPORTED), loopStatus !== AstalMpris.Loop.UNSUPPORTED),
label: bind(players[0], "loopStatus").as((loopStatus: AstalMpris.Loop) => { image: new Widget.Icon({
icon: bind(players[0], "loopStatus").as((loopStatus) => {
switch(loopStatus) {
case AstalMpris.Loop.TRACK:
return "media-playlist-repeat-song-symbolic";
case AstalMpris.Loop.PLAYLIST:
return "media-playlist-repeat-symbolic";
}
return "loop-arrow-symbolic";
})
} as Widget.IconProps),
tooltipText: bind(players[0], "loopStatus").as((loopStatus) => {
switch(loopStatus) { switch(loopStatus) {
case AstalMpris.Loop.TRACK: return "󰑘"; case AstalMpris.Loop.TRACK:
case AstalMpris.Loop.PLAYLIST: return "󰑖"; return "Loop song";
default: return "󰑗";
case AstalMpris.Loop.PLAYLIST:
return "Loop playlist";
} }
return "No loop";
}), }),
tooltipText: "Toggle Loop",
onClick: () => players[0].loop() onClick: () => players[0].loop()
} as Widget.ButtonProps) } as Widget.ButtonProps)
] ]
+4 -5
View File
@@ -47,11 +47,10 @@ export const NotifHistory = () => {
className: "clear-all", className: "clear-all",
child: new Widget.Box({ child: new Widget.Box({
children: [ children: [
new Widget.Label({ new Widget.Icon({
className: "nf", css: "margin-right: 6px;",
css: "margin-right: 6px", icon: "edit-clear-all-symbolic"
label: "󰎟" } as Widget.IconProps),
} as Widget.LabelProps),
new Widget.Label({ new Widget.Label({
label: tr("clear") label: tr("clear")
} as Widget.LabelProps) } as Widget.LabelProps)
+29 -17
View File
@@ -7,8 +7,9 @@ import { Wallpaper } from "../../scripts/wallpaper";
function LockButton(): Widget.Button { function LockButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
className: "nf", image: new Widget.Icon({
label: "󰌾", icon: "system-lock-screen-symbolic"
} as Widget.IconProps),
onClick: () => { onClick: () => {
Windows.close("control-center"); Windows.close("control-center");
AstalHyprland.get_default().dispatch("exec", "hyprlock"); AstalHyprland.get_default().dispatch("exec", "hyprlock");
@@ -18,8 +19,9 @@ function LockButton(): Widget.Button {
function ColorPickerButton(): Widget.Button { function ColorPickerButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
className: "nf", image: new Widget.Icon({
label: "󰴱", icon: "color-select-symbolic"
} as Widget.IconProps),
onClick: () => AstalHyprland.get_default().dispatch( onClick: () => AstalHyprland.get_default().dispatch(
"exec", "exec",
"sh $HOME/.config/hypr/scripts/color-picker.sh" "sh $HOME/.config/hypr/scripts/color-picker.sh"
@@ -29,8 +31,9 @@ function ColorPickerButton(): Widget.Button {
function ScreenshotButton(): Widget.Button { function ScreenshotButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
className: "nf", image: new Widget.Icon({
label: "󰹑", icon: "applets-screenshooter-symbolic"
} as Widget.IconProps),
onClick: () => { onClick: () => {
Windows.close("control-center"); Windows.close("control-center");
execAsync(`sh ${GLib.get_user_config_dir()}/hypr/scripts/screenshot.sh`); execAsync(`sh ${GLib.get_user_config_dir()}/hypr/scripts/screenshot.sh`);
@@ -40,8 +43,9 @@ function ScreenshotButton(): Widget.Button {
function SelectWallpaperButton(): Widget.Button { function SelectWallpaperButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
className: "nf", image: new Widget.Icon({
label: "󰸉", icon: "preferences-desktop-wallpaper-symbolic"
} as Widget.IconProps),
onClick: () => { onClick: () => {
Windows.close("control-center"); Windows.close("control-center");
Wallpaper.getDefault().pickWallpaper(); Wallpaper.getDefault().pickWallpaper();
@@ -51,8 +55,9 @@ function SelectWallpaperButton(): Widget.Button {
function LogoutButton(): Widget.Button { function LogoutButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
className: "nf", image: new Widget.Icon({
label: "󰗽", icon: "system-shutdown-symbolic"
} as Widget.IconProps),
onClick: () => Windows.open("logout-menu") onClick: () => Windows.open("logout-menu")
} as Widget.ButtonProps); } as Widget.ButtonProps);
} }
@@ -76,13 +81,20 @@ export const QuickActions = () => {
tooltipText: "Host name", tooltipText: "Host name",
label: GLib.get_host_name() label: GLib.get_host_name()
} as Widget.LabelProps), } as Widget.LabelProps),
new Widget.Label({ new Widget.Box({
className: "uptime", children: [
xalign: 0, new Widget.Icon({
tooltipText: "Uptime", icon: "hourglass-symbolic"
onDestroy: () => uptime.drop(), } as Widget.IconProps),
label: uptime().as((uptime: string) => `󰥔 ${uptime}`) new Widget.Label({
} as Widget.LabelProps) className: "uptime",
xalign: 0,
tooltipText: "Uptime",
onDestroy: () => uptime.drop(),
label: uptime()
} as Widget.LabelProps)
]
} as Widget.BoxProps)
] ]
} as Widget.BoxProps), } as Widget.BoxProps),
new Widget.Box({ new Widget.Box({
+11 -16
View File
@@ -12,20 +12,18 @@ export function Sliders() {
className: "sliders", className: "sliders",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
expand: true, expand: true,
spacing: 10,
children: [ children: [
new Widget.Box({ new Widget.Box({
className: "sink speaker", className: "sink speaker",
spacing: 3,
children: bind(Wireplumber.getWireplumber(), "defaultSpeaker").as((sink) => [ children: bind(Wireplumber.getWireplumber(), "defaultSpeaker").as((sink) => [
new Widget.Button({ new Widget.Button({
className: "nf",
onClick: () => Wireplumber.getDefault().toggleMuteSink(), onClick: () => Wireplumber.getDefault().toggleMuteSink(),
children: [ image: new Widget.Icon ({
new Widget.Icon ({ icon: bind(sink, "volumeIcon").as((icon) =>
icon: bind(sink, "volumeIcon").as((icon) => !Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ? icon : "audio-volume-muted-symbolic"),
!Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ? icon : "audio-volume-muted-symbolic"), } as Widget.IconProps),
css: "margin-right: 10px;"
} as Widget.IconProps),
]
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Slider({ new Widget.Slider({
drawValue: false, drawValue: false,
@@ -46,17 +44,14 @@ export function Sliders() {
} as Widget.BoxProps), } as Widget.BoxProps),
new Widget.Box({ new Widget.Box({
className: "source microphone", className: "source microphone",
spacing: 3,
children: bind(Wireplumber.getWireplumber(), "defaultMicrophone").as((source) => [ children: bind(Wireplumber.getWireplumber(), "defaultMicrophone").as((source) => [
new Widget.Button({ new Widget.Button({
className: "nf",
onClick: () => Wireplumber.getDefault().toggleMuteSource(), onClick: () => Wireplumber.getDefault().toggleMuteSource(),
children: [ image: new Widget.Icon ({
new Widget.Icon ({ icon: bind(source, "volumeIcon").as((icon) =>
icon: bind(source, "volumeIcon").as((icon) => !Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ? icon : "microphone-sensitivity-muted-symbolic"),
!Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ? icon : "microphone-sensitivity-muted-symbolic"), } as Widget.IconProps),
css: "margin-right: 10px;"
} as Widget.IconProps),
]
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Slider({ new Widget.Slider({
drawValue: false, drawValue: false,
+48 -14
View File
@@ -1,10 +1,12 @@
import { bind, Variable } from "astal"; import { bind, Gio, Variable } from "astal";
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
import AstalBluetooth from "gi://AstalBluetooth"; import AstalBluetooth from "gi://AstalBluetooth";
import { Page, PageButton } from "./Page"; import { Page, PageButton } from "./Page";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import AstalHyprland from "gi://AstalHyprland"; import AstalHyprland from "gi://AstalHyprland";
import { Windows } from "../../../windows"; import { Windows } from "../../../windows";
import { Notifications } from "../../../scripts/notifications";
import AstalNotifd from "gi://AstalNotifd";
export const BluetoothPage: (() => Page) = () => new Page({ export const BluetoothPage: (() => Page) = () => new Page({
id: "bluetooth", id: "bluetooth",
@@ -13,9 +15,13 @@ export const BluetoothPage: (() => Page) = () => new Page({
className: "bluetooth", className: "bluetooth",
headerButtons: [ headerButtons: [
new Widget.Button({ new Widget.Button({
className: "discover nf", className: "discover",
label: bind(AstalBluetooth.get_default().adapter, "discovering").as((discovering) => image: new Widget.Icon({
!discovering ? '󰑓' : '󰙦'), icon: bind(AstalBluetooth.get_default().adapter, "discovering").as((discovering) =>
!discovering ?
"arrow-circular-top-right-symbolic"
: "media-playback-stop-symbolic")
} as Widget.IconProps),
tooltipText: bind(AstalBluetooth.get_default().adapter, "discovering").as((discovering) => tooltipText: bind(AstalBluetooth.get_default().adapter, "discovering").as((discovering) =>
!discovering ? !discovering ?
tr("control_center.pages.bluetooth.start_discovering") tr("control_center.pages.bluetooth.start_discovering")
@@ -117,8 +123,11 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
bind(dev, "trusted") bind(dev, "trusted")
], (connected, paired, trusted) => paired ? [ ], (connected, paired, trusted) => paired ? [
new Widget.Button({ new Widget.Button({
className: "nf", image: new Widget.Icon({
label: connected ? '󰅖' : "󰢃", icon: connected ?
"list-remove-symbolic"
: "user-trash-symbolic"
} as Widget.IconProps),
tooltipText: tr(connected ? "disconnect" : "control_center.pages.bluetooth.unpair_device"), tooltipText: tr(connected ? "disconnect" : "control_center.pages.bluetooth.unpair_device"),
onClick: () => { onClick: () => {
if(!connected) { if(!connected) {
@@ -130,8 +139,11 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
}, },
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "nf", image: new Widget.Icon({
label: trusted ? "󰫜" : "󰫚", icon: trusted ?
"shield-safe-symbolic"
: "shield-danger-symbolic"
} as Widget.IconProps),
tooltipText: tr(`control_center.pages.bluetooth.${trusted ? "un": ""}trust_device`), tooltipText: tr(`control_center.pages.bluetooth.${trusted ? "un": ""}trust_device`),
onClick: () => dev.set_trusted(!trusted) onClick: () => dev.set_trusted(!trusted)
} as Widget.ButtonProps) } as Widget.ButtonProps)
@@ -141,15 +153,36 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
className: bind(dev, "connected").as((connected) => connected ? "connected" : ""), className: bind(dev, "connected").as((connected) => connected ? "connected" : ""),
title: bind(dev, "alias").as(alias => alias ?? "Unknown Device"), title: bind(dev, "alias").as(alias => alias ?? "Unknown Device"),
icon: dev.icon ?? "bluetooth-active-symbolic", icon: dev.icon ?? "bluetooth-active-symbolic",
description: bind(dev, "connecting").as(connecting =>
connecting ? `${tr("connecting")}...` : ""),
tooltipText: bind(dev, "connected").as(connected => !connected ? tooltipText: bind(dev, "connected").as(connected => !connected ?
tr("connect") tr("connect")
: ""), : ""),
onDestroy: () => devActions.drop(), onDestroy: () => devActions.drop(),
onClick: () => { onClick: () => {
if(dev.connected) return; if(dev.connected) return;
if(!dev.paired) dev.pair();
dev.connect_device(null); let skipConnection: boolean = false;
if(!dev.paired)
(async () => dev.pair())().catch((err: Gio.IOErrorEnum) => {
skipConnection = true;
Notifications.getDefault().sendNotification({
appName: "bluetooth",
summary: "Device pairing error",
body: `Couldn't connect to ${dev.alias ?? dev.name}, an error occurred: ${err.message || err.stack}`,
urgency: AstalNotifd.Urgency.NORMAL
})
});
if(!skipConnection)
(async () => dev.connect_device(null))().catch((err: Gio.IOErrorEnum) =>
Notifications.getDefault().sendNotification({
appName: "bluetooth",
summary: "Device connection error",
body: `Couldn't connect to ${dev.alias ?? dev.name}, an error occurred: ${err.message || err.stack}`,
urgency: AstalNotifd.Urgency.NORMAL
})
);
}, },
endWidget: new Widget.Box({ endWidget: new Widget.Box({
visible: bind(dev, "batteryPercentage").as((batt: number) => visible: bind(dev, "batteryPercentage").as((batt: number) =>
@@ -160,12 +193,13 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
children: [ children: [
new Widget.Label({ new Widget.Label({
halign: Gtk.Align.END, halign: Gtk.Align.END,
label: bind(dev, "batteryPercentage").as((bat: number) => label: bind(dev, "batteryPercentage").as((batt: number) =>
`${Math.floor(bat * 100)}%`) `${Math.floor(batt * 100)}%`)
} as Widget.LabelProps), } as Widget.LabelProps),
new Widget.Icon({ new Widget.Icon({
icon: "battery-symbolic", icon: bind(dev, "batteryPercentage").as(batt =>
css: "font-size: 18px; margin-left: 6px;" `battery-level-${Math.floor(batt * 100)}-symbolic`),
css: "font-size: 16px; margin-left: 6px;"
} as Widget.IconProps) } as Widget.IconProps)
] ]
} as Widget.BoxProps) } as Widget.BoxProps)
+6 -5
View File
@@ -13,11 +13,12 @@ export const PageNetwork: (() => Page) = () => new Page({
className: "network", className: "network",
headerButtons: [ headerButtons: [
new Widget.Button({ new Widget.Button({
className: "reload nf", className: "reload",
label: "󰑓", image: new Widget.Icon({
visible: bind(AstalNetwork.get_default(), "primary").as( icon: "arrow-circular-top-right-symbolic"
(primary: AstalNetwork.Primary) => primary === AstalNetwork.Primary.WIFI } as Widget.IconProps),
), visible: bind(AstalNetwork.get_default(), "primary").as((primary) =>
primary === AstalNetwork.Primary.WIFI),
tooltipText: "Re-scan connections", tooltipText: "Re-scan connections",
onClick: () => AstalNetwork.get_default().wifi.scan() onClick: () => AstalNetwork.get_default().wifi.scan()
} as Widget.ButtonProps) } as Widget.ButtonProps)
+23 -7
View File
@@ -177,6 +177,7 @@ export function PageButton(props: {
icon?: string | Binding<string>; icon?: string | Binding<string>;
title: string | Binding<string>; title: string | Binding<string>;
endWidget?: Gtk.Widget | Binding<Gtk.Widget>; endWidget?: Gtk.Widget | Binding<Gtk.Widget>;
description?: string | Binding<string>;
extraButtons?: Array<Widget.Button> | Binding<Array<Gtk.Widget>>; extraButtons?: Array<Widget.Button> | Binding<Array<Gtk.Widget>>;
onDestroy?: (self: Widget.Box) => void; onDestroy?: (self: Widget.Box) => void;
onClick?: (self: Widget.Button) => void; onClick?: (self: Widget.Button) => void;
@@ -203,13 +204,28 @@ export function PageButton(props: {
visible: props.icon, visible: props.icon,
css: "font-size: 20px; margin-right: 6px;" css: "font-size: 20px; margin-right: 6px;"
} as Widget.IconProps), } as Widget.IconProps),
new Widget.Label({ new Widget.Box({
className: "title", orientation: Gtk.Orientation.VERTICAL,
halign: Gtk.Align.START, expand: true,
hexpand: true, children: [
truncate: true, new Widget.Label({
label: props.title className: "title",
} as Widget.LabelProps), xalign: 0,
truncate: true,
label: props.title
} as Widget.LabelProps),
new Widget.Label({
className: "description",
xalign: 0,
visible: (props.description instanceof Binding) ?
props.description.as(Boolean)
: Boolean(props.description),
label: props.description,
truncate: true,
tooltipText: props.description
} as Widget.LabelProps)
]
} as Widget.BoxProps),
new Widget.Box({ new Widget.Box({
visible: (props.endWidget instanceof Binding) ? visible: (props.endWidget instanceof Binding) ?
props.endWidget.as(Boolean) props.endWidget.as(Boolean)
+4 -1
View File
@@ -11,7 +11,10 @@ export const TileBluetooth = () => {
bind(AstalBluetooth.get_default(), "isConnected") bind(AstalBluetooth.get_default(), "isConnected")
], ],
(powered: boolean, isConnected: boolean) => (powered: boolean, isConnected: boolean) =>
powered ? ( isConnected ? "󰂱" : "󰂯" ) : "󰂲" powered ? ( isConnected ?
"bluetooth-active-symbolic"
: "bluetooth-symbolic"
) : "bluetooth-disabled-symbolic"
); );
return Tile({ return Tile({
title: "Bluetooth", title: "Bluetooth",
@@ -9,7 +9,7 @@ export const TileDND = Tile({
(dnd: boolean) => dnd ? tr("control_center.tiles.enabled") : tr("control_center.tiles.disabled")), (dnd: boolean) => dnd ? tr("control_center.tiles.enabled") : tr("control_center.tiles.disabled")),
onToggledOff: () => Notifications.getDefault().getNotifd().dontDisturb = false, onToggledOff: () => Notifications.getDefault().getNotifd().dontDisturb = false,
onToggledOn: () => Notifications.getDefault().getNotifd().dontDisturb = true, onToggledOn: () => Notifications.getDefault().getNotifd().dontDisturb = true,
icon: "󰍶", icon: "minus-circle-filled-symbolic",
iconSize: 16, iconSize: 16,
toggleState: Notifications.getDefault().getNotifd().dontDisturb toggleState: Notifications.getDefault().getNotifd().dontDisturb
}); });
+5 -6
View File
@@ -33,8 +33,7 @@ export const TileNetwork = () => new Widget.Box({
onToggledOn: () => wifi.set_enabled(true), onToggledOn: () => wifi.set_enabled(true),
onToggledOff: () => wifi.set_enabled(false), onToggledOff: () => wifi.set_enabled(false),
onClickMore: () => TilesPages?.toggle(PageNetwork()), onClickMore: () => TilesPages?.toggle(PageNetwork()),
icon: "󰤨", icon: "network-wireless-signal-excellent-symbolic",
iconSize: 16,
toggleState: bind(wifi, "enabled") toggleState: bind(wifi, "enabled")
} as TileProps)(); } as TileProps)();
@@ -57,12 +56,12 @@ export const TileNetwork = () => new Widget.Box({
icon: bind(wired, "internet").as((internet: AstalNetwork.Internet) => { icon: bind(wired, "internet").as((internet: AstalNetwork.Internet) => {
switch(internet) { switch(internet) {
case AstalNetwork.Internet.CONNECTED: case AstalNetwork.Internet.CONNECTED:
return '󰛳'; return "network-wired-symbolic";
case AstalNetwork.Internet.DISCONNECTED: case AstalNetwork.Internet.DISCONNECTED:
return '󰲛'; return "network-wired-disconnected-symbolic";
} }
return "󰛵"; return "network-wired-no-route-symbolic";
}), }),
iconSize: 16, iconSize: 16,
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) => toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
@@ -78,7 +77,7 @@ export const TileNetwork = () => new Widget.Box({
onToggledOn: () => execAsync("nmcli n on"), onToggledOn: () => execAsync("nmcli n on"),
onToggledOff: () => execAsync("nmcli n off"), onToggledOff: () => execAsync("nmcli n off"),
onClickMore: () => TilesPages?.toggle(PageNetwork()), onClickMore: () => TilesPages?.toggle(PageNetwork()),
icon: "󰲛", icon: "network-wired-disconnected-symbolic",
iconSize: 16, iconSize: 16,
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) => toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED) internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)
@@ -10,7 +10,7 @@ import { Widget } from "astal/gtk3";
export const TileNightLight = () => isInstalled("hyprsunset") ? export const TileNightLight = () => isInstalled("hyprsunset") ?
Tile({ Tile({
title: tr("control_center.tiles.night_light.title"), title: tr("control_center.tiles.night_light.title"),
icon: "󰖔", icon: "weather-clear-night-symbolic",
description: Variable.derive([ description: Variable.derive([
bind(NightLight.getDefault(), "temperature"), bind(NightLight.getDefault(), "temperature"),
bind(NightLight.getDefault(), "gamma") bind(NightLight.getDefault(), "gamma")
+2 -5
View File
@@ -18,19 +18,16 @@ export const TileRecording = () => {
const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix(); const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix();
if(startedAtSeconds <= 0) return "00:00"; if(startedAtSeconds <= 0) return "00:00";
const hours = Math.floor(startedAtSeconds / 120);
const minutes = Math.floor(startedAtSeconds / 60); const minutes = Math.floor(startedAtSeconds / 60);
const seconds = Math.floor(startedAtSeconds % 60); const seconds = Math.floor(startedAtSeconds % 60);
return `${ hours > 0 ? `${hours < 10 ? `0${hours}` : hours }:` : "" return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
}${ minutes < 10 ? `0${minutes}` : minutes
}:${ seconds < 10 ? `0${seconds}` : seconds }`;
}); });
return Tile({ return Tile({
title: tr("control_center.tiles.recording.title") || "Screen Recording", title: tr("control_center.tiles.recording.title") || "Screen Recording",
description: description(), description: description(),
icon: "󰻂", icon: "media-record-symbolic",
visible: wfRecorderInstalled, visible: wfRecorderInstalled,
onDestroy: () => description.drop(), onDestroy: () => description.drop(),
onToggledOff: () => Recording.getDefault().stopRecording(), onToggledOff: () => Recording.getDefault().stopRecording(),
+8 -5
View File
@@ -65,11 +65,14 @@ export function Tile(props: TileProps): (() => Gtk.Widget) {
expand: true, expand: true,
hexpand: true, hexpand: true,
children: [ children: [
new Widget.Label({ new Widget.Icon({
className: "icon nf", className: "icon",
label: props.icon || "icon", icon: props.icon,
css: `label { font-size: ${props.iconSize || 12}px; }` visible: (props.icon instanceof Binding) ?
} as Widget.LabelProps), props.icon.as(Boolean)
: Boolean(props.icon),
css: `font-size: ${props.iconSize ?? 16}px;`
} as Widget.IconProps),
new Widget.Box({ new Widget.Box({
className: "text", className: "text",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
+16 -8
View File
@@ -52,8 +52,10 @@ export const LogoutMenu = (mon: number) => new Widget.Window({
height_request: 360, height_request: 360,
children: [ children: [
new Widget.Button({ new Widget.Button({
className: "poweroff nf", className: "poweroff",
label: "󰐥", image: new Widget.Icon({
icon: "system-shutdown-symbolic"
} as Widget.IconProps),
onClick: () => AskPopup({ onClick: () => AskPopup({
title: "Power Off", title: "Power Off",
text: "Are you sure you want to power off? Unsaved work will be lost.", text: "Are you sure you want to power off? Unsaved work will be lost.",
@@ -64,8 +66,10 @@ export const LogoutMenu = (mon: number) => new Widget.Window({
}) })
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "reboot nf", className: "reboot",
label: "󰜉", image: new Widget.Icon({
icon: "arrow-circular-top-right-symbolic"
} as Widget.IconProps),
onClick: () => AskPopup({ onClick: () => AskPopup({
title: "Reboot", title: "Reboot",
text: "Are you sure you want to Reboot? Unsaved work will be lost.", text: "Are you sure you want to Reboot? Unsaved work will be lost.",
@@ -76,8 +80,10 @@ export const LogoutMenu = (mon: number) => new Widget.Window({
}) })
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "suspend nf", className: "suspend",
label: "󰤄", image: new Widget.Icon({
icon: "weather-clear-night-symbolic"
} as Widget.IconProps),
onClick: () => AskPopup({ onClick: () => AskPopup({
title: "Suspend", title: "Suspend",
text: "Are you sure you want to Suspend?", text: "Are you sure you want to Suspend?",
@@ -85,8 +91,10 @@ export const LogoutMenu = (mon: number) => new Widget.Window({
}) })
} as Widget.ButtonProps), } as Widget.ButtonProps),
new Widget.Button({ new Widget.Button({
className: "logout nf", className: "logout",
label: "󰗽", image: new Widget.Icon({
icon: "system-log-out-symbolic"
} as Widget.IconProps),
onClick: () => AskPopup({ onClick: () => AskPopup({
title: "Log out", title: "Log out",
text: "Are you sure you want to log out? Your session will be ended.", text: "Are you sure you want to log out? Your session will be ended.",