From c01ddce201b5f9523c2e5dace3c6a5b69bf33bec Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Wed, 25 Sep 2024 00:49:08 -0700 Subject: [PATCH 1/3] 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', + }), /* ****************************** From 91bd49e1a8cebb7c27eebe9407921ce84a155674 Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Wed, 25 Sep 2024 00:51:11 -0700 Subject: [PATCH 2/3] Update description of option to ignore workspaces. --- widget/settings/pages/config/bar/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/widget/settings/pages/config/bar/index.ts b/widget/settings/pages/config/bar/index.ts index b8f5b81..e5eb5dc 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -300,6 +300,9 @@ export const BarSettings = (): Scrollable => { Option({ opt: options.bar.workspaces.ignored, title: 'Ignored Workspaces', + subtitle: + 'An array of workspace numbers to ignore.\n' + + 'A regular expression can be passed in the array to ignore workspaces that match the regex.', type: 'object', }), From 69c468296b1bdc4d2fe543628a130b507533e908 Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Wed, 25 Sep 2024 01:05:47 -0700 Subject: [PATCH 3/3] Update the ignored workspaces rule to a regex instead of array. --- lib/types/options.d.ts | 3 --- modules/bar/workspaces/helpers.ts | 26 +++++++---------------- options.ts | 3 +-- widget/settings/pages/config/bar/index.ts | 6 ++---- 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/lib/types/options.d.ts b/lib/types/options.d.ts index e9dd76d..beb31f3 100644 --- a/lib/types/options.d.ts +++ b/lib/types/options.d.ts @@ -215,6 +215,3 @@ 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 9e3642e..0e0229d 100644 --- a/modules/bar/workspaces/helpers.ts +++ b/modules/bar/workspaces/helpers.ts @@ -1,6 +1,5 @@ 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'; @@ -72,28 +71,19 @@ type ThrottledScrollHandlers = { throttledScrollDown: () => void; }; -export const isWorkspaceIgnored = ( - ignoredWorkspaces: Variable, - workspaceNumber: number, -): boolean => { - const ignoredValues = ignoredWorkspaces.value; +export const isWorkspaceIgnored = (ignoredWorkspaces: Variable, workspaceNumber: number): boolean => { + if (ignoredWorkspaces.value === '') return false; - return ignoredValues.some((ignore: IgnoredWorkspace) => { - if (typeof ignore === 'number') { - return ignore === workspaceNumber; - } - if (typeof ignore === 'string') { - return new RegExp(ignore).test(workspaceNumber.toString()); - } - return true; - }); + const ignoredWsRegex = new RegExp(ignoredWorkspaces.value); + + return ignoredWsRegex.test(workspaceNumber.toString()); }; const navigateWorkspace = ( direction: 'next' | 'prev', currentMonitorWorkspaces: Variable, activeWorkspaces: boolean, - ignoredWorkspaces: Variable, + ignoredWorkspaces: Variable, ): void => { const workspacesList = activeWorkspaces ? hyprland.workspaces.filter((ws) => hyprland.active.monitor.id === ws.monitorID).map((ws) => ws.id) @@ -120,7 +110,7 @@ const navigateWorkspace = ( export const goToNextWS = ( currentMonitorWorkspaces: Variable, activeWorkspaces: boolean, - ignoredWorkspaces: Variable, + ignoredWorkspaces: Variable, ): void => { navigateWorkspace('next', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces); }; @@ -128,7 +118,7 @@ export const goToNextWS = ( export const goToPrevWS = ( currentMonitorWorkspaces: Variable, activeWorkspaces: boolean, - ignoredWorkspaces: Variable, + ignoredWorkspaces: Variable, ): void => { navigateWorkspace('prev', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces); }; diff --git a/options.ts b/options.ts index 2b84bd3..c2a2f60 100644 --- a/options.ts +++ b/options.ts @@ -13,7 +13,6 @@ import { ActiveWsIndicator, BarButtonStyles, BarLocation, - IgnoredWorkspaces, NotificationAnchor, OSDAnchor, OSDOrientation, @@ -862,7 +861,7 @@ const options = mkOptions(OPTIONS, { }, workspaces: { show_icons: opt(false), - ignored: opt([]), + 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 e5eb5dc..7dd3a80 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -300,10 +300,8 @@ export const BarSettings = (): Scrollable => { Option({ opt: options.bar.workspaces.ignored, title: 'Ignored Workspaces', - subtitle: - 'An array of workspace numbers to ignore.\n' + - 'A regular expression can be passed in the array to ignore workspaces that match the regex.', - type: 'object', + subtitle: 'A regex that defines workspaces to ignore', + type: 'string', }), /*