Add systray

This commit is contained in:
ferrreo 2023-08-29 12:31:10 +01:00
parent 9fa4194b43
commit 46c13c0fb1
17 changed files with 287 additions and 159 deletions

View File

@ -1,3 +1,9 @@
pika-hyprland-settings (1.1.3-99pika1) lunar; urgency=medium
* Add systray
-- ferrreo <harderthanfire@gmail.com> Sat, 01 Oct 2022 14:50:00 +0300
pika-hyprland-settings (1.1.2-99pika1) lunar; urgency=medium pika-hyprland-settings (1.1.2-99pika1) lunar; urgency=medium
* Fix weather * Fix weather

View File

@ -11,6 +11,11 @@ cp -f /etc/skel/.config/examples/.profile /etc/skel/
then then
cp -a /etc/skel/. "${u}/" cp -a /etc/skel/. "${u}/"
fi fi
if ! test -f "${u}/.config/ags/widgets/systray.js"
then
cp -af /etc/skel/.config/ags/. "${u}/.config/ags/"
fi
done done
mkdir -p /etc/greetd/ mkdir -p /etc/greetd/

View File

@ -0,0 +1,7 @@
export default {
numberOfWorkspaces: 10,
city: "Stoke-On-Trent",
weatherUpdateInterval: 900,
isAmerican: false,
sysinfoUpdateInterval: "2s",
}

View File

@ -14,4 +14,4 @@ export default {
bar, bar,
calendar, calendar,
], ],
} };

View File

@ -0,0 +1,163 @@
import barConfig from './barConfig.js';
export const getTemp = (temp) => {
if (barConfig?.isAmerican) {
return Math.round((temp * 9 / 5) + 32) + "°F";
}
return temp + "°C";
};
export const getWeatherSymbol = (weatherCode) => {
const dt = new Date();
const hour = dt.getHours();
if (hour < 7 || hour > 21) {
return NIGHT_WEATHER_SYMBOL[WWO_CODE[weatherCode]];
}
return WEATHER_SYMBOL[WWO_CODE[weatherCode]];
};
export const getFormattedTime = () => {
const date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
if (hours < 10) hours = "0" + hours;
if (minutes < 10) minutes = "0" + minutes;
return `${hours}:${minutes}`
}
export const getFormattedDate = () => {
const date = new Date();
const day = date.getDay();
const month = date.getMonth();
let dayOfMonth = date.getDate();
switch (dayOfMonth) {
case 1:
case 21:
case 31:
dayOfMonth += "st";
break;
case 2:
case 22:
dayOfMonth += "nd";
break;
case 3:
case 23:
dayOfMonth += "rd";
break;
default:
dayOfMonth += "th";
}
return `${days[day]}, ${dayOfMonth} ${months[month]}`;
}
export const getBattery = (batt) => {
if (batt?.charging) {
return "battery_charging_full";
}
if (batt?.charged) {
return "battery_full";
}
return battIcons[Math.floor(battIcons.length * (batt?.percent / 100))];
}
const battIcons = ["battery_0_bar","battery_1_bar","battery_2_bar","battery_3_bar","battery_4_bar","battery_5_bar", "battery_6_bar"];
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const WWO_CODE = {
"113": "Sunny",
"116": "PartlyCloudy",
"119": "Cloudy",
"122": "VeryCloudy",
"143": "Fog",
"176": "LightShowers",
"179": "LightSleetShowers",
"182": "LightSleet",
"185": "LightSleet",
"200": "ThunderyShowers",
"227": "LightSnow",
"230": "HeavySnow",
"248": "Fog",
"260": "Fog",
"263": "LightShowers",
"266": "LightRain",
"281": "LightSleet",
"284": "LightSleet",
"293": "LightRain",
"296": "LightRain",
"299": "HeavyShowers",
"302": "HeavyRain",
"305": "HeavyShowers",
"308": "HeavyRain",
"311": "LightSleet",
"314": "LightSleet",
"317": "LightSleet",
"320": "LightSnow",
"323": "LightSnowShowers",
"326": "LightSnowShowers",
"329": "HeavySnow",
"332": "HeavySnow",
"335": "HeavySnowShowers",
"338": "HeavySnow",
"350": "LightSleet",
"353": "LightShowers",
"356": "HeavyShowers",
"359": "HeavyRain",
"362": "LightSleetShowers",
"365": "LightSleetShowers",
"368": "LightSnowShowers",
"371": "HeavySnowShowers",
"374": "LightSleetShowers",
"377": "LightSleet",
"386": "ThunderyShowers",
"389": "ThunderyHeavyRain",
"392": "ThunderySnowShowers",
"395": "HeavySnowShowers",
}
const WEATHER_SYMBOL = {
"Unknown": "air",
"Cloudy": "cloud",
"Fog": "foggy",
"HeavyRain": "rainy",
"HeavyShowers": "rainy",
"HeavySnow": "snowing",
"HeavySnowShowers": "snowing",
"LightRain": "rainy",
"LightShowers": "rainy",
"LightSleet": "rainy",
"LightSleetShowers": "rainy",
"LightSnow": "cloudy_snowing",
"LightSnowShowers": "cloudy_snowing",
"PartlyCloudy": "partly_cloudy_day",
"Sunny": "clear_day",
"ThunderyHeavyRain": "thunderstorm",
"ThunderyShowers": "thunderstorm",
"ThunderySnowShowers": "thunderstorm",
"VeryCloudy": "cloud",
}
const NIGHT_WEATHER_SYMBOL = {
"Unknown": "air",
"Cloudy": "cloud",
"Fog": "foggy",
"HeavyRain": "rainy",
"HeavyShowers": "rainy",
"HeavySnow": "snowing",
"HeavySnowShowers": "snowing",
"LightRain": "rainy",
"LightShowers": "rainy",
"LightSleet": "rainy",
"LightSleetShowers": "rainy",
"LightSnow": "cloudy_snowing",
"LightSnowShowers": "cloudy_snowing",
"PartlyCloudy": "partly_cloudy_night",
"Sunny": "clear_night",
"ThunderyHeavyRain": "thunderstorm",
"ThunderyShowers": "thunderstorm",
"ThunderySnowShowers": "thunderstorm",
"VeryCloudy": "cloud",
}

