From c01ddce201b5f9523c2e5dace3c6a5b69bf33bec Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Wed, 25 Sep 2024 00:49:08 -0700 Subject: [PATCH] Added the ability to ignore workspaces by number or regex --- lib/types/options.d.ts | 3 + modules/bar/workspaces/helpers.ts | 123 ++++++++++---------- modules/bar/workspaces/variants/default.ts | 13 ++- modules/bar/workspaces/variants/occupied.ts | 9 +- options.ts | 2 + widget/settings/pages/config/bar/index.ts | 5 + 6 files changed, 87 insertions(+), 68 deletions(-) diff --git a/lib/types/options.d.ts b/lib/types/options.d.ts index beb31f3..e9dd76d 100644 --- a/lib/types/options.d.ts +++ b/lib/types/options.d.ts @@ -215,3 +215,6 @@ export type ColorMapKey = keyof typeof defaultColorMap; export type ColorMapValue = (typeof defaultColorMap)[ColorMapKey]; export type ScalingPriority = 'gdk' | 'hyprland' | 'both'; + +export type IgnoredWorkspace = number | string; +export type IgnoredWorkspaces = IgnoredWorkspace[]; diff --git a/modules/bar/workspaces/helpers.ts b/modules/bar/workspaces/helpers.ts index 26d6d9c..9e3642e 100644 --- a/modules/bar/workspaces/helpers.ts +++ b/modules/bar/workspaces/helpers.ts @@ -1,10 +1,11 @@ const hyprland = await Service.import('hyprland'); +import { IgnoredWorkspace, IgnoredWorkspaces } from 'lib/types/options'; import { MonitorMap, WorkspaceMap, WorkspaceRule } from 'lib/types/workspace'; import options from 'options'; import { Variable } from 'types/variable'; -const { workspaces, reverse_scroll } = options.bar.workspaces; +const { workspaces, reverse_scroll, ignored } = options.bar.workspaces; export const getWorkspacesForMonitor = (curWs: number, wsRules: WorkspaceMap, monitor: number): boolean => { if (!wsRules || !Object.keys(wsRules).length) { @@ -66,63 +67,70 @@ export const getCurrentMonitorWorkspaces = (monitor: number): number[] => { return monitorWorkspaces[currentMonitorName]; }; -export const goToNextWS = (currentMonitorWorkspaces: Variable, activeWorkspaces: boolean): void => { - if (activeWorkspaces === true) { - const activeWses = hyprland.workspaces.filter((ws) => hyprland.active.monitor.id === ws.monitorID); +type ThrottledScrollHandlers = { + throttledScrollUp: () => void; + throttledScrollDown: () => void; +}; - let nextIndex = hyprland.active.workspace.id + 1; - if (nextIndex > activeWses[activeWses.length - 1].id) { - nextIndex = activeWses[0].id; +export const isWorkspaceIgnored = ( + ignoredWorkspaces: Variable, + workspaceNumber: number, +): boolean => { + const ignoredValues = ignoredWorkspaces.value; + + return ignoredValues.some((ignore: IgnoredWorkspace) => { + if (typeof ignore === 'number') { + return ignore === workspaceNumber; } - - hyprland.messageAsync(`dispatch workspace ${nextIndex}`); - } else if (currentMonitorWorkspaces.value === undefined) { - let nextIndex = hyprland.active.workspace.id + 1; - if (nextIndex > workspaces.value) { - nextIndex = 0; + if (typeof ignore === 'string') { + return new RegExp(ignore).test(workspaceNumber.toString()); } + return true; + }); +}; - hyprland.messageAsync(`dispatch workspace ${nextIndex}`); - } else { - const curWorkspace = hyprland.active.workspace.id; - const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); - let nextIndex = indexOfWs + 1; - if (nextIndex >= currentMonitorWorkspaces.value.length) { - nextIndex = 0; +const navigateWorkspace = ( + direction: 'next' | 'prev', + currentMonitorWorkspaces: Variable, + activeWorkspaces: boolean, + ignoredWorkspaces: Variable, +): void => { + const workspacesList = activeWorkspaces + ? hyprland.workspaces.filter((ws) => hyprland.active.monitor.id === ws.monitorID).map((ws) => ws.id) + : currentMonitorWorkspaces.value || Array.from({ length: workspaces.value }, (_, i) => i + 1); + + if (workspacesList.length === 0) return; + + const currentIndex = workspacesList.indexOf(hyprland.active.workspace.id); + const step = direction === 'next' ? 1 : -1; + let newIndex = (currentIndex + step + workspacesList.length) % workspacesList.length; + let attempts = 0; + + while (attempts < workspacesList.length) { + const targetWS = workspacesList[newIndex]; + if (!isWorkspaceIgnored(ignoredWorkspaces, targetWS)) { + hyprland.messageAsync(`dispatch workspace ${targetWS}`); + return; } - - hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`); + newIndex = (newIndex + step + workspacesList.length) % workspacesList.length; + attempts++; } }; -export const goToPrevWS = (currentMonitorWorkspaces: Variable, activeWorkspaces: boolean): void => { - if (activeWorkspaces === true) { - const activeWses = hyprland.workspaces.filter((ws) => hyprland.active.monitor.id === ws.monitorID); +export const goToNextWS = ( + currentMonitorWorkspaces: Variable, + activeWorkspaces: boolean, + ignoredWorkspaces: Variable, +): void => { + navigateWorkspace('next', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces); +}; - let prevIndex = hyprland.active.workspace.id - 1; - if (prevIndex < activeWses[0].id) { - prevIndex = activeWses[activeWses.length - 1].id; - } - - hyprland.messageAsync(`dispatch workspace ${prevIndex}`); - } else if (currentMonitorWorkspaces.value === undefined) { - let prevIndex = hyprland.active.workspace.id - 1; - - if (prevIndex <= 0) { - prevIndex = workspaces.value; - } - - hyprland.messageAsync(`dispatch workspace ${prevIndex}`); - } else { - const curWorkspace = hyprland.active.workspace.id; - const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); - let prevIndex = indexOfWs - 1; - if (prevIndex < 0) { - prevIndex = currentMonitorWorkspaces.value.length - 1; - } - - hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`); - } +export const goToPrevWS = ( + currentMonitorWorkspaces: Variable, + activeWorkspaces: boolean, + ignoredWorkspaces: Variable, +): void => { + navigateWorkspace('prev', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces); }; export function throttle void>(func: T, limit: number): T { @@ -138,29 +146,24 @@ export function throttle void>(func: T, limit: } as T; } -type ThrottledScrollHandlers = { - throttledScrollUp: () => void; - throttledScrollDown: () => void; -}; - export const createThrottledScrollHandlers = ( scrollSpeed: number, currentMonitorWorkspaces: Variable, activeWorkspaces: boolean = false, ): ThrottledScrollHandlers => { const throttledScrollUp = throttle(() => { - if (reverse_scroll.value === true) { - goToPrevWS(currentMonitorWorkspaces, activeWorkspaces); + if (reverse_scroll.value) { + goToPrevWS(currentMonitorWorkspaces, activeWorkspaces, ignored); } else { - goToNextWS(currentMonitorWorkspaces, activeWorkspaces); + goToNextWS(currentMonitorWorkspaces, activeWorkspaces, ignored); } }, 200 / scrollSpeed); const throttledScrollDown = throttle(() => { - if (reverse_scroll.value === true) { - goToNextWS(currentMonitorWorkspaces, activeWorkspaces); + if (reverse_scroll.value) { + goToNextWS(currentMonitorWorkspaces, activeWorkspaces, ignored); } else { - goToPrevWS(currentMonitorWorkspaces, activeWorkspaces); + goToPrevWS(currentMonitorWorkspaces, activeWorkspaces, ignored); } }, 200 / scrollSpeed); diff --git a/modules/bar/workspaces/variants/default.ts b/modules/bar/workspaces/variants/default.ts index ccd985b..2d4057c 100644 --- a/modules/bar/workspaces/variants/default.ts +++ b/modules/bar/workspaces/variants/default.ts @@ -1,24 +1,27 @@ const hyprland = await Service.import('hyprland'); import options from 'options'; -import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers'; +import { getWorkspaceRules, getWorkspacesForMonitor, isWorkspaceIgnored } 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; +const { workspaces, monitorSpecific, workspaceMask, spacing, ignored } = options.bar.workspaces; export const defaultWses = (monitor: number): BoxWidget => { return Widget.Box({ children: Utils.merge( - [workspaces.bind('value'), monitorSpecific.bind()], + [workspaces.bind('value'), monitorSpecific.bind('value'), ignored.bind('value')], (workspaces: number, monitorSpecific: boolean) => { return range(workspaces || 8) - .filter((i) => { + .filter((workspaceNumber) => { if (!monitorSpecific) { return true; } const workspaceRules = getWorkspaceRules(); - return getWorkspacesForMonitor(i, workspaceRules, monitor); + return ( + getWorkspacesForMonitor(workspaceNumber, workspaceRules, monitor) && + !isWorkspaceIgnored(ignored, workspaceNumber) + ); }) .sort((a, b) => { return a - b; diff --git a/modules/bar/workspaces/variants/occupied.ts b/modules/bar/workspaces/variants/occupied.ts index 028ec4a..0a4c72b 100644 --- a/modules/bar/workspaces/variants/occupied.ts +++ b/modules/bar/workspaces/variants/occupied.ts @@ -1,13 +1,13 @@ const hyprland = await Service.import('hyprland'); import options from 'options'; -import { getWorkspaceRules, getWorkspacesForMonitor } from '../helpers'; +import { getWorkspaceRules, getWorkspacesForMonitor, isWorkspaceIgnored } from '../helpers'; import { Workspace } from 'types/service/hyprland'; 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; +const { workspaces, monitorSpecific, workspaceMask, spacing, ignored } = options.bar.workspaces; export const occupiedWses = (monitor: number): BoxWidget => { return Widget.Box({ @@ -28,6 +28,7 @@ export const occupiedWses = (monitor: number): BoxWidget => { options.bar.workspaces.workspaceIconMap.bind('value'), options.bar.workspaces.showWsIcons.bind('value'), options.theme.matugen.bind('value'), + ignored.bind('value'), ], ( monitorSpecific: boolean, @@ -61,7 +62,6 @@ export const occupiedWses = (monitor: number): BoxWidget => { 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[]); @@ -87,6 +87,9 @@ export const occupiedWses = (monitor: number): BoxWidget => { } return allWkspcs + .filter((workspaceNumber) => { + return !isWorkspaceIgnored(ignored, workspaceNumber); + }) .sort((a, b) => { return a - b; }) diff --git a/options.ts b/options.ts index 7a5bd92..2b84bd3 100644 --- a/options.ts +++ b/options.ts @@ -13,6 +13,7 @@ import { ActiveWsIndicator, BarButtonStyles, BarLocation, + IgnoredWorkspaces, NotificationAnchor, OSDAnchor, OSDOrientation, @@ -861,6 +862,7 @@ const options = mkOptions(OPTIONS, { }, workspaces: { show_icons: opt(false), + ignored: opt([]), show_numbered: opt(false), showWsIcons: opt(false), numbered_active_indicator: opt('underline'), diff --git a/widget/settings/pages/config/bar/index.ts b/widget/settings/pages/config/bar/index.ts index 3b88553..b8f5b81 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -297,6 +297,11 @@ export const BarSettings = (): Scrollable => { title: 'Scrolling Speed', type: 'number', }), + Option({ + opt: options.bar.workspaces.ignored, + title: 'Ignored Workspaces', + type: 'object', + }), /* ******************************