💥 fix(ags): bar/media widget, new appearance to control center

This commit is contained in:
retrozinndev
2025-02-05 16:31:23 -03:00
parent 8bd3245dab
commit 0bd0b53589
16 changed files with 278 additions and 282 deletions
+8 -8
View File
@@ -2,16 +2,16 @@
import { GLib } from "astal"; import { GLib } from "astal";
export const i18nKeys = { const i18nKeys = {
"en_US": () => import("./lang/en_US"), "en_US": (() => import("./lang/en_US")!)(),
"pt_BR": () => import("./lang/pt_BR") "pt_BR": (() => import("./lang/pt_BR")!)()
} }
export const languages: Array<string> = (() => const languages: Array<string> = (() =>
Object.keys(i18nKeys))() Object.keys(i18nKeys))()
const defaultLanguage: string = languages[0]; const defaultLanguage: string = languages[0];
let language: string = defaultLanguage; let language: string = getSystemLanguage();
export function getSystemLanguage(): string { export function getSystemLanguage(): string {
const sysLanguage: (string|null|undefined) = GLib.getenv("LANG") || GLib.getenv("LANGUAGE"); const sysLanguage: (string|null|undefined) = GLib.getenv("LANG") || GLib.getenv("LANGUAGE");
@@ -40,14 +40,14 @@ export function setLanguage(lang: keyof typeof i18nKeys): (string|Error) {
} }
export function tr(key: string): (string|undefined) { export function tr(key: string): (string|undefined) {
let langKeys: Object = i18nKeys[language as keyof typeof i18nKeys];
let result = i18nKeys[language as keyof typeof i18nKeys], let result = i18nKeys[language as keyof typeof i18nKeys],
defResult = i18nKeys[defaultLanguage as keyof typeof i18nKeys]; defResult = i18nKeys[defaultLanguage as keyof typeof i18nKeys];
key.split('.').map((keyString: string) => { for(const keyString in key.split('.')) {
console.log(result);
result = result[keyString as keyof typeof result]; result = result[keyString as keyof typeof result];
defResult = defResult[keyString as keyof typeof defResult]; defResult = defResult[keyString as keyof typeof defResult];
}); }
return (result as never) || (defResult as never) || undefined; return (result as never) || (defResult as never) || undefined;
} }
+5
View File
@@ -1,3 +1,8 @@
export default { export default {
"language": "English (United States)", "language": "English (United States)",
"bar": {
"logo": {
"tooltip": "Applications"
}
}
} }
+6 -1
View File
@@ -1,3 +1,8 @@
export default { export default {
"language": "Português (Brasil)" "language": "Português (Brasil)",
"bar": {
"logo": {
"tooltip": "Aplicativos"
}
}
} }
+4
View File
@@ -27,4 +27,8 @@ export class Windows {
public close(window: Widget.Window): void { public close(window: Widget.Window): void {
window.hide(); window.hide();
} }
public toggle(window: Widget.Window): void {
window.is_visible() ? this.close(window) : this.open(window);
}
} }
+51 -1
View File
@@ -1,4 +1,54 @@
@use "sass:color";
@use "./style/bar"; @use "./style/bar";
@use "./style/wal"; @use "./style/wal";
@use "./style/osd"; @use "./style/osd";
@use "./style/control-center" @use "./style/control-center";
* {
all: unset;
transition: 120ms linear;
}
.button-row {
& > button {
background: color.scale($color: wal.$foreground, $lightness: -30%, $alpha: 70%);
margin: 0 1px;
border-radius: 2px;
&:hover {
background: color.scale($color: wal.$foreground, $lightness: -30%, $alpha: 100%);
}
&:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
&:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
}
}
menu {
padding: 4px;
background: wal.$background;
border-radius: 14px;
& > separator {
margin: 0 4px;
color: wal.$background;
}
& > menuitem {
padding: 8px 0px;
border-radius: 10px;
font-size: 12px;
font-weight: 600;
&:hover {
background: wal.$color1;
}
}
}
+116 -140
View File
@@ -6,31 +6,6 @@
padding-bottom: 0px; padding-bottom: 0px;
& * { & * {
all: unset;
transition: 120ms linear;
menu {
padding: 4px;
background: wal.$background;
border-radius: 14px;
& > separator {
margin: 0 4px;
color: wal.$background;
}
& > menuitem {
padding: 8px 0px;
border-radius: 10px;
font-size: 12px;
font-weight: 600;
&:hover {
background: wal.$color1;
}
}
}
button { button {
padding: 6px 8px; padding: 6px 8px;
border-radius: 12px; border-radius: 12px;
@@ -39,6 +14,13 @@
background: wal.$color1; background: wal.$color1;
} }
} }
label {
font-size: 12px;
font-family: "Cantarell", "Noto Sans";
font-weight: 500;
}
} }
// Style widget groups // Style widget groups
@@ -62,152 +44,146 @@
} }
} }
} }
}
.bar-container label { .workspaces {
font-size: 12px; padding: 2px 2px;
font-family: "Cantarell", "Noto Sans";
font-weight: 500;
}
.workspaces { & button {
padding: 2px 2px; all: unset;
border-radius: 16px;
transition: 80ms linear;
padding: 12px 12px;
background: wal.$color1;
margin: 1px 2px;
& button { &.focus {
all: unset; background: wal.$foreground;
border-radius: 16px; padding: 12px 20px;
transition: 80ms linear; }
padding: 12px 12px;
background: wal.$color1;
margin: 1px 2px;
&.focus {
background: wal.$foreground;
padding: 12px 20px;
} }
} }
}
.focused-window { .focused-client {
padding: 0 6px;
& > .icon {
margin-right: 6px;
}
& > .text-content {
& > .class {
font-size: 9px;
font-weight: 500;
font-family: monospace;
color: adjust-hue($color: wal.$foreground, $degrees: 100deg);
margin-top: 1px;
}
& > .title {
font-size: 11.5px;
font-weight: 500;
margin-top: -2px;
}
}
}
.logo button {
padding: 0 11px;
padding-right: 16px;
& label {
font-size: 14px;
}
}
.media-eventbox {
& > .media > box {
border-radius: 12px;
background: wal.$color2;
padding: 0 6px; padding: 0 6px;
& .icon { & > .icon {
margin-right: 6px; margin-right: 6px;
}
& > .text-content {
& > .class {
font-size: 9px;
font-weight: 500;
font-family: monospace;
color: adjust-hue($color: wal.$foreground, $degrees: 100deg);
margin-top: 1px;
}
& > .title {
font-size: 11.5px;
font-weight: 500;
margin-top: -2px;
}
}
}
.logo button {
padding: 0 11px;
padding-right: 16px;
& label {
font-size: 14px; font-size: 14px;
} }
} }
&.reveal { .media-eventbox {
& .media > box { & > .media > box {
border-top-right-radius: 0; border-radius: 12px;
border-bottom-right-radius: 0; background: wal.$color1;
padding: 0 6px;
& .icon {
margin-right: 6px;
font-size: 14px;
}
} }
& .media-controls {
padding-left: 3px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
background: scale-color($color: wal.$color3, $lightness: -12%);
& > button { &.reveal {
margin: 0px 1px; & .media > box {
border-radius: 4px; border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
& .media-controls {
padding-left: 3px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
background: scale-color($color: wal.$color1, $lightness: -20%);
&:first-child { & > button {
border-top-left-radius: 12px; margin: 0px 1px;
border-bottom-left-radius: 12px; border-radius: 4px;
margin-left: 0;
}
&:last-child { &:first-child {
border-top-right-radius: 12px; border-top-left-radius: 12px;
border-bottom-right-radius: 12px; border-bottom-left-radius: 12px;
margin-right: 0; margin-left: 0;
}
&:last-child {
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
margin-right: 0;
}
} }
} }
} }
} }
}
.tray { .tray {
padding: 0 6px; padding: 0 6px;
& .item { & .item {
all: unset; all: unset;
&:hover { &:hover {
background: none; background: none;
} }
margin: 0 6px; margin: 0 6px;
padding: 0; padding: 0;
&:first-child { &:first-child {
margin-left: 0; margin-left: 0;
} }
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
}
} }
} }
}
.audio { .audio {
&:hover > box { &:hover > box {
background: wal.$color1; background: wal.$color1;
}
& > box {
padding: 0 9px;
border-radius: 12px;
}
& .sink .icon {
margin-right: 6px;
}
& .source .icon {
margin-right: 4px;
}
} }
& > box { .cc-toggle button {
padding: 0 9px; $padding-inline: 12px;
border-radius: 12px; padding-left: $padding-inline;
} padding-right: calc($padding-inline + 2px);
& .sink .icon {
margin-right: 6px;
}
& .source .icon {
margin-right: 4px;
} }
} }
.cc-toggle button {
$padding-inline: 12px;
padding-left: $padding-inline;
padding-right: calc($padding-inline + 2px);
}
+11 -38
View File
@@ -1,52 +1,25 @@
@use "sass:color";
@use "./wal"; @use "./wal";
.control-center-container { .control-center-container {
all: unset; background: rgba(wal.$background, .65);
background: rgba(wal.$background, .6); border-radius: 24px;
border-top-left-radius: 16px; padding: 24px 22px;
border-bottom-left-radius: 16px;
& * {
all: unset;
transition: 120ms linear;
}
& { & {
& button { & button {
padding: 4px 6px; padding: 4px 6px;
background: scale-color($color: wal.$color1, $lightness: -20%);
border-radius: 12px;
} }
& .quickactions { & .quickactions {
padding: 10px 16px; background: wal.$color1;
& .button-row { & .hostname {
& > button { font-size: 15px;
margin: 5px 2px; font-weight: 600;
border-radius: 2px; }
padding: 0 8px;
background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: .7);
& label { & .uptime {
font-size: 16px; font-size: 12px;
}
&:hover {
background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: 1);
}
&:first-child {
margin-left: 0;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
&:last-child {
margin-right: 0;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
}
} }
} }
} }
+3 -2
View File
@@ -1,5 +1,5 @@
import { bind } from "astal"; import { bind, Process } from "astal";
import { Astal, Widget } from "astal/gtk3"; import { Widget } from "astal/gtk3";
import AstalWp from "gi://AstalWp?version=0.1"; import AstalWp from "gi://AstalWp?version=0.1";
import { Wireplumber } from "../../scripts/volume"; import { Wireplumber } from "../../scripts/volume";
@@ -8,6 +8,7 @@ const wp = AstalWp.get_default();
export function Audio() { export function Audio() {
return wp && new Widget.EventBox({ return wp && new Widget.EventBox({
className: "audio", className: "audio",
onClick: () => Process.exec_async("astal toggle control-center", () => {}),
child: new Widget.Box({ child: new Widget.Box({
children: [ children: [
new Widget.EventBox({ new Widget.EventBox({
-11
View File
@@ -1,11 +0,0 @@
import {Process} from "astal";
import { Box, Button } from "astal/gtk3/widget";
export function CCToggle() {
return (
<Box className={"cc-toggle"}>
<Button onClick={() => Process.exec("eww open --toggle control-center")}
label={"󰂚"}/>
</Box>
)
}
@@ -5,9 +5,9 @@ import { getAppIcon } from "../../scripts/apps";
const hyprland = AstalHyprland.get_default(); const hyprland = AstalHyprland.get_default();
export function FocusedWindow() { export function FocusedClient() {
return new Widget.Box({ return new Widget.Box({
className: "focused-window", className: "focused-client",
visible: bind(hyprland, "focusedClient").as(Boolean), visible: bind(hyprland, "focusedClient").as(Boolean),
children: [ children: [
new Widget.Icon({ new Widget.Icon({
+7 -4
View File
@@ -1,10 +1,13 @@
import { Widget } from "astal/gtk3";
import { Box, Button } from "astal/gtk3/widget"; import { Box, Button } from "astal/gtk3/widget";
import AstalHyprland from "gi://AstalHyprland"; import AstalHyprland from "gi://AstalHyprland";
import { tr } from "../../i18n/intl";
export function Logo() { export function Logo() {
return ( return new Widget.Box({
<Box className={"logo"}> className: "logo",
//tooltipText: tr("bar.logo.tooltip"),
child:
<Button onClick={ () => AstalHyprland.get_default().dispatch("exec", "anyrun") } label={""} /> <Button onClick={ () => AstalHyprland.get_default().dispatch("exec", "anyrun") } label={""} />
</Box> } as Widget.BoxProps);
)
} }
+54 -58
View File
@@ -4,7 +4,6 @@ import AstalMpris from "gi://AstalMpris";
import { Separator, SeparatorProps } from "../Separator"; import { Separator, SeparatorProps } from "../Separator";
const mpris: AstalMpris.Mpris = AstalMpris.get_default(); const mpris: AstalMpris.Mpris = AstalMpris.get_default();
let defaultPlayer: (AstalMpris.Player|undefined) = mpris.get_players()?.[0];
const playerIcons = { const playerIcons = {
spotify: '󰓇', spotify: '󰓇',
@@ -15,80 +14,77 @@ const playerIcons = {
} }
export function Media(): JSX.Element { export function Media(): JSX.Element {
bind(mpris, "players")?.as((players: Array<AstalMpris.Player>) => {
defaultPlayer = players?.[0] as AstalMpris.Player;
});
const mediaControlsRevealer: Widget.Revealer = new Widget.Revealer({ const mediaControlsRevealer: Widget.Revealer = new Widget.Revealer({
transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT, transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT,
transitionDuration: 260, transitionDuration: 260,
revealChild: false, revealChild: false,
child: new Widget.Box({ child: new Widget.Box({
className: "media-controls", className: "media-controls",
expand: false,
homogeneous: false, homogeneous: false,
children: [ children: bind(mpris, "players").as((players: Array<AstalMpris.Player>) =>
new Widget.Button({ players[0] ? [
className: "previous", new Widget.Button({
label: "󰒮", className: "previous",
onClick: () => { label: "󰒮",
if(bind(defaultPlayer!, "canGoPrevious").as(Boolean)) onClick: () => players[0].canGoPrevious && players[0].previous()
defaultPlayer?.previous(); } as Widget.ButtonProps),
} new Widget.Button({
} as Widget.ButtonProps), className: "pause",
new Widget.Button({ label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
className: "pause", status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊"),
label: bind(defaultPlayer!, "playback_status").as((status: AstalMpris.PlaybackStatus) => { onClick: () => {
return status === AstalMpris.PlaybackStatus.PLAYING ? "󰏤" : "󰐊" players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
}), players[0].play()
onClick: () => { :
if(bind(defaultPlayer!, "canPlay").as(Boolean) players[0].pause()
|| bind(defaultPlayer!, "canPause").as(Boolean)) }
defaultPlayer?.play_pause(); } as Widget.ButtonProps),
} new Widget.Button({
} as Widget.ButtonProps), className: "next",
new Widget.Button({ label: "󰒭",
className: "next", onClick: () => players[0].canGoNext && players[0].next()
label: "󰒭", } as Widget.ButtonProps)
onClick: () => { ] : new Widget.Label({
if(bind(defaultPlayer!, "canGoNext").as(Boolean)) label: "Don't Stop The Music!"
defaultPlayer?.next(); } as Widget.LabelProps)
} )
} as Widget.ButtonProps)
]
} as Widget.BoxProps) } as Widget.BoxProps)
} as Widget.RevealerProps); } as Widget.RevealerProps);
const mediaWidget = new Widget.EventBox({ const mediaWidget = new Widget.EventBox({
className: "media-eventbox", className: "media-eventbox",
visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => players?.[0]).as(Boolean), visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => players[0]).as(Boolean),
child: new Widget.Box({ child: new Widget.Box({
className: "media", className: "media",
children: [ children: [
new Widget.Box({ new Widget.Box({
children: [ children: bind(mpris, "players").as((players: Array<AstalMpris.Player>) =>
new Widget.Label({ players[0] ? [
className: "icon", new Widget.Label({
label: defaultPlayer && bind(defaultPlayer, "busName")?.as((name: string) => { className: "icon",
const banana: Array<string> = name.split('.'); label: bind(players[0], "busName").as((busName: string) => {
const playerName: string = banana[banana.length-1]; const playerName: string = busName.split('.')[busName.split('.').length-1];
return playerIcons[playerName as keyof typeof playerIcons] || '󰎇'; return playerIcons[playerName as keyof typeof playerIcons] || "󰎇";
}) })
} as Widget.LabelProps), } as Widget.LabelProps),
new Widget.Label({ new Widget.Label({
className: "title", className: "title",
label: defaultPlayer && bind(defaultPlayer, "title")?.as((title: string) => title) label: bind(players[0], "title").as((title: string) => title || "No Title")
} as Widget.LabelProps), } as Widget.LabelProps),
Separator({ Separator({
size: 2, size: 2,
cssColor: `rgb(180, 180, 180)`, cssColor: `rgb(180, 180, 180)`,
alpha: 1 alpha: 1
} as SeparatorProps), } as SeparatorProps),
new Widget.Label({ new Widget.Label({
className: "artist", className: "artist",
label: defaultPlayer && bind(defaultPlayer, "artist")?.as((artist: string) => artist) label: bind(players[0], "artist").as((artist: string) => artist || "No Artist")
} as Widget.LabelProps)
] : new Widget.Label({
label: "Crazy to think this widget didn't disappear yet!"
} as Widget.LabelProps) } as Widget.LabelProps)
] )
} as Widget.BoxProps), } as Widget.BoxProps),
mediaControlsRevealer mediaControlsRevealer
] ]
+1 -1
View File
@@ -49,7 +49,7 @@ function LogoutButton(): Widget.Button {
return new Widget.Button({ return new Widget.Button({
label: "󰗽", label: "󰗽",
onClick: () => Process.exec_async( onClick: () => Process.exec_async(
"bash -c 'loginctl terminate-user $USER'", "bash -c 'wlogout -b 5'",
() => {} () => {}
) )
} as Widget.ButtonProps); } as Widget.ButtonProps);
@@ -1,4 +1,4 @@
import { Binding } from "astal"; import { Binding, Variable } from "astal";
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
export interface NormalTileProps { export interface NormalTileProps {
@@ -14,9 +14,6 @@ export interface NormalTileProps {
export function MoreTile(props: NormalTileProps): Gtk.Widget { export function MoreTile(props: NormalTileProps): Gtk.Widget {
let toggleState: boolean = props?.toggleState !== undefined ?
props.toggleState : false;
const mainEventBox = new Widget.EventBox({ const mainEventBox = new Widget.EventBox({
onClick: () => toggleState ? props.onToggledOff() : props.onToggledOn(), onClick: () => toggleState ? props.onToggledOff() : props.onToggledOn(),
expand: true, expand: true,
+5 -7
View File
@@ -2,12 +2,11 @@ import { Gdk, Astal, Gtk, Widget } from "astal/gtk3";
import { Clock } from "../widget/bar/Clock"; import { Clock } from "../widget/bar/Clock";
import { Logo } from "../widget/bar/Logo"; import { Logo } from "../widget/bar/Logo";
import { CCToggle } from "../widget/bar/CCToggle";
import { Tray } from "../widget/bar/Tray"; import { Tray } from "../widget/bar/Tray";
import { Workspaces } from "../widget/bar/Workspaces"; import { Workspaces } from "../widget/bar/Workspaces";
import { Audio } from "../widget/bar/Audio"; import { Audio } from "../widget/bar/Audio";
import { FocusedWindow } from "../widget/bar/FocusedWindow"; import { FocusedClient } from "../widget/bar/FocusedClient";
//import { Media } from "../widget/bar/Media"; import { Media } from "../widget/bar/Media";
export const Bar: Widget.Window = new Widget.Window({ export const Bar: Widget.Window = new Widget.Window({
className: "bar", className: "bar",
@@ -34,7 +33,7 @@ export const Bar: Widget.Window = new Widget.Window({
children: [ children: [
Logo(), Logo(),
Workspaces(), Workspaces(),
FocusedWindow() FocusedClient()
] ]
} as Widget.BoxProps), } as Widget.BoxProps),
centerWidget: new Widget.Box({ centerWidget: new Widget.Box({
@@ -43,7 +42,7 @@ export const Bar: Widget.Window = new Widget.Window({
halign: Gtk.Align.CENTER, halign: Gtk.Align.CENTER,
children: [ children: [
Clock(), Clock(),
/*<Media />*/ Media()
] ]
} as Widget.BoxProps), } as Widget.BoxProps),
endWidget: new Widget.Box({ endWidget: new Widget.Box({
@@ -52,8 +51,7 @@ export const Bar: Widget.Window = new Widget.Window({
halign: Gtk.Align.END, halign: Gtk.Align.END,
children: [ children: [
Tray(), Tray(),
Audio(), Audio()
CCToggle()
] ]
} as Widget.BoxProps) } as Widget.BoxProps)
} as Widget.CenterBoxProps) } as Widget.CenterBoxProps)
+4 -5
View File
@@ -1,10 +1,7 @@
import { Astal, Gdk, Gtk, Widget } from "astal/gtk3"; import { Astal, Gdk, Gtk, Widget } from "astal/gtk3";
import { QuickActions } from "../widget/control-center/QuickActions"; import { QuickActions } from "../widget/control-center/QuickActions";
import { Bar } from "./Bar";
import { Tiles } from "../widget/control-center/Tiles"; import { Tiles } from "../widget/control-center/Tiles";
const monitorHeight: number = Gdk.Screen.get_default()?.get_monitor_geometry(0)?.height!;
const widgetsContainer: Widget.Box = new Widget.Box({ const widgetsContainer: Widget.Box = new Widget.Box({
className: "control-center-container", className: "control-center-container",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
@@ -17,9 +14,11 @@ export const ControlCenter: Widget.Window = new Widget.Window({
namespace: "control-center", namespace: "control-center",
canFocus: true, canFocus: true,
exclusivity: Astal.Exclusivity.NORMAL, exclusivity: Astal.Exclusivity.NORMAL,
anchor: Astal.WindowAnchor.RIGHT, anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
margin_top: 10,
margin_right: 10,
width_request: 450, width_request: 450,
height_request: Bar.is_visible() ? monitorHeight - Bar.get_size()[1] - 18 : 700, height_request: 400,
monitor: 0, monitor: 0,
visible: false visible: false
} as Widget.WindowProps, widgetsContainer); } as Widget.WindowProps, widgetsContainer);