View File

@ -5,6 +5,7 @@ import { Mem } from "../widgets/mem.js";
import { Clock } from "../widgets/clock.js"; import { Clock } from "../widgets/clock.js";
import { Weather } from '../widgets/weather.js'; import { Weather } from '../widgets/weather.js';
import { Batt } from '../widgets/batt.js'; import { Batt } from '../widgets/batt.js';
import { Systray } from "../widgets/systray.js";
export const System = () => Widget.EventBox({ export const System = () => Widget.EventBox({
onPrimaryClick: () => App.toggleWindow('calendar'), onPrimaryClick: () => App.toggleWindow('calendar'),
@ -24,6 +25,7 @@ export const System = () => Widget.EventBox({
Mem(), Mem(),
Batt(), Batt(),
Weather(), Weather(),
Systray(),
Clock() Clock()
], ],
}), }),

View File

@ -1,6 +1,7 @@
const { Widget } = ags; const { Widget } = ags;
const { execAsync } = ags.Utils; const { execAsync } = ags.Utils;
import { gohypr } from '../services/gohypr.js'; import { gohypr } from '../services/gohypr.js';
import barConfig from '../barConfig.js';
export const Workspaces = () => Widget.EventBox({ export const Workspaces = () => Widget.EventBox({
onScrollUp: () => execAsync('hyprctl dispatch workspace -1'), onScrollUp: () => execAsync('hyprctl dispatch workspace -1'),
@ -10,7 +11,7 @@ export const Workspaces = () => Widget.EventBox({
Widget.Box({ Widget.Box({
halign: 'center', halign: 'center',
children: [Widget.Box({ children: [Widget.Box({
children: Array.from({ length: 10 }, (_, i) => i + 1).map(i => (Widget.Button({ children: Array.from({ length: barConfig?.numberOfWorkspaces }, (_, i) => i + 1).map(i => (Widget.Button({
className: 'bar-ws-button', className: 'bar-ws-button',
onClicked: () => execAsync(`hyprctl dispatch workspace ${i}`).catch(print), onClicked: () => execAsync(`hyprctl dispatch workspace ${i}`).catch(print),
child: Widget.Label({ child: Widget.Label({

View File

@ -1,10 +1,10 @@
[general] [general]
mode = normal mode = normal
framerate = 15 framerate = 30
autosens = 0 autosens = 1
;overshoot = 10 ;overshoot = 10
sensitivity = 750 ;sensitivity = 750
bars = 15 bars = 15
[input] [input]
@ -35,4 +35,4 @@ gradient_color_8 = '#C1838E'
[smoothing] [smoothing]
monstercat = 1 monstercat = 1
gravity = 1000000 gravity = 1000000
noise_reduction = 0.34 noise_reduction = 34

View File

@ -431,12 +431,17 @@ calendar {
} }
tooltip { tooltip {
background-color: transparent;
border: none;
> * > *{
background-color: $background; background-color: $background;
border-radius: 1.5rem;
color: $onBackground; color: $onBackground;
border-radius: 5rem;
padding: 1rem; padding: 1rem;
margin: 1rem; margin: 0.5rem;
border: 1px solid $onBackground; box-shadow: 0.25rem 0 0.25rem 0 $background;
}
} }
.musicbox { .musicbox {
@ -464,3 +469,36 @@ tooltip {
background-color: $secondaryContainer; background-color: $secondaryContainer;
} }
} }
.systray-icon {
margin-right: 0.5rem;
margin-left: 0.25rem;
}
menu {
background: shade($background, 70%);
border-radius: 12px;
border: 2px solid shade($secondaryContainer, 40%);
margin-top: 2rem;
padding: 1rem 0;
color: $onBackground;
}
menu > menuitem {
padding: 0.4em 1.5rem;
background: transparent;
transition: 0.2s ease background;
}
menu > menuitem:hover {
background: rgba(255, 255, 255, 0.4);
}
menu > menuitem check:checked ~ label {
color: $onBackground;
font-weight: 600;
}
menubar > menuitem {
margin-left: 0.6rem;
}

View File

@ -1,8 +1,7 @@
const { App } = ags; const { App } = ags;
import { deflisten } from './deflisten.js'; import { deflisten } from './deflisten.js';
import barConfig from '../barConfig.js';
const udpateDelay = "2s"; export const gostat = deflisten('gostat', `${App.configDir}/programs/gostat ${barConfig.sysinfoUpdateInterval}`, (line) => {
export const gostat = deflisten('gostat', `${App.configDir}/programs/gostat ${udpateDelay}`, (line) => {
return JSON.parse(line); return JSON.parse(line);
}); });

View File

@ -1,5 +1,6 @@
const { Widget } = ags; const { Widget } = ags;
const { Battery } = ags.Service; const { Battery } = ags.Service;
import { getBattery } from '../lib.js';
export const Batt = () => Widget.Box({ export const Batt = () => Widget.Box({
halign: 'end', halign: 'end',
@ -34,16 +35,3 @@ export const Batt = () => Widget.Box({
}), }),
], ],
}); });
function getBattery(batt) {
if (batt?.charging) {
return "battery_charging_full";
}
if (batt?.charged) {
return "battery_full";
}
return battIcons[Math.floor(battIcons.length * (batt?.percent / 100))];
}
const battIcons = ["battery_0_bar","battery_1_bar","battery_2_bar","battery_3_bar","battery_4_bar","battery_5_bar", "battery_6_bar"];

View File

@ -1,4 +1,5 @@
const { Widget } = ags; const { Widget } = ags;
import { getFormattedTime, getFormattedDate } from '../lib.js';
export const Clock = () => Widget.Box({ export const Clock = () => Widget.Box({
vertical: true, vertical: true,
@ -22,40 +23,3 @@ export const Clock = () => Widget.Box({
], ],
}); });
function getFormattedTime() {
const date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
if (hours < 10) hours = "0" + hours;
if (minutes < 10) minutes = "0" + minutes;
return `${hours}:${minutes}`
}
function getFormattedDate() {
const date = new Date();
const day = date.getDay();
const month = date.getMonth();
let dayOfMonth = date.getDate();
switch (dayOfMonth) {
case 1:
case 21:
case 31:
dayOfMonth += "st";
break;
case 2:
case 22:
dayOfMonth += "nd";
break;
case 3:
case 23:
dayOfMonth += "rd";
break;
default:
dayOfMonth += "th";
}
return `${days[day]}, ${dayOfMonth} ${months[month]}`;
}
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

View File

@ -1,5 +1,6 @@
const { Widget } = ags; const { Widget } = ags;
import { gostat } from '../services/gostat.js'; import { gostat } from '../services/gostat.js';
import { getTemp } from '../lib.js';
export const CpuTemp = () => Widget.Box({ export const CpuTemp = () => Widget.Box({
halign: 'end', halign: 'end',
@ -20,7 +21,7 @@ export const CpuTemp = () => Widget.Box({
className: 'txt-norm txt', className: 'txt-norm txt',
connections: [[gostat, label => { connections: [[gostat, label => {
if (gostat?.state?.cputemp) { if (gostat?.state?.cputemp) {
label.label = gostat?.state?.cputemp + "°C"; label.label = getTemp(gostat?.state?.cputemp);
} }
}]], }]],
}), }),

View File

@ -0,0 +1,29 @@
const { SystemTray } = ags.Service;
const { Widget } = ags;
export const Systray = () => Widget.Box({
className: 'systray',
connections: [[SystemTray, box => {
const arr = SystemTray.items;
box.children = arr.map(item =>{
const icon = SystemTray.get_icon(item, 24);
if (!icon) {
return;
}
icon.set_margin_left(7);
const btn = Widget.Button({
//call the Activate function when icon is clicked
//Note: if item.ItemIsMenu is true, left click should open menu
onPrimaryClick: (_, event) => item.ActivateAsync(event.get_root_coords()[1], event.get_root_coords()[2]),
//open menu on right click.
//Note: if item.Menu is not set item.ContextMenuAsync(x, y) should be called.
onSecondaryClick: (_, event) => {
item.AgsMenu.popup_at_widget(btn, 8, 2, event);
},
//show icon
className: 'systray-icon',
child: icon,
tooltipMarkup: SystemTray.get_tooltip_markup(item)
} ); return btn;});
}]],
});

View File

@ -27,7 +27,7 @@ export const Visualiser = () => Widget.Box({
} }
let count = 0; let count = 0;
for (const child of box.get_children()) { for (const child of box.get_children()) {
child.fraction = cvjson?.service.state[count] / 1000; child.fraction = cvjson?.service.state[count] / 1500;
count++; count++;
} }
}], }],

View File

@ -1,7 +1,7 @@
const { Widget } = ags; const { Widget } = ags;
const { exec } = ags.Utils; const { execAsync } = ags.Utils;
import barConfig from '../barConfig.js';
const city = "Stoke-On-Trent"; import { getWeatherSymbol, getTemp } from '../lib.js';
export const Weather = () => Widget.Box({ export const Weather = () => Widget.Box({
halign: 'end', halign: 'end',
@ -12,9 +12,9 @@ export const Weather = () => Widget.Box({
Widget.Label({ Widget.Label({
halign: 'end', halign: 'end',
valign: 'center', valign: 'center',
className: 'txt-larger txt', className: 'txt-larger txt icon-material',
style: 'margin-right: 0.5rem; margin-top: 1px;', style: 'margin-right: 0.5rem; margin-top: 1px;',
label: '🌦', label: 'rainy',
}), }),
Widget.Label({ Widget.Label({
halign: 'end', halign: 'end',
@ -22,91 +22,16 @@ export const Weather = () => Widget.Box({
className: 'txt-norm txt', className: 'txt-norm txt',
}), }),
], ],
connections: [[900000, async box => { connections: [[barConfig?.weatherUpdateInterval * 1000, async box => {
try {
// timeout here is to delay enough on boot that network has time to connect - this is a hack but for something that updates so rarely it's fine
setTimeout(() => { setTimeout(() => {
let weather = exec(`curl https://wttr.in/${city}?format=j1`); execAsync(`curl https://wttr.in/${barConfig?.city}?format=j1`)
weather = JSON.parse(weather); .then(output => {
const weather = JSON.parse(output);
const weatherCode = weather.current_condition[0].weatherCode; const weatherCode = weather.current_condition[0].weatherCode;
box.children[0].label = WEATHER_SYMBOL[WWO_CODE[weatherCode]]; box.tooltipText = weather.current_condition[0].weatherDesc[0].value;
box.children[1].label = weather.current_condition[0].temp_C + "°C"; box.children[0].label = getWeatherSymbol(weatherCode);
box.children[1].label = getTemp(weather.current_condition[0].temp_C);
}).catch(console.error)
}, 5000); }, 5000);
} catch (err) {
console.log(err);
}
}]], }]],
}); });
const WWO_CODE = {
"113": "Sunny",
"116": "PartlyCloudy",
"119": "Cloudy",
"122": "VeryCloudy",
"143": "Fog",
"176": "LightShowers",
"179": "LightSleetShowers",
"182": "LightSleet",
"185": "LightSleet",
"200": "ThunderyShowers",
"227": "LightSnow",
"230": "HeavySnow",
"248": "Fog",
"260": "Fog",
"263": "LightShowers",
"266": "LightRain",
"281": "LightSleet",
"284": "LightSleet",
"293": "LightRain",
"296": "LightRain",
"299": "HeavyShowers",
"302": "HeavyRain",
"305": "HeavyShowers",
"308": "HeavyRain",
"311": "LightSleet",
"314": "LightSleet",
"317": "LightSleet",
"320": "LightSnow",
"323": "LightSnowShowers",
"326": "LightSnowShowers",
"329": "HeavySnow",
"332": "HeavySnow",
"335": "HeavySnowShowers",
"338": "HeavySnow",
"350": "LightSleet",
"353": "LightShowers",
"356": "HeavyShowers",
"359": "HeavyRain",
"362": "LightSleetShowers",
"365": "LightSleetShowers",
"368": "LightSnowShowers",
"371": "HeavySnowShowers",
"374": "LightSleetShowers",
"377": "LightSleet",
"386": "ThunderyShowers",
"389": "ThunderyHeavyRain",
"392": "ThunderySnowShowers",
"395": "HeavySnowShowers",
}
const WEATHER_SYMBOL = {
"Unknown": "✨",
"Cloudy": "☁️",
"Fog": "🌫",
"HeavyRain": "🌧",
"HeavyShowers": "🌧",
"HeavySnow": "❄️",
"HeavySnowShowers": "❄️",
"LightRain": "🌦",
"LightShowers": "🌦",
"LightSleet": "🌧",
"LightSleetShowers": "🌧",
"LightSnow": "🌨",
"LightSnowShowers": "🌨",
"PartlyCloudy": "⛅️",
"Sunny": "☀️",
"ThunderyHeavyRain": "🌩",
"ThunderyShowers": "⛈",
"ThunderySnowShowers": "⛈",
"VeryCloudy": "☁️",
}