Implement framework for custom modules and out of the box custom modules as well. (#213)
* Create declarative module scaffolding * Added ram module (WIP) * Updates to options, styling and more. * Added function for styling custom modules. * Added utility functions and cleaned up code * Type and fn name updates. * Update module utils to handle absent values. * Added icon color in style2 that was missing. * Linted utils.ts * Add CPU module and update RAM module to use /proc/meminfo. * Added disk storage module. * Consolidate code * Added netstat module and removed elements from systray default ignore list. * Added keyboard layout module. * Fix hook types and move module to customModules directory * Added updates modules. * Spacing updates * Added weather module. * Added power menu and power module in bar. Increased update default interval to 6 ours. * Updated styling of bar buttons, made power menu label toggleable, etc. * Consolidate code and add dynamic tooltips based on data being used. * Make default custom mogules matugen compatible * Update base theme * Fix custom module background coloring * Remove testing opacity. * Update themes to account for new modules * Update nix stuff for libgtop (Need someone to test this) * Update nix * Update fractions to multiplications * Move styling in style directory * Implement a polling framework for variables that can dynamically adjust polling intervals. * Netstat module updates when interface name is changed. * Readme update
This commit is contained in:
76
customModules/PollVar.ts
Normal file
76
customModules/PollVar.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
import { Binding } from "types/service";
|
||||
import { Variable as VariableType } from "types/variable";
|
||||
|
||||
type GenericFunction = (...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* @param {VariableType<T>} targetVariable - The Variable to update with the function's result.
|
||||
* @param {Array<VariableType<any>>} trackers - Array of trackers to watch.
|
||||
* @param {Binding<any, any, unknown>} pollingInterval - The polling interval in milliseconds.
|
||||
* @param {GenericFunction} someFunc - The function to execute at each interval, which updates the Variable.
|
||||
* @param {...any} params - Parameters to pass to someFunc.
|
||||
*/
|
||||
export const pollVariable = <T>(
|
||||
targetVariable: VariableType<T>,
|
||||
trackers: Array<Binding<any, any, unknown>>,
|
||||
pollingInterval: Binding<any, any, unknown>,
|
||||
someFunc: GenericFunction,
|
||||
...params: any[]
|
||||
): void => {
|
||||
let intervalInstance: number | null = null;
|
||||
|
||||
const intervalFn = (pollIntrvl: number) => {
|
||||
if (intervalInstance !== null) {
|
||||
GLib.source_remove(intervalInstance);
|
||||
}
|
||||
|
||||
intervalInstance = Utils.interval(pollIntrvl, () => {
|
||||
targetVariable.value = someFunc(...params);
|
||||
});
|
||||
};
|
||||
|
||||
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
|
||||
intervalFn(pollIntrvl);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {VariableType<T>} targetVariable - The Variable to update with the result of the command.
|
||||
* @param {Binding<any, any, unknown>} pollingInterval - The polling interval in milliseconds.
|
||||
* @param {string} someCommand - The bash command to execute.
|
||||
* @param {GenericFunction} someFunc - The function to execute after processing the command result.
|
||||
* @param {...any} params - Parameters to pass to someFunc.
|
||||
*/
|
||||
export const pollVariableBash = <T>(
|
||||
targetVariable: VariableType<T>,
|
||||
trackers: Array<Binding<any, any, unknown>>,
|
||||
pollingInterval: Binding<any, any, unknown>,
|
||||
someCommand: string,
|
||||
someFunc: (res: any, ...params: any[]) => T,
|
||||
...params: any[]
|
||||
): void => {
|
||||
let intervalInstance: number | null = null;
|
||||
|
||||
const intervalFn = (pollIntrvl: number) => {
|
||||
if (intervalInstance !== null) {
|
||||
GLib.source_remove(intervalInstance);
|
||||
}
|
||||
|
||||
intervalInstance = Utils.interval(pollIntrvl, () => {
|
||||
Utils.execAsync(`bash -c "${someCommand}"`).then((res: any) => {
|
||||
try {
|
||||
targetVariable.value = someFunc(res, ...params);
|
||||
} catch (error) {
|
||||
console.warn(`An error occurred when running interval bash function: ${error}`);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(`Error running command "${someCommand}": ${err}`));
|
||||
});
|
||||
};
|
||||
|
||||
// Set up the interval initially with the provided polling interval
|
||||
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
|
||||
intervalFn(pollIntrvl);
|
||||
});
|
||||
};
|
||||
494
customModules/config.ts
Normal file
494
customModules/config.ts
Normal file
@@ -0,0 +1,494 @@
|
||||
import { Option } from 'widget/settings/shared/Option';
|
||||
import { Header } from 'widget/settings/shared/Header';
|
||||
|
||||
import options from 'options';
|
||||
|
||||
export const CustomModuleSettings = () =>
|
||||
Widget.Scrollable({
|
||||
vscroll: 'automatic',
|
||||
hscroll: 'automatic',
|
||||
class_name: 'menu-theme-page customModules paged-container',
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-theme-page paged-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
/*
|
||||
************************************
|
||||
* GENERAL *
|
||||
************************************
|
||||
*/
|
||||
Header('General'),
|
||||
Option({
|
||||
opt: options.bar.customModules.scrollSpeed,
|
||||
title: 'Scrolling Speed',
|
||||
type: 'number',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* RAM *
|
||||
************************************
|
||||
*/
|
||||
Header('RAM'),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.ram.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.labelType,
|
||||
title: 'Label Type',
|
||||
type: 'enum',
|
||||
enums: ['used/total', 'used', 'free', 'percentage'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.round,
|
||||
title: 'Round',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.pollingInterval,
|
||||
title: 'Polling Interval',
|
||||
type: 'number',
|
||||
min: 100,
|
||||
max: 60 * 24 * 1000,
|
||||
increment: 1000,
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.ram.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* CPU *
|
||||
************************************
|
||||
*/
|
||||
Header('CPU'),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.cpu.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.round,
|
||||
title: 'Round',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.pollingInterval,
|
||||
title: 'Polling Interval',
|
||||
type: 'number',
|
||||
min: 100,
|
||||
max: 60 * 24 * 1000,
|
||||
increment: 1000,
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.scrollUp,
|
||||
title: 'Scroll Up',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.cpu.scrollDown,
|
||||
title: 'Scroll Down',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* STORAGE *
|
||||
************************************
|
||||
*/
|
||||
Header('Storage'),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.icon,
|
||||
title: 'Storage Icon',
|
||||
type: 'enum',
|
||||
enums: ['', '', '', '', '', ''],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.storage.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.labelType,
|
||||
title: 'Label Type',
|
||||
type: 'enum',
|
||||
enums: ['used/total', 'used', 'free', 'percentage'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.round,
|
||||
title: 'Round',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.pollingInterval,
|
||||
title: 'Polling Interval',
|
||||
type: 'number',
|
||||
min: 100,
|
||||
max: 60 * 24 * 1000,
|
||||
increment: 1000,
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.storage.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* NETSTAT *
|
||||
************************************
|
||||
*/
|
||||
Header('Netstat'),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.networkInterface,
|
||||
title: 'Network Interface',
|
||||
subtitle:
|
||||
"Name of the network interface to poll.\nHINT: Get list of interfaces with 'cat /proc/net/dev'",
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.icon,
|
||||
title: 'Netstat Icon',
|
||||
type: 'enum',
|
||||
enums: [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.rateUnit,
|
||||
title: 'Rate Unit',
|
||||
type: 'enum',
|
||||
enums: ['GiB', 'MiB', 'KiB', 'auto'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.netstat.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.labelType,
|
||||
title: 'Label Type',
|
||||
type: 'enum',
|
||||
enums: ['full', 'in', 'out'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.round,
|
||||
title: 'Round',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.pollingInterval,
|
||||
title: 'Polling Interval',
|
||||
type: 'number',
|
||||
min: 100,
|
||||
max: 60 * 24 * 1000,
|
||||
increment: 1000,
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.netstat.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* KEYBOARD LAYOUT *
|
||||
************************************
|
||||
*/
|
||||
Header('Keyboard Layout'),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.icon,
|
||||
title: 'kbLayout Icon',
|
||||
type: 'enum',
|
||||
enums: ['', '', '', '', ''],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.labelType,
|
||||
title: 'Label Type',
|
||||
type: 'enum',
|
||||
enums: ['layout', 'code'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.kbLayout.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.scrollUp,
|
||||
title: 'Scroll Up',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.kbLayout.scrollDown,
|
||||
title: 'Scroll Down',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* UPDATES *
|
||||
************************************
|
||||
*/
|
||||
Header('Updates'),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.updateCommand,
|
||||
title: 'Check Updates Command',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.icon,
|
||||
title: 'Updates Icon',
|
||||
type: 'enum',
|
||||
enums: [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.padZero,
|
||||
title: 'Pad with 0',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.updates.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.pollingInterval,
|
||||
title: 'Polling Interval',
|
||||
type: 'number',
|
||||
subtitle: "WARNING: Be careful of your package manager\'s rate limit.",
|
||||
min: 100,
|
||||
max: 60 * 24 * 1000,
|
||||
increment: 1000,
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.scrollUp,
|
||||
title: 'Scroll Up',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.updates.scrollDown,
|
||||
title: 'Scroll Down',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* WEATHER *
|
||||
************************************
|
||||
*/
|
||||
Header('Weather'),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.label,
|
||||
title: 'Show Label',
|
||||
type: 'boolean',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.unit,
|
||||
title: 'Units',
|
||||
type: 'enum',
|
||||
enums: ['imperial', 'metric'],
|
||||
}),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.weather.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.scrollUp,
|
||||
title: 'Scroll Up',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.weather.scrollDown,
|
||||
title: 'Scroll Down',
|
||||
type: 'string',
|
||||
}),
|
||||
|
||||
/*
|
||||
************************************
|
||||
* POWER *
|
||||
************************************
|
||||
*/
|
||||
Header('Power'),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.power.spacing,
|
||||
title: 'Spacing',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.icon,
|
||||
title: 'Power Button Icon',
|
||||
type: 'enum',
|
||||
enums: ['', '', '', '', '', ''],
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.leftClick,
|
||||
title: 'Left Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.rightClick,
|
||||
title: 'Right Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.middleClick,
|
||||
title: 'Middle Click',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.scrollUp,
|
||||
title: 'Scroll Up',
|
||||
type: 'string',
|
||||
}),
|
||||
Option({
|
||||
opt: options.bar.customModules.power.scrollDown,
|
||||
title: 'Scroll Down',
|
||||
type: 'string',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
23
customModules/cpu/computeCPU.ts
Normal file
23
customModules/cpu/computeCPU.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// @ts-expect-error
|
||||
import GTop from 'gi://GTop';
|
||||
|
||||
const defaultCpuData: number = 0;
|
||||
|
||||
let previousCpuData = new GTop.glibtop_cpu();
|
||||
GTop.glibtop_get_cpu(previousCpuData);
|
||||
|
||||
export const computeCPU = () => {
|
||||
const currentCpuData = new GTop.glibtop_cpu();
|
||||
GTop.glibtop_get_cpu(currentCpuData);
|
||||
|
||||
// Calculate the differences from the previous to current data
|
||||
const totalDiff = currentCpuData.total - previousCpuData.total;
|
||||
const idleDiff = currentCpuData.idle - previousCpuData.idle;
|
||||
|
||||
const cpuUsagePercentage = totalDiff > 0 ? ((totalDiff - idleDiff) / totalDiff) * 100 : 0;
|
||||
|
||||
previousCpuData = currentCpuData;
|
||||
|
||||
return cpuUsagePercentage;
|
||||
}
|
||||
|
||||
83
customModules/cpu/index.ts
Normal file
83
customModules/cpu/index.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import options from "options";
|
||||
|
||||
// @ts-expect-error
|
||||
import GTop from 'gi://GTop';
|
||||
|
||||
// Module initializer
|
||||
import { module } from "../module"
|
||||
|
||||
// import { CpuData } from "lib/types/customModules/cpu";
|
||||
import Button from "types/widgets/button";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
|
||||
// Utility Methods
|
||||
import { inputHandler } from "customModules/utils";
|
||||
import { computeCPU } from "./computeCPU";
|
||||
import { pollVariable } from "customModules/PollVar";
|
||||
|
||||
// All the user configurable options for the cpu module that are needed
|
||||
const {
|
||||
label,
|
||||
round,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
pollingInterval
|
||||
} = options.bar.customModules.cpu;
|
||||
|
||||
export const cpuUsage = Variable(0);
|
||||
|
||||
pollVariable(
|
||||
// Variable to poll and update with the result of the function passed in
|
||||
cpuUsage,
|
||||
// Variables that should trigger the polling function to update when they change
|
||||
[round.bind('value')],
|
||||
// Interval at which to poll
|
||||
pollingInterval.bind('value'),
|
||||
// Function to execute to get the network data
|
||||
computeCPU,
|
||||
);
|
||||
|
||||
export const Cpu = () => {
|
||||
const renderLabel = (cpuUsg: number, rnd: boolean) => {
|
||||
return rnd ? `${Math.round(cpuUsg)}%` : `${cpuUsg.toFixed(2)}%`;
|
||||
}
|
||||
|
||||
const cpuModule = module({
|
||||
textIcon: "",
|
||||
label: Utils.merge(
|
||||
[cpuUsage.bind("value"), round.bind("value")],
|
||||
(cpuUsg, rnd) => {
|
||||
return renderLabel(cpuUsg, rnd);
|
||||
}),
|
||||
tooltipText: "CPU",
|
||||
boxClass: "cpu",
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return cpuModule;
|
||||
}
|
||||
|
||||
21
customModules/kblayout/getLayout.ts
Normal file
21
customModules/kblayout/getLayout.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { HyprctlDeviceLayout, HyprctlKeyboard, KbLabelType } from "lib/types/customModules/kbLayout";
|
||||
import { layoutMap } from "./layouts";
|
||||
|
||||
export const getKeyboardLayout = (obj: string, format: KbLabelType) => {
|
||||
let hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj);
|
||||
let keyboards = hyprctlDevices['keyboards'];
|
||||
|
||||
if (keyboards.length === 0) {
|
||||
return "No KB!"
|
||||
}
|
||||
|
||||
let mainKb = keyboards.find((kb: HyprctlKeyboard) => kb.main);
|
||||
|
||||
if (!mainKb) {
|
||||
mainKb = keyboards[keyboards.length - 1];
|
||||
}
|
||||
|
||||
let layout = mainKb['active_keymap'];
|
||||
|
||||
return format === "code" ? layoutMap[layout] || layout : layout;
|
||||
}
|
||||
73
customModules/kblayout/index.ts
Normal file
73
customModules/kblayout/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
|
||||
import options from "options";
|
||||
import { module } from "../module"
|
||||
|
||||
import { inputHandler } from "customModules/utils";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import Button from "types/widgets/button";
|
||||
import Label from "types/widgets/label";
|
||||
import { getKeyboardLayout } from "./getLayout";
|
||||
|
||||
const {
|
||||
label,
|
||||
labelType,
|
||||
icon,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
} = options.bar.customModules.kbLayout;
|
||||
|
||||
export const KbInput = () => {
|
||||
const keyboardModule = module({
|
||||
textIcon: icon.bind("value"),
|
||||
tooltipText: "",
|
||||
labelHook: (self: Label<Gtk.Widget>): void => {
|
||||
self.hook(hyprland, () => {
|
||||
Utils.execAsync('hyprctl devices -j')
|
||||
.then((obj) => {
|
||||
self.label = getKeyboardLayout(obj, labelType.value);
|
||||
})
|
||||
.catch((err) => { console.error(err); });
|
||||
}, "keyboard-layout");
|
||||
|
||||
self.hook(labelType, () => {
|
||||
Utils.execAsync('hyprctl devices -j')
|
||||
.then((obj) => {
|
||||
self.label = getKeyboardLayout(obj, labelType.value);
|
||||
})
|
||||
.catch((err) => { console.error(err); });
|
||||
});
|
||||
},
|
||||
|
||||
boxClass: "kblayout",
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return keyboardModule;
|
||||
}
|
||||
|
||||
|
||||
584
customModules/kblayout/layouts.ts
Normal file
584
customModules/kblayout/layouts.ts
Normal file
@@ -0,0 +1,584 @@
|
||||
export const layoutMap = {
|
||||
"Abkhazian (Russia)": "RU (Ab)",
|
||||
"Akan": "GH (Akan)",
|
||||
"Albanian": "AL",
|
||||
"Albanian (Plisi)": "AL (Plisi)",
|
||||
"Albanian (Veqilharxhi)": "AL (Veqilharxhi)",
|
||||
"Amharic": "ET",
|
||||
"Arabic": "ARA",
|
||||
"Arabic (Algeria)": "DZ (Ar)",
|
||||
"Arabic (AZERTY, Eastern Arabic numerals)": "ARA (Azerty Digits)",
|
||||
"Arabic (AZERTY)": "ARA (Azerty)",
|
||||
"Arabic (Buckwalter)": "ARA (Buckwalter)",
|
||||
"Arabic (Eastern Arabic numerals)": "ARA (Digits)",
|
||||
"Arabic (Macintosh)": "ARA (Mac)",
|
||||
"Arabic (Morocco)": "MA",
|
||||
"Arabic (OLPC)": "ARA (Olpc)",
|
||||
"Arabic (Pakistan)": "PK (Ara)",
|
||||
"Arabic (QWERTY, Eastern Arabic numerals)": "ARA (Qwerty Digits)",
|
||||
"Arabic (QWERTY)": "ARA (Qwerty)",
|
||||
"Arabic (Syria)": "SY",
|
||||
"Armenian": "AM",
|
||||
"Armenian (alt. eastern)": "AM (Eastern-Alt)",
|
||||
"Armenian (alt. phonetic)": "AM (Phonetic-Alt)",
|
||||
"Armenian (eastern)": "AM (Eastern)",
|
||||
"Armenian (phonetic)": "AM (Phonetic)",
|
||||
"Armenian (western)": "AM (Western)",
|
||||
"Asturian (Spain, with bottom-dot H and L)": "ES (Ast)",
|
||||
"Avatime": "GH (Avn)",
|
||||
"Azerbaijani": "AZ",
|
||||
"Azerbaijani (Cyrillic)": "AZ (Cyrillic)",
|
||||
"Azerbaijani (Iran)": "IR (Azb)",
|
||||
"Bambara": "ML",
|
||||
"Bangla": "BD",
|
||||
"Bangla (India, Baishakhi InScript)": "IN (Ben Inscript)",
|
||||
"Bangla (India, Baishakhi)": "IN (Ben Baishakhi)",
|
||||
"Bangla (India, Bornona)": "IN (Ben Bornona)",
|
||||
"Bangla (India, Gitanjali)": "IN (Ben Gitanjali)",
|
||||
"Bangla (India, Probhat)": "IN (Ben Probhat)",
|
||||
"Bangla (India)": "IN (Ben)",
|
||||
"Bangla (Probhat)": "BD (Probhat)",
|
||||
"Bashkirian": "RU (Bak)",
|
||||
"Belarusian": "BY",
|
||||
"Belarusian (intl.)": "BY (Intl)",
|
||||
"Belarusian (Latin)": "BY (Latin)",
|
||||
"Belarusian (legacy)": "BY (Legacy)",
|
||||
"Belarusian (phonetic)": "BY (Phonetic)",
|
||||
"Belgian": "BE",
|
||||
"Belgian (alt.)": "BE (Oss)",
|
||||
"Belgian (ISO, alt.)": "BE (Iso-Alternate)",
|
||||
"Belgian (Latin-9 only, alt.)": "BE (Oss Latin9)",
|
||||
"Belgian (no dead keys)": "BE (Nodeadkeys)",
|
||||
"Belgian (Wang 724 AZERTY)": "BE (Wang)",
|
||||
"Berber (Algeria, Latin)": "DZ",
|
||||
"Berber (Algeria, Tifinagh)": "DZ (Ber)",
|
||||
"Berber (Morocco, Tifinagh alt.)": "MA (Tifinagh-Alt)",
|
||||
"Berber (Morocco, Tifinagh extended phonetic)": "MA (Tifinagh-Extended-Phonetic)",
|
||||
"Berber (Morocco, Tifinagh extended)": "MA (Tifinagh-Extended)",
|
||||
"Berber (Morocco, Tifinagh phonetic, alt.)": "MA (Tifinagh-Alt-Phonetic)",
|
||||
"Berber (Morocco, Tifinagh phonetic)": "MA (Tifinagh-Phonetic)",
|
||||
"Berber (Morocco, Tifinagh)": "MA (Tifinagh)",
|
||||
"Bosnian": "BA",
|
||||
"Bosnian (US, with Bosnian digraphs)": "BA (Unicodeus)",
|
||||
"Bosnian (US)": "BA (Us)",
|
||||
"Bosnian (with Bosnian digraphs)": "BA (Unicode)",
|
||||
"Bosnian (with guillemets)": "BA (Alternatequotes)",
|
||||
"Braille": "BRAI",
|
||||
"Braille (left-handed inverted thumb)": "BRAI (Left Hand Invert)",
|
||||
"Braille (left-handed)": "BRAI (Left Hand)",
|
||||
"Braille (right-handed inverted thumb)": "BRAI (Right Hand Invert)",
|
||||
"Braille (right-handed)": "BRAI (Right Hand)",
|
||||
"Breton (France)": "FR (Bre)",
|
||||
"Bulgarian": "BG",
|
||||
"Bulgarian (enhanced)": "BG (Bekl)",
|
||||
"Bulgarian (new phonetic)": "BG (Bas Phonetic)",
|
||||
"Bulgarian (traditional phonetic)": "BG (Phonetic)",
|
||||
"Burmese": "MM",
|
||||
"Burmese Zawgyi": "MM (Zawgyi)",
|
||||
"Cameroon (AZERTY, intl.)": "CM (Azerty)",
|
||||
"Cameroon (Dvorak, intl.)": "CM (Dvorak)",
|
||||
"Cameroon Multilingual (QWERTY, intl.)": "CM (Qwerty)",
|
||||
"Canadian (CSA)": "CA (Multix)",
|
||||
"Catalan (Spain, with middle-dot L)": "ES (Cat)",
|
||||
"Cherokee": "US (Chr)",
|
||||
"Chinese": "CN",
|
||||
"Chuvash": "RU (Cv)",
|
||||
"Chuvash (Latin)": "RU (Cv Latin)",
|
||||
"CloGaelach": "IE (CloGaelach)",
|
||||
"Crimean Tatar (Turkish Alt-Q)": "UA (Crh Alt)",
|
||||
"Crimean Tatar (Turkish F)": "UA (Crh F)",
|
||||
"Crimean Tatar (Turkish Q)": "UA (Crh)",
|
||||
"Croatian": "HR",
|
||||
"Croatian (US, with Croatian digraphs)": "HR (Unicodeus)",
|
||||
"Croatian (US)": "HR (Us)",
|
||||
"Croatian (with Croatian digraphs)": "HR (Unicode)",
|
||||
"Croatian (with guillemets)": "HR (Alternatequotes)",
|
||||
"Czech": "CZ",
|
||||
"Czech (QWERTY, extended backslash)": "CZ (Qwerty Bksl)",
|
||||
"Czech (QWERTY, Macintosh)": "CZ (Qwerty-Mac)",
|
||||
"Czech (QWERTY)": "CZ (Qwerty)",
|
||||
"Czech (UCW, only accented letters)": "CZ (Ucw)",
|
||||
"Czech (US, Dvorak, UCW support)": "CZ (Dvorak-Ucw)",
|
||||
"Czech (with <\\|> key)": "CZ (Bksl)",
|
||||
"Danish": "DK",
|
||||
"Danish (Dvorak)": "DK (Dvorak)",
|
||||
"Danish (Macintosh, no dead keys)": "DK (Mac Nodeadkeys)",
|
||||
"Danish (Macintosh)": "DK (Mac)",
|
||||
"Danish (no dead keys)": "DK (Nodeadkeys)",
|
||||
"Danish (Windows)": "DK (Winkeys)",
|
||||
"Dari": "AF",
|
||||
"Dari (Afghanistan, OLPC)": "AF (Fa-Olpc)",
|
||||
"Dhivehi": "MV",
|
||||
"Dutch": "NL",
|
||||
"Dutch (Macintosh)": "NL (Mac)",
|
||||
"Dutch (standard)": "NL (Std)",
|
||||
"Dutch (US)": "NL (Us)",
|
||||
"Dzongkha": "BT",
|
||||
"English (Australian)": "AU",
|
||||
"English (Cameroon)": "CM",
|
||||
"English (Canada)": "CA (Eng)",
|
||||
"English (classic Dvorak)": "US (Dvorak-Classic)",
|
||||
"English (Colemak-DH ISO)": "US (Colemak Dh Iso)",
|
||||
"English (Colemak-DH)": "US (Colemak Dh)",
|
||||
"English (Colemak)": "US (Colemak)",
|
||||
"English (Dvorak, alt. intl.)": "US (Dvorak-Alt-Intl)",
|
||||
"English (Dvorak, intl., with dead keys)": "US (Dvorak-Intl)",
|
||||
"English (Dvorak, left-handed)": "US (Dvorak-L)",
|
||||
"English (Dvorak, Macintosh)": "US (Dvorak-Mac)",
|
||||
"English (Dvorak, right-handed)": "US (Dvorak-R)",
|
||||
"English (Dvorak)": "US (Dvorak)",
|
||||
"English (Ghana, GILLBT)": "GH (Gillbt)",
|
||||
"English (Ghana, multilingual)": "GH (Generic)",
|
||||
"English (Ghana)": "GH",
|
||||
"English (India, with rupee)": "IN (Eng)",
|
||||
"English (intl., with AltGr dead keys)": "US (Altgr-Intl)",
|
||||
"English (Macintosh)": "US (Mac)",
|
||||
"English (Mali, US, intl.)": "ML (Us-Intl)",
|
||||
"English (Mali, US, Macintosh)": "ML (Us-Mac)",
|
||||
"English (Nigeria)": "NG",
|
||||
"English (Norman)": "US (Norman)",
|
||||
"English (programmer Dvorak)": "US (Dvp)",
|
||||
"English (South Africa)": "ZA",
|
||||
"English (the divide/multiply toggle the layout)": "US (Olpc2)",
|
||||
"English (UK, Colemak-DH)": "GB (Colemak Dh)",
|
||||
"English (UK, Colemak)": "GB (Colemak)",
|
||||
"English (UK, Dvorak, with UK punctuation)": "GB (Dvorakukp)",
|
||||
"English (UK, Dvorak)": "GB (Dvorak)",
|
||||
"English (UK, extended, Windows)": "GB (Extd)",
|
||||
"English (UK, intl., with dead keys)": "GB (Intl)",
|
||||
"English (UK, Macintosh, intl.)": "GB (Mac Intl)",
|
||||
"English (UK, Macintosh)": "GB (Mac)",
|
||||
"English (UK)": "GB",
|
||||
"English (US, alt. intl.)": "US (Alt-Intl)",
|
||||
"English (US, euro on 5)": "US (Euro)",
|
||||
"English (US, intl., with dead keys)": "US (Intl)",
|
||||
"English (US, Symbolic)": "US (Symbolic)",
|
||||
"English (US)": "US",
|
||||
"English (Workman, intl., with dead keys)": "US (Workman-Intl)",
|
||||
"English (Workman)": "US (Workman)",
|
||||
"Esperanto": "EPO",
|
||||
"Esperanto (Brazil, Nativo)": "BR (Nativo-Epo)",
|
||||
"Esperanto (legacy)": "EPO (Legacy)",
|
||||
"Esperanto (Portugal, Nativo)": "PT (Nativo-Epo)",
|
||||
"Estonian": "EE",
|
||||
"Estonian (Dvorak)": "EE (Dvorak)",
|
||||
"Estonian (no dead keys)": "EE (Nodeadkeys)",
|
||||
"Estonian (US)": "EE (Us)",
|
||||
"Ewe": "GH (Ewe)",
|
||||
"Faroese": "FO",
|
||||
"Faroese (no dead keys)": "FO (Nodeadkeys)",
|
||||
"Filipino": "PH",
|
||||
"Filipino (Capewell-Dvorak, Baybayin)": "PH (Capewell-Dvorak-Bay)",
|
||||
"Filipino (Capewell-Dvorak, Latin)": "PH (Capewell-Dvorak)",
|
||||
"Filipino (Capewell-QWERF 2006, Baybayin)": "PH (Capewell-Qwerf2k6-Bay)",
|
||||
"Filipino (Capewell-QWERF 2006, Latin)": "PH (Capewell-Qwerf2k6)",
|
||||
"Filipino (Colemak, Baybayin)": "PH (Colemak-Bay)",
|
||||
"Filipino (Colemak, Latin)": "PH (Colemak)",
|
||||
"Filipino (Dvorak, Baybayin)": "PH (Dvorak-Bay)",
|
||||
"Filipino (Dvorak, Latin)": "PH (Dvorak)",
|
||||
"Filipino (QWERTY, Baybayin)": "PH (Qwerty-Bay)",
|
||||
"Finnish": "FI",
|
||||
"Finnish (classic, no dead keys)": "FI (Nodeadkeys)",
|
||||
"Finnish (classic)": "FI (Classic)",
|
||||
"Finnish (Macintosh)": "FI (Mac)",
|
||||
"Finnish (Windows)": "FI (Winkeys)",
|
||||
"French": "FR",
|
||||
"French (alt., Latin-9 only)": "FR (Oss Latin9)",
|
||||
"French (alt., no dead keys)": "FR (Oss Nodeadkeys)",
|
||||
"French (alt.)": "FR (Oss)",
|
||||
"French (AZERTY, AFNOR)": "FR (Afnor)",
|
||||
"French (AZERTY)": "FR (Azerty)",
|
||||
"French (BEPO, AFNOR)": "FR (Bepo Afnor)",
|
||||
"French (BEPO, Latin-9 only)": "FR (Bepo Latin9)",
|
||||
"French (BEPO)": "FR (Bepo)",
|
||||
"French (Cameroon)": "CM (French)",
|
||||
"French (Canada, Dvorak)": "CA (Fr-Dvorak)",
|
||||
"French (Canada, legacy)": "CA (Fr-Legacy)",
|
||||
"French (Canada)": "CA",
|
||||
"French (Democratic Republic of the Congo)": "CD",
|
||||
"French (Dvorak)": "FR (Dvorak)",
|
||||
"French (legacy, alt., no dead keys)": "FR (Latin9 Nodeadkeys)",
|
||||
"French (legacy, alt.)": "FR (Latin9)",
|
||||
"French (Macintosh)": "FR (Mac)",
|
||||
"French (Mali, alt.)": "ML (Fr-Oss)",
|
||||
"French (Morocco)": "MA (French)",
|
||||
"French (no dead keys)": "FR (Nodeadkeys)",
|
||||
"French (Switzerland, Macintosh)": "CH (Fr Mac)",
|
||||
"French (Switzerland, no dead keys)": "CH (Fr Nodeadkeys)",
|
||||
"French (Switzerland)": "CH (Fr)",
|
||||
"French (Togo)": "TG",
|
||||
"French (US)": "FR (Us)",
|
||||
"Friulian (Italy)": "IT (Fur)",
|
||||
"Fula": "GH (Fula)",
|
||||
"Ga": "GH (Ga)",
|
||||
"Georgian": "GE",
|
||||
"Georgian (ergonomic)": "GE (Ergonomic)",
|
||||
"Georgian (France, AZERTY Tskapo)": "FR (Geo)",
|
||||
"Georgian (Italy)": "IT (Geo)",
|
||||
"Georgian (MESS)": "GE (Mess)",
|
||||
"German": "DE",
|
||||
"German (Austria, Macintosh)": "AT (Mac)",
|
||||
"German (Austria, no dead keys)": "AT (Nodeadkeys)",
|
||||
"German (Austria)": "AT",
|
||||
"German (dead acute)": "DE (Deadacute)",
|
||||
"German (dead grave acute)": "DE (Deadgraveacute)",
|
||||
"German (dead tilde)": "DE (Deadtilde)",
|
||||
"German (Dvorak)": "DE (Dvorak)",
|
||||
"German (E1)": "DE (E1)",
|
||||
"German (E2)": "DE (E2)",
|
||||
"German (Macintosh, no dead keys)": "DE (Mac Nodeadkeys)",
|
||||
"German (Macintosh)": "DE (Mac)",
|
||||
"German (Neo 2)": "DE (Neo)",
|
||||
"German (no dead keys)": "DE (Nodeadkeys)",
|
||||
"German (QWERTY)": "DE (Qwerty)",
|
||||
"German (Switzerland, legacy)": "CH (Legacy)",
|
||||
"German (Switzerland, Macintosh)": "CH (De Mac)",
|
||||
"German (Switzerland, no dead keys)": "CH (De Nodeadkeys)",
|
||||
"German (Switzerland)": "CH",
|
||||
"German (T3)": "DE (T3)",
|
||||
"German (US)": "DE (Us)",
|
||||
"Greek": "GR",
|
||||
"Greek (extended)": "GR (Extended)",
|
||||
"Greek (no dead keys)": "GR (Nodeadkeys)",
|
||||
"Greek (polytonic)": "GR (Polytonic)",
|
||||
"Greek (simple)": "GR (Simple)",
|
||||
"Gujarati": "IN (Guj)",
|
||||
"Hanyu Pinyin Letters (with AltGr dead keys)": "CN (Altgr-Pinyin)",
|
||||
"Hausa (Ghana)": "GH (Hausa)",
|
||||
"Hausa (Nigeria)": "NG (Hausa)",
|
||||
"Hawaiian": "US (Haw)",
|
||||
"Hebrew": "IL",
|
||||
"Hebrew (Biblical, Tiro)": "IL (Biblical)",
|
||||
"Hebrew (lyx)": "IL (Lyx)",
|
||||
"Hebrew (phonetic)": "IL (Phonetic)",
|
||||
"Hindi (Bolnagri)": "IN (Bolnagri)",
|
||||
"Hindi (KaGaPa, phonetic)": "IN (Hin-Kagapa)",
|
||||
"Hindi (Wx)": "IN (Hin-Wx)",
|
||||
"Hungarian": "HU",
|
||||
"Hungarian (no dead keys)": "HU (Nodeadkeys)",
|
||||
"Hungarian (QWERTY, 101-key, comma, dead keys)": "HU (101 Qwerty Comma Dead)",
|
||||
"Hungarian (QWERTY, 101-key, comma, no dead keys)": "HU (101 Qwerty Comma Nodead)",
|
||||
"Hungarian (QWERTY, 101-key, dot, dead keys)": "HU (101 Qwerty Dot Dead)",
|
||||
"Hungarian (QWERTY, 101-key, dot, no dead keys)": "HU (101 Qwerty Dot Nodead)",
|
||||
"Hungarian (QWERTY, 102-key, comma, dead keys)": "HU (102 Qwerty Comma Dead)",
|
||||
"Hungarian (QWERTY, 102-key, comma, no dead keys)": "HU (102 Qwerty Comma Nodead)",
|
||||
"Hungarian (QWERTY, 102-key, dot, dead keys)": "HU (102 Qwerty Dot Dead)",
|
||||
"Hungarian (QWERTY, 102-key, dot, no dead keys)": "HU (102 Qwerty Dot Nodead)",
|
||||
"Hungarian (QWERTY)": "HU (Qwerty)",
|
||||
"Hungarian (QWERTZ, 101-key, comma, dead keys)": "HU (101 Qwertz Comma Dead)",
|
||||
"Hungarian (QWERTZ, 101-key, comma, no dead keys)": "HU (101 Qwertz Comma Nodead)",
|
||||
"Hungarian (QWERTZ, 101-key, dot, dead keys)": "HU (101 Qwertz Dot Dead)",
|
||||
"Hungarian (QWERTZ, 101-key, dot, no dead keys)": "HU (101 Qwertz Dot Nodead)",
|
||||
"Hungarian (QWERTZ, 102-key, comma, dead keys)": "HU (102 Qwertz Comma Dead)",
|
||||
"Hungarian (QWERTZ, 102-key, comma, no dead keys)": "HU (102 Qwertz Comma Nodead)",
|
||||
"Hungarian (QWERTZ, 102-key, dot, dead keys)": "HU (102 Qwertz Dot Dead)",
|
||||
"Hungarian (QWERTZ, 102-key, dot, no dead keys)": "HU (102 Qwertz Dot Nodead)",
|
||||
"Hungarian (standard)": "HU (Standard)",
|
||||
"Icelandic": "IS",
|
||||
"Icelandic (Dvorak)": "IS (Dvorak)",
|
||||
"Icelandic (Macintosh, legacy)": "IS (Mac Legacy)",
|
||||
"Icelandic (Macintosh)": "IS (Mac)",
|
||||
"Igbo": "NG (Igbo)",
|
||||
"Indian": "IN",
|
||||
"Indic IPA": "IN (Iipa)",
|
||||
"Indonesian (Arab Melayu, extended phonetic)": "ID (Melayu-Phoneticx)",
|
||||
"Indonesian (Arab Melayu, phonetic)": "ID (Melayu-Phonetic)",
|
||||
"Indonesian (Arab Pegon, phonetic)": "ID (Pegon-Phonetic)",
|
||||
"Indonesian (Latin)": "ID",
|
||||
"Inuktitut": "CA (Ike)",
|
||||
"Iraqi": "IQ",
|
||||
"Irish": "IE",
|
||||
"Irish (UnicodeExpert)": "IE (UnicodeExpert)",
|
||||
"Italian": "IT",
|
||||
"Italian (IBM 142)": "IT (Ibm)",
|
||||
"Italian (intl., with dead keys)": "IT (Intl)",
|
||||
"Italian (Macintosh)": "IT (Mac)",
|
||||
"Italian (no dead keys)": "IT (Nodeadkeys)",
|
||||
"Italian (US)": "IT (Us)",
|
||||
"Italian (Windows)": "IT (Winkeys)",
|
||||
"Japanese": "JP",
|
||||
"Japanese (Dvorak)": "JP (Dvorak)",
|
||||
"Japanese (Kana 86)": "JP (Kana86)",
|
||||
"Japanese (Kana)": "JP (Kana)",
|
||||
"Japanese (Macintosh)": "JP (Mac)",
|
||||
"Japanese (OADG 109A)": "JP (OADG109A)",
|
||||
"Javanese": "ID (Javanese)",
|
||||
"Kabyle (AZERTY, with dead keys)": "DZ (Azerty-Deadkeys)",
|
||||
"Kabyle (QWERTY, UK, with dead keys)": "DZ (Qwerty-Gb-Deadkeys)",
|
||||
"Kabyle (QWERTY, US, with dead keys)": "DZ (Qwerty-Us-Deadkeys)",
|
||||
"Kalmyk": "RU (Xal)",
|
||||
"Kannada": "IN (Kan)",
|
||||
"Kannada (KaGaPa, phonetic)": "IN (Kan-Kagapa)",
|
||||
"Kashubian": "PL (Csb)",
|
||||
"Kazakh": "KZ",
|
||||
"Kazakh (extended)": "KZ (Ext)",
|
||||
"Kazakh (Latin)": "KZ (Latin)",
|
||||
"Kazakh (with Russian)": "KZ (Kazrus)",
|
||||
"Khmer (Cambodia)": "KH",
|
||||
"Kikuyu": "KE (Kik)",
|
||||
"Komi": "RU (Kom)",
|
||||
"Korean": "KR",
|
||||
"Korean (101/104-key compatible)": "KR (Kr104)",
|
||||
"Kurdish (Iran, Arabic-Latin)": "IR (Ku Ara)",
|
||||
"Kurdish (Iran, F)": "IR (Ku F)",
|
||||
"Kurdish (Iran, Latin Alt-Q)": "IR (Ku Alt)",
|
||||
"Kurdish (Iran, Latin Q)": "IR (Ku)",
|
||||
"Kurdish (Iraq, Arabic-Latin)": "IQ (Ku Ara)",
|
||||
"Kurdish (Iraq, F)": "IQ (Ku F)",
|
||||
"Kurdish (Iraq, Latin Alt-Q)": "IQ (Ku Alt)",
|
||||
"Kurdish (Iraq, Latin Q)": "IQ (Ku)",
|
||||
"Kurdish (Syria, F)": "SY (Ku F)",
|
||||
"Kurdish (Syria, Latin Alt-Q)": "SY (Ku Alt)",
|
||||
"Kurdish (Syria, Latin Q)": "SY (Ku)",
|
||||
"Kurdish (Turkey, F)": "TR (Ku F)",
|
||||
"Kurdish (Turkey, Latin Alt-Q)": "TR (Ku Alt)",
|
||||
"Kurdish (Turkey, Latin Q)": "TR (Ku)",
|
||||
"Kyrgyz": "KG",
|
||||
"Kyrgyz (phonetic)": "KG (Phonetic)",
|
||||
"Lao": "LA",
|
||||
"Lao (STEA)": "LA (Stea)",
|
||||
"Latvian": "LV",
|
||||
"Latvian (adapted)": "LV (Adapted)",
|
||||
"Latvian (apostrophe)": "LV (Apostrophe)",
|
||||
"Latvian (ergonomic, ŪGJRMV)": "LV (Ergonomic)",
|
||||
"Latvian (F)": "LV (Fkey)",
|
||||
"Latvian (modern)": "LV (Modern)",
|
||||
"Latvian (tilde)": "LV (Tilde)",
|
||||
"Lithuanian": "LT",
|
||||
"Lithuanian (IBM LST 1205-92)": "LT (Ibm)",
|
||||
"Lithuanian (LEKP)": "LT (Lekp)",
|
||||
"Lithuanian (LEKPa)": "LT (Lekpa)",
|
||||
"Lithuanian (Ratise)": "LT (Ratise)",
|
||||
"Lithuanian (standard)": "LT (Std)",
|
||||
"Lithuanian (US)": "LT (Us)",
|
||||
"Lower Sorbian": "DE (Dsb)",
|
||||
"Lower Sorbian (QWERTZ)": "DE (Dsb Qwertz)",
|
||||
"Macedonian": "MK",
|
||||
"Macedonian (no dead keys)": "MK (Nodeadkeys)",
|
||||
"Malay (Jawi, Arabic Keyboard)": "MY",
|
||||
"Malay (Jawi, phonetic)": "MY (Phonetic)",
|
||||
"Malayalam": "IN (Mal)",
|
||||
"Malayalam (enhanced InScript, with rupee)": "IN (Mal Enhanced)",
|
||||
"Malayalam (Lalitha)": "IN (Mal Lalitha)",
|
||||
"Maltese": "MT",
|
||||
"Maltese (UK, with AltGr overrides)": "MT (Alt-Gb)",
|
||||
"Maltese (US, with AltGr overrides)": "MT (Alt-Us)",
|
||||
"Maltese (US)": "MT (Us)",
|
||||
"Manipuri (Eeyek)": "IN (Eeyek)",
|
||||
"Maori": "MAO",
|
||||
"Marathi (enhanced InScript)": "IN (Marathi)",
|
||||
"Marathi (KaGaPa, phonetic)": "IN (Mar-Kagapa)",
|
||||
"Mari": "RU (Chm)",
|
||||
"Mmuock": "CM (Mmuock)",
|
||||
"Moldavian": "MD",
|
||||
"Moldavian (Gagauz)": "MD (Gag)",
|
||||
"Mon": "MM (Mnw)",
|
||||
"Mon (A1)": "MM (Mnw-A1)",
|
||||
"Mongolian": "MN",
|
||||
"Mongolian (Bichig)": "CN (Mon Trad)",
|
||||
"Mongolian (Galik)": "CN (Mon Trad Galik)",
|
||||
"Mongolian (Manchu Galik)": "CN (Mon Manchu Galik)",
|
||||
"Mongolian (Manchu)": "CN (Mon Trad Manchu)",
|
||||
"Mongolian (Todo Galik)": "CN (Mon Todo Galik)",
|
||||
"Mongolian (Todo)": "CN (Mon Trad Todo)",
|
||||
"Mongolian (Xibe)": "CN (Mon Trad Xibe)",
|
||||
"Montenegrin": "ME",
|
||||
"Montenegrin (Cyrillic, with guillemets)": "ME (Cyrillicalternatequotes)",
|
||||
"Montenegrin (Cyrillic, ZE and ZHE swapped)": "ME (Cyrillicyz)",
|
||||
"Montenegrin (Cyrillic)": "ME (Cyrillic)",
|
||||
"Montenegrin (Latin, QWERTY)": "ME (Latinyz)",
|
||||
"Montenegrin (Latin, Unicode, QWERTY)": "ME (Latinunicodeyz)",
|
||||
"Montenegrin (Latin, Unicode)": "ME (Latinunicode)",
|
||||
"Montenegrin (Latin, with guillemets)": "ME (Latinalternatequotes)",
|
||||
"N'Ko (AZERTY)": "GN",
|
||||
"Nepali": "NP",
|
||||
"Northern Saami (Finland)": "FI (Smi)",
|
||||
"Northern Saami (Norway, no dead keys)": "NO (Smi Nodeadkeys)",
|
||||
"Northern Saami (Norway)": "NO (Smi)",
|
||||
"Northern Saami (Sweden)": "SE (Smi)",
|
||||
"Norwegian": "NO",
|
||||
"Norwegian (Colemak)": "NO (Colemak)",
|
||||
"Norwegian (Dvorak)": "NO (Dvorak)",
|
||||
"Norwegian (Macintosh, no dead keys)": "NO (Mac Nodeadkeys)",
|
||||
"Norwegian (Macintosh)": "NO (Mac)",
|
||||
"Norwegian (no dead keys)": "NO (Nodeadkeys)",
|
||||
"Norwegian (Windows)": "NO (Winkeys)",
|
||||
"Occitan": "FR (Oci)",
|
||||
"Ogham": "IE (Ogam)",
|
||||
"Ogham (IS434)": "IE (Ogam Is434)",
|
||||
"Ol Chiki": "IN (Olck)",
|
||||
"Old Turkic": "TR (Otk)",
|
||||
"Old Turkic (F)": "TR (Otkf)",
|
||||
"Oriya": "IN (Ori)",
|
||||
"Oriya (Bolnagri)": "IN (Ori-Bolnagri)",
|
||||
"Oriya (Wx)": "IN (Ori-Wx)",
|
||||
"Ossetian (Georgia)": "GE (Os)",
|
||||
"Ossetian (legacy)": "RU (Os Legacy)",
|
||||
"Ossetian (Windows)": "RU (Os Winkeys)",
|
||||
"Ottoman (F)": "TR (Otf)",
|
||||
"Ottoman (Q)": "TR (Ot)",
|
||||
"Pannonian Rusyn": "RS (Rue)",
|
||||
"Pashto": "AF (Ps)",
|
||||
"Pashto (Afghanistan, OLPC)": "AF (Ps-Olpc)",
|
||||
"Persian": "IR",
|
||||
"Persian (with Persian keypad)": "IR (Pes Keypad)",
|
||||
"Polish": "PL",
|
||||
"Polish (British keyboard)": "GB (Pl)",
|
||||
"Polish (Dvorak, with Polish quotes on key 1)": "PL (Dvorak Altquotes)",
|
||||
"Polish (Dvorak, with Polish quotes on quotemark key)": "PL (Dvorak Quotes)",
|
||||
"Polish (Dvorak)": "PL (Dvorak)",
|
||||
"Polish (legacy)": "PL (Legacy)",
|
||||
"Polish (programmer Dvorak)": "PL (Dvp)",
|
||||
"Polish (QWERTZ)": "PL (Qwertz)",
|
||||
"Portuguese": "PT",
|
||||
"Portuguese (Brazil, Dvorak)": "BR (Dvorak)",
|
||||
"Portuguese (Brazil, IBM/Lenovo ThinkPad)": "BR (Thinkpad)",
|
||||
"Portuguese (Brazil, Nativo for US keyboards)": "BR (Nativo-Us)",
|
||||
"Portuguese (Brazil, Nativo)": "BR (Nativo)",
|
||||
"Portuguese (Brazil, no dead keys)": "BR (Nodeadkeys)",
|
||||
"Portuguese (Brazil)": "BR",
|
||||
"Portuguese (Macintosh, no dead keys)": "PT (Mac Nodeadkeys)",
|
||||
"Portuguese (Macintosh)": "PT (Mac)",
|
||||
"Portuguese (Nativo for US keyboards)": "PT (Nativo-Us)",
|
||||
"Portuguese (Nativo)": "PT (Nativo)",
|
||||
"Portuguese (no dead keys)": "PT (Nodeadkeys)",
|
||||
"Punjabi (Gurmukhi Jhelum)": "IN (Jhelum)",
|
||||
"Punjabi (Gurmukhi)": "IN (Guru)",
|
||||
"Romanian": "RO",
|
||||
"Romanian (Germany, no dead keys)": "DE (Ro Nodeadkeys)",
|
||||
"Romanian (Germany)": "DE (Ro)",
|
||||
"Romanian (standard)": "RO (Std)",
|
||||
"Romanian (Windows)": "RO (Winkeys)",
|
||||
"Russian": "RU",
|
||||
"Russian (Belarus)": "BY (Ru)",
|
||||
"Russian (Czech, phonetic)": "CZ (Rus)",
|
||||
"Russian (DOS)": "RU (Dos)",
|
||||
"Russian (engineering, EN)": "RU (Ruchey En)",
|
||||
"Russian (engineering, RU)": "RU (Ruchey Ru)",
|
||||
"Russian (Georgia)": "GE (Ru)",
|
||||
"Russian (Germany, phonetic)": "DE (Ru)",
|
||||
"Russian (Kazakhstan, with Kazakh)": "KZ (Ruskaz)",
|
||||
"Russian (legacy)": "RU (Legacy)",
|
||||
"Russian (Macintosh)": "RU (Mac)",
|
||||
"Russian (phonetic, AZERTY)": "RU (Phonetic Azerty)",
|
||||
"Russian (phonetic, Dvorak)": "RU (Phonetic Dvorak)",
|
||||
"Russian (phonetic, French)": "RU (Phonetic Fr)",
|
||||
"Russian (phonetic, Windows)": "RU (Phonetic Winkeys)",
|
||||
"Russian (phonetic, YAZHERTY)": "RU (Phonetic YAZHERTY)",
|
||||
"Russian (phonetic)": "RU (Phonetic)",
|
||||
"Russian (Poland, phonetic Dvorak)": "PL (Ru Phonetic Dvorak)",
|
||||
"Russian (Sweden, phonetic, no dead keys)": "SE (Rus Nodeadkeys)",
|
||||
"Russian (Sweden, phonetic)": "SE (Rus)",
|
||||
"Russian (typewriter, legacy)": "RU (Typewriter-Legacy)",
|
||||
"Russian (typewriter)": "RU (Typewriter)",
|
||||
"Russian (Ukraine, standard RSTU)": "UA (Rstu Ru)",
|
||||
"Russian (US, phonetic)": "US (Rus)",
|
||||
"Saisiyat (Taiwan)": "TW (Saisiyat)",
|
||||
"Samogitian": "LT (Sgs)",
|
||||
"Sanskrit (KaGaPa, phonetic)": "IN (San-Kagapa)",
|
||||
"Scottish Gaelic": "GB (Gla)",
|
||||
"Serbian": "RS",
|
||||
"Serbian (Cyrillic, with guillemets)": "RS (Alternatequotes)",
|
||||
"Serbian (Cyrillic, ZE and ZHE swapped)": "RS (Yz)",
|
||||
"Serbian (Latin, QWERTY)": "RS (Latinyz)",
|
||||
"Serbian (Latin, Unicode, QWERTY)": "RS (Latinunicodeyz)",
|
||||
"Serbian (Latin, Unicode)": "RS (Latinunicode)",
|
||||
"Serbian (Latin, with guillemets)": "RS (Latinalternatequotes)",
|
||||
"Serbian (Latin)": "RS (Latin)",
|
||||
"Serbian (Russia)": "RU (Srp)",
|
||||
"Serbo-Croatian (US)": "US (Hbs)",
|
||||
"Shan": "MM (Shn)",
|
||||
"Shan (Zawgyi Tai)": "MM (Zgt)",
|
||||
"Sicilian": "IT (Scn)",
|
||||
"Silesian": "PL (Szl)",
|
||||
"Sindhi": "PK (Snd)",
|
||||
"Sinhala (phonetic)": "LK",
|
||||
"Sinhala (US)": "LK (Us)",
|
||||
"Slovak": "SK",
|
||||
"Slovak (extended backslash)": "SK (Bksl)",
|
||||
"Slovak (QWERTY, extended backslash)": "SK (Qwerty Bksl)",
|
||||
"Slovak (QWERTY)": "SK (Qwerty)",
|
||||
"Slovenian": "SI",
|
||||
"Slovenian (US)": "SI (Us)",
|
||||
"Slovenian (with guillemets)": "SI (Alternatequotes)",
|
||||
"Spanish": "ES",
|
||||
"Spanish (dead tilde)": "ES (Deadtilde)",
|
||||
"Spanish (Dvorak)": "ES (Dvorak)",
|
||||
"Spanish (Latin American, Colemak)": "LATAM (Colemak)",
|
||||
"Spanish (Latin American, dead tilde)": "LATAM (Deadtilde)",
|
||||
"Spanish (Latin American, Dvorak)": "LATAM (Dvorak)",
|
||||
"Spanish (Latin American, no dead keys)": "LATAM (Nodeadkeys)",
|
||||
"Spanish (Latin American)": "LATAM",
|
||||
"Spanish (Macintosh)": "ES (Mac)",
|
||||
"Spanish (no dead keys)": "ES (Nodeadkeys)",
|
||||
"Spanish (Windows)": "ES (Winkeys)",
|
||||
"Swahili (Kenya)": "KE",
|
||||
"Swahili (Tanzania)": "TZ",
|
||||
"Swedish": "SE",
|
||||
"Swedish (Dvorak, intl.)": "SE (Us Dvorak)",
|
||||
"Swedish (Dvorak)": "SE (Dvorak)",
|
||||
"Swedish (Macintosh)": "SE (Mac)",
|
||||
"Swedish (no dead keys)": "SE (Nodeadkeys)",
|
||||
"Swedish (Svdvorak)": "SE (Svdvorak)",
|
||||
"Swedish (US)": "SE (Us)",
|
||||
"Swedish Sign Language": "SE (Swl)",
|
||||
"Syriac": "SY (Syc)",
|
||||
"Syriac (phonetic)": "SY (Syc Phonetic)",
|
||||
"Taiwanese": "TW",
|
||||
"Taiwanese (indigenous)": "TW (Indigenous)",
|
||||
"Tajik": "TJ",
|
||||
"Tajik (legacy)": "TJ (Legacy)",
|
||||
"Tamil (InScript, with Arabic numerals)": "IN (Tam)",
|
||||
"Tamil (InScript, with Tamil numerals)": "IN (Tam Tamilnumbers)",
|
||||
"Tamil (Sri Lanka, TamilNet '99, TAB encoding)": "LK (Tam TAB)",
|
||||
"Tamil (Sri Lanka, TamilNet '99)": "LK (Tam Unicode)",
|
||||
"Tamil (TamilNet '99 with Tamil numerals)": "IN (Tamilnet Tamilnumbers)",
|
||||
"Tamil (TamilNet '99, TAB encoding)": "IN (Tamilnet TAB)",
|
||||
"Tamil (TamilNet '99, TSCII encoding)": "IN (Tamilnet TSCII)",
|
||||
"Tamil (TamilNet '99)": "IN (Tamilnet)",
|
||||
"Tarifit": "MA (Rif)",
|
||||
"Tatar": "RU (Tt)",
|
||||
"Telugu": "IN (Tel)",
|
||||
"Telugu (KaGaPa, phonetic)": "IN (Tel-Kagapa)",
|
||||
"Telugu (Sarala)": "IN (Tel-Sarala)",
|
||||
"Thai": "TH",
|
||||
"Thai (Pattachote)": "TH (Pat)",
|
||||
"Thai (TIS-820.2538)": "TH (Tis)",
|
||||
"Tibetan": "CN (Tib)",
|
||||
"Tibetan (with ASCII numerals)": "CN (Tib Asciinum)",
|
||||
"Tswana": "BW",
|
||||
"Turkish": "TR",
|
||||
"Turkish (Alt-Q)": "TR (Alt)",
|
||||
"Turkish (E)": "TR (E)",
|
||||
"Turkish (F)": "TR (F)",
|
||||
"Turkish (Germany)": "DE (Tr)",
|
||||
"Turkish (intl., with dead keys)": "TR (Intl)",
|
||||
"Turkmen": "TM",
|
||||
"Turkmen (Alt-Q)": "TM (Alt)",
|
||||
"Udmurt": "RU (Udm)",
|
||||
"Ukrainian": "UA",
|
||||
"Ukrainian (homophonic)": "UA (Homophonic)",
|
||||
"Ukrainian (legacy)": "UA (Legacy)",
|
||||
"Ukrainian (macOS)": "UA (MacOS)",
|
||||
"Ukrainian (phonetic)": "UA (Phonetic)",
|
||||
"Ukrainian (standard RSTU)": "UA (Rstu)",
|
||||
"Ukrainian (typewriter)": "UA (Typewriter)",
|
||||
"Ukrainian (Windows)": "UA (Winkeys)",
|
||||
"Urdu (alt. phonetic)": "IN (Urd-Phonetic3)",
|
||||
"Urdu (Pakistan, CRULP)": "PK (Urd-Crulp)",
|
||||
"Urdu (Pakistan, NLA)": "PK (Urd-Nla)",
|
||||
"Urdu (Pakistan)": "PK",
|
||||
"Urdu (phonetic)": "IN (Urd-Phonetic)",
|
||||
"Urdu (Windows)": "IN (Urd-Winkeys)",
|
||||
"Uyghur": "CN (Ug)",
|
||||
"Uzbek": "UZ",
|
||||
"Uzbek (Afghanistan, OLPC)": "AF (Uz-Olpc)",
|
||||
"Uzbek (Afghanistan)": "AF (Uz)",
|
||||
"Uzbek (Latin)": "UZ (Latin)",
|
||||
"Vietnamese": "VN",
|
||||
"Vietnamese (France)": "VN (Fr)",
|
||||
"Vietnamese (US)": "VN (Us)",
|
||||
"Wolof": "SN",
|
||||
"Yakut": "RU (Sah)",
|
||||
"Yoruba": "NG (Yoruba)"
|
||||
};
|
||||
82
customModules/module.ts
Normal file
82
customModules/module.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Module } from "lib/types/bar";
|
||||
import options from "options";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import { Binding } from "types/service";
|
||||
import { Variable as VariableType } from "types/variable";
|
||||
|
||||
const { style } = options.theme.bar.buttons;
|
||||
|
||||
const undefinedVar = Variable(undefined);
|
||||
|
||||
export const module = ({
|
||||
icon,
|
||||
textIcon,
|
||||
label,
|
||||
tooltipText,
|
||||
boxClass,
|
||||
props = {},
|
||||
showLabelBinding = undefinedVar.bind("value"),
|
||||
showLabel,
|
||||
labelHook,
|
||||
hook
|
||||
}: Module) => {
|
||||
const getIconWidget = () => {
|
||||
let iconWidget: Gtk.Widget | undefined;
|
||||
|
||||
if (icon !== undefined) {
|
||||
iconWidget = Widget.Icon({
|
||||
class_name: `txt-icon bar-button-icon module-icon ${boxClass}`,
|
||||
icon: icon
|
||||
}) as unknown as Gtk.Widget;
|
||||
} else if (textIcon !== undefined) {
|
||||
iconWidget = Widget.Label({
|
||||
class_name: `txt-icon bar-button-icon module-icon ${boxClass}`,
|
||||
label: textIcon
|
||||
}) as unknown as Gtk.Widget;
|
||||
}
|
||||
|
||||
return iconWidget;
|
||||
}
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([style.bind("value"), showLabelBinding], (style: string, shwLabel: boolean) => {
|
||||
const shouldShowLabel = shwLabel || showLabel;
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
};
|
||||
return `${boxClass} ${styleMap[style]} ${!shouldShowLabel ? "no-label" : ""}`;
|
||||
}),
|
||||
tooltip_text: tooltipText,
|
||||
children: Utils.merge(
|
||||
[showLabelBinding],
|
||||
(showLabelBinding): Gtk.Widget[] => {
|
||||
const childrenArray: Gtk.Widget[] = [];
|
||||
const iconWidget = getIconWidget();
|
||||
|
||||
if (iconWidget !== undefined) {
|
||||
childrenArray.push(iconWidget);
|
||||
}
|
||||
|
||||
if (showLabelBinding) {
|
||||
childrenArray.push(
|
||||
Widget.Label({
|
||||
class_name: `bar-button-label module-label ${boxClass}`,
|
||||
label: label,
|
||||
setup: labelHook,
|
||||
}) as unknown as Gtk.Widget
|
||||
);
|
||||
}
|
||||
return childrenArray;
|
||||
}
|
||||
) as Binding<VariableType<Gtk.Widget[]>, any, Gtk.Widget[]>,
|
||||
setup: hook,
|
||||
}),
|
||||
tooltip_text: tooltipText,
|
||||
isVisible: true,
|
||||
boxClass,
|
||||
props
|
||||
};
|
||||
};
|
||||
106
customModules/netstat/computeNetwork.ts
Normal file
106
customModules/netstat/computeNetwork.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import GLib from 'gi://GLib';
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import { NetworkResourceData } from 'lib/types/customModules/network';
|
||||
import { GET_DEFAULT_NETSTAT_DATA } from 'lib/types/defaults/netstat';
|
||||
import { RateUnit } from 'lib/types/bar';
|
||||
|
||||
let previousNetUsage = { rx: 0, tx: 0, time: 0 };
|
||||
|
||||
const formatRate = (rate: number, type: string, round: boolean): string => {
|
||||
const fixed = round ? 0 : 2;
|
||||
|
||||
switch (true) {
|
||||
case type === 'KiB':
|
||||
return `${(rate / 1e3).toFixed(fixed)} KiB/s`;
|
||||
case type === 'MiB':
|
||||
return `${(rate / 1e6).toFixed(fixed)} MiB/s`;
|
||||
case type === 'GiB':
|
||||
return `${(rate / 1e9).toFixed(fixed)} GiB/s`;
|
||||
case rate >= 1e9:
|
||||
return `${(rate / 1e9).toFixed(fixed)} GiB/s`;
|
||||
case rate >= 1e6:
|
||||
return `${(rate / 1e6).toFixed(fixed)} MiB/s`;
|
||||
case rate >= 1e3:
|
||||
return `${(rate / 1e3).toFixed(fixed)} KiB/s`;
|
||||
default:
|
||||
return `${rate.toFixed(fixed)} bytes/s`;
|
||||
}
|
||||
};
|
||||
|
||||
interface NetworkUsage {
|
||||
name: string;
|
||||
rx: number;
|
||||
tx: number;
|
||||
}
|
||||
|
||||
const parseInterfaceData = (line: string): NetworkUsage | null => {
|
||||
const trimmedLine = line.trim();
|
||||
if (!trimmedLine || trimmedLine.startsWith('Inter-') || trimmedLine.startsWith('face')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [iface, rx, , , , , , , , tx] = trimmedLine.split(/\s+/);
|
||||
const rxValue = parseInt(rx, 10);
|
||||
const txValue = parseInt(tx, 10);
|
||||
const cleanedIface = iface.replace(':', '');
|
||||
|
||||
return { name: cleanedIface, rx: rxValue, tx: txValue };
|
||||
};
|
||||
|
||||
const isValidInterface = (iface: NetworkUsage | null, interfaceName: string): boolean => {
|
||||
if (!iface) return false;
|
||||
if (interfaceName) return iface.name === interfaceName;
|
||||
return iface.name !== 'lo' && iface.rx > 0 && iface.tx > 0;
|
||||
};
|
||||
|
||||
const getNetworkUsage = (interfaceName: string = ''): NetworkUsage => {
|
||||
const [success, data] = GLib.file_get_contents('/proc/net/dev');
|
||||
if (!success) {
|
||||
console.error('Failed to read /proc/net/dev');
|
||||
return { name: '', rx: 0, tx: 0 };
|
||||
}
|
||||
|
||||
const lines = new TextDecoder('utf-8').decode(data).split('\n');
|
||||
for (const line of lines) {
|
||||
const iface = parseInterfaceData(line);
|
||||
if (isValidInterface(iface, interfaceName)) {
|
||||
return iface!;
|
||||
}
|
||||
}
|
||||
|
||||
return { name: '', rx: 0, tx: 0 };
|
||||
};
|
||||
|
||||
export const computeNetwork = (round: VariableType<boolean>, interfaceNameVar: VariableType<string>, dataType: VariableType<RateUnit>): NetworkResourceData => {
|
||||
const rateUnit = dataType.value;
|
||||
const interfaceName = interfaceNameVar ? interfaceNameVar.value : '';
|
||||
|
||||
const DEFAULT_NETSTAT_DATA = GET_DEFAULT_NETSTAT_DATA(rateUnit);
|
||||
try {
|
||||
const { rx, tx, name } = getNetworkUsage(interfaceName);
|
||||
const currentTime = Date.now();
|
||||
|
||||
if (!name) {
|
||||
return DEFAULT_NETSTAT_DATA;
|
||||
}
|
||||
|
||||
if (previousNetUsage.time === 0) {
|
||||
previousNetUsage = { rx, tx, time: currentTime };
|
||||
return DEFAULT_NETSTAT_DATA;
|
||||
}
|
||||
|
||||
const timeDiff = Math.max((currentTime - previousNetUsage.time) / 1000, 1);
|
||||
const rxRate = (rx - previousNetUsage.rx) / timeDiff;
|
||||
const txRate = (tx - previousNetUsage.tx) / timeDiff;
|
||||
|
||||
previousNetUsage = { rx, tx, time: currentTime };
|
||||
|
||||
return {
|
||||
in: formatRate(rxRate, rateUnit, round.value),
|
||||
out: formatRate(txRate, rateUnit, round.value),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error calculating network usage:', error);
|
||||
return DEFAULT_NETSTAT_DATA;
|
||||
}
|
||||
};
|
||||
113
customModules/netstat/index.ts
Normal file
113
customModules/netstat/index.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import options from 'options';
|
||||
import { module } from '../module';
|
||||
import { inputHandler } from 'customModules/utils';
|
||||
import { computeNetwork } from './computeNetwork';
|
||||
import { NetstatLabelType } from 'lib/types/bar';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
|
||||
import Button from 'types/widgets/button';
|
||||
import { NetworkResourceData } from 'lib/types/customModules/network';
|
||||
import { NETWORK_LABEL_TYPES } from 'lib/types/defaults/bar';
|
||||
import { GET_DEFAULT_NETSTAT_DATA } from 'lib/types/defaults/netstat';
|
||||
import { pollVariable } from 'customModules/PollVar';
|
||||
|
||||
const {
|
||||
label,
|
||||
labelType,
|
||||
networkInterface,
|
||||
rateUnit,
|
||||
icon,
|
||||
round,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
pollingInterval,
|
||||
} = options.bar.customModules.netstat;
|
||||
|
||||
export const networkUsage = Variable<NetworkResourceData>(
|
||||
GET_DEFAULT_NETSTAT_DATA(rateUnit.value),
|
||||
);
|
||||
|
||||
pollVariable(
|
||||
// Variable to poll and update with the result of the function passed in
|
||||
networkUsage,
|
||||
// Variables that should trigger the polling function to update when they change
|
||||
[rateUnit.bind('value'), networkInterface.bind('value'), round.bind('value')],
|
||||
// Interval at which to poll
|
||||
pollingInterval.bind('value'),
|
||||
// Function to execute to get the network data
|
||||
computeNetwork,
|
||||
// Optional parameters to pass to the function
|
||||
// round is a boolean that determines whether to round the values
|
||||
round,
|
||||
// Optional parameters to pass to the function
|
||||
// networkInterface is the interface name to filter the data
|
||||
networkInterface,
|
||||
// Optional parameters to pass to the function
|
||||
// rateUnit is the unit to display the data in
|
||||
// e.g. KiB, MiB, GiB, etc.
|
||||
rateUnit,
|
||||
);
|
||||
|
||||
export const Netstat = () => {
|
||||
const renderNetworkLabel = (
|
||||
lblType: NetstatLabelType,
|
||||
network: NetworkResourceData,
|
||||
): string => {
|
||||
switch (lblType) {
|
||||
case 'in':
|
||||
return `↓ ${network.in}`;
|
||||
case 'out':
|
||||
return `↑ ${network.out}`;
|
||||
default:
|
||||
return `↓ ${network.in} ↑ ${network.out}`;
|
||||
}
|
||||
};
|
||||
|
||||
const netstatModule = module({
|
||||
textIcon: icon.bind('value'),
|
||||
label: Utils.merge(
|
||||
[networkUsage.bind('value'), labelType.bind('value')],
|
||||
(network: NetworkResourceData, lblTyp: NetstatLabelType) => renderNetworkLabel(lblTyp, network),
|
||||
),
|
||||
tooltipText: labelType.bind('value').as((lblTyp) => {
|
||||
return lblTyp === 'full' ? 'Ingress / Egress' : lblTyp === 'in' ? 'Ingress' : 'Egress';
|
||||
}),
|
||||
boxClass: 'netstat',
|
||||
showLabelBinding: label.bind('value'),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
fn: () => {
|
||||
labelType.value =
|
||||
NETWORK_LABEL_TYPES[
|
||||
(NETWORK_LABEL_TYPES.indexOf(labelType.value) + 1) % NETWORK_LABEL_TYPES.length
|
||||
] as NetstatLabelType;
|
||||
},
|
||||
},
|
||||
onScrollDown: {
|
||||
fn: () => {
|
||||
labelType.value =
|
||||
NETWORK_LABEL_TYPES[
|
||||
(NETWORK_LABEL_TYPES.indexOf(labelType.value) - 1 + NETWORK_LABEL_TYPES.length) %
|
||||
NETWORK_LABEL_TYPES.length
|
||||
] as NetstatLabelType;
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return netstatModule;
|
||||
};
|
||||
|
||||
46
customModules/power/index.ts
Normal file
46
customModules/power/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import options from "options";
|
||||
import { module } from "../module"
|
||||
|
||||
import { inputHandler } from "customModules/utils";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import Button from "types/widgets/button";
|
||||
|
||||
const {
|
||||
icon,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
} = options.bar.customModules.power;
|
||||
|
||||
export const Power = () => {
|
||||
const powerModule = module({
|
||||
tooltipText: "Power Menu",
|
||||
textIcon: icon.bind("value"),
|
||||
boxClass: "powermodule",
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return powerModule;
|
||||
}
|
||||
42
customModules/ram/computeRam.ts
Normal file
42
customModules/ram/computeRam.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
const GLib = imports.gi.GLib;
|
||||
|
||||
import { divide } from 'customModules/utils';
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
|
||||
export const calculateRamUsage = (round: VariableType<boolean>) => {
|
||||
try {
|
||||
const [success, meminfoBytes] = GLib.file_get_contents('/proc/meminfo');
|
||||
|
||||
if (!success || !meminfoBytes) {
|
||||
throw new Error('Failed to read /proc/meminfo or file content is null.');
|
||||
}
|
||||
|
||||
const meminfo = new TextDecoder('utf-8').decode(meminfoBytes);
|
||||
|
||||
const totalMatch = meminfo.match(/MemTotal:\s+(\d+)/);
|
||||
const availableMatch = meminfo.match(/MemAvailable:\s+(\d+)/);
|
||||
|
||||
if (!totalMatch || !availableMatch) {
|
||||
throw new Error('Failed to parse /proc/meminfo for memory values.');
|
||||
}
|
||||
|
||||
const totalRamInBytes = parseInt(totalMatch[1], 10) * 1024;
|
||||
const availableRamInBytes = parseInt(availableMatch[1], 10) * 1024;
|
||||
|
||||
let usedRam = totalRamInBytes - availableRamInBytes;
|
||||
usedRam = isNaN(usedRam) || usedRam < 0 ? 0 : usedRam;
|
||||
|
||||
|
||||
return {
|
||||
percentage: divide([totalRamInBytes, usedRam], round.value),
|
||||
total: totalRamInBytes,
|
||||
used: usedRam,
|
||||
free: availableRamInBytes,
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error calculating RAM usage:', error);
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
88
customModules/ram/index.ts
Normal file
88
customModules/ram/index.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import options from "options";
|
||||
|
||||
// Module initializer
|
||||
import { module } from "../module"
|
||||
|
||||
// Types
|
||||
import { GenericResourceData } from "lib/types/customModules/generic";
|
||||
import Button from "types/widgets/button";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
|
||||
// Helper Methods
|
||||
import { calculateRamUsage } from "./computeRam";
|
||||
|
||||
// Utility Methods
|
||||
import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils";
|
||||
import { ResourceLabelType } from "lib/types/bar";
|
||||
|
||||
// Global Constants
|
||||
import { LABEL_TYPES } from "lib/types/defaults/bar";
|
||||
import { pollVariable } from "customModules/PollVar";
|
||||
|
||||
// All the user configurable options for the ram module that are needed
|
||||
const {
|
||||
label,
|
||||
labelType,
|
||||
round,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
pollingInterval
|
||||
} = options.bar.customModules.ram;
|
||||
|
||||
const defaultRamData: GenericResourceData = { total: 0, used: 0, percentage: 0, free: 0 };
|
||||
const ramUsage = Variable(defaultRamData);
|
||||
|
||||
pollVariable(
|
||||
ramUsage,
|
||||
[round.bind('value')],
|
||||
pollingInterval.bind('value'),
|
||||
calculateRamUsage,
|
||||
round,
|
||||
);
|
||||
|
||||
export const Ram = () => {
|
||||
|
||||
const ramModule = module({
|
||||
textIcon: "",
|
||||
label: Utils.merge(
|
||||
[ramUsage.bind("value"), labelType.bind("value"), round.bind("value")],
|
||||
(rmUsg: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => {
|
||||
const returnValue = renderResourceLabel(lblTyp, rmUsg, round);
|
||||
|
||||
return returnValue;
|
||||
}),
|
||||
tooltipText: labelType.bind("value").as(lblTyp => {
|
||||
return formatTooltip('RAM', lblTyp);
|
||||
}),
|
||||
boxClass: "ram",
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
fn: () => {
|
||||
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType;
|
||||
}
|
||||
},
|
||||
onScrollDown: {
|
||||
fn: () => {
|
||||
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return ramModule;
|
||||
}
|
||||
32
customModules/storage/computeStorage.ts
Normal file
32
customModules/storage/computeStorage.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// @ts-expect-error
|
||||
import GTop from 'gi://GTop';
|
||||
|
||||
import { divide } from 'customModules/utils';
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
|
||||
let previousFsUsage = new GTop.glibtop_fsusage();
|
||||
|
||||
export const computeStorage = (round: VariableType<boolean>) => {
|
||||
try {
|
||||
const currentFsUsage = new GTop.glibtop_fsusage();
|
||||
|
||||
GTop.glibtop_get_fsusage(currentFsUsage, "/");
|
||||
|
||||
const total = currentFsUsage.blocks * currentFsUsage.block_size;
|
||||
const available = currentFsUsage.bavail * currentFsUsage.block_size;
|
||||
const used = total - available;
|
||||
|
||||
previousFsUsage = currentFsUsage;
|
||||
|
||||
return {
|
||||
total,
|
||||
used,
|
||||
free: available,
|
||||
percentage: divide([total, used], round.value),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error calculating RAM usage:', error);
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
78
customModules/storage/index.ts
Normal file
78
customModules/storage/index.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import options from "options";
|
||||
import { module } from "../module"
|
||||
|
||||
import { formatTooltip, inputHandler, renderResourceLabel } from "customModules/utils";
|
||||
import { computeStorage } from "./computeStorage";
|
||||
import { ResourceLabelType } from "lib/types/bar";
|
||||
import { GenericResourceData } from "lib/types/customModules/generic";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import Button from "types/widgets/button";
|
||||
import { LABEL_TYPES } from "lib/types/defaults/bar";
|
||||
import { pollVariable } from "customModules/PollVar";
|
||||
|
||||
const {
|
||||
label,
|
||||
labelType,
|
||||
icon,
|
||||
round,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
pollingInterval
|
||||
} = options.bar.customModules.storage;
|
||||
|
||||
const defaultStorageData = { total: 0, used: 0, percentage: 0, free: 0 };
|
||||
|
||||
const storageUsage = Variable(defaultStorageData);
|
||||
|
||||
pollVariable(
|
||||
storageUsage,
|
||||
[round.bind('value')],
|
||||
pollingInterval.bind('value'),
|
||||
computeStorage,
|
||||
round,
|
||||
);
|
||||
|
||||
export const Storage = () => {
|
||||
const storageModule = module({
|
||||
textIcon: icon.bind("value"),
|
||||
label: Utils.merge(
|
||||
[storageUsage.bind("value"), labelType.bind("value"), round.bind("value")],
|
||||
(storage: GenericResourceData, lblTyp: ResourceLabelType, round: boolean) => {
|
||||
return renderResourceLabel(lblTyp, storage, round);
|
||||
}),
|
||||
tooltipText: labelType.bind("value").as(lblTyp => {
|
||||
return formatTooltip('Storage', lblTyp);
|
||||
|
||||
}),
|
||||
boxClass: "storage",
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
fn: () => {
|
||||
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) + 1) % LABEL_TYPES.length] as ResourceLabelType;
|
||||
}
|
||||
},
|
||||
onScrollDown: {
|
||||
fn: () => {
|
||||
labelType.value = LABEL_TYPES[(LABEL_TYPES.indexOf(labelType.value) - 1 + LABEL_TYPES.length) % LABEL_TYPES.length] as ResourceLabelType;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return storageModule;
|
||||
}
|
||||
106
customModules/theme.ts
Normal file
106
customModules/theme.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Option } from "widget/settings/shared/Option";
|
||||
import { Header } from "widget/settings/shared/Header";
|
||||
|
||||
import options from "options";
|
||||
|
||||
export const CustomModuleTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page customModules paged-container",
|
||||
child: Widget.Box({
|
||||
class_name: "bar-theme-page paged-container",
|
||||
vertical: true,
|
||||
children: [
|
||||
Header('RAM'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.ram.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.ram.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.ram.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.ram.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('CPU'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.cpu.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.cpu.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.cpu.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.cpu.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Storage'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.storage.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.storage.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.storage.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.storage.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Netstat'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.netstat.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.netstat.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.netstat.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.netstat.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Keyboard Layout'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.kbLayout.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.kbLayout.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.kbLayout.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.kbLayout.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Updates'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.updates.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.updates.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.updates.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.updates.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Weather'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.weather.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.weather.text, title: 'Text', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.weather.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.weather.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
|
||||
Header('Power'),
|
||||
Option({ opt: options.theme.bar.buttons.modules.power.icon, title: 'Icon', type: 'color' }),
|
||||
Option({ opt: options.theme.bar.buttons.modules.power.background, title: 'Label Background', type: 'color' }),
|
||||
Option({
|
||||
opt: options.theme.bar.buttons.modules.power.icon_background,
|
||||
title: 'Icon Background',
|
||||
subtitle: 'Applies a background color to the icon section of the button.\nRequires \'split\' button styling.',
|
||||
type: 'color'
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
72
customModules/updates/index.ts
Normal file
72
customModules/updates/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import options from "options";
|
||||
import { module } from "../module"
|
||||
|
||||
import { inputHandler } from "customModules/utils";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import Button from "types/widgets/button";
|
||||
import { Variable as VariableType } from "types/variable";
|
||||
import { pollVariableBash } from "customModules/PollVar";
|
||||
|
||||
const {
|
||||
updateCommand,
|
||||
label,
|
||||
padZero,
|
||||
pollingInterval,
|
||||
icon,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
} = options.bar.customModules.updates;
|
||||
|
||||
const pendingUpdates: VariableType<string> = Variable(" 0");
|
||||
|
||||
const processUpdateCount = (updateCount: string) => {
|
||||
if (!padZero.value) return updateCount;
|
||||
return `${updateCount.padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
pollVariableBash(
|
||||
pendingUpdates,
|
||||
[padZero.bind('value')],
|
||||
pollingInterval.bind('value'),
|
||||
`bash -c "${updateCommand.value}"`,
|
||||
processUpdateCount,
|
||||
);
|
||||
|
||||
export const Updates = () => {
|
||||
const updatesModule = module({
|
||||
textIcon: icon.bind("value"),
|
||||
tooltipText: pendingUpdates.bind("value").as(v => `${v} updates available`),
|
||||
boxClass: "updates",
|
||||
label: pendingUpdates.bind("value"),
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return updatesModule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
246
customModules/utils.ts
Normal file
246
customModules/utils.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import { ResourceLabelType } from 'lib/types/bar';
|
||||
import { GenericResourceData } from 'lib/types/customModules/generic';
|
||||
import { Binding } from 'lib/utils';
|
||||
import { openMenu } from 'modules/bar/utils';
|
||||
import options from 'options';
|
||||
import Gdk from 'types/@girs/gdk-3.0/gdk-3.0';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import Button from 'types/widgets/button';
|
||||
|
||||
const { scrollSpeed } = options.bar.customModules;
|
||||
|
||||
export const runAsyncCommand = (
|
||||
cmd: string,
|
||||
fn: Function,
|
||||
events: { clicked: any; event: Gdk.Event }
|
||||
): void => {
|
||||
if (cmd.startsWith('menu:')) {
|
||||
// if the command starts with 'menu:', then it is a menu command
|
||||
// and we should App.toggleMenu("menuName") based on the input menu:menuName. Ignoring spaces and case
|
||||
const menuName = cmd.split(':')[1].trim().toLowerCase();
|
||||
|
||||
openMenu(events.clicked, events.event, `${menuName}menu`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.execAsync(`bash -c "${cmd}"`)
|
||||
.then((output) => {
|
||||
if (fn !== undefined) {
|
||||
fn(output);
|
||||
}
|
||||
})
|
||||
.catch((err) =>
|
||||
console.error(`Error running command "${cmd}": ${err})`)
|
||||
);
|
||||
};
|
||||
|
||||
export function throttle<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
limit: number
|
||||
): T {
|
||||
let inThrottle: boolean;
|
||||
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
||||
if (!inThrottle) {
|
||||
func.apply(this, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => {
|
||||
inThrottle = false;
|
||||
}, limit);
|
||||
}
|
||||
} as T;
|
||||
}
|
||||
|
||||
export const throttledScrollHandler = (interval: number) =>
|
||||
throttle((cmd: string, fn: Function | undefined) => {
|
||||
Utils.execAsync(`bash -c "${cmd}"`)
|
||||
.then((output) => {
|
||||
if (fn !== undefined) {
|
||||
fn(output);
|
||||
}
|
||||
})
|
||||
.catch((err) =>
|
||||
console.error(`Error running command "${cmd}": ${err}`)
|
||||
);
|
||||
}, 200 / interval);
|
||||
|
||||
const dummyVar = Variable('');
|
||||
|
||||
export const inputHandler = (
|
||||
self: Button<Gtk.Widget, Gtk.Widget>,
|
||||
{
|
||||
onPrimaryClick,
|
||||
onSecondaryClick,
|
||||
onMiddleClick,
|
||||
onScrollUp,
|
||||
onScrollDown,
|
||||
}
|
||||
) => {
|
||||
const sanitizeInput = (input: VariableType<string>): string => {
|
||||
if (input === undefined) {
|
||||
return '';
|
||||
}
|
||||
return input.value;
|
||||
};
|
||||
|
||||
const updateHandlers = (): void => {
|
||||
const interval = scrollSpeed.value;
|
||||
const throttledHandler = throttledScrollHandler(interval);
|
||||
|
||||
self.on_primary_click = (clicked: any, event: Gdk.Event) =>
|
||||
runAsyncCommand(
|
||||
sanitizeInput(onPrimaryClick?.cmd || dummyVar),
|
||||
onPrimaryClick.fn,
|
||||
{ clicked, event }
|
||||
);
|
||||
|
||||
self.on_secondary_click = (clicked: any, event: Gdk.Event) =>
|
||||
runAsyncCommand(
|
||||
sanitizeInput(onSecondaryClick?.cmd || dummyVar),
|
||||
onSecondaryClick.fn,
|
||||
{ clicked, event }
|
||||
);
|
||||
|
||||
self.on_middle_click = (clicked: any, event: Gdk.Event) =>
|
||||
runAsyncCommand(
|
||||
sanitizeInput(onMiddleClick?.cmd || dummyVar),
|
||||
onMiddleClick.fn,
|
||||
{ clicked, event }
|
||||
);
|
||||
|
||||
self.on_scroll_up = () =>
|
||||
throttledHandler(
|
||||
sanitizeInput(onScrollUp?.cmd || dummyVar),
|
||||
onScrollUp.fn
|
||||
);
|
||||
|
||||
self.on_scroll_down = () =>
|
||||
throttledHandler(
|
||||
sanitizeInput(onScrollDown?.cmd || dummyVar),
|
||||
onScrollDown.fn
|
||||
);
|
||||
};
|
||||
|
||||
// Initial setup of event handlers
|
||||
updateHandlers();
|
||||
|
||||
const sanitizeVariable = (
|
||||
someVar: VariableType<string> | undefined
|
||||
): Binding<string> => {
|
||||
if (someVar === undefined || typeof someVar.bind !== 'function') {
|
||||
return dummyVar.bind('value');
|
||||
}
|
||||
return someVar.bind('value');
|
||||
};
|
||||
|
||||
// Re-run the update whenever scrollSpeed changes
|
||||
Utils.merge(
|
||||
[
|
||||
scrollSpeed.bind('value'),
|
||||
sanitizeVariable(onPrimaryClick),
|
||||
sanitizeVariable(onSecondaryClick),
|
||||
sanitizeVariable(onMiddleClick),
|
||||
sanitizeVariable(onScrollUp),
|
||||
sanitizeVariable(onScrollDown),
|
||||
],
|
||||
updateHandlers
|
||||
);
|
||||
};
|
||||
|
||||
export const divide = ([total, used]: number[], round: boolean) => {
|
||||
const percentageTotal = (used / total) * 100;
|
||||
if (round) {
|
||||
return total > 0 ? Math.round(percentageTotal) : 0;
|
||||
}
|
||||
return total > 0 ? parseFloat(percentageTotal.toFixed(2)) : 0;
|
||||
|
||||
};
|
||||
|
||||
export const formatSizeInKiB = (sizeInBytes: number, round: boolean) => {
|
||||
const sizeInGiB = sizeInBytes / (1024 ** 1);
|
||||
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
|
||||
};
|
||||
export const formatSizeInMiB = (sizeInBytes: number, round: boolean) => {
|
||||
const sizeInGiB = sizeInBytes / (1024 ** 2);
|
||||
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
|
||||
};
|
||||
export const formatSizeInGiB = (sizeInBytes: number, round: boolean) => {
|
||||
const sizeInGiB = sizeInBytes / (1024 ** 3);
|
||||
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
|
||||
};
|
||||
export const formatSizeInTiB = (sizeInBytes: number, round: boolean) => {
|
||||
const sizeInGiB = sizeInBytes / (1024 ** 4);
|
||||
return round ? Math.round(sizeInGiB) : parseFloat(sizeInGiB.toFixed(2));
|
||||
};
|
||||
|
||||
export const autoFormatSize = (sizeInBytes: number, round: boolean) => {
|
||||
// auto convert to GiB, MiB, KiB, TiB, or bytes
|
||||
if (sizeInBytes >= 1024 ** 4) return formatSizeInTiB(sizeInBytes, round);
|
||||
if (sizeInBytes >= 1024 ** 3) return formatSizeInGiB(sizeInBytes, round);
|
||||
if (sizeInBytes >= 1024 ** 2) return formatSizeInMiB(sizeInBytes, round);
|
||||
if (sizeInBytes >= 1024 ** 1) return formatSizeInKiB(sizeInBytes, round);
|
||||
|
||||
return sizeInBytes;
|
||||
}
|
||||
|
||||
export const getPostfix = (sizeInBytes: number) => {
|
||||
if (sizeInBytes >= 1024 ** 4) return 'TiB';
|
||||
if (sizeInBytes >= 1024 ** 3) return 'GiB';
|
||||
if (sizeInBytes >= 1024 ** 2) return 'MiB';
|
||||
if (sizeInBytes >= 1024 ** 1) return 'KiB';
|
||||
|
||||
return 'B';
|
||||
}
|
||||
|
||||
export const renderResourceLabel = (
|
||||
lblType: ResourceLabelType,
|
||||
rmUsg: GenericResourceData,
|
||||
round: boolean
|
||||
) => {
|
||||
const { used, total, percentage, free } = rmUsg;
|
||||
|
||||
const formatFunctions = {
|
||||
TiB: formatSizeInTiB,
|
||||
GiB: formatSizeInGiB,
|
||||
MiB: formatSizeInMiB,
|
||||
KiB: formatSizeInKiB,
|
||||
B: (size: number, _: boolean) => size
|
||||
};
|
||||
|
||||
// Get them datas in proper GiB, MiB, KiB, TiB, or bytes
|
||||
const totalSizeFormatted = autoFormatSize(total, round);
|
||||
// get the postfix: one of [TiB, GiB, MiB, KiB, B]
|
||||
const postfix = getPostfix(total);
|
||||
|
||||
// Determine which format function to use
|
||||
const formatUsed = formatFunctions[postfix] || formatFunctions['B'];
|
||||
const usedSizeFormatted = formatUsed(used, round);
|
||||
|
||||
if (lblType === "used/total") {
|
||||
return `${usedSizeFormatted}/${totalSizeFormatted} ${postfix}`;
|
||||
}
|
||||
if (lblType === "used") {
|
||||
return `${autoFormatSize(used, round)} ${getPostfix(used)}`;
|
||||
}
|
||||
if (lblType === "free") {
|
||||
return `${autoFormatSize(free, round)} ${getPostfix(free)}`;
|
||||
}
|
||||
|
||||
return `${percentage}%`;
|
||||
};
|
||||
|
||||
export const formatTooltip = (dataType: string, lblTyp: ResourceLabelType) => {
|
||||
switch (lblTyp) {
|
||||
case 'used':
|
||||
return `Used ${dataType}`;
|
||||
case 'free':
|
||||
return `Free ${dataType}`;
|
||||
case 'used/total':
|
||||
return `Used/Total ${dataType}`;
|
||||
case 'percentage':
|
||||
return `Percentage ${dataType} Usage`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
67
customModules/weather/index.ts
Normal file
67
customModules/weather/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import options from "options";
|
||||
import { module } from "../module"
|
||||
|
||||
import { inputHandler } from "customModules/utils";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0";
|
||||
import Button from "types/widgets/button";
|
||||
import { getWeatherStatusIcon, globalWeatherVar } from "globals/weather";
|
||||
|
||||
const {
|
||||
label,
|
||||
unit,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
} = options.bar.customModules.weather;
|
||||
|
||||
export const Weather = () => {
|
||||
|
||||
const networkModule = module({
|
||||
icon: Utils.merge([globalWeatherVar.bind("value")], (wthr) => {
|
||||
const weatherStatusIcon = getWeatherStatusIcon(wthr);
|
||||
return weatherStatusIcon;
|
||||
}),
|
||||
tooltipText: globalWeatherVar.bind("value").as(v => `Weather Status: ${v.current.condition.text}`),
|
||||
boxClass: "weather-custom",
|
||||
label: Utils.merge(
|
||||
[globalWeatherVar.bind("value"), unit.bind("value")],
|
||||
(wthr, unt) => {
|
||||
if (unt === "imperial") {
|
||||
return `${Math.ceil(wthr.current.temp_f)}° F`;
|
||||
} else {
|
||||
return `${Math.ceil(wthr.current.temp_c)}° C`;
|
||||
}
|
||||
},
|
||||
),
|
||||
showLabelBinding: label.bind("value"),
|
||||
props: {
|
||||
setup: (self: Button<Gtk.Widget, Gtk.Widget>) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return networkModule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user