diff --git a/modules/bar/workspaces/helpers.ts b/modules/bar/workspaces/helpers.ts index 26d6d9c..0e0229d 100644 --- a/modules/bar/workspaces/helpers.ts +++ b/modules/bar/workspaces/helpers.ts @@ -4,7 +4,7 @@ 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 +66,61 @@ 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 => { + if (ignoredWorkspaces.value === '') return false; + + const ignoredWsRegex = new RegExp(ignoredWorkspaces.value); + + return ignoredWsRegex.test(workspaceNumber.toString()); +}; + +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 ${nextIndex}`); - } else if (currentMonitorWorkspaces.value === undefined) { - let nextIndex = hyprland.active.workspace.id + 1; - if (nextIndex > workspaces.value) { - nextIndex = 0; - } - - 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; - } - - 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 +136,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..c2a2f60 100644 --- a/options.ts +++ b/options.ts @@ -861,6 +861,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..7dd3a80 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -297,6 +297,12 @@ export const BarSettings = (): Scrollable => { title: 'Scrolling Speed', type: 'number', }), + Option({ + opt: options.bar.workspaces.ignored, + title: 'Ignored Workspaces', + subtitle: 'A regex that defines workspaces to ignore', + type: 'string', + }), /* ******************************