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 -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"),
} as Widget.LabelProps),
new Widget.Button({
className: "close nf",
className: "close",
onClick: () => onClose && onClose(notification),
image: new Widget.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 AstalMpris from "gi://AstalMpris";
import { Separator, SeparatorProps } from "../Separator";
import { Windows } from "../../windows";
import { Clipboard } from "../../scripts/clipboard";
const playerIcons = {
spotify: '󰓇',
clapper: '󰿎',
mpv: '',
spotube: '󰋋',
firefox: '󰈹'
spotify: "spotify-symbolic",
mpv: "mpv-symbolic",
Clapper: "com.github.rafostar.Clapper-symbolic"
}
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>) =>
players[0] ? [
new Widget.Button({
className: "link nf",
label: "󰌹",
className: "link",
image: new Widget.Icon({
icon: "edit-paste-symbolic"
} as Widget.IconProps),
tooltipText: "Copy link to Clipboard",
visible: bind(players[0], "metadata").as((_metadata: GLib.HashTable) =>
players[0].get_meta("xesam:url") === null),
onClick: () => execAsync(`sh -c "wl-copy \\"$(playerctl metadata 'xesam:url')\\""`)
visible: bind(players[0], "metadata").as((meta) =>
Boolean(meta["xesam:url"]?.get_string()[0])),
onClick: () => Clipboard.getDefault().copyAsync(
players[0].metadata["xesam:url"].get_string()[0]
)
} as Widget.ButtonProps),
new Widget.Button({
className: "previous nf",
label: "󰒮",
className: "previous",
image: new Widget.Icon({
icon: "media-skip-backward-symbolic"
} as Widget.IconProps),
tooltipText: "Previous",
onClick: () => players[0].canGoPrevious && players[0].previous()
} as Widget.ButtonProps),
new Widget.Button({
className: "pause nf",
tooltipText: bind(players[0], "playback_status").as((status: AstalMpris.PlaybackStatus) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"),
label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊"),
onClick: () => {
players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
players[0].play()
:
players[0].pause()
}
className: "play-pause",
tooltipText: bind(players[0], "playback_status").as((status) =>
status === AstalMpris.PlaybackStatus.PLAYING ?
"Pause"
: "Play"),
image: new Widget.Icon({
icon: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
status === AstalMpris.PlaybackStatus.PLAYING ?
"media-playback-pause-symbolic"
: "media-playback-start-symbolic")
} as Widget.IconProps),
onClick: () => players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
players[0].play()
: players[0].pause()
} as Widget.ButtonProps),
new Widget.Button({
className: "next nf",
label: "󰒭",
className: "next",
image: new Widget.Icon({
icon: "media-skip-forward-symbolic"
} as Widget.IconProps),
tooltipText: "Next",
onClick: () => players[0].canGoNext && players[0].next()
} as Widget.ButtonProps)
@@ -80,13 +90,17 @@ export function Media(): Gtk.Widget {
spacing: 4,
children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
players[0] ? [
new Widget.Label({
className: "icon nf",
label: bind(players[0], "busName").as((busName: string) => {
const playerName: string = busName.split('.')[busName.split('.').length-1];
return playerIcons[playerName.toLowerCase() as keyof typeof playerIcons] || "󰎇";
new Widget.Icon({
icon: bind(players[0], "busName").as((busName: string) => {
const splitName = busName.split('.').filter(str => str !== "");
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({
className: "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",
endpoint: Wireplumber.getDefault().getDefaultSink(),
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({
className: "source",
endpoint: Wireplumber.getDefault().getDefaultSource(),
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()
]
@@ -69,9 +71,10 @@ function StatusIcons(): Gtk.Widget {
bind(AstalBluetooth.get_default(), "isConnected")
], (powered, connected) => {
return powered ? (
connected ? "󰂱"
: "󰂯"
) : "󰂲"
connected ?
"bluetooth-active-symbolic"
: "bluetooth-symbolic"
) : "bluetooth-disabled-symbolic"
});
const networkIcon: Variable<string> = Variable.derive([
@@ -82,15 +85,15 @@ function StatusIcons(): Gtk.Widget {
(primary, wired, wifi) => {
switch(primary) {
case AstalNetwork.Primary.WIRED: return wired ?
"󰛳"
: "󰛵";
"network-wired-symbolic"
: "network-wired-no-route-symbolic";
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([
@@ -103,18 +106,15 @@ function StatusIcons(): Gtk.Widget {
const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix();
if(startedAtSeconds <= 0) return "00:00";
const hours = Math.floor(startedAtSeconds / 120);
const minutes = Math.floor(startedAtSeconds / 60);
const seconds = Math.floor(startedAtSeconds % 60);
return `${ hours > 0 ? `${hours < 10 ? `0${hours}` : hours }:` : ""
}${ minutes < 10 ? `0${minutes}` : minutes
}:${ seconds < 10 ? `0${seconds}` : seconds }`;
return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
});
return new Widget.Box({
className: "status-icons",
spacing: 3,
spacing: 8,
children: [
new Widget.Revealer({
revealChild: bind(Recording.getDefault(), "recording"),
@@ -127,10 +127,11 @@ function StatusIcons(): Gtk.Widget {
tooltipText: tr("control_center.tiles.recording.enabled_desc"),
child: new Widget.Box({
children: [
new Widget.Label({
className: "recording nf state",
label: '󰻃'
} as Widget.LabelProps),
new Widget.Icon({
className: "recording state",
icon: "media-record-symbolic",
css: "margin-right: 4px;"
} as Widget.IconProps),
new Widget.Label({
className: "rec-time",
label: recordingTimer()
@@ -139,32 +140,31 @@ function StatusIcons(): Gtk.Widget {
} as Widget.BoxProps)
} as Widget.EventBoxProps)
} as Widget.RevealerProps),
new Widget.Label({
className: "bluetooth nf state",
new Widget.Icon({
className: "bluetooth state",
visible: bind(AstalBluetooth.get_default(), "adapter").as(Boolean),
label: bluetoothIcon(),
icon: bluetoothIcon(),
onDestroy: () => bluetoothIcon.drop()
} as Widget.LabelProps),
new Widget.Label({
className: "network nf state",
label: networkIcon(),
} as Widget.IconProps),
new Widget.Icon({
className: "network state",
icon: networkIcon(),
onDestroy: () => networkIcon.drop()
} as Widget.LabelProps),
} as Widget.IconProps),
new Widget.Box({
children: [
new Widget.Label({
className: "bell nf state",
label: bind(Notifications.getDefault().getNotifd(), "dontDisturb").as((dnd: boolean) =>
dnd ? "󰂠" : "󰂚")
} as Widget.LabelProps),
new Widget.Label({
className: "notification-count nf",
xalign: 0,
yalign: 0.25,
new Widget.Icon({
className: "bell state",
icon: bind(Notifications.getDefault().getNotifd(), "dontDisturb").as((dnd) =>
dnd ? "minus-circle-filled-symbolic"
: "preferences-system-notifications-symbolic")
} as Widget.IconProps),
new Widget.Icon({
className: "notification-count",
visible: bind(Notifications.getDefault(), "history").as(history =>
history.length > 0),
label: '󰧞'
} as Widget.LabelProps)
icon: "circle-filled-symbolic"
} as Widget.IconProps)
]
} as Widget.BoxProps)
]
+58 -28
View File
@@ -96,59 +96,89 @@ export function BigMedia(): Gtk.Widget {
className: "controls button-row",
children: [
new Widget.Button({
className: "link nf",
label: "󰌹",
className: "link",
image: new Widget.Icon({
icon: "edit-paste-symbolic"
} as Widget.IconProps),
tooltipText: "Copy link to Clipboard",
visible: bind(players[0], "metadata").as((_meta: GLib.HashTable) =>
players[0].get_meta("xesam:url") === null),
onClick: () => execAsync(`sh -c "wl-copy \\"$(playerctl metadata 'xesam:url')\\""`)
} as Widget.ButtonProps),
new Widget.Button({
className: "shuffle nf",
visible: bind(players[0], "shuffleStatus").as((shuffleStatus: AstalMpris.Shuffle) =>
className: "shuffle",
visible: bind(players[0], "shuffleStatus").as((shuffleStatus) =>
shuffleStatus !== AstalMpris.Shuffle.UNSUPPORTED),
label: bind(players[0], "shuffleStatus").as((shuffleStatus: AstalMpris.Shuffle) =>
shuffleStatus === AstalMpris.Shuffle.ON ? "󰒝" : "󰒞"),
tooltipText: "Toggle Shuffle",
image: new Widget.Icon({
icon: bind(players[0], "shuffleStatus").as((shuffleStatus) =>
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()
} as Widget.ButtonProps),
new Widget.Button({
className: "previous nf",
label: "󰒮",
className: "previous",
image: new Widget.Icon({
icon: "media-skip-backward-symbolic"
} as Widget.IconProps),
tooltipText: "Previous",
onClick: () => players[0].canGoPrevious && players[0].previous()
} as Widget.ButtonProps),
new Widget.Button({
className: "pause nf",
tooltipText: bind(players[0], "playback_status").as((status: AstalMpris.PlaybackStatus) =>
className: "pause",
tooltipText: bind(players[0], "playback_status").as((status) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"),
label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊"),
onClick: () => {
players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
players[0].play()
:
players[0].pause()
}
image: new Widget.Icon({
icon: bind(players[0], "playbackStatus").as((status) =>
status === AstalMpris.PlaybackStatus.PLAYING ?
"media-playback-pause-symbolic"
: "media-playback-start-symbolic"),
} as Widget.IconProps),
onClick: () => players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
players[0].play()
: players[0].pause()
} as Widget.ButtonProps),
new Widget.Button({
className: "next nf",
label: "󰒭",
className: "next",
image: new Widget.Icon({
icon: "media-skip-forward-symbolic"
} as Widget.IconProps),
tooltipText: "Next",
onClick: () => players[0].canGoNext && players[0].next()
} as Widget.ButtonProps),
new Widget.Button({
className: "repeat nf",
visible: bind(players[0], "loopStatus").as((loopStatus: AstalMpris.Loop) =>
className: "repeat",
visible: bind(players[0], "loopStatus").as((loopStatus) =>
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) {
case AstalMpris.Loop.TRACK: return "󰑘";
case AstalMpris.Loop.PLAYLIST: return "󰑖";
default: return "󰑗";
case AstalMpris.Loop.TRACK:
return "Loop song";
case AstalMpris.Loop.PLAYLIST:
return "Loop playlist";
}
return "No loop";
}),
tooltipText: "Toggle Loop",
onClick: () => players[0].loop()
} as Widget.ButtonProps)
]
+4 -5
View File
@@ -47,11 +47,10 @@ export const NotifHistory = () => {
className: "clear-all",
child: new Widget.Box({
children: [
new Widget.Label({
className: "nf",
css: "margin-right: 6px",
label: "󰎟"
} as Widget.LabelProps),
new Widget.Icon({
css: "margin-right: 6px;",
icon: "edit-clear-all-symbolic"
} as Widget.IconProps),
new Widget.Label({
label: tr("clear")
} as Widget.LabelProps)
+29 -17
View File
@@ -7,8 +7,9 @@ import { Wallpaper } from "../../scripts/wallpaper";
function LockButton(): Widget.Button {
return new Widget.Button({
className: "nf",
label: "󰌾",
image: new Widget.Icon({
icon: "system-lock-screen-symbolic"
} as Widget.IconProps),
onClick: () => {
Windows.close("control-center");
AstalHyprland.get_default().dispatch("exec", "hyprlock");
@@ -18,8 +19,9 @@ function LockButton(): Widget.Button {
function ColorPickerButton(): Widget.Button {
return new Widget.Button({
className: "nf",
label: "󰴱",
image: new Widget.Icon({
icon: "color-select-symbolic"
} as Widget.IconProps),
onClick: () => AstalHyprland.get_default().dispatch(
"exec",
"sh $HOME/.config/hypr/scripts/color-picker.sh"
@@ -29,8 +31,9 @@ function ColorPickerButton(): Widget.Button {
function ScreenshotButton(): Widget.Button {
return new Widget.Button({
className: "nf",
label: "󰹑",
image: new Widget.Icon({
icon: "applets-screenshooter-symbolic"
} as Widget.IconProps),
onClick: () => {
Windows.close("control-center");
execAsync(`sh ${GLib.get_user_config_dir()}/hypr/scripts/screenshot.sh`);
@@ -40,8 +43,9 @@ function ScreenshotButton(): Widget.Button {
function SelectWallpaperButton(): Widget.Button {
return new Widget.Button({
className: "nf",
label: "󰸉",
image: new Widget.Icon({
icon: "preferences-desktop-wallpaper-symbolic"
} as Widget.IconProps),
onClick: () => {
Windows.close("control-center");
Wallpaper.getDefault().pickWallpaper();
@@ -51,8 +55,9 @@ function SelectWallpaperButton(): Widget.Button {
function LogoutButton(): Widget.Button {
return new Widget.Button({
className: "nf",
label: "󰗽",
image: new Widget.Icon({
icon: "system-shutdown-symbolic"
} as Widget.IconProps),
onClick: () => Windows.open("logout-menu")
} as Widget.ButtonProps);
}
@@ -76,13 +81,20 @@ export const QuickActions = () => {
tooltipText: "Host name",
label: GLib.get_host_name()
} as Widget.LabelProps),
new Widget.Label({
className: "uptime",
xalign: 0,
tooltipText: "Uptime",
onDestroy: () => uptime.drop(),
label: uptime().as((uptime: string) => `󰥔 ${uptime}`)
} as Widget.LabelProps)
new Widget.Box({
children: [
new Widget.Icon({
icon: "hourglass-symbolic"
} as Widget.IconProps),
new Widget.Label({
className: "uptime",
xalign: 0,
tooltipText: "Uptime",
onDestroy: () => uptime.drop(),
label: uptime()
} as Widget.LabelProps)
]
} as Widget.BoxProps)
]
} as Widget.BoxProps),
new Widget.Box({
+11 -16
View File
@@ -12,20 +12,18 @@ export function Sliders() {
className: "sliders",
orientation: Gtk.Orientation.VERTICAL,
expand: true,
spacing: 10,
children: [
new Widget.Box({
className: "sink speaker",
spacing: 3,
children: bind(Wireplumber.getWireplumber(), "defaultSpeaker").as((sink) => [
new Widget.Button({
className: "nf",
onClick: () => Wireplumber.getDefault().toggleMuteSink(),
children: [
new Widget.Icon ({
icon: bind(sink, "volumeIcon").as((icon) =>
!Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ? icon : "audio-volume-muted-symbolic"),
css: "margin-right: 10px;"
} as Widget.IconProps),
]
image: new Widget.Icon ({
icon: bind(sink, "volumeIcon").as((icon) =>
!Wireplumber.getDefault().isMutedSink() && Wireplumber.getDefault().getSinkVolume() > 0 ? icon : "audio-volume-muted-symbolic"),
} as Widget.IconProps),
} as Widget.ButtonProps),
new Widget.Slider({
drawValue: false,
@@ -46,17 +44,14 @@ export function Sliders() {
} as Widget.BoxProps),
new Widget.Box({
className: "source microphone",
spacing: 3,
children: bind(Wireplumber.getWireplumber(), "defaultMicrophone").as((source) => [
new Widget.Button({
className: "nf",
onClick: () => Wireplumber.getDefault().toggleMuteSource(),
children: [
new Widget.Icon ({
icon: bind(source, "volumeIcon").as((icon) =>
!Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ? icon : "microphone-sensitivity-muted-symbolic"),
css: "margin-right: 10px;"
} as Widget.IconProps),
]
image: new Widget.Icon ({
icon: bind(source, "volumeIcon").as((icon) =>
!Wireplumber.getDefault().isMutedSource() && Wireplumber.getDefault().getSourceVolume() > 0 ? icon : "microphone-sensitivity-muted-symbolic"),
} as Widget.IconProps),
} as Widget.ButtonProps),
new Widget.Slider({
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 AstalBluetooth from "gi://AstalBluetooth";
import { Page, PageButton } from "./Page";
import { tr } from "../../../i18n/intl";
import AstalHyprland from "gi://AstalHyprland";
import { Windows } from "../../../windows";
import { Notifications } from "../../../scripts/notifications";
import AstalNotifd from "gi://AstalNotifd";
export const BluetoothPage: (() => Page) = () => new Page({
id: "bluetooth",
@@ -13,9 +15,13 @@ export const BluetoothPage: (() => Page) = () => new Page({
className: "bluetooth",
headerButtons: [
new Widget.Button({
className: "discover nf",
label: bind(AstalBluetooth.get_default().adapter, "discovering").as((discovering) =>
!discovering ? '󰑓' : '󰙦'),
className: "discover",
image: new Widget.Icon({
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) =>
!discovering ?
tr("control_center.pages.bluetooth.start_discovering")
@@ -117,8 +123,11 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
bind(dev, "trusted")
], (connected, paired, trusted) => paired ? [
new Widget.Button({
className: "nf",
label: connected ? '󰅖' : "󰢃",
image: new Widget.Icon({
icon: connected ?
"list-remove-symbolic"
: "user-trash-symbolic"
} as Widget.IconProps),
tooltipText: tr(connected ? "disconnect" : "control_center.pages.bluetooth.unpair_device"),
onClick: () => {
if(!connected) {
@@ -130,8 +139,11 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
},
} as Widget.ButtonProps),
new Widget.Button({
className: "nf",
label: trusted ? "󰫜" : "󰫚",
image: new Widget.Icon({
icon: trusted ?
"shield-safe-symbolic"
: "shield-danger-symbolic"
} as Widget.IconProps),
tooltipText: tr(`control_center.pages.bluetooth.${trusted ? "un": ""}trust_device`),
onClick: () => dev.set_trusted(!trusted)
} as Widget.ButtonProps)
@@ -141,15 +153,36 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
className: bind(dev, "connected").as((connected) => connected ? "connected" : ""),
title: bind(dev, "alias").as(alias => alias ?? "Unknown Device"),
icon: dev.icon ?? "bluetooth-active-symbolic",
description: bind(dev, "connecting").as(connecting =>
connecting ? `${tr("connecting")}...` : ""),
tooltipText: bind(dev, "connected").as(connected => !connected ?
tr("connect")
: ""),
onDestroy: () => devActions.drop(),
onClick: () => {
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({
visible: bind(dev, "batteryPercentage").as((batt: number) =>
@@ -160,12 +193,13 @@ function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
children: [
new Widget.Label({
halign: Gtk.Align.END,
label: bind(dev, "batteryPercentage").as((bat: number) =>
`${Math.floor(bat * 100)}%`)
label: bind(dev, "batteryPercentage").as((batt: number) =>
`${Math.floor(batt * 100)}%`)
} as Widget.LabelProps),
new Widget.Icon({
icon: "battery-symbolic",
css: "font-size: 18px; margin-left: 6px;"
icon: bind(dev, "batteryPercentage").as(batt =>
`battery-level-${Math.floor(batt * 100)}-symbolic`),
css: "font-size: 16px; margin-left: 6px;"
} as Widget.IconProps)
]
} as Widget.BoxProps)
+6 -5
View File
@@ -13,11 +13,12 @@ export const PageNetwork: (() => Page) = () => new Page({
className: "network",
headerButtons: [
new Widget.Button({
className: "reload nf",
label: "󰑓",
visible: bind(AstalNetwork.get_default(), "primary").as(
(primary: AstalNetwork.Primary) => primary === AstalNetwork.Primary.WIFI
),
className: "reload",
image: new Widget.Icon({
icon: "arrow-circular-top-right-symbolic"
} as Widget.IconProps),
visible: bind(AstalNetwork.get_default(), "primary").as((primary) =>
primary === AstalNetwork.Primary.WIFI),
tooltipText: "Re-scan connections",
onClick: () => AstalNetwork.get_default().wifi.scan()
} as Widget.ButtonProps)
+23 -7
View File
@@ -177,6 +177,7 @@ export function PageButton(props: {
icon?: string | Binding<string>;
title: string | Binding<string>;
endWidget?: Gtk.Widget | Binding<Gtk.Widget>;
description?: string | Binding<string>;
extraButtons?: Array<Widget.Button> | Binding<Array<Gtk.Widget>>;
onDestroy?: (self: Widget.Box) => void;
onClick?: (self: Widget.Button) => void;
@@ -203,13 +204,28 @@ export function PageButton(props: {
visible: props.icon,
css: "font-size: 20px; margin-right: 6px;"
} as Widget.IconProps),
new Widget.Label({
className: "title",
halign: Gtk.Align.START,
hexpand: true,
truncate: true,
label: props.title
} as Widget.LabelProps),
new Widget.Box({
orientation: Gtk.Orientation.VERTICAL,
expand: true,
children: [
new Widget.Label({
className: "title",
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({
visible: (props.endWidget instanceof Binding) ?
props.endWidget.as(Boolean)
+4 -1
View File
@@ -11,7 +11,10 @@ export const TileBluetooth = () => {
bind(AstalBluetooth.get_default(), "isConnected")
],
(powered: boolean, isConnected: boolean) =>
powered ? ( isConnected ? "󰂱" : "󰂯" ) : "󰂲"
powered ? ( isConnected ?
"bluetooth-active-symbolic"
: "bluetooth-symbolic"
) : "bluetooth-disabled-symbolic"
);
return Tile({
title: "Bluetooth",
@@ -9,7 +9,7 @@ export const TileDND = Tile({
(dnd: boolean) => dnd ? tr("control_center.tiles.enabled") : tr("control_center.tiles.disabled")),
onToggledOff: () => Notifications.getDefault().getNotifd().dontDisturb = false,
onToggledOn: () => Notifications.getDefault().getNotifd().dontDisturb = true,
icon: "󰍶",
icon: "minus-circle-filled-symbolic",
iconSize: 16,
toggleState: Notifications.getDefault().getNotifd().dontDisturb
});
+5 -6
View File
@@ -33,8 +33,7 @@ export const TileNetwork = () => new Widget.Box({
onToggledOn: () => wifi.set_enabled(true),
onToggledOff: () => wifi.set_enabled(false),
onClickMore: () => TilesPages?.toggle(PageNetwork()),
icon: "󰤨",
iconSize: 16,
icon: "network-wireless-signal-excellent-symbolic",
toggleState: bind(wifi, "enabled")
} as TileProps)();
@@ -57,12 +56,12 @@ export const TileNetwork = () => new Widget.Box({
icon: bind(wired, "internet").as((internet: AstalNetwork.Internet) => {
switch(internet) {
case AstalNetwork.Internet.CONNECTED:
return '󰛳';
return "network-wired-symbolic";
case AstalNetwork.Internet.DISCONNECTED:
return '󰲛';
return "network-wired-disconnected-symbolic";
}
return "󰛵";
return "network-wired-no-route-symbolic";
}),
iconSize: 16,
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
@@ -78,7 +77,7 @@ export const TileNetwork = () => new Widget.Box({
onToggledOn: () => execAsync("nmcli n on"),
onToggledOff: () => execAsync("nmcli n off"),
onClickMore: () => TilesPages?.toggle(PageNetwork()),
icon: "󰲛",
icon: "network-wired-disconnected-symbolic",
iconSize: 16,
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)
@@ -10,7 +10,7 @@ import { Widget } from "astal/gtk3";
export const TileNightLight = () => isInstalled("hyprsunset") ?
Tile({
title: tr("control_center.tiles.night_light.title"),
icon: "󰖔",
icon: "weather-clear-night-symbolic",
description: Variable.derive([
bind(NightLight.getDefault(), "temperature"),
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();
if(startedAtSeconds <= 0) return "00:00";
const hours = Math.floor(startedAtSeconds / 120);
const minutes = Math.floor(startedAtSeconds / 60);
const seconds = Math.floor(startedAtSeconds % 60);
return `${ hours > 0 ? `${hours < 10 ? `0${hours}` : hours }:` : ""
}${ minutes < 10 ? `0${minutes}` : minutes
}:${ seconds < 10 ? `0${seconds}` : seconds }`;
return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
});
return Tile({
title: tr("control_center.tiles.recording.title") || "Screen Recording",
description: description(),
icon: "󰻂",
icon: "media-record-symbolic",
visible: wfRecorderInstalled,
onDestroy: () => description.drop(),
onToggledOff: () => Recording.getDefault().stopRecording(),
+8 -5
View File
@@ -65,11 +65,14 @@ export function Tile(props: TileProps): (() => Gtk.Widget) {
expand: true,
hexpand: true,
children: [
new Widget.Label({
className: "icon nf",
label: props.icon || "icon",
css: `label { font-size: ${props.iconSize || 12}px; }`
} as Widget.LabelProps),
new Widget.Icon({
className: "icon",
icon: props.icon,
visible: (props.icon instanceof Binding) ?
props.icon.as(Boolean)
: Boolean(props.icon),
css: `font-size: ${props.iconSize ?? 16}px;`
} as Widget.IconProps),
new Widget.Box({
className: "text",
orientation: Gtk.Orientation.VERTICAL,