Implemented strict linting standards and prettier formatting config. (#248)
* Implemented strict linting standards and prettier formatting config. * More linter fixes and type updates. * More linter updates and type fixes * Remove noisy comments * Linter and type updates * Linter, formatting and type updates. * Linter updates * Type updates * Type updates * fixed all linter errors * Fixed all linting, formatting and type issues. * Resolve merge conflicts.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
const hyprland = await Service.import('hyprland');
|
||||
|
||||
import {
|
||||
Menu,
|
||||
Workspaces, ClientTitle, Media,
|
||||
Workspaces,
|
||||
ClientTitle,
|
||||
Media,
|
||||
Notifications,
|
||||
Volume,
|
||||
Network,
|
||||
@@ -20,54 +22,57 @@ import {
|
||||
Updates,
|
||||
Weather,
|
||||
Power,
|
||||
} from "./Exports"
|
||||
} from './Exports';
|
||||
|
||||
import { BarItemBox as WidgetContainer } from "../shared/barItemBox.js";
|
||||
import options from "options";
|
||||
import Gdk from "gi://Gdk?version=3.0";
|
||||
import Button from "types/widgets/button.js";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0.js";
|
||||
import { BarItemBox as WidgetContainer } from '../shared/barItemBox.js';
|
||||
import options from 'options';
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js';
|
||||
|
||||
import './SideEffects';
|
||||
import { WindowLayer } from "lib/types/options.js";
|
||||
import { WindowLayer } from 'lib/types/options.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
|
||||
const { layouts } = options.bar;
|
||||
|
||||
export type BarWidget = keyof typeof widget;
|
||||
|
||||
type Section = "battery"
|
||||
| "dashboard"
|
||||
| "workspaces"
|
||||
| "windowtitle"
|
||||
| "media"
|
||||
| "notifications"
|
||||
| "volume"
|
||||
| "network"
|
||||
| "bluetooth"
|
||||
| "clock"
|
||||
| "ram"
|
||||
| "cpu"
|
||||
| "storage"
|
||||
| "netstat"
|
||||
| "kbinput"
|
||||
| "updates"
|
||||
| "weather"
|
||||
| "power"
|
||||
| "systray";
|
||||
type Section =
|
||||
| 'battery'
|
||||
| 'dashboard'
|
||||
| 'workspaces'
|
||||
| 'windowtitle'
|
||||
| 'media'
|
||||
| 'notifications'
|
||||
| 'volume'
|
||||
| 'network'
|
||||
| 'bluetooth'
|
||||
| 'clock'
|
||||
| 'ram'
|
||||
| 'cpu'
|
||||
| 'storage'
|
||||
| 'netstat'
|
||||
| 'kbinput'
|
||||
| 'updates'
|
||||
| 'weather'
|
||||
| 'power'
|
||||
| 'systray';
|
||||
|
||||
type Layout = {
|
||||
left: Section[],
|
||||
middle: Section[],
|
||||
right: Section[],
|
||||
}
|
||||
left: Section[];
|
||||
middle: Section[];
|
||||
right: Section[];
|
||||
};
|
||||
|
||||
type BarLayout = {
|
||||
[key: string]: Layout
|
||||
}
|
||||
[key: string]: Layout;
|
||||
};
|
||||
|
||||
const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
|
||||
const matchingKey = Object.keys(layouts).find(key => key === monitor.toString());
|
||||
const wildcard = Object.keys(layouts).find(key => key === "*");
|
||||
const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString());
|
||||
const wildcard = Object.keys(layouts).find((key) => key === '*');
|
||||
|
||||
if (matchingKey) {
|
||||
return layouts[matchingKey];
|
||||
@@ -77,62 +82,48 @@ const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
|
||||
return layouts[wildcard];
|
||||
}
|
||||
|
||||
return {
|
||||
left: [
|
||||
"dashboard",
|
||||
"workspaces",
|
||||
"windowtitle"
|
||||
],
|
||||
middle: [
|
||||
"media"
|
||||
],
|
||||
right: [
|
||||
"volume",
|
||||
"network",
|
||||
"bluetooth",
|
||||
"battery",
|
||||
"systray",
|
||||
"clock",
|
||||
"notifications"
|
||||
]
|
||||
return {
|
||||
left: ['dashboard', 'workspaces', 'windowtitle'],
|
||||
middle: ['media'],
|
||||
right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const widget = {
|
||||
battery: () => WidgetContainer(BatteryLabel()),
|
||||
dashboard: () => WidgetContainer(Menu()),
|
||||
workspaces: (monitor: number) => WidgetContainer(Workspaces(monitor)),
|
||||
windowtitle: () => WidgetContainer(ClientTitle()),
|
||||
media: () => WidgetContainer(Media()),
|
||||
notifications: () => WidgetContainer(Notifications()),
|
||||
volume: () => WidgetContainer(Volume()),
|
||||
network: () => WidgetContainer(Network()),
|
||||
bluetooth: () => WidgetContainer(Bluetooth()),
|
||||
clock: () => WidgetContainer(Clock()),
|
||||
systray: () => WidgetContainer(SysTray()),
|
||||
ram: () => WidgetContainer(Ram()),
|
||||
cpu: () => WidgetContainer(Cpu()),
|
||||
storage: () => WidgetContainer(Storage()),
|
||||
netstat: () => WidgetContainer(Netstat()),
|
||||
kbinput: () => WidgetContainer(KbInput()),
|
||||
updates: () => WidgetContainer(Updates()),
|
||||
weather: () => WidgetContainer(Weather()),
|
||||
power: () => WidgetContainer(Power()),
|
||||
battery: (): Button<Child, Attribute> => WidgetContainer(BatteryLabel()),
|
||||
dashboard: (): Button<Child, Attribute> => WidgetContainer(Menu()),
|
||||
workspaces: (monitor: number): Button<Child, Attribute> => WidgetContainer(Workspaces(monitor)),
|
||||
windowtitle: (): Button<Child, Attribute> => WidgetContainer(ClientTitle()),
|
||||
media: (): Button<Child, Attribute> => WidgetContainer(Media()),
|
||||
notifications: (): Button<Child, Attribute> => WidgetContainer(Notifications()),
|
||||
volume: (): Button<Child, Attribute> => WidgetContainer(Volume()),
|
||||
network: (): Button<Child, Attribute> => WidgetContainer(Network()),
|
||||
bluetooth: (): Button<Child, Attribute> => WidgetContainer(Bluetooth()),
|
||||
clock: (): Button<Child, Attribute> => WidgetContainer(Clock()),
|
||||
systray: (): Button<Child, Attribute> => WidgetContainer(SysTray()),
|
||||
ram: (): Button<Child, Attribute> => WidgetContainer(Ram()),
|
||||
cpu: (): Button<Child, Attribute> => WidgetContainer(Cpu()),
|
||||
storage: (): Button<Child, Attribute> => WidgetContainer(Storage()),
|
||||
netstat: (): Button<Child, Attribute> => WidgetContainer(Netstat()),
|
||||
kbinput: (): Button<Child, Attribute> => WidgetContainer(KbInput()),
|
||||
updates: (): Button<Child, Attribute> => WidgetContainer(Updates()),
|
||||
weather: (): Button<Child, Attribute> => WidgetContainer(Weather()),
|
||||
power: (): Button<Child, Attribute> => WidgetContainer(Power()),
|
||||
};
|
||||
|
||||
type GdkMonitors = {
|
||||
[key: string]: {
|
||||
key: string,
|
||||
model: string,
|
||||
used: boolean
|
||||
}
|
||||
key: string;
|
||||
model: string;
|
||||
used: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
function getGdkMonitors(): GdkMonitors {
|
||||
const display = Gdk.Display.get_default();
|
||||
|
||||
if (display === null) {
|
||||
console.error("Failed to get Gdk display.");
|
||||
console.error('Failed to get Gdk display.');
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -162,35 +153,35 @@ function getGdkMonitors(): GdkMonitors {
|
||||
* NOTE: Some more funky stuff being done by GDK.
|
||||
* We render windows/bar based on the monitor ID. So if you have 3 monitors, then your
|
||||
* monitor IDs will be [0, 1, 2]. Hyprland will NEVER change what ID belongs to what monitor.
|
||||
*
|
||||
*
|
||||
* So if hyprland determines id 0 = DP-1, even after you unplug, shut off or restart your monitor,
|
||||
* the id 0 will ALWAYS be DP-1.
|
||||
*
|
||||
*
|
||||
* However, GDK (the righteous genius that it is) will change the order of ID anytime your monitor
|
||||
* setup is changed. So if you unplug your monitor and plug it back it, it now becomes the last id.
|
||||
* So if DP-1 was id 0 and you unplugged it, it will reconfigure to id 2. This sucks because now
|
||||
* there's a mismtach between what GDK determines the monitor is at id 2 and what Hyprland determines
|
||||
* is at id 2.
|
||||
*
|
||||
*
|
||||
* So for that reason, we need to redirect the input `monitor` that the Bar module takes in, to the
|
||||
* proper Hyprland monitor. So when monitor id 0 comes in, we need to find what the id of that monitor
|
||||
* is being determined as by Hyprland so the bars show up on the right monitors.
|
||||
*
|
||||
*
|
||||
* Since GTK3 doesn't contain connection names and only monitor models, we have to make the best guess
|
||||
* in the case that there are multiple models in the same resolution with the same scale. We find the
|
||||
* 'right' monitor by checking if the model matches along with the resolution and scale. If monitor at
|
||||
* ID 0 for GDK is being reported as 'MSI MAG271CQR' we find the same model in the Hyprland monitor list
|
||||
* and check if the resolution and scaling is the same... if it is then we determine it's a match.
|
||||
*
|
||||
*
|
||||
* The edge-case that we just can't handle is if you have the same monitors in the same resolution at the same
|
||||
* scale. So if you've got 2 'MSI MAG271CQR' monitors at 2560x1440 at scale 1, then we just match the first
|
||||
* monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration.
|
||||
* You may have the bar showing up on the wrong one in this case because we don't know what the connector id
|
||||
* monitor in the list as the first match and then the second 'MSI MAG271CQR' as a match in the 2nd iteration.
|
||||
* You may have the bar showing up on the wrong one in this case because we don't know what the connector id
|
||||
* is of either of these monitors (DP-1, DP-2) which are unique values - as these are only in GTK4.
|
||||
*
|
||||
*
|
||||
* Keep in mind though, this is ONLY an issue if you change your monitor setup by plugging in a new one, restarting
|
||||
* an existing one or shutting it off.
|
||||
*
|
||||
*
|
||||
* If your monitors aren't changed in the current session you're in then none of this safeguarding is relevant.
|
||||
*
|
||||
* Fun stuff really... :facepalm:
|
||||
@@ -200,7 +191,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
|
||||
const gdkMonitors = getGdkMonitors();
|
||||
|
||||
if (Object.keys(gdkMonitors).length === 0) {
|
||||
console.error("No GDK monitors were found.");
|
||||
console.error('No GDK monitors were found.');
|
||||
return monitor;
|
||||
}
|
||||
|
||||
@@ -208,7 +199,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
|
||||
const gdkMonitor = gdkMonitors[monitor];
|
||||
|
||||
// First pass: Strict matching including the monitor index (i.e., hypMon.id === monitor + resolution+scale criteria)
|
||||
const directMatch = hyprland.monitors.find(hypMon => {
|
||||
const directMatch = hyprland.monitors.find((hypMon) => {
|
||||
const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`;
|
||||
return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id) && hypMon.id === monitor;
|
||||
});
|
||||
@@ -219,7 +210,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
|
||||
}
|
||||
|
||||
// Second pass: Relaxed matching without considering the monitor index
|
||||
const hyprlandMonitor = hyprland.monitors.find(hypMon => {
|
||||
const hyprlandMonitor = hyprland.monitors.find((hypMon) => {
|
||||
const hyprlandKey = `${hypMon.model}_${hypMon.width}x${hypMon.height}_${hypMon.scale}`;
|
||||
return gdkMonitor.key.startsWith(hyprlandKey) && !usedHyprlandMonitors.has(hypMon.id);
|
||||
});
|
||||
@@ -230,7 +221,7 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
|
||||
}
|
||||
|
||||
// Fallback: Find the first available monitor ID that hasn't been used
|
||||
const fallbackMonitor = hyprland.monitors.find(hypMon => !usedHyprlandMonitors.has(hypMon.id));
|
||||
const fallbackMonitor = hyprland.monitors.find((hypMon) => !usedHyprlandMonitors.has(hypMon.id));
|
||||
|
||||
if (fallbackMonitor) {
|
||||
usedHyprlandMonitors.add(fallbackMonitor.id);
|
||||
@@ -253,62 +244,63 @@ const gdkMonitorIdToHyprlandId = (monitor: number, usedHyprlandMonitors: Set<num
|
||||
export const Bar = (() => {
|
||||
const usedHyprlandMonitors = new Set<number>();
|
||||
|
||||
return (monitor: number) => {
|
||||
return (monitor: number): Window<Child, Attribute> => {
|
||||
const hyprlandMonitor = gdkMonitorIdToHyprlandId(monitor, usedHyprlandMonitors);
|
||||
|
||||
return Widget.Window({
|
||||
name: `bar-${hyprlandMonitor}`,
|
||||
class_name: "bar",
|
||||
class_name: 'bar',
|
||||
monitor,
|
||||
visible: true,
|
||||
anchor: ["top", "left", "right"],
|
||||
exclusivity: "exclusive",
|
||||
anchor: ['top', 'left', 'right'],
|
||||
exclusivity: 'exclusive',
|
||||
layer: Utils.merge(
|
||||
[
|
||||
options.theme.bar.layer.bind("value"),
|
||||
options.tear.bind("value")
|
||||
],
|
||||
(
|
||||
barLayer: WindowLayer,
|
||||
tear: boolean
|
||||
) => {
|
||||
if (tear && barLayer === "overlay") {
|
||||
return "top";
|
||||
[options.theme.bar.layer.bind('value'), options.tear.bind('value')],
|
||||
(barLayer: WindowLayer, tear: boolean) => {
|
||||
if (tear && barLayer === 'overlay') {
|
||||
return 'top';
|
||||
}
|
||||
return barLayer;
|
||||
}),
|
||||
},
|
||||
),
|
||||
child: Widget.Box({
|
||||
class_name: 'bar-panel-container',
|
||||
child: Widget.CenterBox({
|
||||
class_name: 'bar-panel',
|
||||
css: 'padding: 1px',
|
||||
startWidget: Widget.Box({
|
||||
class_name: "box-left",
|
||||
class_name: 'box-left',
|
||||
hexpand: true,
|
||||
setup: self => {
|
||||
setup: (self) => {
|
||||
self.hook(layouts, (self) => {
|
||||
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
|
||||
self.children = foundLayout.left.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
self.children = foundLayout.left
|
||||
.filter((mod) => Object.keys(widget).includes(mod))
|
||||
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
});
|
||||
},
|
||||
}),
|
||||
centerWidget: Widget.Box({
|
||||
class_name: "box-center",
|
||||
hpack: "center",
|
||||
setup: self => {
|
||||
class_name: 'box-center',
|
||||
hpack: 'center',
|
||||
setup: (self) => {
|
||||
self.hook(layouts, (self) => {
|
||||
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
|
||||
self.children = foundLayout.middle.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
self.children = foundLayout.middle
|
||||
.filter((mod) => Object.keys(widget).includes(mod))
|
||||
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
});
|
||||
},
|
||||
}),
|
||||
endWidget: Widget.Box({
|
||||
class_name: "box-right",
|
||||
hpack: "end",
|
||||
setup: self => {
|
||||
class_name: 'box-right',
|
||||
hpack: 'end',
|
||||
setup: (self) => {
|
||||
self.hook(layouts, (self) => {
|
||||
const foundLayout = getLayoutForMonitor(hyprlandMonitor, layouts.value as BarLayout);
|
||||
self.children = foundLayout.right.filter(mod => Object.keys(widget).includes(mod)).map(w => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
self.children = foundLayout.right
|
||||
.filter((mod) => Object.keys(widget).includes(mod))
|
||||
.map((w) => widget[w](hyprlandMonitor) as Button<Gtk.Widget, unknown>);
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { Menu } from "./menu/index";
|
||||
import { Workspaces } from "./workspaces/index";
|
||||
import { ClientTitle } from "./window_title/index";
|
||||
import { Media } from "./media/index";
|
||||
import { Notifications } from "./notifications/index";
|
||||
import { Volume } from "./volume/index";
|
||||
import { Network } from "./network/index";
|
||||
import { Bluetooth } from "./bluetooth/index";
|
||||
import { BatteryLabel } from "./battery/index";
|
||||
import { Clock } from "./clock/index";
|
||||
import { SysTray } from "./systray/index";
|
||||
import { Menu } from './menu/index';
|
||||
import { Workspaces } from './workspaces/index';
|
||||
import { ClientTitle } from './window_title/index';
|
||||
import { Media } from './media/index';
|
||||
import { Notifications } from './notifications/index';
|
||||
import { Volume } from './volume/index';
|
||||
import { Network } from './network/index';
|
||||
import { Bluetooth } from './bluetooth/index';
|
||||
import { BatteryLabel } from './battery/index';
|
||||
import { Clock } from './clock/index';
|
||||
import { SysTray } from './systray/index';
|
||||
|
||||
// Custom Modules
|
||||
import { Ram } from "../../customModules/ram/index";
|
||||
import { Cpu } from "../../customModules/cpu/index";
|
||||
import { Storage } from "customModules/storage/index";
|
||||
import { Netstat } from "customModules/netstat/index";
|
||||
import { KbInput } from "customModules/kblayout/index";
|
||||
import { Updates } from "customModules/updates/index";
|
||||
import { Weather } from "customModules/weather/index";
|
||||
import { Power } from "customModules/power/index";
|
||||
import { Ram } from '../../customModules/ram/index';
|
||||
import { Cpu } from '../../customModules/cpu/index';
|
||||
import { Storage } from 'customModules/storage/index';
|
||||
import { Netstat } from 'customModules/netstat/index';
|
||||
import { KbInput } from 'customModules/kblayout/index';
|
||||
import { Updates } from 'customModules/updates/index';
|
||||
import { Weather } from 'customModules/weather/index';
|
||||
import { Power } from 'customModules/power/index';
|
||||
|
||||
export {
|
||||
Menu,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import options from "options";
|
||||
import options from 'options';
|
||||
|
||||
const { showIcon, showTime } = options.bar.clock;
|
||||
|
||||
showIcon.connect("changed", () => {
|
||||
showIcon.connect('changed', () => {
|
||||
if (!showTime.value && !showIcon.value) {
|
||||
showTime.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
showTime.connect("changed", () => {
|
||||
showTime.connect('changed', () => {
|
||||
if (!showTime.value && !showIcon.value) {
|
||||
showIcon.value = true;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
const battery = await Service.import("battery");
|
||||
const battery = await Service.import('battery');
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const { label: show_label } = options.bar.battery;
|
||||
|
||||
const BatteryLabel = () => {
|
||||
const BatteryLabel = (): BarBoxChild => {
|
||||
const isVis = Variable(battery.available);
|
||||
|
||||
const batIcon = Utils.merge([battery.bind("percent"), battery.bind("charging"), battery.bind("charged")],
|
||||
const batIcon = Utils.merge(
|
||||
[battery.bind('percent'), battery.bind('charging'), battery.bind('charged')],
|
||||
(batPercent: number, batCharging, batCharged) => {
|
||||
if (batCharged)
|
||||
return `battery-level-100-charged-symbolic`;
|
||||
else
|
||||
return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`;
|
||||
});
|
||||
if (batCharged) return `battery-level-100-charged-symbolic`;
|
||||
else return `battery-level-${Math.floor(batPercent / 10) * 10}${batCharging ? '-charging' : ''}-symbolic`;
|
||||
},
|
||||
);
|
||||
|
||||
battery.connect("changed", ({ available }) => {
|
||||
battery.connect('changed', ({ available }) => {
|
||||
isVis.value = available;
|
||||
});
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
const formatTime = (seconds: number): Record<string, number> => {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
return { hours, minutes };
|
||||
};
|
||||
|
||||
const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean) => {
|
||||
const generateTooltip = (timeSeconds: number, isCharging: boolean, isCharged: boolean): string => {
|
||||
if (isCharged) {
|
||||
return "Fully Charged!!!";
|
||||
return 'Fully Charged!!!';
|
||||
}
|
||||
|
||||
const { hours, minutes } = formatTime(timeSeconds);
|
||||
@@ -41,60 +44,56 @@ const BatteryLabel = () => {
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
return `battery ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
|
||||
}),
|
||||
visible: battery.bind("available"),
|
||||
tooltip_text: battery.bind("time_remaining").as((t) => t.toString()),
|
||||
children: Utils.merge(
|
||||
[battery.bind("available"), show_label.bind("value")],
|
||||
(batAvail, showLabel) => {
|
||||
if (batAvail && showLabel) {
|
||||
return [
|
||||
Widget.Icon({
|
||||
class_name: "bar-button-icon battery",
|
||||
icon: batIcon
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "bar-button-label battery",
|
||||
label: battery.bind("percent").as((p) => `${Math.floor(p)}%`),
|
||||
}),
|
||||
];
|
||||
} else if (batAvail && !showLabel) {
|
||||
return [
|
||||
Widget.Icon({
|
||||
class_name: "bar-button-icon battery",
|
||||
icon: batIcon
|
||||
})
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), show_label.bind('value')],
|
||||
(style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `battery ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
visible: battery.bind('available'),
|
||||
tooltip_text: battery.bind('time_remaining').as((t) => t.toString()),
|
||||
children: Utils.merge([battery.bind('available'), show_label.bind('value')], (batAvail, showLabel) => {
|
||||
if (batAvail && showLabel) {
|
||||
return [
|
||||
Widget.Icon({
|
||||
class_name: 'bar-button-icon battery',
|
||||
icon: batIcon,
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: 'bar-button-label battery',
|
||||
label: battery.bind('percent').as((p) => `${Math.floor(p)}%`),
|
||||
}),
|
||||
];
|
||||
} else if (batAvail && !showLabel) {
|
||||
return [
|
||||
Widget.Icon({
|
||||
class_name: 'bar-button-icon battery',
|
||||
icon: batIcon,
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
setup: (self) => {
|
||||
self.hook(battery, () => {
|
||||
if (battery.available) {
|
||||
self.tooltip_text = generateTooltip(
|
||||
battery.time_remaining,
|
||||
battery.charging,
|
||||
battery.charged,
|
||||
);
|
||||
self.tooltip_text = generateTooltip(battery.time_remaining, battery.charging, battery.charged);
|
||||
}
|
||||
});
|
||||
},
|
||||
}),
|
||||
isVis,
|
||||
boxClass: "battery",
|
||||
boxClass: 'battery',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "energymenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'energymenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
const bluetooth = await Service.import('bluetooth')
|
||||
const bluetooth = await Service.import('bluetooth');
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import options from "options";
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from 'options';
|
||||
import { openMenu } from '../utils.js';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const { label } = options.bar.bluetooth;
|
||||
|
||||
const Bluetooth = () => {
|
||||
const Bluetooth = (): BarBoxChild => {
|
||||
const btIcon = Widget.Label({
|
||||
label: bluetooth.bind("enabled").as((v) => v ? "" : ""),
|
||||
class_name: "bar-button-icon bluetooth txt-icon bar",
|
||||
label: bluetooth.bind('enabled').as((v) => (v ? '' : '')),
|
||||
class_name: 'bar-button-icon bluetooth txt-icon bar',
|
||||
});
|
||||
|
||||
const btText = Widget.Label({
|
||||
label: Utils.merge([
|
||||
bluetooth.bind("enabled"),
|
||||
bluetooth.bind("connected_devices"),
|
||||
],
|
||||
(btEnabled, btDevices) => {
|
||||
return btEnabled && btDevices.length ? ` Connected (${btDevices.length})`
|
||||
: btEnabled ? "On"
|
||||
: "Off"
|
||||
|
||||
}),
|
||||
class_name: "bar-button-label bluetooth",
|
||||
label: Utils.merge([bluetooth.bind('enabled'), bluetooth.bind('connected_devices')], (btEnabled, btDevices) => {
|
||||
return btEnabled && btDevices.length ? ` Connected (${btDevices.length})` : btEnabled ? 'On' : 'Off';
|
||||
}),
|
||||
class_name: 'bar-button-label bluetooth',
|
||||
});
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), label.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
return `bluetooth ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
|
||||
}),
|
||||
children: options.bar.bluetooth.label.bind("value").as((showLabel) => {
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), label.bind('value')],
|
||||
(style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `bluetooth ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
children: options.bar.bluetooth.label.bind('value').as((showLabel) => {
|
||||
if (showLabel) {
|
||||
return [btIcon, btText];
|
||||
}
|
||||
@@ -44,14 +43,13 @@ const Bluetooth = () => {
|
||||
}),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "bluetooth",
|
||||
boxClass: 'bluetooth',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "bluetoothmenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'bluetoothmenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export { Bluetooth }
|
||||
export { Bluetooth };
|
||||
|
||||
@@ -1,45 +1,46 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import GLib from "gi://GLib";
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
import GLib from 'gi://GLib';
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { DateTime } from 'types/@girs/glib-2.0/glib-2.0.cjs';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
const { format, icon, showIcon, showTime } = options.bar.clock;
|
||||
const { style } = options.theme.bar.buttons;
|
||||
|
||||
|
||||
const date = Variable(GLib.DateTime.new_now_local(), {
|
||||
poll: [1000, () => GLib.DateTime.new_now_local()],
|
||||
poll: [1000, (): DateTime => GLib.DateTime.new_now_local()],
|
||||
});
|
||||
const time = Utils.derive([date, format], (c, f) => c.format(f) || "");
|
||||
|
||||
const Clock = () => {
|
||||
const time = Utils.derive([date, format], (c, f) => c.format(f) || '');
|
||||
|
||||
const Clock = (): BarBoxChild => {
|
||||
const clockTime = Widget.Label({
|
||||
class_name: "bar-button-label clock bar",
|
||||
class_name: 'bar-button-label clock bar',
|
||||
label: time.bind(),
|
||||
});
|
||||
|
||||
const clockIcon = Widget.Label({
|
||||
label: icon.bind("value"),
|
||||
class_name: "bar-button-icon clock txt-icon bar",
|
||||
label: icon.bind('value'),
|
||||
class_name: 'bar-button-icon clock txt-icon bar',
|
||||
});
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([
|
||||
style.bind("value"),
|
||||
showIcon.bind("value"), showTime.bind("value")
|
||||
], (btnStyle, shwIcn, shwLbl) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
|
||||
return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? "no-label" : ""} ${!shwIcn ? "no-icon" : ""}`;
|
||||
}),
|
||||
children: Utils.merge([showIcon.bind("value"), showTime.bind("value")], (shIcn, shTm) => {
|
||||
className: Utils.merge(
|
||||
[style.bind('value'), showIcon.bind('value'), showTime.bind('value')],
|
||||
(btnStyle, shwIcn, shwLbl) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
|
||||
return `bluetooth ${styleMap[btnStyle]} ${!shwLbl ? 'no-label' : ''} ${!shwIcn ? 'no-icon' : ''}`;
|
||||
},
|
||||
),
|
||||
children: Utils.merge([showIcon.bind('value'), showTime.bind('value')], (shIcn, shTm) => {
|
||||
if (shIcn && !shTm) {
|
||||
return [clockIcon];
|
||||
} else if (shTm && !shIcn) {
|
||||
@@ -47,13 +48,13 @@ const Clock = () => {
|
||||
}
|
||||
|
||||
return [clockIcon, clockTime];
|
||||
})
|
||||
}),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "clock",
|
||||
boxClass: 'clock',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "calendarmenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'calendarmenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
const mpris = await Service.import("mpris");
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
const mpris = await Service.import('mpris');
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { getCurrentPlayer } from 'lib/shared/media.js';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const { show_artist, truncation, truncation_size, show_label, show_active_only } = options.bar.media;
|
||||
|
||||
const Media = () => {
|
||||
const Media = (): BarBoxChild => {
|
||||
const activePlayer = Variable(mpris.players[0]);
|
||||
const isVis = Variable(!show_active_only.value);
|
||||
|
||||
show_active_only.connect("changed", () => {
|
||||
show_active_only.connect('changed', () => {
|
||||
isVis.value = !show_active_only.value || mpris.players.length > 0;
|
||||
});
|
||||
|
||||
mpris.connect("changed", () => {
|
||||
mpris.connect('changed', () => {
|
||||
const curPlayer = getCurrentPlayer(activePlayer.value);
|
||||
activePlayer.value = curPlayer;
|
||||
isVis.value = !show_active_only.value || mpris.players.length > 0;
|
||||
@@ -22,41 +25,37 @@ const Media = () => {
|
||||
|
||||
const getIconForPlayer = (playerName: string): string => {
|
||||
const windowTitleMap = [
|
||||
["Firefox", ""],
|
||||
["Microsoft Edge", ""],
|
||||
["Discord", ""],
|
||||
["Plex", ""],
|
||||
["Spotify", ""],
|
||||
["(.*)", ""],
|
||||
['Firefox', ''],
|
||||
['Microsoft Edge', ''],
|
||||
['Discord', ''],
|
||||
['Plex', ''],
|
||||
['Spotify', ''],
|
||||
['(.*)', ''],
|
||||
];
|
||||
|
||||
const foundMatch = windowTitleMap.find((wt) =>
|
||||
RegExp(wt[0], "i").test(playerName),
|
||||
);
|
||||
const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0], 'i').test(playerName));
|
||||
|
||||
return foundMatch ? foundMatch[1] : "";
|
||||
return foundMatch ? foundMatch[1] : '';
|
||||
};
|
||||
|
||||
const songIcon = Variable("");
|
||||
const songIcon = Variable('');
|
||||
|
||||
const mediaLabel = Utils.watch("Media", [mpris, show_artist, truncation, truncation_size, show_label], () => {
|
||||
const mediaLabel = Utils.watch('Media', [mpris, show_artist, truncation, truncation_size, show_label], () => {
|
||||
if (activePlayer.value && show_label.value) {
|
||||
const { track_title, identity, track_artists } = activePlayer.value;
|
||||
songIcon.value = getIconForPlayer(identity);
|
||||
const trackArtist = show_artist.value
|
||||
? ` - ${track_artists.join(', ')}`
|
||||
: ``;
|
||||
const trackArtist = show_artist.value ? ` - ${track_artists.join(', ')}` : ``;
|
||||
const truncatedLabel = truncation.value
|
||||
? `${track_title + trackArtist}`.substring(0, truncation_size.value)
|
||||
: `${track_title + trackArtist}`;
|
||||
|
||||
return track_title.length === 0
|
||||
? `No media playing...`
|
||||
: ((truncatedLabel.length < truncation_size.value) || !truncation.value)
|
||||
? `${truncatedLabel}`
|
||||
: `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`;
|
||||
: truncatedLabel.length < truncation_size.value || !truncation.value
|
||||
? `${truncatedLabel}`
|
||||
: `${truncatedLabel.substring(0, truncatedLabel.length - 3)}...`;
|
||||
} else {
|
||||
songIcon.value = getIconForPlayer(activePlayer.value?.identity || "");
|
||||
songIcon.value = getIconForPlayer(activePlayer.value?.identity || '');
|
||||
return `Media`;
|
||||
}
|
||||
});
|
||||
@@ -65,23 +64,26 @@ const Media = () => {
|
||||
component: Widget.Box({
|
||||
visible: false,
|
||||
child: Widget.Box({
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), show_label.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
return `media ${styleMap[style]}`;
|
||||
}),
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), show_label.bind('value')],
|
||||
(style) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `media ${styleMap[style]}`;
|
||||
},
|
||||
),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "bar-button-icon media txt-icon bar",
|
||||
label: songIcon.bind("value").as(v => v || ""),
|
||||
class_name: 'bar-button-icon media txt-icon bar',
|
||||
label: songIcon.bind('value').as((v) => v || ''),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "bar-button-label media",
|
||||
class_name: 'bar-button-label media',
|
||||
label: mediaLabel,
|
||||
}),
|
||||
],
|
||||
@@ -89,13 +91,13 @@ const Media = () => {
|
||||
}),
|
||||
}),
|
||||
isVis,
|
||||
boxClass: "media",
|
||||
name: "media",
|
||||
boxClass: 'media',
|
||||
name: 'media',
|
||||
props: {
|
||||
on_scroll_up: () => activePlayer.value?.next(),
|
||||
on_scroll_down: () => activePlayer.value?.previous(),
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "mediamenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'mediamenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const Menu = () => {
|
||||
const Menu = (): BarBoxChild => {
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value")], (style) => {
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind('value')], (style) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `dashboard ${styleMap[style]}`;
|
||||
}),
|
||||
child: Widget.Label({
|
||||
class_name: "bar-menu_label bar-button_icon txt-icon bar",
|
||||
label: options.bar.launcher.icon.bind("value"),
|
||||
class_name: 'bar-menu_label bar-button_icon txt-icon bar',
|
||||
label: options.bar.launcher.icon.bind('value'),
|
||||
}),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "dashboard",
|
||||
boxClass: 'dashboard',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "dashboardmenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'dashboardmenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,70 +1,77 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
const network = await Service.import("network");
|
||||
import options from "options";
|
||||
import { openMenu } from "../utils.js";
|
||||
const network = await Service.import('network');
|
||||
import options from 'options';
|
||||
import { openMenu } from '../utils.js';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const { label: networkLabel, truncation, truncation_size } = options.bar.network;
|
||||
|
||||
const Network = () => {
|
||||
const Network = (): BarBoxChild => {
|
||||
return {
|
||||
component: Widget.Box({
|
||||
vpack: "fill",
|
||||
vpack: 'fill',
|
||||
vexpand: true,
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), networkLabel.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
return `network ${styleMap[style]}${!showLabel ? " no-label" : ""}`;
|
||||
}),
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), networkLabel.bind('value')],
|
||||
(style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `network ${styleMap[style]}${!showLabel ? ' no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
children: [
|
||||
Widget.Icon({
|
||||
class_name: "bar-button-icon network",
|
||||
icon: Utils.merge([
|
||||
network.bind("primary"),
|
||||
network.bind("wifi"),
|
||||
network.bind("wired")
|
||||
], (pmry, wfi, wrd) => {
|
||||
if (pmry === "wired") {
|
||||
return wrd.icon_name;
|
||||
}
|
||||
return wfi.icon_name;
|
||||
})
|
||||
class_name: 'bar-button-icon network',
|
||||
icon: Utils.merge(
|
||||
[network.bind('primary'), network.bind('wifi'), network.bind('wired')],
|
||||
(pmry, wfi, wrd) => {
|
||||
if (pmry === 'wired') {
|
||||
return wrd.icon_name;
|
||||
}
|
||||
return wfi.icon_name;
|
||||
},
|
||||
),
|
||||
}),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
child: Utils.merge([
|
||||
network.bind("primary"),
|
||||
network.bind("wifi"),
|
||||
networkLabel.bind("value"),
|
||||
truncation.bind("value"),
|
||||
truncation_size.bind("value")
|
||||
], (pmry, wfi, showLbl, trunc, tSize) => {
|
||||
if (!showLbl) {
|
||||
return Widget.Box();
|
||||
}
|
||||
if (pmry === "wired") {
|
||||
vpack: 'center',
|
||||
child: Utils.merge(
|
||||
[
|
||||
network.bind('primary'),
|
||||
network.bind('wifi'),
|
||||
networkLabel.bind('value'),
|
||||
truncation.bind('value'),
|
||||
truncation_size.bind('value'),
|
||||
],
|
||||
(pmry, wfi, showLbl, trunc, tSize) => {
|
||||
if (!showLbl) {
|
||||
return Widget.Box();
|
||||
}
|
||||
if (pmry === 'wired') {
|
||||
return Widget.Label({
|
||||
class_name: 'bar-button-label network',
|
||||
label: 'Wired'.substring(0, tSize),
|
||||
});
|
||||
}
|
||||
return Widget.Label({
|
||||
class_name: "bar-button-label network",
|
||||
label: "Wired".substring(0, tSize),
|
||||
})
|
||||
}
|
||||
return Widget.Label({
|
||||
class_name: "bar-button-label network",
|
||||
label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : "--",
|
||||
})
|
||||
|
||||
})
|
||||
class_name: 'bar-button-label network',
|
||||
label: wfi.ssid ? `${trunc ? wfi.ssid.substring(0, tSize) : wfi.ssid}` : '--',
|
||||
});
|
||||
},
|
||||
),
|
||||
}),
|
||||
]
|
||||
],
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "network",
|
||||
boxClass: 'network',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "networkmenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'networkmenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,51 +1,49 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { filterNotifications } from 'lib/shared/notifications.js';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const { show_total } = options.bar.notifications;
|
||||
const { ignore } = options.notifications;
|
||||
|
||||
const notifs = await Service.import("notifications");
|
||||
const notifs = await Service.import('notifications');
|
||||
|
||||
export const Notifications = () => {
|
||||
export const Notifications = (): BarBoxChild => {
|
||||
return {
|
||||
component: Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
className: Utils.merge(
|
||||
[
|
||||
options.theme.bar.buttons.style.bind("value"),
|
||||
show_total.bind("value")
|
||||
],
|
||||
(
|
||||
style,
|
||||
showTotal
|
||||
) => {
|
||||
[options.theme.bar.buttons.style.bind('value'), show_total.bind('value')],
|
||||
(style, showTotal) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `notifications ${styleMap[style]} ${!showTotal ? "no-label" : ""}`;
|
||||
}),
|
||||
return `notifications ${styleMap[style]} ${!showTotal ? 'no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
child: Widget.Box({
|
||||
hpack: "start",
|
||||
class_name: "bar-notifications",
|
||||
hpack: 'start',
|
||||
class_name: 'bar-notifications',
|
||||
children: Utils.merge(
|
||||
[notifs.bind("notifications"), notifs.bind("dnd"), show_total.bind("value"), ignore.bind("value")],
|
||||
[notifs.bind('notifications'), notifs.bind('dnd'), show_total.bind('value'), ignore.bind('value')],
|
||||
(notif, dnd, showTotal, ignoredNotifs) => {
|
||||
const filteredNotifications = filterNotifications(notif, ignoredNotifs);
|
||||
|
||||
const notifIcon = Widget.Label({
|
||||
hpack: "center",
|
||||
class_name: "bar-button-icon notifications txt-icon bar",
|
||||
label: dnd ? "" : filteredNotifications.length > 0 ? "" : "",
|
||||
hpack: 'center',
|
||||
class_name: 'bar-button-icon notifications txt-icon bar',
|
||||
label: dnd ? '' : filteredNotifications.length > 0 ? '' : '',
|
||||
});
|
||||
|
||||
const notifLabel = Widget.Label({
|
||||
hpack: "center",
|
||||
class_name: "bar-button-label notifications",
|
||||
hpack: 'center',
|
||||
class_name: 'bar-button-label notifications',
|
||||
label: filteredNotifications.length.toString(),
|
||||
});
|
||||
|
||||
@@ -58,10 +56,10 @@ export const Notifications = () => {
|
||||
}),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "notifications",
|
||||
boxClass: 'notifications',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "notificationsmenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'notificationsmenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,49 +1,43 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import { BarBoxChild, SelfButton } from 'lib/types/bar';
|
||||
import { Notify } from 'lib/utils';
|
||||
const systemtray = await Service.import("systemtray");
|
||||
import options from "options";
|
||||
const systemtray = await Service.import('systemtray');
|
||||
import options from 'options';
|
||||
|
||||
const { ignore } = options.bar.systray;
|
||||
|
||||
const SysTray = () => {
|
||||
const SysTray = (): BarBoxChild => {
|
||||
const isVis = Variable(false);
|
||||
|
||||
const items = Utils.merge(
|
||||
[systemtray.bind("items"), ignore.bind("value")],
|
||||
(items, ignored) => {
|
||||
const filteredTray = items.filter(({ id }) => !ignored.includes(id));
|
||||
const items = Utils.merge([systemtray.bind('items'), ignore.bind('value')], (items, ignored) => {
|
||||
const filteredTray = items.filter(({ id }) => !ignored.includes(id));
|
||||
|
||||
isVis.value = filteredTray.length > 0;
|
||||
isVis.value = filteredTray.length > 0;
|
||||
|
||||
return filteredTray.map((item) => {
|
||||
if (item.menu !== undefined) {
|
||||
item.menu["class_name"] = "systray-menu";
|
||||
}
|
||||
|
||||
return Widget.Button({
|
||||
cursor: "pointer",
|
||||
child: Widget.Icon({
|
||||
class_name: "systray-icon",
|
||||
icon: item.bind("icon"),
|
||||
}),
|
||||
on_primary_click: (_: any, event: Gdk.Event) => item.activate(event),
|
||||
on_secondary_click: (_, event) => item.openMenu(event),
|
||||
onMiddleClick: () => Notify({ summary: "App Name", body: item.id }),
|
||||
tooltip_markup: item.bind("tooltip_markup"),
|
||||
});
|
||||
return filteredTray.map((item) => {
|
||||
return Widget.Button({
|
||||
cursor: 'pointer',
|
||||
child: Widget.Icon({
|
||||
class_name: 'systray-icon',
|
||||
icon: item.bind('icon'),
|
||||
}),
|
||||
on_primary_click: (_: SelfButton, event: Gdk.Event) => item.activate(event),
|
||||
on_secondary_click: (_, event) => item.openMenu(event),
|
||||
onMiddleClick: () => Notify({ summary: 'App Name', body: item.id }),
|
||||
tooltip_markup: item.bind('tooltip_markup'),
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
class_name: "systray",
|
||||
class_name: 'systray',
|
||||
children: items,
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "systray",
|
||||
boxClass: 'systray',
|
||||
isVis,
|
||||
props: {}
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
import { Child } from 'lib/types/widget';
|
||||
import Button from 'types/widgets/button';
|
||||
|
||||
export const closeAllMenus = () => {
|
||||
export const closeAllMenus = (): void => {
|
||||
const menuWindows = App.windows
|
||||
.filter((w) => {
|
||||
if (w.name) {
|
||||
@@ -18,7 +20,7 @@ export const closeAllMenus = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const openMenu = (clicked: any, event: Gdk.Event, window: string) => {
|
||||
export const openMenu = (clicked: Button<Child, Child>, event: Gdk.Event, window: string): void => {
|
||||
/*
|
||||
* NOTE: We have to make some adjustments so the menu pops up relatively
|
||||
* to the center of the button clicked. We don't want the menu to spawn
|
||||
|
||||
@@ -1,59 +1,63 @@
|
||||
import Gdk from 'gi://Gdk?version=3.0';
|
||||
const audio = await Service.import("audio");
|
||||
import { openMenu } from "../utils.js";
|
||||
import options from "options";
|
||||
const audio = await Service.import('audio');
|
||||
import { openMenu } from '../utils.js';
|
||||
import options from 'options';
|
||||
import { Binding } from 'lib/utils.js';
|
||||
import { VolumeIcons } from 'lib/types/volume.js';
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import { Bind } from 'lib/types/variable.js';
|
||||
import Button from 'types/widgets/button.js';
|
||||
import { Child } from 'lib/types/widget.js';
|
||||
|
||||
const Volume = () => {
|
||||
const Volume = (): BarBoxChild => {
|
||||
const icons: VolumeIcons = {
|
||||
101: "",
|
||||
66: "",
|
||||
34: "",
|
||||
1: "",
|
||||
0: "",
|
||||
101: '',
|
||||
66: '',
|
||||
34: '',
|
||||
1: '',
|
||||
0: '',
|
||||
};
|
||||
|
||||
const getIcon = () => {
|
||||
const getIcon = (): Bind => {
|
||||
const icon: Binding<number> = Utils.merge(
|
||||
[audio.speaker.bind("is_muted"), audio.speaker.bind("volume")],
|
||||
[audio.speaker.bind('is_muted'), audio.speaker.bind('volume')],
|
||||
(isMuted, vol) => {
|
||||
return isMuted
|
||||
? 0
|
||||
: [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100) || 101;
|
||||
return isMuted ? 0 : [101, 66, 34, 1, 0].find((threshold) => threshold <= vol * 100) || 101;
|
||||
},
|
||||
);
|
||||
|
||||
return icon.as((i: number) => i !== undefined ? icons[i] : icons[101]);
|
||||
return icon.as((i: number) => (i !== undefined ? icons[i] : icons[101]));
|
||||
};
|
||||
|
||||
const volIcn = Widget.Label({
|
||||
hexpand: true,
|
||||
label: getIcon(),
|
||||
class_name: "bar-button-icon volume txt-icon bar",
|
||||
class_name: 'bar-button-icon volume txt-icon bar',
|
||||
});
|
||||
|
||||
const volPct = Widget.Label({
|
||||
hexpand: true,
|
||||
label: audio.speaker.bind("volume").as((v) => `${Math.round(v * 100)}%`),
|
||||
class_name: "bar-button-label volume",
|
||||
label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`),
|
||||
class_name: 'bar-button-label volume',
|
||||
});
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), options.bar.volume.label.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
|
||||
return `volume ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
|
||||
}),
|
||||
children: options.bar.volume.label.bind("value").as((showLabel) => {
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), options.bar.volume.label.bind('value')],
|
||||
(style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `volume ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
children: options.bar.volume.label.bind('value').as((showLabel) => {
|
||||
if (showLabel) {
|
||||
return [volIcn, volPct];
|
||||
}
|
||||
@@ -61,10 +65,10 @@ const Volume = () => {
|
||||
}),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "volume",
|
||||
boxClass: 'volume',
|
||||
props: {
|
||||
on_primary_click: (clicked: any, event: Gdk.Event) => {
|
||||
openMenu(clicked, event, "audiomenu");
|
||||
on_primary_click: (clicked: Button<Child, Child>, event: Gdk.Event): void => {
|
||||
openMenu(clicked, event, 'audiomenu');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,122 +1,119 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import { BarBoxChild } from 'lib/types/bar';
|
||||
import options from 'options';
|
||||
import { ActiveClient } from 'types/service/hyprland'
|
||||
import Label from "types/widgets/label";
|
||||
import { ActiveClient } from 'types/service/hyprland';
|
||||
|
||||
const filterTitle = (windowtitle: ActiveClient) => {
|
||||
const filterTitle = (windowtitle: ActiveClient): Record<string, string> => {
|
||||
const windowTitleMap = [
|
||||
// user provided values
|
||||
...options.bar.windowtitle.title_map.value,
|
||||
// Original Entries
|
||||
["kitty", "", "Kitty Terminal"],
|
||||
["firefox", "", "Firefox"],
|
||||
["microsoft-edge", "", "Edge"],
|
||||
["discord", "", "Discord"],
|
||||
["vesktop", "", "Vesktop"],
|
||||
["org.kde.dolphin", "", "Dolphin"],
|
||||
["plex", "", "Plex"],
|
||||
["steam", "", "Steam"],
|
||||
["spotify", "", "Spotify"],
|
||||
["ristretto", "", "Ristretto"],
|
||||
["obsidian", "", "Obsidian"],
|
||||
['kitty', '', 'Kitty Terminal'],
|
||||
['firefox', '', 'Firefox'],
|
||||
['microsoft-edge', '', 'Edge'],
|
||||
['discord', '', 'Discord'],
|
||||
['vesktop', '', 'Vesktop'],
|
||||
['org.kde.dolphin', '', 'Dolphin'],
|
||||
['plex', '', 'Plex'],
|
||||
['steam', '', 'Steam'],
|
||||
['spotify', '', 'Spotify'],
|
||||
['ristretto', '', 'Ristretto'],
|
||||
['obsidian', '', 'Obsidian'],
|
||||
|
||||
// Browsers
|
||||
["google-chrome", "", "Google Chrome"],
|
||||
["brave-browser", "", "Brave Browser"],
|
||||
["chromium", "", "Chromium"],
|
||||
["opera", "", "Opera"],
|
||||
["vivaldi", "", "Vivaldi"],
|
||||
["waterfox", "", "Waterfox"],
|
||||
["thorium", "", "Waterfox"],
|
||||
["tor-browser", "", "Tor Browser"],
|
||||
["floorp", "", "Floorp"],
|
||||
['google-chrome', '', 'Google Chrome'],
|
||||
['brave-browser', '', 'Brave Browser'],
|
||||
['chromium', '', 'Chromium'],
|
||||
['opera', '', 'Opera'],
|
||||
['vivaldi', '', 'Vivaldi'],
|
||||
['waterfox', '', 'Waterfox'],
|
||||
['thorium', '', 'Waterfox'],
|
||||
['tor-browser', '', 'Tor Browser'],
|
||||
['floorp', '', 'Floorp'],
|
||||
|
||||
// Terminals
|
||||
["gnome-terminal", "", "GNOME Terminal"],
|
||||
["konsole", "", "Konsole"],
|
||||
["alacritty", "", "Alacritty"],
|
||||
["wezterm", "", "Wezterm"],
|
||||
["foot", "", "Foot Terminal"],
|
||||
["tilix", "", "Tilix"],
|
||||
["xterm", "", "XTerm"],
|
||||
["urxvt", "", "URxvt"],
|
||||
["st", "", "st Terminal"],
|
||||
['gnome-terminal', '', 'GNOME Terminal'],
|
||||
['konsole', '', 'Konsole'],
|
||||
['alacritty', '', 'Alacritty'],
|
||||
['wezterm', '', 'Wezterm'],
|
||||
['foot', '', 'Foot Terminal'],
|
||||
['tilix', '', 'Tilix'],
|
||||
['xterm', '', 'XTerm'],
|
||||
['urxvt', '', 'URxvt'],
|
||||
['st', '', 'st Terminal'],
|
||||
|
||||
// Development Tools
|
||||
["code", "", "Visual Studio Code"],
|
||||
["vscode", "", "VS Code"],
|
||||
["sublime-text", "", "Sublime Text"],
|
||||
["atom", "", "Atom"],
|
||||
["android-studio", "", "Android Studio"],
|
||||
["intellij-idea", "", "IntelliJ IDEA"],
|
||||
["pycharm", "", "PyCharm"],
|
||||
["webstorm", "", "WebStorm"],
|
||||
["phpstorm", "", "PhpStorm"],
|
||||
["eclipse", "", "Eclipse"],
|
||||
["netbeans", "", "NetBeans"],
|
||||
["docker", "", "Docker"],
|
||||
["vim", "", "Vim"],
|
||||
["neovim", "", "Neovim"],
|
||||
["neovide", "", "Neovide"],
|
||||
["emacs", "", "Emacs"],
|
||||
['code', '', 'Visual Studio Code'],
|
||||
['vscode', '', 'VS Code'],
|
||||
['sublime-text', '', 'Sublime Text'],
|
||||
['atom', '', 'Atom'],
|
||||
['android-studio', '', 'Android Studio'],
|
||||
['intellij-idea', '', 'IntelliJ IDEA'],
|
||||
['pycharm', '', 'PyCharm'],
|
||||
['webstorm', '', 'WebStorm'],
|
||||
['phpstorm', '', 'PhpStorm'],
|
||||
['eclipse', '', 'Eclipse'],
|
||||
['netbeans', '', 'NetBeans'],
|
||||
['docker', '', 'Docker'],
|
||||
['vim', '', 'Vim'],
|
||||
['neovim', '', 'Neovim'],
|
||||
['neovide', '', 'Neovide'],
|
||||
['emacs', '', 'Emacs'],
|
||||
|
||||
// Communication Tools
|
||||
["slack", "", "Slack"],
|
||||
["telegram-desktop", "", "Telegram"],
|
||||
["org.telegram.desktop", "", "Telegram"],
|
||||
["whatsapp", "", "WhatsApp"],
|
||||
["teams", "", "Microsoft Teams"],
|
||||
["skype", "", "Skype"],
|
||||
["thunderbird", "", "Thunderbird"],
|
||||
['slack', '', 'Slack'],
|
||||
['telegram-desktop', '', 'Telegram'],
|
||||
['org.telegram.desktop', '', 'Telegram'],
|
||||
['whatsapp', '', 'WhatsApp'],
|
||||
['teams', '', 'Microsoft Teams'],
|
||||
['skype', '', 'Skype'],
|
||||
['thunderbird', '', 'Thunderbird'],
|
||||
|
||||
// File Managers
|
||||
["nautilus", "", "Files (Nautilus)"],
|
||||
["thunar", "", "Thunar"],
|
||||
["pcmanfm", "", "PCManFM"],
|
||||
["nemo", "", "Nemo"],
|
||||
["ranger", "", "Ranger"],
|
||||
["doublecmd", "", "Double Commander"],
|
||||
["krusader", "", "Krusader"],
|
||||
['nautilus', '', 'Files (Nautilus)'],
|
||||
['thunar', '', 'Thunar'],
|
||||
['pcmanfm', '', 'PCManFM'],
|
||||
['nemo', '', 'Nemo'],
|
||||
['ranger', '', 'Ranger'],
|
||||
['doublecmd', '', 'Double Commander'],
|
||||
['krusader', '', 'Krusader'],
|
||||
|
||||
// Media Players
|
||||
["vlc", "", "VLC Media Player"],
|
||||
["mpv", "", "MPV"],
|
||||
["rhythmbox", "", "Rhythmbox"],
|
||||
['vlc', '', 'VLC Media Player'],
|
||||
['mpv', '', 'MPV'],
|
||||
['rhythmbox', '', 'Rhythmbox'],
|
||||
|
||||
// Graphics Tools
|
||||
["gimp", "", "GIMP"],
|
||||
["inkscape", "", "Inkscape"],
|
||||
["krita", "", "Krita"],
|
||||
["blender", "", "Blender"],
|
||||
['gimp', '', 'GIMP'],
|
||||
['inkscape', '', 'Inkscape'],
|
||||
['krita', '', 'Krita'],
|
||||
['blender', '', 'Blender'],
|
||||
|
||||
// Video Editing
|
||||
["kdenlive", "", "Kdenlive"],
|
||||
['kdenlive', '', 'Kdenlive'],
|
||||
|
||||
// Games and Gaming Platforms
|
||||
["lutris", "", "Lutris"],
|
||||
["heroic", "", "Heroic Games Launcher"],
|
||||
["minecraft", "", "Minecraft"],
|
||||
["csgo", "", "CS:GO"],
|
||||
["dota2", "", "Dota 2"],
|
||||
['lutris', '', 'Lutris'],
|
||||
['heroic', '', 'Heroic Games Launcher'],
|
||||
['minecraft', '', 'Minecraft'],
|
||||
['csgo', '', 'CS:GO'],
|
||||
['dota2', '', 'Dota 2'],
|
||||
|
||||
// Office and Productivity
|
||||
["evernote", "", "Evernote"],
|
||||
["sioyek", "", "Sioyek"],
|
||||
|
||||
['evernote', '', 'Evernote'],
|
||||
['sioyek', '', 'Sioyek'],
|
||||
|
||||
// Cloud Services and Sync
|
||||
["dropbox", "", "Dropbox"],
|
||||
['dropbox', '', 'Dropbox'],
|
||||
|
||||
// Desktop
|
||||
["^$", "", "Desktop"],
|
||||
['^$', '', 'Desktop'],
|
||||
|
||||
// Fallback icon
|
||||
["(.+)", "", `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`],
|
||||
['(.+)', '', `${windowtitle.class.charAt(0).toUpperCase() + windowtitle.class.slice(1)}`],
|
||||
];
|
||||
|
||||
const foundMatch = windowTitleMap.find((wt) =>
|
||||
RegExp(wt[0]).test(windowtitle.class.toLowerCase()),
|
||||
);
|
||||
const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0]).test(windowtitle.class.toLowerCase()));
|
||||
|
||||
// return the default icon if no match is found or
|
||||
// if the array element matched is not of size 3
|
||||
@@ -129,15 +126,15 @@ const filterTitle = (windowtitle: ActiveClient) => {
|
||||
|
||||
return {
|
||||
icon: foundMatch[1],
|
||||
label: foundMatch[2]
|
||||
label: foundMatch[2],
|
||||
};
|
||||
};
|
||||
|
||||
const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: boolean) => {
|
||||
const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: boolean): string => {
|
||||
if (useCustomTitle) return filterTitle(client).label;
|
||||
if (useClassName) return client.class;
|
||||
|
||||
let title = client.title;
|
||||
const title = client.title;
|
||||
// If the title is empty or only filled with spaces, fallback to the class name
|
||||
if (title.length === 0 || title.match(/^ *$/)) {
|
||||
return client.class;
|
||||
@@ -145,51 +142,76 @@ const getTitle = (client: ActiveClient, useCustomTitle: boolean, useClassName: b
|
||||
return title;
|
||||
};
|
||||
|
||||
const truncateTitle = (title: string, max_size: number) => {
|
||||
const truncateTitle = (title: string, max_size: number): string => {
|
||||
if (max_size > 0 && title.length > max_size) {
|
||||
return title.substring(0, max_size).trim() + "...";
|
||||
return title.substring(0, max_size).trim() + '...';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
const ClientTitle = () => {
|
||||
const ClientTitle = (): BarBoxChild => {
|
||||
const { custom_title, class_name, label, icon, truncation, truncation_size } = options.bar.windowtitle;
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
className: Utils.merge([options.theme.bar.buttons.style.bind("value"), label.bind("value")], (style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: "style1",
|
||||
split: "style2",
|
||||
wave: "style3",
|
||||
wave2: "style3",
|
||||
};
|
||||
return `windowtitle ${styleMap[style]} ${!showLabel ? "no-label" : ""}`;
|
||||
}),
|
||||
children:
|
||||
Utils.merge(
|
||||
[hyprland.active.bind("client"), custom_title.bind("value"), class_name.bind("value"), label.bind("value"),
|
||||
icon.bind("value"), truncation.bind("value"), truncation_size.bind("value")],
|
||||
(client, useCustomTitle, useClassName, showLabel, showIcon, truncate, truncationSize) => {
|
||||
const children: Label<any>[] = [];
|
||||
if (showIcon) {
|
||||
children.push(Widget.Label({
|
||||
class_name: "bar-button-icon windowtitle txt-icon bar",
|
||||
className: Utils.merge(
|
||||
[options.theme.bar.buttons.style.bind('value'), label.bind('value')],
|
||||
(style, showLabel) => {
|
||||
const styleMap = {
|
||||
default: 'style1',
|
||||
split: 'style2',
|
||||
wave: 'style3',
|
||||
wave2: 'style3',
|
||||
};
|
||||
return `windowtitle ${styleMap[style]} ${!showLabel ? 'no-label' : ''}`;
|
||||
},
|
||||
),
|
||||
children: Utils.merge(
|
||||
[
|
||||
hyprland.active.bind('client'),
|
||||
custom_title.bind('value'),
|
||||
class_name.bind('value'),
|
||||
label.bind('value'),
|
||||
icon.bind('value'),
|
||||
truncation.bind('value'),
|
||||
truncation_size.bind('value'),
|
||||
],
|
||||
(client, useCustomTitle, useClassName, showLabel, showIcon, truncate, truncationSize) => {
|
||||
if (showIcon) {
|
||||
return [
|
||||
Widget.Label({
|
||||
class_name: 'bar-button-icon windowtitle txt-icon bar',
|
||||
label: filterTitle(client).icon,
|
||||
}));
|
||||
}
|
||||
if (showLabel) {
|
||||
children.push(Widget.Label({
|
||||
class_name: `bar-button-label windowtitle ${showIcon ? "" : "no-icon"}`,
|
||||
label: truncateTitle(getTitle(client, useCustomTitle, useClassName), truncate ? truncationSize : -1),
|
||||
}));
|
||||
}
|
||||
return children;
|
||||
}),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`,
|
||||
label: truncateTitle(
|
||||
getTitle(client, useCustomTitle, useClassName),
|
||||
truncate ? truncationSize : -1,
|
||||
),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
if (showLabel) {
|
||||
return [
|
||||
Widget.Label({
|
||||
class_name: `bar-button-label windowtitle ${showIcon ? '' : 'no-icon'}`,
|
||||
label: truncateTitle(
|
||||
getTitle(client, useCustomTitle, useClassName),
|
||||
truncate ? truncationSize : -1,
|
||||
),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "windowtitle",
|
||||
props: {}
|
||||
boxClass: 'windowtitle',
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
const hyprland = await Service.import('hyprland');
|
||||
|
||||
import { WorkspaceMap, WorkspaceRule } from "lib/types/workspace";
|
||||
import options from "options";
|
||||
import { Variable } from "types/variable";
|
||||
|
||||
const {
|
||||
workspaces,
|
||||
reverse_scroll,
|
||||
} = options.bar.workspaces;
|
||||
import { MonitorMap, WorkspaceMap, WorkspaceRule } from 'lib/types/workspace';
|
||||
import options from 'options';
|
||||
import { Variable } from 'types/variable';
|
||||
|
||||
const { workspaces, reverse_scroll } = options.bar.workspaces;
|
||||
|
||||
export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, monitor: number): boolean => {
|
||||
if (!wsRules || !Object.keys(wsRules).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const monitorMap = {};
|
||||
const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor }));
|
||||
const monitors = [...new Map([...workspaceMonitorList, ...hyprland.monitors].map(item => [item.id, item])).values()];
|
||||
const monitorMap: MonitorMap = {};
|
||||
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ id: m.monitorID, name: m.monitor }));
|
||||
const monitors = [
|
||||
...new Map([...workspaceMonitorList, ...hyprland.monitors].map((item) => [item.id, item])).values(),
|
||||
];
|
||||
|
||||
monitors.forEach((m) => (monitorMap[m.id] = m.name));
|
||||
|
||||
@@ -32,9 +30,9 @@ export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, mo
|
||||
|
||||
export const getWorkspaceRules = (): WorkspaceMap => {
|
||||
try {
|
||||
const rules = Utils.exec("hyprctl workspacerules -j");
|
||||
const rules = Utils.exec('hyprctl workspacerules -j');
|
||||
|
||||
const workspaceRules = {};
|
||||
const workspaceRules: WorkspaceMap = {};
|
||||
|
||||
JSON.parse(rules).forEach((rule: WorkspaceRule, index: number) => {
|
||||
if (Object.hasOwnProperty.call(workspaceRules, rule.monitor)) {
|
||||
@@ -60,13 +58,13 @@ export const getCurrentMonitorWorkspaces = (monitor: number): number[] => {
|
||||
}
|
||||
|
||||
const monitorWorkspaces = getWorkspaceRules();
|
||||
const monitorMap = {};
|
||||
const monitorMap: MonitorMap = {};
|
||||
hyprland.monitors.forEach((m) => (monitorMap[m.id] = m.name));
|
||||
|
||||
const currentMonitorName = monitorMap[monitor];
|
||||
|
||||
return monitorWorkspaces[currentMonitorName];
|
||||
}
|
||||
};
|
||||
|
||||
export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean): void => {
|
||||
if (activeWorkspaces === true) {
|
||||
@@ -74,18 +72,17 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
|
||||
|
||||
let nextIndex = hyprland.active.workspace.id + 1;
|
||||
if (nextIndex > activeWses[activeWses.length - 1].id) {
|
||||
|
||||
nextIndex = activeWses[0].id;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${nextIndex}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${nextIndex}`);
|
||||
} else if (currentMonitorWorkspaces.value === undefined) {
|
||||
let nextIndex = hyprland.active.workspace.id + 1;
|
||||
if (nextIndex > workspaces.value) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${nextIndex}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${nextIndex}`);
|
||||
} else {
|
||||
const curWorkspace = hyprland.active.workspace.id;
|
||||
const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace);
|
||||
@@ -94,9 +91,9 @@ export const goToNextWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean): void => {
|
||||
if (activeWorkspaces === true) {
|
||||
@@ -104,11 +101,10 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
|
||||
|
||||
let prevIndex = hyprland.active.workspace.id - 1;
|
||||
if (prevIndex < activeWses[0].id) {
|
||||
|
||||
prevIndex = activeWses[activeWses.length - 1].id;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${prevIndex}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${prevIndex}`);
|
||||
} else if (currentMonitorWorkspaces.value === undefined) {
|
||||
let prevIndex = hyprland.active.workspace.id - 1;
|
||||
|
||||
@@ -116,7 +112,7 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
|
||||
prevIndex = workspaces.value;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${prevIndex}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${prevIndex}`);
|
||||
} else {
|
||||
const curWorkspace = hyprland.active.workspace.id;
|
||||
const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace);
|
||||
@@ -125,11 +121,11 @@ export const goToPrevWS = (currentMonitorWorkspaces: Variable<number[]>, activeW
|
||||
prevIndex = currentMonitorWorkspaces.value.length - 1;
|
||||
}
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`)
|
||||
hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function throttle<T extends (...args: any[]) => void>(func: T, limit: number): T {
|
||||
export function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number): T {
|
||||
let inThrottle: boolean;
|
||||
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
||||
if (!inThrottle) {
|
||||
@@ -147,7 +143,11 @@ type ThrottledScrollHandlers = {
|
||||
throttledScrollDown: () => void;
|
||||
};
|
||||
|
||||
export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonitorWorkspaces: Variable<number[]>, activeWorkspaces: boolean = false): ThrottledScrollHandlers => {
|
||||
export const createThrottledScrollHandlers = (
|
||||
scrollSpeed: number,
|
||||
currentMonitorWorkspaces: Variable<number[]>,
|
||||
activeWorkspaces: boolean = false,
|
||||
): ThrottledScrollHandlers => {
|
||||
const throttledScrollUp = throttle(() => {
|
||||
if (reverse_scroll.value === true) {
|
||||
goToPrevWS(currentMonitorWorkspaces, activeWorkspaces);
|
||||
@@ -165,4 +165,4 @@ export const createThrottledScrollHandlers = (scrollSpeed: number, currentMonito
|
||||
}, 200 / scrollSpeed);
|
||||
|
||||
return { throttledScrollUp, throttledScrollDown };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,42 +1,54 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
import options from "options";
|
||||
import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces, getWorkspaceRules, getWorkspacesForMonitor } from "./helpers";
|
||||
import { Workspace } from "types/service/hyprland";
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import options from 'options';
|
||||
import {
|
||||
createThrottledScrollHandlers,
|
||||
getCurrentMonitorWorkspaces,
|
||||
getWorkspaceRules,
|
||||
getWorkspacesForMonitor,
|
||||
} from './helpers';
|
||||
import { Workspace } from 'types/service/hyprland';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { BarBoxChild, SelfButton } from 'lib/types/bar';
|
||||
|
||||
const {
|
||||
workspaces,
|
||||
monitorSpecific,
|
||||
workspaceMask,
|
||||
scroll_speed,
|
||||
spacing
|
||||
} = options.bar.workspaces;
|
||||
const { workspaces, monitorSpecific, workspaceMask, scroll_speed, spacing } = options.bar.workspaces;
|
||||
|
||||
function range(length: number, start = 1) {
|
||||
function range(length: number, start = 1): number[] {
|
||||
return Array.from({ length }, (_, i) => i + start);
|
||||
}
|
||||
|
||||
const Workspaces = (monitor = -1) => {
|
||||
const Workspaces = (monitor = -1): BarBoxChild => {
|
||||
const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces(monitor));
|
||||
|
||||
workspaces.connect("changed", () => {
|
||||
currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor)
|
||||
})
|
||||
workspaces.connect('changed', () => {
|
||||
currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor);
|
||||
});
|
||||
|
||||
const renderClassnames = (showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string, i: number) => {
|
||||
const renderClassnames = (
|
||||
showIcons: boolean,
|
||||
showNumbered: boolean,
|
||||
numberedActiveIndicator: string,
|
||||
i: number,
|
||||
): string => {
|
||||
if (showIcons) {
|
||||
return `workspace-icon txt-icon bar`;
|
||||
}
|
||||
if (showNumbered) {
|
||||
const numActiveInd = hyprland.active.workspace.id === i
|
||||
? numberedActiveIndicator
|
||||
: "";
|
||||
const numActiveInd = hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
|
||||
|
||||
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
|
||||
}
|
||||
return "default";
|
||||
}
|
||||
return 'default';
|
||||
};
|
||||
|
||||
const renderLabel = (showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, i: number, index: number) => {
|
||||
const renderLabel = (
|
||||
showIcons: boolean,
|
||||
available: string,
|
||||
active: string,
|
||||
occupied: string,
|
||||
workspaceMask: boolean,
|
||||
i: number,
|
||||
index: number,
|
||||
): string => {
|
||||
if (showIcons) {
|
||||
if (hyprland.active.workspace.id === i) {
|
||||
return active;
|
||||
@@ -44,20 +56,16 @@ const Workspaces = (monitor = -1) => {
|
||||
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
|
||||
return occupied;
|
||||
}
|
||||
if (
|
||||
monitor !== -1
|
||||
) {
|
||||
if (monitor !== -1) {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
return workspaceMask
|
||||
? `${index + 1}`
|
||||
: `${i}`;
|
||||
}
|
||||
const defaultWses = () => {
|
||||
return workspaceMask ? `${index + 1}` : `${i}`;
|
||||
};
|
||||
const defaultWses = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
children: Utils.merge(
|
||||
[workspaces.bind("value"), monitorSpecific.bind()],
|
||||
[workspaces.bind('value'), monitorSpecific.bind()],
|
||||
(workspaces: number, monitorSpecific: boolean) => {
|
||||
return range(workspaces || 8)
|
||||
.filter((i) => {
|
||||
@@ -72,49 +80,57 @@ const Workspaces = (monitor = -1) => {
|
||||
})
|
||||
.map((i, index) => {
|
||||
return Widget.Button({
|
||||
class_name: "workspace-button",
|
||||
class_name: 'workspace-button',
|
||||
on_primary_click: () => {
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`)
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`);
|
||||
},
|
||||
child: Widget.Label({
|
||||
attribute: i,
|
||||
vpack: "center",
|
||||
css: spacing.bind("value").as(sp => `margin: 0rem ${0.375 * sp}rem;`),
|
||||
vpack: 'center',
|
||||
css: spacing.bind('value').as((sp) => `margin: 0rem ${0.375 * sp}rem;`),
|
||||
class_name: Utils.merge(
|
||||
[
|
||||
options.bar.workspaces.show_icons.bind("value"),
|
||||
options.bar.workspaces.show_numbered.bind("value"),
|
||||
options.bar.workspaces.numbered_active_indicator.bind("value"),
|
||||
options.bar.workspaces.icons.available.bind("value"),
|
||||
options.bar.workspaces.icons.active.bind("value"),
|
||||
options.bar.workspaces.icons.occupied.bind("value"),
|
||||
hyprland.active.workspace.bind("id")
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.show_numbered.bind('value'),
|
||||
options.bar.workspaces.numbered_active_indicator.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string) => {
|
||||
(
|
||||
showIcons: boolean,
|
||||
showNumbered: boolean,
|
||||
numberedActiveIndicator: string,
|
||||
) => {
|
||||
if (showIcons) {
|
||||
return `workspace-icon txt-icon bar`;
|
||||
}
|
||||
if (showNumbered) {
|
||||
const numActiveInd = hyprland.active.workspace.id === i
|
||||
? numberedActiveIndicator
|
||||
: "";
|
||||
const numActiveInd =
|
||||
hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
|
||||
|
||||
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
|
||||
}
|
||||
return "default";
|
||||
return 'default';
|
||||
},
|
||||
),
|
||||
label: Utils.merge(
|
||||
[
|
||||
options.bar.workspaces.show_icons.bind("value"),
|
||||
options.bar.workspaces.icons.available.bind("value"),
|
||||
options.bar.workspaces.icons.active.bind("value"),
|
||||
options.bar.workspaces.icons.occupied.bind("value"),
|
||||
workspaceMask.bind("value"),
|
||||
hyprland.active.workspace.bind("id")
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
workspaceMask.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(showIcons: boolean, available: string, active: string, occupied: string, workspaceMask: boolean, _: number) => {
|
||||
(
|
||||
showIcons: boolean,
|
||||
available: string,
|
||||
active: string,
|
||||
occupied: string,
|
||||
workspaceMask: boolean,
|
||||
) => {
|
||||
if (showIcons) {
|
||||
if (hyprland.active.workspace.id === i) {
|
||||
return active;
|
||||
@@ -122,52 +138,45 @@ const Workspaces = (monitor = -1) => {
|
||||
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
|
||||
return occupied;
|
||||
}
|
||||
if (
|
||||
monitor !== -1
|
||||
) {
|
||||
if (monitor !== -1) {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
return workspaceMask
|
||||
? `${index + 1}`
|
||||
: `${i}`;
|
||||
return workspaceMask ? `${index + 1}` : `${i}`;
|
||||
},
|
||||
),
|
||||
setup: (self) => {
|
||||
self.hook(hyprland, () => {
|
||||
self.toggleClassName('active', hyprland.active.workspace.id === i);
|
||||
self.toggleClassName(
|
||||
"active",
|
||||
hyprland.active.workspace.id === i,
|
||||
);
|
||||
self.toggleClassName(
|
||||
"occupied",
|
||||
'occupied',
|
||||
(hyprland.getWorkspace(i)?.windows || 0) > 0,
|
||||
);
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
const occupiedWses = () => {
|
||||
),
|
||||
});
|
||||
};
|
||||
const occupiedWses = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
children: Utils.merge(
|
||||
[
|
||||
monitorSpecific.bind("value"),
|
||||
hyprland.bind("workspaces"),
|
||||
workspaceMask.bind("value"),
|
||||
workspaces.bind("value"),
|
||||
options.bar.workspaces.show_icons.bind("value"),
|
||||
options.bar.workspaces.icons.available.bind("value"),
|
||||
options.bar.workspaces.icons.active.bind("value"),
|
||||
options.bar.workspaces.icons.occupied.bind("value"),
|
||||
options.bar.workspaces.show_numbered.bind("value"),
|
||||
options.bar.workspaces.numbered_active_indicator.bind("value"),
|
||||
spacing.bind("value"),
|
||||
hyprland.active.workspace.bind("id"),
|
||||
monitorSpecific.bind('value'),
|
||||
hyprland.bind('workspaces'),
|
||||
workspaceMask.bind('value'),
|
||||
workspaces.bind('value'),
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
options.bar.workspaces.show_numbered.bind('value'),
|
||||
options.bar.workspaces.numbered_active_indicator.bind('value'),
|
||||
spacing.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(
|
||||
monitorSpecific: boolean,
|
||||
@@ -185,29 +194,37 @@ const Workspaces = (monitor = -1) => {
|
||||
) => {
|
||||
let allWkspcs = range(totalWkspcs || 8);
|
||||
|
||||
const activeWorkspaces = wkSpaces.map(w => w.id);
|
||||
const activeWorkspaces = wkSpaces.map((w) => w.id);
|
||||
const workspaceRules = getWorkspaceRules();
|
||||
|
||||
// Sometimes hyprland doesn't have all the monitors in the list
|
||||
// so we complement it with monitors from the workspace list
|
||||
const workspaceMonitorList = hyprland?.workspaces?.map(m => ({ id: m.monitorID, name: m.monitor }));
|
||||
const curMonitor = hyprland.monitors.find(m => m.id === monitor)
|
||||
|| workspaceMonitorList.find(m => m.id === monitor);
|
||||
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({
|
||||
id: m.monitorID,
|
||||
name: m.monitor,
|
||||
}));
|
||||
const curMonitor =
|
||||
hyprland.monitors.find((m) => m.id === monitor) ||
|
||||
workspaceMonitorList.find((m) => m.id === monitor);
|
||||
|
||||
// go through each key in workspaceRules and flatten the array
|
||||
const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => {
|
||||
return [...acc, ...workspaceRules[k]];
|
||||
}, [] as number[]);
|
||||
|
||||
const activesForMonitor = activeWorkspaces.filter(w => {
|
||||
if (curMonitor && Object.hasOwnProperty.call(workspaceRules, curMonitor.name) && workspacesWithRules.includes(w)) {
|
||||
const activesForMonitor = activeWorkspaces.filter((w) => {
|
||||
if (
|
||||
curMonitor &&
|
||||
Object.hasOwnProperty.call(workspaceRules, curMonitor.name) &&
|
||||
workspacesWithRules.includes(w)
|
||||
) {
|
||||
return workspaceRules[curMonitor.name].includes(w);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (monitorSpecific) {
|
||||
const wrkspcsInRange = range(totalWkspcs).filter(w => {
|
||||
const wrkspcsInRange = range(totalWkspcs).filter((w) => {
|
||||
return getWorkspacesForMonitor(w, workspaceRules, monitor);
|
||||
});
|
||||
allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])];
|
||||
@@ -221,51 +238,53 @@ const Workspaces = (monitor = -1) => {
|
||||
})
|
||||
.map((i, index) => {
|
||||
return Widget.Button({
|
||||
class_name: "workspace-button",
|
||||
class_name: 'workspace-button',
|
||||
on_primary_click: () => {
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`)
|
||||
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`);
|
||||
},
|
||||
child: Widget.Label({
|
||||
attribute: i,
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
css: `margin: 0rem ${0.375 * spacing}rem;`,
|
||||
class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i),
|
||||
label: renderLabel(showIcons, available, active, occupied, workspaceMask, i, index),
|
||||
setup: (self) => {
|
||||
self.toggleClassName(
|
||||
"active",
|
||||
activeId === i,
|
||||
);
|
||||
self.toggleClassName(
|
||||
"occupied",
|
||||
(hyprland.getWorkspace(i)?.windows || 0) > 0,
|
||||
);
|
||||
self.toggleClassName('active', activeId === i);
|
||||
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
|
||||
},
|
||||
})
|
||||
}),
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
component: Widget.Box({
|
||||
class_name: "workspaces",
|
||||
child: options.bar.workspaces.hideUnoccupied.bind("value").as(hideUnoccupied => hideUnoccupied ? occupiedWses() : defaultWses()),
|
||||
class_name: 'workspaces',
|
||||
child: options.bar.workspaces.hideUnoccupied
|
||||
.bind('value')
|
||||
.as((hideUnoccupied) => (hideUnoccupied ? occupiedWses() : defaultWses())),
|
||||
}),
|
||||
isVisible: true,
|
||||
boxClass: "workspaces",
|
||||
boxClass: 'workspaces',
|
||||
props: {
|
||||
setup: (self: any) => {
|
||||
Utils.merge([scroll_speed.bind("value"), options.bar.workspaces.hideUnoccupied.bind("value")], (scroll_speed, hideUnoccupied) => {
|
||||
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(scroll_speed, currentMonitorWorkspaces, hideUnoccupied)
|
||||
self.on_scroll_up = throttledScrollUp;
|
||||
self.on_scroll_down = throttledScrollDown;
|
||||
});
|
||||
}
|
||||
}
|
||||
setup: (self: SelfButton): void => {
|
||||
Utils.merge(
|
||||
[scroll_speed.bind('value'), options.bar.workspaces.hideUnoccupied.bind('value')],
|
||||
(scroll_speed, hideUnoccupied) => {
|
||||
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
|
||||
scroll_speed,
|
||||
currentMonitorWorkspaces,
|
||||
hideUnoccupied,
|
||||
);
|
||||
self.on_scroll_up = throttledScrollUp;
|
||||
self.on_scroll_down = throttledScrollDown;
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
43
modules/bar/workspaces/utils.ts
Normal file
43
modules/bar/workspaces/utils.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
const hyprland = await Service.import('hyprland');
|
||||
|
||||
export const renderClassnames = (
|
||||
showIcons: boolean,
|
||||
showNumbered: boolean,
|
||||
numberedActiveIndicator: string,
|
||||
i: number,
|
||||
): string => {
|
||||
if (showIcons) {
|
||||
return `workspace-icon txt-icon bar`;
|
||||
}
|
||||
if (showNumbered) {
|
||||
const numActiveInd = hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
|
||||
|
||||
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
|
||||
}
|
||||
return 'default';
|
||||
};
|
||||
|
||||
export const renderLabel = (
|
||||
showIcons: boolean,
|
||||
available: string,
|
||||
active: string,
|
||||
occupied: string,
|
||||
workspaceMask: boolean,
|
||||
i: number,
|
||||
index: number,
|
||||
monitor: number,
|
||||
): string => {
|
||||
if (showIcons) {
|
||||
if (hyprland.active.workspace.id === i) {
|
||||
return active;
|
||||
}
|
||||
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
|
||||
return occupied;
|
||||
}
|
||||
if (monitor !== -1) {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
|
||||
return workspaceMask ? `${index + 1}` : `${i}`;
|
||||
};
|
||||
99
modules/bar/workspaces/variants/default.ts
Normal file
99
modules/bar/workspaces/variants/default.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import options from 'options';
|
||||
import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers';
|
||||
import { range } from 'lib/utils';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
|
||||
export const defaultWses = (monitor: number): BoxWidget => {
|
||||
return Widget.Box({
|
||||
children: Utils.merge(
|
||||
[workspaces.bind('value'), monitorSpecific.bind()],
|
||||
(workspaces: number, monitorSpecific: boolean) => {
|
||||
return range(workspaces || 8)
|
||||
.filter((i) => {
|
||||
if (!monitorSpecific) {
|
||||
return true;
|
||||
}
|
||||
const workspaceRules = getWorkspaceRules();
|
||||
return getWorkspacesForMonitor(i, workspaceRules, monitor);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return a - b;
|
||||
})
|
||||
.map((i, index) => {
|
||||
return Widget.Button({
|
||||
class_name: 'workspace-button',
|
||||
on_primary_click: () => {
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`);
|
||||
},
|
||||
child: Widget.Label({
|
||||
attribute: i,
|
||||
vpack: 'center',
|
||||
css: spacing.bind('value').as((sp) => `margin: 0rem ${0.375 * sp}rem;`),
|
||||
class_name: Utils.merge(
|
||||
[
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.show_numbered.bind('value'),
|
||||
options.bar.workspaces.numbered_active_indicator.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(showIcons: boolean, showNumbered: boolean, numberedActiveIndicator: string) => {
|
||||
if (showIcons) {
|
||||
return `workspace-icon txt-icon bar`;
|
||||
}
|
||||
if (showNumbered) {
|
||||
const numActiveInd =
|
||||
hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
|
||||
|
||||
return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
|
||||
}
|
||||
return 'default';
|
||||
},
|
||||
),
|
||||
label: Utils.merge(
|
||||
[
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
workspaceMask.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(
|
||||
showIcons: boolean,
|
||||
available: string,
|
||||
active: string,
|
||||
occupied: string,
|
||||
workspaceMask: boolean,
|
||||
) => {
|
||||
if (showIcons) {
|
||||
if (hyprland.active.workspace.id === i) {
|
||||
return active;
|
||||
}
|
||||
if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
|
||||
return occupied;
|
||||
}
|
||||
if (monitor !== -1) {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
return workspaceMask ? `${index + 1}` : `${i}`;
|
||||
},
|
||||
),
|
||||
setup: (self) => {
|
||||
self.hook(hyprland, () => {
|
||||
self.toggleClassName('active', hyprland.active.workspace.id === i);
|
||||
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
});
|
||||
};
|
||||
114
modules/bar/workspaces/variants/occupied.ts
Normal file
114
modules/bar/workspaces/variants/occupied.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import options from 'options';
|
||||
import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers';
|
||||
import { Workspace } from 'types/service/hyprland';
|
||||
import { renderClassnames, renderLabel } from '../utils';
|
||||
import { range } from 'lib/utils';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
|
||||
|
||||
export const occupiedWses = (monitor: number): BoxWidget => {
|
||||
return Widget.Box({
|
||||
children: Utils.merge(
|
||||
[
|
||||
monitorSpecific.bind('value'),
|
||||
hyprland.bind('workspaces'),
|
||||
workspaceMask.bind('value'),
|
||||
workspaces.bind('value'),
|
||||
options.bar.workspaces.show_icons.bind('value'),
|
||||
options.bar.workspaces.icons.available.bind('value'),
|
||||
options.bar.workspaces.icons.active.bind('value'),
|
||||
options.bar.workspaces.icons.occupied.bind('value'),
|
||||
options.bar.workspaces.show_numbered.bind('value'),
|
||||
options.bar.workspaces.numbered_active_indicator.bind('value'),
|
||||
spacing.bind('value'),
|
||||
hyprland.active.workspace.bind('id'),
|
||||
],
|
||||
(
|
||||
monitorSpecific: boolean,
|
||||
wkSpaces: Workspace[],
|
||||
workspaceMask: boolean,
|
||||
totalWkspcs: number,
|
||||
showIcons: boolean,
|
||||
available: string,
|
||||
active: string,
|
||||
occupied: string,
|
||||
showNumbered: boolean,
|
||||
numberedActiveIndicator: string,
|
||||
spacing: number,
|
||||
activeId: number,
|
||||
) => {
|
||||
let allWkspcs = range(totalWkspcs || 8);
|
||||
|
||||
const activeWorkspaces = wkSpaces.map((w) => w.id);
|
||||
const workspaceRules = getWorkspaceRules();
|
||||
|
||||
// Sometimes hyprland doesn't have all the monitors in the list
|
||||
// so we complement it with monitors from the workspace list
|
||||
const workspaceMonitorList = hyprland?.workspaces?.map((m) => ({ id: m.monitorID, name: m.monitor }));
|
||||
const curMonitor =
|
||||
hyprland.monitors.find((m) => m.id === monitor) ||
|
||||
workspaceMonitorList.find((m) => m.id === monitor);
|
||||
|
||||
// go through each key in workspaceRules and flatten the array
|
||||
const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => {
|
||||
return [...acc, ...workspaceRules[k]];
|
||||
}, [] as number[]);
|
||||
|
||||
const activesForMonitor = activeWorkspaces.filter((w) => {
|
||||
if (
|
||||
curMonitor &&
|
||||
Object.hasOwnProperty.call(workspaceRules, curMonitor.name) &&
|
||||
workspacesWithRules.includes(w)
|
||||
) {
|
||||
return workspaceRules[curMonitor.name].includes(w);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (monitorSpecific) {
|
||||
const wrkspcsInRange = range(totalWkspcs).filter((w) => {
|
||||
return getWorkspacesForMonitor(w, workspaceRules, monitor);
|
||||
});
|
||||
allWkspcs = [...new Set([...activesForMonitor, ...wrkspcsInRange])];
|
||||
} else {
|
||||
allWkspcs = [...new Set([...allWkspcs, ...activeWorkspaces])];
|
||||
}
|
||||
|
||||
return allWkspcs
|
||||
.sort((a, b) => {
|
||||
return a - b;
|
||||
})
|
||||
.map((i, index) => {
|
||||
return Widget.Button({
|
||||
class_name: 'workspace-button',
|
||||
on_primary_click: () => {
|
||||
hyprland.messageAsync(`dispatch workspace ${i}`);
|
||||
},
|
||||
child: Widget.Label({
|
||||
attribute: i,
|
||||
vpack: 'center',
|
||||
css: `margin: 0rem ${0.375 * spacing}rem;`,
|
||||
class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i),
|
||||
label: renderLabel(
|
||||
showIcons,
|
||||
available,
|
||||
active,
|
||||
occupied,
|
||||
workspaceMask,
|
||||
i,
|
||||
index,
|
||||
monitor,
|
||||
),
|
||||
setup: (self) => {
|
||||
self.toggleClassName('active', activeId === i);
|
||||
self.toggleClassName('occupied', (hyprland.getWorkspace(i)?.windows || 0) > 0);
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user