diff --git a/README.md b/README.md
index 024bace..13eb8e0 100644
--- a/README.md
+++ b/README.md
@@ -11,21 +11,26 @@
# HyprPanel 🚀
+
A panel built for Hyprland with [AGS](https://github.com/Aylur/ags)

## Installation
+
The [HyprPanel Wiki](https://hyprpanel.com/getting_started/installation.html) contains in depth instructions for installing the panel and all of its dependencies. The instructions below are general instructions for installing the panel.
## Requirements
+
Bun
```sh
curl -fsSL https://bun.sh/install | bash && \
sudo ln -s $HOME/.bun/bin/bun /usr/local/bin/bun
```
+
Additional dependencies:
+
```sh
pipewire
libgtop
@@ -45,6 +50,7 @@ gnome-bluetooth-3.0
```
Optional Dependencies:
+
```sh
## Used for Tracking GPU Usage in your Dashboard (NVidia only)
python
@@ -58,11 +64,13 @@ pacman-contrib
```
Arch (pacman):
+
```bash
sudo pacman -S pipewire libgtop bluez bluez-utils btop networkmanager dart-sass wl-clipboard brightnessctl swww python gnome-bluetooth-3.0 pacman-contrib
```
Arch (AUR):
+
```bash
yay -S grimblast-git gpu-screen-recorder hyprpicker matugen-bin python-gpustat aylurs-gtk-shell-git
```
@@ -72,32 +80,43 @@ For NixOS/Home-Manager, see [NixOS & Home-Manager instructions](#nixos--home-man
## Instructions
### AGS
+
Once everything is installed you need to put the contents of this repo in `~/.config/ags`.
If you already have something in `~/.config/ags`, it's recommended that you back it up with:
+
```bash
mv $HOME/.config/ags $HOME/.config/ags.bkup
```
+
Otherwise you can use this command to install the panel:
+
```bash
git clone https://github.com/Jas-SinghFSU/HyprPanel.git && \
ln -s $(pwd)/HyprPanel $HOME/.config/ags
```
+
### Nerd Fonts
+
Additionally, you need to ensure that you have a [Nerd Font](https://www.nerdfonts.com/font-downloads) installed for your icons to render properly.
### Launch the panel
+
Afterwards you can run the panel with the following command in your terminal:
+
```bash
ags
```
Or you can add it to your Hyprland config (hyprland.conf) to auto-start with:
+
```bash
exec-once = ags
```
### NixOS & Home-Manager
+
Alternatively, if you're using NixOS and/or Home-Manager, you can setup AGS using the provided Nix Flake. First, add the repository to your Flake's inputs, and enable the overlay.
+
```nix
# flake.nix
@@ -105,7 +124,7 @@ Alternatively, if you're using NixOS and/or Home-Manager, you can setup AGS usin
inputs.hyprpanel.url = "github:Jas-SinghFSU/HyprPanel";
# ...
- outputs = { self, nixpkgs, ... }@inputs:
+ outputs = { self, nixpkgs, ... }@inputs:
let
# ...
system = "x86_64-linux"; # change to whatever your system should be.
@@ -175,6 +194,7 @@ The panel is automatically scaled based on your font size in `Configuration > Ge
### Specifying bar layouts per monitor
To specify layouts for each monitor you can create a JSON object such as:
+
```JSON
{
"0": {
@@ -229,19 +249,21 @@ To specify layouts for each monitor you can create a JSON object such as:
```
Where each monitor is defined by its index (0, 1, 2 in this case) and each section (left, middle, right) contains one or more of the following modules:
+
```js
-"battery"
-"dashboard"
-"workspaces"
-"windowtitle"
-"media"
-"notifications"
-"volume"
-"network"
-"bluetooth"
-"clock"
-"systray"
+'battery';
+'dashboard';
+'workspaces';
+'windowtitle';
+'media';
+'notifications';
+'volume';
+'network';
+'bluetooth';
+'clock';
+'systray';
```
+
Since the text-box in the options dialog isn't sufficient, it is recommended that you create this JSON configuration in a text editor elsewhere and paste it into the layout text-box under Configuration > Bar > "Bar Layouts for Monitors".
### Additional Configuration
diff --git a/lib/constants/colors.ts b/lib/constants/colors.ts
new file mode 100644
index 0000000..95ac5cf
--- /dev/null
+++ b/lib/constants/colors.ts
@@ -0,0 +1,142 @@
+export const namedColors = new Set([
+ 'alice blue',
+ 'antique white',
+ 'aqua',
+ 'aquamarine',
+ 'azure',
+ 'beige',
+ 'bisque',
+ 'black',
+ 'blanched almond',
+ 'blue',
+ 'blue violet',
+ 'brown',
+ 'burlywood',
+ 'cadet blue',
+ 'chartreuse',
+ 'chocolate',
+ 'coral',
+ 'cornflower blue',
+ 'cornsilk',
+ 'crimson',
+ 'cyan',
+ 'dark blue',
+ 'dark cyan',
+ 'dark goldenrod',
+ 'dark gray',
+ 'dark green',
+ 'dark khaki',
+ 'dark magenta',
+ 'dark olive green',
+ 'dark orange',
+ 'dark orchid',
+ 'dark red',
+ 'dark salmon',
+ 'dark sea green',
+ 'dark slate blue',
+ 'dark slate gray',
+ 'dark turquoise',
+ 'dark violet',
+ 'deep pink',
+ 'deep sky blue',
+ 'dim gray',
+ 'dodger blue',
+ 'firebrick',
+ 'floral white',
+ 'forest green',
+ 'fuchsia',
+ 'gainsboro',
+ 'ghost white',
+ 'gold',
+ 'goldenrod',
+ 'gray',
+ 'green',
+ 'green yellow',
+ 'honeydew',
+ 'hot pink',
+ 'indian red',
+ 'indigo',
+ 'ivory',
+ 'khaki',
+ 'lavender',
+ 'lavender blush',
+ 'lawn green',
+ 'lemon chiffon',
+ 'light blue',
+ 'light coral',
+ 'light cyan',
+ 'light goldenrod yellow',
+ 'light green',
+ 'light grey',
+ 'light pink',
+ 'light salmon',
+ 'light sea green',
+ 'light sky blue',
+ 'light slate gray',
+ 'light steel blue',
+ 'light yellow',
+ 'lime',
+ 'lime green',
+ 'linen',
+ 'magenta',
+ 'maroon',
+ 'medium aquamarine',
+ 'medium blue',
+ 'medium orchid',
+ 'medium purple',
+ 'medium sea green',
+ 'medium slate blue',
+ 'medium spring green',
+ 'medium turquoise',
+ 'medium violet red',
+ 'midnight blue',
+ 'mint cream',
+ 'misty rose',
+ 'moccasin',
+ 'navajo white',
+ 'navy',
+ 'old lace',
+ 'olive',
+ 'olive drab',
+ 'orange',
+ 'orange red',
+ 'orchid',
+ 'pale goldenrod',
+ 'pale green',
+ 'pale turquoise',
+ 'pale violet red',
+ 'papaya whip',
+ 'peach puff',
+ 'peru',
+ 'pink',
+ 'plum',
+ 'powder blue',
+ 'purple',
+ 'red',
+ 'rosy brown',
+ 'royal blue',
+ 'saddle brown',
+ 'salmon',
+ 'sandy brown',
+ 'sea green',
+ 'seashell',
+ 'sienna',
+ 'silver',
+ 'sky blue',
+ 'slate blue',
+ 'slate gray',
+ 'snow',
+ 'spring green',
+ 'steel blue',
+ 'tan',
+ 'teal',
+ 'thistle',
+ 'tomato',
+ 'turquoise',
+ 'violet',
+ 'wheat',
+ 'white',
+ 'white smoke',
+ 'yellow',
+ 'yellow green',
+]);
diff --git a/lib/types/workspace.d.ts b/lib/types/workspace.d.ts
index afc1522..9c46c15 100644
--- a/lib/types/workspace.d.ts
+++ b/lib/types/workspace.d.ts
@@ -10,3 +10,16 @@ export type WorkspaceMap = {
export type MonitorMap = {
[key: number]: string;
};
+
+export type WorkspaceIcons = {
+ [key: string]: string;
+};
+
+export type WorkspaceIconsColored = {
+ [key: string]: {
+ color: string;
+ icon: string;
+ };
+};
+
+export type WorkspaceIconMap = WorkspaceIcons | WorkspaceIconsColored;
diff --git a/lib/utils.ts b/lib/utils.ts
index 925f035..8c50008 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -10,6 +10,7 @@ import GdkPixbuf from 'gi://GdkPixbuf';
import { NotificationArgs } from 'types/utils/notify';
import { SubstituteKeys } from './types/utils';
import { Window } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
+import { namedColors } from './constants/colors';
export type Binding = import('types/service').Binding;
@@ -166,3 +167,25 @@ export function getPosition(pos: NotificationAnchor | OSDAnchor): ('top' | 'bott
return positionMap[pos] || ['top'];
}
+export const isValidGjsColor = (color: string): boolean => {
+ const colorLower = color.toLowerCase().trim();
+
+ if (namedColors.has(colorLower)) {
+ return true;
+ }
+
+ const hexColorRegex = /^#(?:[a-fA-F0-9]{3,4}|[a-fA-F0-9]{6,8})$/;
+
+ const rgbRegex = /^rgb\(\s*(\d{1,3}%?\s*,\s*){2}\d{1,3}%?\s*\)$/;
+ const rgbaRegex = /^rgba\(\s*(\d{1,3}%?\s*,\s*){3}(0|1|0?\.\d+)\s*\)$/;
+
+ if (hexColorRegex.test(color)) {
+ return true;
+ }
+
+ if (rgbRegex.test(colorLower) || rgbaRegex.test(colorLower)) {
+ return true;
+ }
+
+ return false;
+};
diff --git a/modules/bar/workspaces/index.ts b/modules/bar/workspaces/index.ts
index 586c8b9..161d292 100644
--- a/modules/bar/workspaces/index.ts
+++ b/modules/bar/workspaces/index.ts
@@ -1,20 +1,10 @@
-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 { createThrottledScrollHandlers, getCurrentMonitorWorkspaces } from './helpers';
import { BarBoxChild, SelfButton } from 'lib/types/bar';
+import { occupiedWses } from './variants/occupied';
+import { defaultWses } from './variants/default';
-const { workspaces, monitorSpecific, workspaceMask, scroll_speed, spacing } = options.bar.workspaces;
-
-function range(length: number, start = 1): number[] {
- return Array.from({ length }, (_, i) => i + start);
-}
+const { workspaces, scroll_speed } = options.bar.workspaces;
const Workspaces = (monitor = -1): BarBoxChild => {
const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces(monitor));
@@ -23,249 +13,12 @@ const Workspaces = (monitor = -1): BarBoxChild => {
currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces(monitor);
});
- 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';
- };
-
- 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;
- }
- if ((hyprland.getWorkspace(i)?.windows || 0) > 0) {
- return occupied;
- }
- if (monitor !== -1) {
- return available;
- }
- }
- return workspaceMask ? `${index + 1}` : `${i}`;
- };
- const defaultWses = (): 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,
- );
- });
- },
- }),
- });
- });
- },
- ),
- });
- };
- 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: 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),
- setup: (self) => {
- 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())),
+ .as((hideUnoccupied) => (hideUnoccupied ? occupiedWses(monitor) : defaultWses(monitor))),
}),
isVisible: true,
boxClass: 'workspaces',
diff --git a/modules/bar/workspaces/utils.ts b/modules/bar/workspaces/utils.ts
index 81b3d96..ed29582 100644
--- a/modules/bar/workspaces/utils.ts
+++ b/modules/bar/workspaces/utils.ts
@@ -1,18 +1,60 @@
+import { WorkspaceIconMap } from 'lib/types/workspace';
+import { isValidGjsColor } from 'lib/utils';
+
const hyprland = await Service.import('hyprland');
+const getWsIcon = (wsIconMap: WorkspaceIconMap, i: number): string => {
+ const iconEntry = wsIconMap[i];
+
+ if (!iconEntry) {
+ return `${i}`;
+ }
+
+ const hasIcon = typeof iconEntry === 'object' && 'icon' in iconEntry && iconEntry.icon !== '';
+
+ if (typeof iconEntry === 'string' && iconEntry !== '') {
+ return iconEntry;
+ }
+
+ if (hasIcon) {
+ return iconEntry.icon;
+ }
+
+ return `${i}`;
+};
+
+export const getWsColor = (wsIconMap: WorkspaceIconMap, i: number): string => {
+ const iconEntry = wsIconMap[i];
+ if (!iconEntry) {
+ return '';
+ }
+
+ const hasColor = typeof iconEntry === 'object' && 'color' in iconEntry && iconEntry.color !== '';
+ if (hasColor && isValidGjsColor(iconEntry.color)) {
+ return `color: ${iconEntry.color}; border-bottom-color: ${iconEntry.color};`;
+ }
+ return '';
+};
+
export const renderClassnames = (
showIcons: boolean,
showNumbered: boolean,
numberedActiveIndicator: string,
+ showWsIcons: boolean,
i: number,
): string => {
if (showIcons) {
return `workspace-icon txt-icon bar`;
}
- if (showNumbered) {
- const numActiveInd = hyprland.active.workspace.id === i ? numberedActiveIndicator : '';
+ if (showNumbered || showWsIcons) {
+ const numActiveInd = hyprland.active.workspace.id === i ? `${numberedActiveIndicator}` : '';
- return `workspace-number can_${numberedActiveIndicator} ${numActiveInd}`;
+ const className =
+ `workspace-number can_${numberedActiveIndicator} ` +
+ `${numActiveInd} ` +
+ `${showWsIcons ? 'txt-icon' : ''}`;
+
+ return className;
}
return 'default';
};
@@ -23,6 +65,8 @@ export const renderLabel = (
active: string,
occupied: string,
workspaceMask: boolean,
+ showWsIcons: boolean,
+ wsIconMap: WorkspaceIconMap,
i: number,
index: number,
monitor: number,
@@ -38,6 +82,8 @@ export const renderLabel = (
return available;
}
}
-
+ if (showWsIcons) {
+ return getWsIcon(wsIconMap, i);
+ }
return workspaceMask ? `${index + 1}` : `${i}`;
};
diff --git a/modules/bar/workspaces/variants/default.ts b/modules/bar/workspaces/variants/default.ts
index 141212d..ccd985b 100644
--- a/modules/bar/workspaces/variants/default.ts
+++ b/modules/bar/workspaces/variants/default.ts
@@ -3,6 +3,8 @@ import options from 'options';
import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers';
import { range } from 'lib/utils';
import { BoxWidget } from 'lib/types/widget';
+import { getWsColor, renderClassnames, renderLabel } from '../utils';
+import { WorkspaceIconMap } from 'lib/types/workspace';
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
export const defaultWses = (monitor: number): BoxWidget => {
@@ -30,28 +32,48 @@ export const defaultWses = (monitor: number): BoxWidget => {
child: Widget.Label({
attribute: i,
vpack: 'center',
- css: spacing.bind('value').as((sp) => `margin: 0rem ${0.375 * sp}rem;`),
+ css: Utils.merge(
+ [
+ spacing.bind('value'),
+ options.bar.workspaces.showWsIcons.bind('value'),
+ options.bar.workspaces.workspaceIconMap.bind('value'),
+ options.theme.matugen.bind('value'),
+ ],
+ (
+ sp: number,
+ showWsIcons: boolean,
+ workspaceIconMap: WorkspaceIconMap,
+ matugen: boolean,
+ ) => {
+ return (
+ `margin: 0rem ${0.375 * sp}rem;` +
+ `${showWsIcons && !matugen ? getWsColor(workspaceIconMap, i) : ''}`
+ );
+ },
+ ),
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.showWsIcons.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';
+ (
+ showIcons: boolean,
+ showNumbered: boolean,
+ numberedActiveIndicator: string,
+ showWsIcons: boolean,
+ ) => {
+ return renderClassnames(
+ showIcons,
+ showNumbered,
+ numberedActiveIndicator,
+ showWsIcons,
+ i,
+ );
},
),
label: Utils.merge(
@@ -60,6 +82,8 @@ export const defaultWses = (monitor: number): BoxWidget => {
options.bar.workspaces.icons.available.bind('value'),
options.bar.workspaces.icons.active.bind('value'),
options.bar.workspaces.icons.occupied.bind('value'),
+ options.bar.workspaces.workspaceIconMap.bind('value'),
+ options.bar.workspaces.showWsIcons.bind('value'),
workspaceMask.bind('value'),
hyprland.active.workspace.bind('id'),
],
@@ -68,20 +92,22 @@ export const defaultWses = (monitor: number): BoxWidget => {
available: string,
active: string,
occupied: string,
+ wsIconMap: WorkspaceIconMap,
+ showWsIcons: boolean,
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}`;
+ return renderLabel(
+ showIcons,
+ available,
+ active,
+ occupied,
+ workspaceMask,
+ showWsIcons,
+ wsIconMap,
+ i,
+ index,
+ monitor,
+ );
},
),
setup: (self) => {
diff --git a/modules/bar/workspaces/variants/occupied.ts b/modules/bar/workspaces/variants/occupied.ts
index a7efe95..028ec4a 100644
--- a/modules/bar/workspaces/variants/occupied.ts
+++ b/modules/bar/workspaces/variants/occupied.ts
@@ -2,9 +2,10 @@ 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 { getWsColor, renderClassnames, renderLabel } from '../utils';
import { range } from 'lib/utils';
import { BoxWidget } from 'lib/types/widget';
+import { WorkspaceIconMap } from 'lib/types/workspace';
const { workspaces, monitorSpecific, workspaceMask, spacing } = options.bar.workspaces;
@@ -24,6 +25,9 @@ export const occupiedWses = (monitor: number): BoxWidget => {
options.bar.workspaces.numbered_active_indicator.bind('value'),
spacing.bind('value'),
hyprland.active.workspace.bind('id'),
+ options.bar.workspaces.workspaceIconMap.bind('value'),
+ options.bar.workspaces.showWsIcons.bind('value'),
+ options.theme.matugen.bind('value'),
],
(
monitorSpecific: boolean,
@@ -38,6 +42,9 @@ export const occupiedWses = (monitor: number): BoxWidget => {
numberedActiveIndicator: string,
spacing: number,
activeId: number,
+ wsIconMap: WorkspaceIconMap,
+ showWsIcons: boolean,
+ matugen: boolean,
) => {
let allWkspcs = range(totalWkspcs || 8);
@@ -46,7 +53,10 @@ export const occupiedWses = (monitor: number): BoxWidget => {
// 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 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);
@@ -89,14 +99,24 @@ export const occupiedWses = (monitor: number): BoxWidget => {
child: Widget.Label({
attribute: i,
vpack: 'center',
- css: `margin: 0rem ${0.375 * spacing}rem;`,
- class_name: renderClassnames(showIcons, showNumbered, numberedActiveIndicator, i),
+ css:
+ `margin: 0rem ${0.375 * spacing}rem;` +
+ `${showWsIcons && !matugen ? getWsColor(wsIconMap, i) : ''}`,
+ class_name: renderClassnames(
+ showIcons,
+ showNumbered,
+ numberedActiveIndicator,
+ showWsIcons,
+ i,
+ ),
label: renderLabel(
showIcons,
available,
active,
occupied,
workspaceMask,
+ showWsIcons,
+ wsIconMap,
i,
index,
monitor,
diff --git a/options.ts b/options.ts
index 14e2dfd..104e7c7 100644
--- a/options.ts
+++ b/options.ts
@@ -20,6 +20,7 @@ import {
} from 'lib/types/options';
import { MatugenScheme, MatugenTheme, MatugenVariations } from 'lib/types/options';
import { UnitType } from 'lib/types/weather';
+import { WorkspaceIcons, WorkspaceIconsColored } from 'lib/types/workspace';
// WARN: CHANGING THESE VALUES WILL PREVENT MATUGEN COLOR GENERATION FOR THE CHANGED VALUE
export const colors = {
@@ -191,6 +192,7 @@ const options = mkOptions(OPTIONS, {
numbered_active_highlighted_text_color: opt(colors.mantle),
numbered_active_underline_color: opt(colors.pink),
spacing: opt('0.5em'),
+ fontSize: opt('1.2em'),
},
windowtitle: {
background: opt(colors.base2),
@@ -810,12 +812,14 @@ const options = mkOptions(OPTIONS, {
workspaces: {
show_icons: opt(false),
show_numbered: opt(false),
+ showWsIcons: opt(false),
numbered_active_indicator: opt('underline'),
icons: {
available: opt(''),
active: opt('ï„‘'),
occupied: opt(''),
},
+ workspaceIconMap: opt({}),
workspaces: opt(10),
spacing: opt(1),
monitorSpecific: opt(true),
diff --git a/scss/style/bar/workspace.scss b/scss/style/bar/workspace.scss
index 776c7f5..3480e87 100644
--- a/scss/style/bar/workspace.scss
+++ b/scss/style/bar/workspace.scss
@@ -1,81 +1,82 @@
.workspaces {
- label {
- font-size: 0.2em;
- min-width: 4em;
- min-height: 4em;
- border-radius: 1.9rem * .6;
- transition: 300ms * .5;
- background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available);
- color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available);
+ label {
+ font-size: 0.2em;
+ min-width: 4em;
+ min-height: 4em;
+ border-radius: 1.9rem * 0.6;
+ transition: 300ms * 0.5;
+ background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available);
+ color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available);
- &.occupied {
- background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied);
- color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied);
- min-width: 4em;
- min-height: 4em;
- }
+ &.occupied {
+ background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied);
+ color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied);
+ min-width: 4em;
+ min-height: 4em;
+ }
- &.active {
- color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active);
- background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active);
- min-width: 12em;
- min-height: 4em;
- }
+ &.active {
+ color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active);
+ background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-active);
+ min-width: 12em;
+ min-height: 4em;
+ }
- &.workspace-icon {
- background-color: transparent;
- min-width: 0em;
- min-height: 0em;
- border-radius: 0em;
- transition: 300ms * .5;
- font-size: 1em;
- }
+ &.workspace-icon {
+ background-color: transparent;
+ min-width: 0em;
+ min-height: 0em;
+ border-radius: 0em;
+ transition: 300ms * 0.5;
+ font-size: 1em;
+ }
- &.workspace-number {
- background-color: transparent;
- min-width: 0em;
- min-height: 0em;
- border-radius: 0em;
- transition: 0ms;
- padding: 0em 0.2em;
- font-size: 1.2em;
- }
+ &.workspace-number {
+ background-color: transparent;
+ min-width: 0em;
+ min-height: 0em;
+ border-radius: 0em;
+ transition: 0ms;
+ padding: 0em 0.2em;
+ font-size: $bar-buttons-workspaces-fontSize;
+ }
- &.underline {
- border-top: 0.1em solid transparent;
- border-bottom: 0.1em solid $bar-buttons-workspaces-numbered_active_underline_color;
- }
+ &.underline {
+ border-top: 0.1em solid transparent;
+ border-bottom: 0.1em solid $bar-buttons-workspaces-numbered_active_underline_color;
+ }
- &.highlight {
- color: $bar-buttons-workspaces-numbered_active_highlighted_text_color;
- border-radius: $bar-buttons-workspaces-numbered_active_highlight_border;
- background-color: $bar-buttons-workspaces-active;
- padding: 0em $bar-buttons-workspaces-numbered_active_highlight_padding;
+ &.highlight {
+ color: $bar-buttons-workspaces-numbered_active_highlighted_text_color;
+ border-radius: $bar-buttons-workspaces-numbered_active_highlight_border;
+ background-color: $bar-buttons-workspaces-active;
+ padding: 0em $bar-buttons-workspaces-numbered_active_highlight_padding;
+ }
}
- }
}
.workspace-button {
- &:hover label {
- color: $bar-buttons-workspaces-hover;
+ &:hover label {
+ color: $bar-buttons-workspaces-hover;
- &.default {
- background-color: $bar-buttons-workspaces-hover;
+ &.default {
+ background-color: $bar-buttons-workspaces-hover;
+ }
}
- }
- &:hover .can_underline {
- border-top: 0.1em solid transparent;
- border-bottom: 0.1em solid if($bar-buttons-monochrome, $bar-buttons-workspaces-hover, $bar-buttons-workspaces-hover);
- }
+ &:hover .can_underline {
+ border-top: 0.1em solid transparent;
+ border-bottom: 0.1em solid
+ if($bar-buttons-monochrome, $bar-buttons-workspaces-hover, $bar-buttons-workspaces-hover);
+ }
- &:hover .can_highlight {
- background-color: $bar-buttons-workspaces-hover;
- color: $bar-buttons-workspaces-numbered_active_highlighted_text_color;
- border-radius: $bar-buttons-workspaces-numbered_active_highlight_border;
- }
+ &:hover .can_highlight {
+ background-color: $bar-buttons-workspaces-hover;
+ color: $bar-buttons-workspaces-numbered_active_highlighted_text_color;
+ border-radius: $bar-buttons-workspaces-numbered_active_highlight_border;
+ }
}
.style2.workspaces {
- padding: $bar-buttons-padding_y $bar-buttons-padding_x;
+ padding: $bar-buttons-padding_y $bar-buttons-padding_x;
}
diff --git a/widget/settings/pages/config/bar/index.ts b/widget/settings/pages/config/bar/index.ts
index ef685c7..a213b88 100644
--- a/widget/settings/pages/config/bar/index.ts
+++ b/widget/settings/pages/config/bar/index.ts
@@ -136,6 +136,14 @@ export const BarSettings = (): Scrollable => {
******************************
*/
Header('Workspaces'),
+ Option({
+ opt: options.theme.bar.buttons.workspaces.fontSize,
+ title: 'Indicator Size',
+ subtitle:
+ 'Only applicable to numbered workspaces and mapped icons\n' +
+ 'Adjust with caution as it may cause the bar to expand',
+ type: 'string',
+ }),
Option({
opt: options.bar.workspaces.show_icons,
title: 'Show Workspace Icons',
@@ -180,6 +188,16 @@ export const BarSettings = (): Scrollable => {
subtitle: 'Only applicable if Workspace Numbers are enabled',
type: 'string',
}),
+ Option({
+ opt: options.bar.workspaces.showWsIcons,
+ title: 'Map Workspaces to Icons',
+ type: 'boolean',
+ }),
+ Option({
+ opt: options.bar.workspaces.workspaceIconMap,
+ title: 'Workspace Icon Mappings',
+ type: 'object',
+ }),
Option({
opt: options.bar.workspaces.spacing,
title: 'Spacing',
diff --git a/widget/settings/side_effects/index.ts b/widget/settings/side_effects/index.ts
index a93d03b..6e268c1 100644
--- a/widget/settings/side_effects/index.ts
+++ b/widget/settings/side_effects/index.ts
@@ -1,6 +1,6 @@
import options from 'options';
-const { show_numbered, show_icons } = options.bar.workspaces;
+const { show_numbered, show_icons, showWsIcons } = options.bar.workspaces;
const { monochrome: monoBar } = options.theme.bar.buttons;
const { monochrome: monoMenu } = options.theme.bar.menus;
const { matugen } = options.theme;
@@ -8,12 +8,21 @@ const { matugen } = options.theme;
show_numbered.connect('changed', ({ value }) => {
if (value === true) {
show_icons.value = false;
+ showWsIcons.value = false;
}
});
show_icons.connect('changed', ({ value }) => {
if (value === true) {
show_numbered.value = false;
+ showWsIcons.value = false;
+ }
+});
+
+showWsIcons.connect('changed', ({ value }) => {
+ if (value === true) {
+ show_numbered.value = false;
+ show_icons.value = false;
}
});