Workspaces now show up on their appropriate monitors. (#681)
* Workspaces now show up on their appropriate monitors. * Fixed undefined rules showing up.
This commit is contained in:
@@ -315,7 +315,6 @@ in
|
|||||||
bar.workspaces.applicationIconEmptyWorkspace = mkStrOption "";
|
bar.workspaces.applicationIconEmptyWorkspace = mkStrOption "";
|
||||||
bar.workspaces.applicationIconFallback = mkStrOption "";
|
bar.workspaces.applicationIconFallback = mkStrOption "";
|
||||||
bar.workspaces.applicationIconOncePerWorkspace = mkBoolOption true;
|
bar.workspaces.applicationIconOncePerWorkspace = mkBoolOption true;
|
||||||
bar.workspaces.hideUnoccupied = mkBoolOption true;
|
|
||||||
bar.workspaces.icons.active = mkStrOption "";
|
bar.workspaces.icons.active = mkStrOption "";
|
||||||
bar.workspaces.icons.available = mkStrOption "";
|
bar.workspaces.icons.available = mkStrOption "";
|
||||||
bar.workspaces.icons.occupied = mkStrOption "";
|
bar.workspaces.icons.occupied = mkStrOption "";
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ import { layoutMap } from './layouts';
|
|||||||
* This function parses the provided JSON string to extract the keyboard layout information.
|
* This function parses the provided JSON string to extract the keyboard layout information.
|
||||||
* It returns the layout in the specified format, either as a code or a human-readable string.
|
* It returns the layout in the specified format, either as a code or a human-readable string.
|
||||||
*
|
*
|
||||||
* @param obj The JSON string containing the keyboard layout information.
|
* @param layoutData The JSON string containing the keyboard layout information.
|
||||||
* @param format The format in which to return the layout, either 'code' or 'label'.
|
* @param format The format in which to return the layout, either 'code' or 'label'.
|
||||||
*
|
*
|
||||||
* @returns The keyboard layout in the specified format. If no keyboards are found, returns 'Unknown' or 'Unknown Layout'.
|
* @returns The keyboard layout in the specified format. If no keyboards are found, returns 'Unknown' or 'Unknown Layout'.
|
||||||
*/
|
*/
|
||||||
export const getKeyboardLayout = (obj: string, format: KbLabelType): LayoutKeys | LayoutValues => {
|
export const getKeyboardLayout = (layoutData: string, format: KbLabelType): LayoutKeys | LayoutValues => {
|
||||||
const hyprctlDevices: HyprctlDeviceLayout = JSON.parse(obj);
|
const hyprctlDevices: HyprctlDeviceLayout = JSON.parse(layoutData);
|
||||||
const keyboards = hyprctlDevices['keyboards'];
|
const keyboards = hyprctlDevices['keyboards'];
|
||||||
|
|
||||||
if (keyboards.length === 0) {
|
if (keyboards.length === 0) {
|
||||||
|
|||||||
@@ -4,13 +4,22 @@ import { Module } from '../../shared/Module';
|
|||||||
import { inputHandler } from 'src/components/bar/utils/helpers';
|
import { inputHandler } from 'src/components/bar/utils/helpers';
|
||||||
import { getKeyboardLayout } from './helpers';
|
import { getKeyboardLayout } from './helpers';
|
||||||
import { BarBoxChild } from 'src/lib/types/bar';
|
import { BarBoxChild } from 'src/lib/types/bar';
|
||||||
import { bind, execAsync } from 'astal';
|
import { bind } from 'astal';
|
||||||
import { useHook } from 'src/lib/shared/hookHandler';
|
import { useHook } from 'src/lib/shared/hookHandler';
|
||||||
import { Astal } from 'astal/gtk3';
|
import { Astal } from 'astal/gtk3';
|
||||||
|
|
||||||
const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } =
|
const { label, labelType, icon, leftClick, rightClick, middleClick, scrollUp, scrollDown } =
|
||||||
options.bar.customModules.kbLayout;
|
options.bar.customModules.kbLayout;
|
||||||
|
|
||||||
|
function setLabel(self: Astal.Label): void {
|
||||||
|
try {
|
||||||
|
const devices = hyprlandService.message('j/devices');
|
||||||
|
self.label = getKeyboardLayout(devices, labelType.get());
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const KbInput = (): BarBoxChild => {
|
export const KbInput = (): BarBoxChild => {
|
||||||
const keyboardModule = Module({
|
const keyboardModule = Module({
|
||||||
textIcon: bind(icon),
|
textIcon: bind(icon),
|
||||||
@@ -20,25 +29,13 @@ export const KbInput = (): BarBoxChild => {
|
|||||||
self,
|
self,
|
||||||
hyprlandService,
|
hyprlandService,
|
||||||
() => {
|
() => {
|
||||||
execAsync('hyprctl devices -j')
|
setLabel(self);
|
||||||
.then((obj) => {
|
|
||||||
self.label = getKeyboardLayout(obj, labelType.get());
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
'keyboard-layout',
|
'keyboard-layout',
|
||||||
);
|
);
|
||||||
|
|
||||||
useHook(self, labelType, () => {
|
useHook(self, labelType, () => {
|
||||||
execAsync('hyprctl devices -j')
|
setLabel(self);
|
||||||
.then((obj) => {
|
|
||||||
self.label = getKeyboardLayout(obj, labelType.get());
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
boxClass: 'kblayout',
|
boxClass: 'kblayout',
|
||||||
|
|||||||
@@ -1,350 +1,384 @@
|
|||||||
import { exec, Variable } from 'astal';
|
import { Variable } from 'astal';
|
||||||
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
||||||
import { hyprlandService } from 'src/lib/constants/services';
|
import { hyprlandService } from 'src/lib/constants/services';
|
||||||
import { MonitorMap, WorkspaceMap, WorkspaceRule } from 'src/lib/types/workspace';
|
import { MonitorMap, WorkspaceMonitorMap, WorkspaceRule } from 'src/lib/types/workspace';
|
||||||
import { range } from 'src/lib/utils';
|
import { range } from 'src/lib/utils';
|
||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
|
|
||||||
const { workspaces, reverse_scroll, ignored } = options.bar.workspaces;
|
const { workspaces, reverse_scroll, ignored } = options.bar.workspaces;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the workspaces for a specific monitor.
|
* A Variable that holds the current map of monitors to the workspace numbers assigned to them.
|
||||||
*
|
|
||||||
* This function checks if a given workspace is valid for a specified monitor based on the workspace rules.
|
|
||||||
*
|
|
||||||
* @param curWs - The current workspace number.
|
|
||||||
* @param wsRules - The workspace rules map.
|
|
||||||
* @param monitor - The monitor ID.
|
|
||||||
* @param workspaceList - The list of workspaces.
|
|
||||||
* @param monitorList - The list of monitors.
|
|
||||||
*
|
|
||||||
* @returns Whether the workspace is valid for the monitor.
|
|
||||||
*/
|
*/
|
||||||
export const getWorkspacesForMonitor = (
|
export const workspaceRules = Variable(getWorkspaceMonitorMap());
|
||||||
curWs: number,
|
|
||||||
wsRules: WorkspaceMap,
|
/**
|
||||||
monitor: number,
|
* A Variable used to force UI or other updates when relevant workspace events occur.
|
||||||
|
*/
|
||||||
|
export const forceUpdater = Variable(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the workspace numbers associated with a specific monitor.
|
||||||
|
*
|
||||||
|
* If only one monitor exists, this will simply return a list of all possible workspaces.
|
||||||
|
* Otherwise, it will consult the workspace rules to determine which workspace numbers
|
||||||
|
* belong to the specified monitor.
|
||||||
|
*
|
||||||
|
* @param monitorId - The numeric identifier of the monitor.
|
||||||
|
*
|
||||||
|
* @returns An array of workspace numbers belonging to the specified monitor.
|
||||||
|
*/
|
||||||
|
export function getWorkspacesForMonitor(monitorId: number): number[] {
|
||||||
|
const allMonitors = hyprlandService.get_monitors();
|
||||||
|
|
||||||
|
if (allMonitors.length === 1) {
|
||||||
|
return Array.from({ length: workspaces.get() }, (_, index) => index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceMonitorRules = getWorkspaceMonitorMap();
|
||||||
|
|
||||||
|
const monitorNameMap: MonitorMap = {};
|
||||||
|
allMonitors.forEach((monitorInstance) => {
|
||||||
|
monitorNameMap[monitorInstance.id] = monitorInstance.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentMonitorName = monitorNameMap[monitorId];
|
||||||
|
|
||||||
|
return workspaceMonitorRules[currentMonitorName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given workspace is valid (assigned) for the specified monitor.
|
||||||
|
*
|
||||||
|
* This function inspects the workspace rules object to determine if the current workspace belongs
|
||||||
|
* to the target monitor. If no workspace rules exist, the function defaults to returning `true`.
|
||||||
|
*
|
||||||
|
* @param workspaceId - The number representing the current workspace.
|
||||||
|
* @param workspaceMonitorRules - The map of monitor names to assigned workspace numbers.
|
||||||
|
* @param monitorId - The numeric identifier for the monitor.
|
||||||
|
* @param workspaceList - A list of Hyprland workspace objects.
|
||||||
|
* @param monitorList - A list of Hyprland monitor objects.
|
||||||
|
*
|
||||||
|
* @returns `true` if the workspace is assigned to the monitor or if no rules exist. Otherwise, `false`.
|
||||||
|
*/
|
||||||
|
function isWorkspaceValidForMonitor(
|
||||||
|
workspaceId: number,
|
||||||
|
workspaceMonitorRules: WorkspaceMonitorMap,
|
||||||
|
monitorId: number,
|
||||||
workspaceList: AstalHyprland.Workspace[],
|
workspaceList: AstalHyprland.Workspace[],
|
||||||
monitorList: AstalHyprland.Monitor[],
|
monitorList: AstalHyprland.Monitor[],
|
||||||
): boolean => {
|
): boolean {
|
||||||
if (!wsRules || !Object.keys(wsRules).length) {
|
const monitorNameMap: MonitorMap = {};
|
||||||
return true;
|
const allWorkspaceInstances = workspaceList ?? [];
|
||||||
}
|
|
||||||
|
|
||||||
const monitorMap: MonitorMap = {};
|
const workspaceMonitorReferences = allWorkspaceInstances
|
||||||
|
.filter((workspaceInstance) => workspaceInstance !== null)
|
||||||
const wsList = workspaceList ?? [];
|
.map((workspaceInstance) => {
|
||||||
|
return {
|
||||||
const workspaceMonitorList = wsList
|
id: workspaceInstance.monitor?.id,
|
||||||
.filter((m) => m !== null)
|
name: workspaceInstance.monitor?.name,
|
||||||
.map((m) => {
|
};
|
||||||
return { id: m.monitor?.id, name: m.monitor?.name };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const monitors = [...new Map([...workspaceMonitorList, ...monitorList].map((item) => [item.id, item])).values()];
|
const mergedMonitorInstances = [
|
||||||
|
...new Map(
|
||||||
|
[...workspaceMonitorReferences, ...monitorList].map((monitorCandidate) => [
|
||||||
|
monitorCandidate.id,
|
||||||
|
monitorCandidate,
|
||||||
|
]),
|
||||||
|
).values(),
|
||||||
|
];
|
||||||
|
|
||||||
monitors.forEach((mon) => (monitorMap[mon.id] = mon.name));
|
mergedMonitorInstances.forEach((monitorInstance) => {
|
||||||
|
monitorNameMap[monitorInstance.id] = monitorInstance.name;
|
||||||
|
});
|
||||||
|
|
||||||
const currentMonitorName = monitorMap[monitor];
|
const currentMonitorName = monitorNameMap[monitorId];
|
||||||
const monitorWSRules = wsRules[currentMonitorName];
|
const currentMonitorWorkspaceRules = workspaceMonitorRules[currentMonitorName];
|
||||||
|
|
||||||
if (monitorWSRules === undefined) {
|
if (currentMonitorWorkspaceRules === undefined) {
|
||||||
return true;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentMonitorWorkspaceRules.includes(workspaceId);
|
||||||
}
|
}
|
||||||
return monitorWSRules.includes(curWs);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the workspace rules.
|
* Fetches a map of monitors to the workspace numbers that belong to them.
|
||||||
*
|
*
|
||||||
* This function fetches and parses the workspace rules from the Hyprland service.
|
* This function communicates with the Hyprland service to retrieve workspace rules in JSON format.
|
||||||
|
* Those rules are parsed, and a map of monitor names to lists of assigned workspace numbers is constructed.
|
||||||
*
|
*
|
||||||
* @returns The workspace rules map.
|
* @returns An object where each key is a monitor name, and each value is an array of workspace numbers.
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceRules = (): WorkspaceMap => {
|
function getWorkspaceMonitorMap(): WorkspaceMonitorMap {
|
||||||
try {
|
try {
|
||||||
const rules = exec('hyprctl workspacerules -j');
|
const rulesResponse = hyprlandService.message('j/workspacerules');
|
||||||
|
const workspaceMonitorRules: WorkspaceMonitorMap = {};
|
||||||
|
const parsedWorkspaceRules = JSON.parse(rulesResponse);
|
||||||
|
|
||||||
const workspaceRules: WorkspaceMap = {};
|
parsedWorkspaceRules.forEach((rule: WorkspaceRule) => {
|
||||||
|
const workspaceNumber = parseInt(rule.workspaceString, 10);
|
||||||
|
|
||||||
JSON.parse(rules).forEach((rule: WorkspaceRule) => {
|
if (rule.monitor === undefined || isNaN(workspaceNumber)) {
|
||||||
const workspaceNum = parseInt(rule.workspaceString, 10);
|
|
||||||
if (isNaN(workspaceNum)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Object.hasOwnProperty.call(workspaceRules, rule.monitor)) {
|
|
||||||
workspaceRules[rule.monitor].push(workspaceNum);
|
const doesMonitorExistInRules = Object.hasOwnProperty.call(workspaceMonitorRules, rule.monitor);
|
||||||
|
|
||||||
|
if (doesMonitorExistInRules) {
|
||||||
|
workspaceMonitorRules[rule.monitor].push(workspaceNumber);
|
||||||
} else {
|
} else {
|
||||||
workspaceRules[rule.monitor] = [workspaceNum];
|
workspaceMonitorRules[rule.monitor] = [workspaceNumber];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return workspaceRules;
|
return workspaceMonitorRules;
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
console.error(err);
|
console.error(error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the current monitor's workspaces.
|
|
||||||
*
|
|
||||||
* This function returns a list of workspace numbers for the specified monitor.
|
|
||||||
*
|
|
||||||
* @param monitor - The monitor ID.
|
|
||||||
*
|
|
||||||
* @returns The list of workspace numbers.
|
|
||||||
*/
|
|
||||||
export const getCurrentMonitorWorkspaces = (monitor: number): number[] => {
|
|
||||||
if (hyprlandService.get_monitors().length === 1) {
|
|
||||||
return Array.from({ length: workspaces.get() }, (_, i) => i + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const monitorWorkspaces = getWorkspaceRules();
|
|
||||||
const monitorMap: MonitorMap = {};
|
|
||||||
hyprlandService.get_monitors().forEach((m) => (monitorMap[m.id] = m.name));
|
|
||||||
|
|
||||||
const currentMonitorName = monitorMap[monitor];
|
|
||||||
|
|
||||||
return monitorWorkspaces[currentMonitorName];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a workspace is ignored.
|
* Checks if a workspace number should be ignored based on a regular expression.
|
||||||
*
|
*
|
||||||
* This function determines if a given workspace number is in the ignored workspaces list.
|
* @param ignoredWorkspacesVariable - A Variable object containing a string pattern of ignored workspaces.
|
||||||
|
* @param workspaceNumber - The numeric representation of the workspace to check.
|
||||||
*
|
*
|
||||||
* @param ignoredWorkspaces - The ignored workspaces variable.
|
* @returns `true` if the workspace should be ignored, otherwise `false`.
|
||||||
* @param workspaceNumber - The workspace number.
|
|
||||||
*
|
|
||||||
* @returns Whether the workspace is ignored.
|
|
||||||
*/
|
*/
|
||||||
export const isWorkspaceIgnored = (ignoredWorkspaces: Variable<string>, workspaceNumber: number): boolean => {
|
function isWorkspaceIgnored(ignoredWorkspacesVariable: Variable<string>, workspaceNumber: number): boolean {
|
||||||
if (ignoredWorkspaces.get() === '') return false;
|
if (ignoredWorkspacesVariable.get() === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const ignoredWsRegex = new RegExp(ignoredWorkspaces.get());
|
const ignoredWorkspacesRegex = new RegExp(ignoredWorkspacesVariable.get());
|
||||||
|
return ignoredWorkspacesRegex.test(workspaceNumber.toString());
|
||||||
return ignoredWsRegex.test(workspaceNumber.toString());
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the next or previous workspace.
|
* Changes the active workspace in the specified direction ('next' or 'prev').
|
||||||
*
|
*
|
||||||
* This function changes the current workspace to the next or previous one, considering active and ignored workspaces.
|
* This function uses the current monitor's set of active or assigned workspaces and
|
||||||
|
* cycles through them in the chosen direction. It also respects the list of ignored
|
||||||
|
* workspaces, skipping any that match the ignored pattern.
|
||||||
*
|
*
|
||||||
* @param direction - The direction to navigate ('next' or 'prev').
|
* @param direction - The direction to navigate ('next' or 'prev').
|
||||||
* @param currentMonitorWorkspaces - The current monitor's workspaces variable.
|
* @param currentMonitorWorkspacesVariable - A Variable containing an array of workspace numbers for the current monitor.
|
||||||
* @param activeWorkspaces - Whether to consider only active workspaces.
|
* @param onlyActiveWorkspaces - Whether to only include active (occupied) workspaces when navigating.
|
||||||
* @param ignoredWorkspaces - The ignored workspaces variable.
|
* @param ignoredWorkspacesVariable - A Variable that contains the ignored workspaces pattern.
|
||||||
*/
|
*/
|
||||||
const navigateWorkspace = (
|
function navigateWorkspace(
|
||||||
direction: 'next' | 'prev',
|
direction: 'next' | 'prev',
|
||||||
currentMonitorWorkspaces: Variable<number[]>,
|
currentMonitorWorkspacesVariable: Variable<number[]>,
|
||||||
activeWorkspaces: boolean,
|
onlyActiveWorkspaces: boolean,
|
||||||
ignoredWorkspaces: Variable<string>,
|
ignoredWorkspacesVariable: Variable<string>,
|
||||||
): void => {
|
): void {
|
||||||
const hyprlandWorkspaces = hyprlandService.get_workspaces() || [];
|
const allHyprlandWorkspaces = hyprlandService.get_workspaces() || [];
|
||||||
const occupiedWorkspaces = hyprlandWorkspaces
|
|
||||||
.filter((ws) => hyprlandService.focusedMonitor.id === ws.monitor?.id)
|
|
||||||
.map((ws) => ws.id);
|
|
||||||
|
|
||||||
const workspacesList = activeWorkspaces
|
const activeWorkspaceIds = allHyprlandWorkspaces
|
||||||
? occupiedWorkspaces
|
.filter((workspaceInstance) => hyprlandService.focusedMonitor.id === workspaceInstance.monitor?.id)
|
||||||
: currentMonitorWorkspaces.get() || Array.from({ length: workspaces.get() }, (_, i) => i + 1);
|
.map((workspaceInstance) => workspaceInstance.id);
|
||||||
|
|
||||||
if (workspacesList.length === 0) return;
|
const assignedOrOccupiedWorkspaces = onlyActiveWorkspaces
|
||||||
|
? activeWorkspaceIds
|
||||||
|
: currentMonitorWorkspacesVariable.get() || Array.from({ length: workspaces.get() }, (_, index) => index + 1);
|
||||||
|
|
||||||
const currentIndex = workspacesList.indexOf(hyprlandService.focusedWorkspace?.id);
|
if (assignedOrOccupiedWorkspaces.length === 0) {
|
||||||
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)) {
|
|
||||||
hyprlandService.dispatch('workspace', targetWS.toString());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newIndex = (newIndex + step + workspacesList.length) % workspacesList.length;
|
|
||||||
|
const workspaceIndex = assignedOrOccupiedWorkspaces.indexOf(hyprlandService.focusedWorkspace?.id);
|
||||||
|
const step = direction === 'next' ? 1 : -1;
|
||||||
|
|
||||||
|
let newIndex = (workspaceIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
|
||||||
|
let attempts = 0;
|
||||||
|
|
||||||
|
while (attempts < assignedOrOccupiedWorkspaces.length) {
|
||||||
|
const targetWorkspaceNumber = assignedOrOccupiedWorkspaces[newIndex];
|
||||||
|
if (!isWorkspaceIgnored(ignoredWorkspacesVariable, targetWorkspaceNumber)) {
|
||||||
|
hyprlandService.dispatch('workspace', targetWorkspaceNumber.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newIndex = (newIndex + step + assignedOrOccupiedWorkspaces.length) % assignedOrOccupiedWorkspaces.length;
|
||||||
attempts++;
|
attempts++;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the next workspace.
|
* Navigates to the next workspace in the current monitor.
|
||||||
*
|
*
|
||||||
* This function changes the current workspace to the next one.
|
* @param currentMonitorWorkspacesVariable - A Variable containing workspace numbers for the current monitor.
|
||||||
*
|
* @param onlyActiveWorkspaces - Whether to only navigate among active (occupied) workspaces.
|
||||||
* @param currentMonitorWorkspaces - The current monitor's workspaces variable.
|
* @param ignoredWorkspacesVariable - A Variable that contains the ignored workspaces pattern.
|
||||||
* @param activeWorkspaces - Whether to consider only active workspaces.
|
|
||||||
* @param ignoredWorkspaces - The ignored workspaces variable.
|
|
||||||
*/
|
*/
|
||||||
export const goToNextWS = (
|
export function goToNextWorkspace(
|
||||||
currentMonitorWorkspaces: Variable<number[]>,
|
currentMonitorWorkspacesVariable: Variable<number[]>,
|
||||||
activeWorkspaces: boolean,
|
onlyActiveWorkspaces: boolean,
|
||||||
ignoredWorkspaces: Variable<string>,
|
ignoredWorkspacesVariable: Variable<string>,
|
||||||
): void => {
|
): void {
|
||||||
navigateWorkspace('next', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces);
|
navigateWorkspace('next', currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignoredWorkspacesVariable);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the previous workspace.
|
* Navigates to the previous workspace in the current monitor.
|
||||||
*
|
*
|
||||||
* This function changes the current workspace to the previous one.
|
* @param currentMonitorWorkspacesVariable - A Variable containing workspace numbers for the current monitor.
|
||||||
*
|
* @param onlyActiveWorkspaces - Whether to only navigate among active (occupied) workspaces.
|
||||||
* @param currentMonitorWorkspaces - The current monitor's workspaces variable.
|
* @param ignoredWorkspacesVariable - A Variable that contains the ignored workspaces pattern.
|
||||||
* @param activeWorkspaces - Whether to consider only active workspaces.
|
|
||||||
* @param ignoredWorkspaces - The ignored workspaces variable.
|
|
||||||
*/
|
*/
|
||||||
export const goToPrevWS = (
|
export function goToPreviousWorkspace(
|
||||||
currentMonitorWorkspaces: Variable<number[]>,
|
currentMonitorWorkspacesVariable: Variable<number[]>,
|
||||||
activeWorkspaces: boolean,
|
onlyActiveWorkspaces: boolean,
|
||||||
ignoredWorkspaces: Variable<string>,
|
ignoredWorkspacesVariable: Variable<string>,
|
||||||
): void => {
|
): void {
|
||||||
navigateWorkspace('prev', currentMonitorWorkspaces, activeWorkspaces, ignoredWorkspaces);
|
navigateWorkspace('prev', currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignoredWorkspacesVariable);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throttles a function to limit its execution rate.
|
* Limits the execution rate of a given function to prevent it from being called too often.
|
||||||
*
|
*
|
||||||
* This function ensures that the provided function is not called more often than the specified limit.
|
* @param func - The function to be throttled.
|
||||||
|
* @param limit - The time limit (in milliseconds) during which calls to `func` are disallowed after the first call.
|
||||||
*
|
*
|
||||||
* @param func - The function to throttle.
|
* @returns The throttled version of the input function.
|
||||||
* @param limit - The time limit in milliseconds.
|
|
||||||
*
|
|
||||||
* @returns The throttled function.
|
|
||||||
*/
|
*/
|
||||||
export function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number): T {
|
export function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number): T {
|
||||||
let inThrottle: boolean;
|
let isThrottleActive: boolean;
|
||||||
|
|
||||||
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
||||||
if (!inThrottle) {
|
if (!isThrottleActive) {
|
||||||
func.apply(this, args);
|
func.apply(this, args);
|
||||||
inThrottle = true;
|
isThrottleActive = true;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
inThrottle = false;
|
isThrottleActive = false;
|
||||||
}, limit);
|
}, limit);
|
||||||
}
|
}
|
||||||
} as T;
|
} as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates throttled scroll handlers for navigating workspaces.
|
* Creates throttled scroll handlers that navigate workspaces upon scrolling, respecting the configured scroll speed.
|
||||||
*
|
*
|
||||||
* This function returns handlers for scrolling up and down through workspaces, throttled by the specified scroll speed.
|
* @param scrollSpeed - The factor by which the scroll navigation is throttled.
|
||||||
|
* @param currentMonitorWorkspacesVariable - A Variable containing the current monitor's workspace numbers.
|
||||||
|
* @param onlyActiveWorkspaces - Whether to only navigate among active (occupied) workspaces.
|
||||||
*
|
*
|
||||||
* @param scrollSpeed - The scroll speed.
|
* @returns An object containing two functions (`throttledScrollUp` and `throttledScrollDown`), both throttled.
|
||||||
* @param currentMonitorWorkspaces - The current monitor's workspaces variable.
|
|
||||||
* @param activeWorkspaces - Whether to consider only active workspaces.
|
|
||||||
*
|
|
||||||
* @returns The throttled scroll handlers.
|
|
||||||
*/
|
*/
|
||||||
export const createThrottledScrollHandlers = (
|
export function initThrottledScrollHandlers(
|
||||||
scrollSpeed: number,
|
scrollSpeed: number,
|
||||||
currentMonitorWorkspaces: Variable<number[]>,
|
currentMonitorWorkspacesVariable: Variable<number[]>,
|
||||||
activeWorkspaces: boolean = true,
|
onlyActiveWorkspaces: boolean = true,
|
||||||
): ThrottledScrollHandlers => {
|
): ThrottledScrollHandlers {
|
||||||
const throttledScrollUp = throttle(() => {
|
const throttledScrollUp = throttle(() => {
|
||||||
if (reverse_scroll.get()) {
|
if (reverse_scroll.get()) {
|
||||||
goToPrevWS(currentMonitorWorkspaces, activeWorkspaces, ignored);
|
goToPreviousWorkspace(currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignored);
|
||||||
} else {
|
} else {
|
||||||
goToNextWS(currentMonitorWorkspaces, activeWorkspaces, ignored);
|
goToNextWorkspace(currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignored);
|
||||||
}
|
}
|
||||||
}, 200 / scrollSpeed);
|
}, 200 / scrollSpeed);
|
||||||
|
|
||||||
const throttledScrollDown = throttle(() => {
|
const throttledScrollDown = throttle(() => {
|
||||||
if (reverse_scroll.get()) {
|
if (reverse_scroll.get()) {
|
||||||
goToNextWS(currentMonitorWorkspaces, activeWorkspaces, ignored);
|
goToNextWorkspace(currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignored);
|
||||||
} else {
|
} else {
|
||||||
goToPrevWS(currentMonitorWorkspaces, activeWorkspaces, ignored);
|
goToPreviousWorkspace(currentMonitorWorkspacesVariable, onlyActiveWorkspaces, ignored);
|
||||||
}
|
}
|
||||||
}, 200 / scrollSpeed);
|
}, 200 / scrollSpeed);
|
||||||
|
|
||||||
return { throttledScrollUp, throttledScrollDown };
|
return { throttledScrollUp, throttledScrollDown };
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the workspaces to render.
|
* Computes which workspace numbers should be rendered for a given monitor.
|
||||||
*
|
*
|
||||||
* This function returns a list of workspace numbers to render based on the total workspaces, workspace list, rules, and monitor.
|
* This function consolidates both active and all possible workspaces (based on rules),
|
||||||
|
* then filters them by the selected monitor if `isMonitorSpecific` is set to `true`.
|
||||||
*
|
*
|
||||||
* @param totalWorkspaces - The total number of workspaces.
|
* @param totalWorkspaces - The total number of workspaces (a fallback if workspace rules are not enforced).
|
||||||
* @param workspaceList - The list of workspaces.
|
* @param workspaceInstances - A list of Hyprland workspace objects.
|
||||||
* @param workspaceRules - The workspace rules map.
|
* @param workspaceMonitorRules - The map of monitor names to assigned workspace numbers.
|
||||||
* @param monitor - The monitor ID.
|
* @param monitorId - The numeric identifier of the monitor.
|
||||||
* @param isMonitorSpecific - Whether the workspaces are monitor-specific.
|
* @param isMonitorSpecific - If `true`, only include the workspaces that match this monitor.
|
||||||
* @param monitorList - The list of monitors.
|
* @param hyprlandMonitorInstances - A list of Hyprland monitor objects.
|
||||||
*
|
*
|
||||||
* @returns The list of workspace numbers to render.
|
* @returns An array of workspace numbers that should be shown.
|
||||||
*/
|
*/
|
||||||
export const getWorkspacesToRender = (
|
export function getWorkspacesToRender(
|
||||||
totalWorkspaces: number,
|
totalWorkspaces: number,
|
||||||
workspaceList: AstalHyprland.Workspace[],
|
workspaceInstances: AstalHyprland.Workspace[],
|
||||||
workspaceRules: WorkspaceMap,
|
workspaceMonitorRules: WorkspaceMonitorMap,
|
||||||
monitor: number,
|
monitorId: number,
|
||||||
isMonitorSpecific: boolean,
|
isMonitorSpecific: boolean,
|
||||||
monitorList: AstalHyprland.Monitor[],
|
hyprlandMonitorInstances: AstalHyprland.Monitor[],
|
||||||
): number[] => {
|
): number[] {
|
||||||
let allWorkspaces = range(totalWorkspaces || 8);
|
let allPotentialWorkspaces = range(totalWorkspaces || 8);
|
||||||
const activeWorkspaces = workspaceList.map((ws) => ws.id);
|
const allWorkspaceInstances = workspaceInstances ?? [];
|
||||||
|
|
||||||
const wsList = workspaceList ?? [];
|
const activeWorkspaceIds = allWorkspaceInstances.map((workspaceInstance) => workspaceInstance.id);
|
||||||
const workspaceMonitorList = wsList.map((ws) => {
|
|
||||||
|
const monitorReferencesForActiveWorkspaces = allWorkspaceInstances.map((workspaceInstance) => {
|
||||||
return {
|
return {
|
||||||
id: ws.monitor?.id || -1,
|
id: workspaceInstance.monitor?.id ?? -1,
|
||||||
name: ws.monitor?.name || '',
|
name: workspaceInstance.monitor?.name ?? '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const curMonitor =
|
const currentMonitorInstance =
|
||||||
monitorList.find((mon) => mon.id === monitor) || workspaceMonitorList.find((mon) => mon.id === monitor);
|
hyprlandMonitorInstances.find((monitorObj) => monitorObj.id === monitorId) ||
|
||||||
|
monitorReferencesForActiveWorkspaces.find((monitorObj) => monitorObj.id === monitorId);
|
||||||
|
|
||||||
const workspacesWithRules = Object.keys(workspaceRules).reduce((acc: number[], k: string) => {
|
const allWorkspacesWithRules = Object.keys(workspaceMonitorRules).reduce(
|
||||||
return [...acc, ...workspaceRules[k]];
|
(accumulator: number[], monitorName: string) => {
|
||||||
}, []);
|
return [...accumulator, ...workspaceMonitorRules[monitorName]];
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const activesForMonitor = activeWorkspaces.filter((w) => {
|
const activeWorkspacesForCurrentMonitor = activeWorkspaceIds.filter((workspaceId) => {
|
||||||
if (
|
if (
|
||||||
curMonitor &&
|
currentMonitorInstance &&
|
||||||
Object.hasOwnProperty.call(workspaceRules, curMonitor.name) &&
|
Object.hasOwnProperty.call(workspaceMonitorRules, currentMonitorInstance.name) &&
|
||||||
workspacesWithRules.includes(w)
|
allWorkspacesWithRules.includes(workspaceId)
|
||||||
) {
|
) {
|
||||||
return workspaceRules[curMonitor.name].includes(w);
|
return workspaceMonitorRules[currentMonitorInstance.name].includes(workspaceId);
|
||||||
}
|
}
|
||||||
return true;
|
const metadataForWorkspace = allWorkspaceInstances.find((workspaceObj) => workspaceObj.id === workspaceId);
|
||||||
|
return metadataForWorkspace?.monitor?.id === monitorId;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isMonitorSpecific) {
|
if (isMonitorSpecific) {
|
||||||
const workspacesInRange = range(totalWorkspaces).filter((ws) => {
|
const validWorkspaceNumbers = range(totalWorkspaces).filter((workspaceNumber) => {
|
||||||
return getWorkspacesForMonitor(ws, workspaceRules, monitor, wsList, monitorList);
|
return isWorkspaceValidForMonitor(
|
||||||
|
workspaceNumber,
|
||||||
|
workspaceMonitorRules,
|
||||||
|
monitorId,
|
||||||
|
allWorkspaceInstances,
|
||||||
|
hyprlandMonitorInstances,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
allWorkspaces = [...new Set([...activesForMonitor, ...workspacesInRange])];
|
allPotentialWorkspaces = [...new Set([...activeWorkspacesForCurrentMonitor, ...validWorkspaceNumbers])];
|
||||||
} else {
|
} else {
|
||||||
allWorkspaces = [...new Set([...allWorkspaces, ...activeWorkspaces])];
|
allPotentialWorkspaces = [...new Set([...allPotentialWorkspaces, ...activeWorkspaceIds])];
|
||||||
}
|
}
|
||||||
|
|
||||||
return allWorkspaces.sort((a, b) => a - b);
|
return allPotentialWorkspaces.filter((workspace) => !isWorkspaceIgnored(ignored, workspace)).sort((a, b) => a - b);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The workspace rules variable.
|
* Subscribes to Hyprland service events related to workspaces to keep the local state updated.
|
||||||
* This variable holds the current workspace rules.
|
*
|
||||||
|
* When certain events occur (like a configuration reload or a client being moved/added/removed),
|
||||||
|
* this function updates the workspace rules or toggles the `forceUpdater` variable to ensure
|
||||||
|
* that any dependent UI or logic is re-rendered or re-run.
|
||||||
*/
|
*/
|
||||||
export const workspaceRules = Variable(getWorkspaceRules());
|
export function initWorkspaceEvents(): void {
|
||||||
|
|
||||||
/**
|
|
||||||
* The force updater variable.
|
|
||||||
* This variable is used to force updates when workspace events occur.
|
|
||||||
*/
|
|
||||||
export const forceUpdater = Variable(true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up connections for workspace events.
|
|
||||||
* This function sets up event listeners for various workspace-related events to update the workspace rules and force updates.
|
|
||||||
*/
|
|
||||||
export const setupConnections = (): void => {
|
|
||||||
hyprlandService.connect('config-reloaded', () => {
|
hyprlandService.connect('config-reloaded', () => {
|
||||||
workspaceRules.set(getWorkspaceRules());
|
workspaceRules.set(getWorkspaceMonitorMap());
|
||||||
});
|
});
|
||||||
|
|
||||||
hyprlandService.connect('client-moved', () => {
|
hyprlandService.connect('client-moved', () => {
|
||||||
@@ -358,9 +392,19 @@ export const setupConnections = (): void => {
|
|||||||
hyprlandService.connect('client-removed', () => {
|
hyprlandService.connect('client-removed', () => {
|
||||||
forceUpdater.set(!forceUpdater.get());
|
forceUpdater.set(!forceUpdater.get());
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttled scroll handler functions for navigating workspaces.
|
||||||
|
*/
|
||||||
type ThrottledScrollHandlers = {
|
type ThrottledScrollHandlers = {
|
||||||
|
/**
|
||||||
|
* Scroll up throttled handler.
|
||||||
|
*/
|
||||||
throttledScrollUp: () => void;
|
throttledScrollUp: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll down throttled handler.
|
||||||
|
*/
|
||||||
throttledScrollDown: () => void;
|
throttledScrollDown: () => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces } from './helpers';
|
import { initThrottledScrollHandlers, getWorkspacesForMonitor } from './helpers';
|
||||||
import { BarBoxChild } from 'src/lib/types/bar';
|
import { BarBoxChild } from 'src/lib/types/bar';
|
||||||
import { WorkspaceModule } from './workspaces';
|
import { WorkspaceModule } from './workspaces';
|
||||||
import { bind, Variable } from 'astal';
|
import { bind, Variable } from 'astal';
|
||||||
@@ -10,10 +10,10 @@ import { isScrollDown, isScrollUp } from 'src/lib/utils';
|
|||||||
const { workspaces, scroll_speed } = options.bar.workspaces;
|
const { workspaces, scroll_speed } = options.bar.workspaces;
|
||||||
|
|
||||||
const Workspaces = (monitor = -1): BarBoxChild => {
|
const Workspaces = (monitor = -1): BarBoxChild => {
|
||||||
const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces(monitor));
|
const currentMonitorWorkspaces = Variable(getWorkspacesForMonitor(monitor));
|
||||||
|
|
||||||
workspaces.subscribe(() => {
|
workspaces.subscribe(() => {
|
||||||
currentMonitorWorkspaces.set(getCurrentMonitorWorkspaces(monitor));
|
currentMonitorWorkspaces.set(getWorkspacesForMonitor(monitor));
|
||||||
});
|
});
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
@@ -35,7 +35,7 @@ const Workspaces = (monitor = -1): BarBoxChild => {
|
|||||||
self.disconnect(scrollHandlers);
|
self.disconnect(scrollHandlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
|
const { throttledScrollUp, throttledScrollDown } = initThrottledScrollHandlers(
|
||||||
scroll_speed,
|
scroll_speed,
|
||||||
currentMonitorWorkspaces,
|
currentMonitorWorkspaces,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { hyprlandService } from 'src/lib/constants/services';
|
import { hyprlandService } from 'src/lib/constants/services';
|
||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
import { forceUpdater, getWorkspacesToRender, isWorkspaceIgnored, setupConnections, workspaceRules } from './helpers';
|
import { forceUpdater, getWorkspacesToRender, initWorkspaceEvents, workspaceRules } from './helpers';
|
||||||
import { getAppIcon, getWsColor, renderClassnames, renderLabel } from './helpers/utils';
|
import { getAppIcon, getWsColor, renderClassnames, renderLabel } from './helpers/utils';
|
||||||
import { ApplicationIcons, WorkspaceIconMap } from 'src/lib/types/workspace';
|
import { ApplicationIcons, WorkspaceIconMap } from 'src/lib/types/workspace';
|
||||||
import { bind, Variable } from 'astal';
|
import { bind, Variable } from 'astal';
|
||||||
@@ -30,7 +30,7 @@ const { available, active, occupied } = options.bar.workspaces.icons;
|
|||||||
const { matugen } = options.theme;
|
const { matugen } = options.theme;
|
||||||
const { smartHighlight } = options.theme.bar.buttons.workspaces;
|
const { smartHighlight } = options.theme.bar.buttons.workspaces;
|
||||||
|
|
||||||
setupConnections();
|
initWorkspaceEvents();
|
||||||
|
|
||||||
export const WorkspaceModule = ({ monitor }: WorkspaceModuleProps): JSX.Element => {
|
export const WorkspaceModule = ({ monitor }: WorkspaceModuleProps): JSX.Element => {
|
||||||
const boxChildren = Variable.derive(
|
const boxChildren = Variable.derive(
|
||||||
@@ -98,10 +98,6 @@ export const WorkspaceModule = ({ monitor }: WorkspaceModuleProps): JSX.Element
|
|||||||
);
|
);
|
||||||
|
|
||||||
return workspacesToRender.map((wsId, index) => {
|
return workspacesToRender.map((wsId, index) => {
|
||||||
if (isWorkspaceIgnored(ignored, wsId)) {
|
|
||||||
return <box />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const appIcons = displayApplicationIcons
|
const appIcons = displayApplicationIcons
|
||||||
? getAppIcon(wsId, appIconOncePerWorkspace, {
|
? getAppIcon(wsId, appIconOncePerWorkspace, {
|
||||||
iconMap: applicationIconMapping,
|
iconMap: applicationIconMapping,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { bind, exec } from 'astal';
|
import { bind, GLib } from 'astal';
|
||||||
import { Gtk } from 'astal/gtk3';
|
import { Gtk } from 'astal/gtk3';
|
||||||
import options from 'src/options.js';
|
import options from 'src/options.js';
|
||||||
import { normalizePath, isAnImage } from 'src/lib/utils.js';
|
import { normalizePath, isAnImage } from 'src/lib/utils.js';
|
||||||
@@ -28,7 +28,8 @@ const ProfileName = (): JSX.Element => {
|
|||||||
halign={Gtk.Align.CENTER}
|
halign={Gtk.Align.CENTER}
|
||||||
label={bind(name).as((profileName) => {
|
label={bind(name).as((profileName) => {
|
||||||
if (profileName === 'system') {
|
if (profileName === 'system') {
|
||||||
return exec('bash -c whoami');
|
const username = GLib.get_user_name();
|
||||||
|
return username;
|
||||||
}
|
}
|
||||||
return profileName;
|
return profileName;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,97 +1,215 @@
|
|||||||
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
|
||||||
|
|
||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
import { bash } from 'src/lib/utils';
|
|
||||||
import { globalEventBoxes } from 'src/globals/dropdown';
|
import { globalEventBoxes } from 'src/globals/dropdown';
|
||||||
import { GLib } from 'astal';
|
import { GLib } from 'astal';
|
||||||
import { EventBox } from 'astal/gtk3/widget';
|
import { EventBox } from 'astal/gtk3/widget';
|
||||||
|
import { hyprlandService } from 'src/lib/constants/services';
|
||||||
const hyprland = AstalHyprland.get_default();
|
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
||||||
|
|
||||||
const { location } = options.theme.bar;
|
const { location } = options.theme.bar;
|
||||||
const { scalingPriority } = options;
|
const { scalingPriority } = options;
|
||||||
|
|
||||||
export const calculateMenuPosition = async (pos: number[], windowName: string): Promise<void> => {
|
/**
|
||||||
|
* Retrieves the dropdown EventBox widget from the global event boxes map using the provided window name.
|
||||||
|
*
|
||||||
|
* @param windowName - A string identifier for the window whose EventBox you want to retrieve.
|
||||||
|
* @returns The EventBox object if found; otherwise, `undefined`.
|
||||||
|
*/
|
||||||
|
function getDropdownEventBox(windowName: string): EventBox | undefined {
|
||||||
|
return globalEventBoxes.get()[windowName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds and returns the currently focused Hyprland monitor object.
|
||||||
|
*
|
||||||
|
* @returns The focused Hyprland monitor, or `undefined` if no match is found.
|
||||||
|
*/
|
||||||
|
function getFocusedHyprlandMonitor(): AstalHyprland.Monitor | undefined {
|
||||||
|
const allMonitors = hyprlandService.get_monitors();
|
||||||
|
return allMonitors.find((monitor) => monitor.id === hyprlandService.focusedMonitor.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the scaled monitor dimensions based on user configuration and environment variables.
|
||||||
|
*
|
||||||
|
* This function applies:
|
||||||
|
* 1. GDK scaling (from the `GDK_SCALE` environment variable).
|
||||||
|
* 2. Hyprland scaling (from the monitor's scale).
|
||||||
|
*
|
||||||
|
* The order in which scaling is applied depends on `scalingPriority`:
|
||||||
|
* - 'both': Apply GDK scale first, then Hyprland scale.
|
||||||
|
* - 'gdk': Apply GDK scale only.
|
||||||
|
* - Otherwise: Apply Hyprland scale only.
|
||||||
|
*
|
||||||
|
* @param width - The original width of the focused Hyprland monitor.
|
||||||
|
* @param height - The original height of the focused Hyprland monitor.
|
||||||
|
* @param monitorScaling - The scale factor reported by Hyprland for this monitor.
|
||||||
|
* @returns An object containing `adjustedWidth` and `adjustedHeight` after scaling is applied.
|
||||||
|
*/
|
||||||
|
function applyMonitorScaling(width: number, height: number, monitorScaling: number): MonitorScaling {
|
||||||
|
const gdkEnvScale = GLib.getenv('GDK_SCALE') || '1';
|
||||||
|
const userScalingPriority = scalingPriority.get();
|
||||||
|
|
||||||
|
let adjustedWidth = width;
|
||||||
|
let adjustedHeight = height;
|
||||||
|
|
||||||
|
if (userScalingPriority === 'both') {
|
||||||
|
const gdkScaleValue = parseFloat(gdkEnvScale);
|
||||||
|
adjustedWidth /= gdkScaleValue;
|
||||||
|
adjustedHeight /= gdkScaleValue;
|
||||||
|
|
||||||
|
adjustedWidth /= monitorScaling;
|
||||||
|
adjustedHeight /= monitorScaling;
|
||||||
|
} else if (/^\d+(\.\d+)?$/.test(gdkEnvScale) && userScalingPriority === 'gdk') {
|
||||||
|
const gdkScaleValue = parseFloat(gdkEnvScale);
|
||||||
|
adjustedWidth /= gdkScaleValue;
|
||||||
|
adjustedHeight /= gdkScaleValue;
|
||||||
|
} else {
|
||||||
|
adjustedWidth /= monitorScaling;
|
||||||
|
adjustedHeight /= monitorScaling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { adjustedWidth, adjustedHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Corrects monitor dimensions if the monitor is rotated (vertical orientation),
|
||||||
|
* which requires swapping the width and height.
|
||||||
|
*
|
||||||
|
* @param monitorWidth - The monitor width after scaling.
|
||||||
|
* @param monitorHeight - The monitor height after scaling.
|
||||||
|
* @param isVertical - Whether the monitor transform indicates a vertical rotation.
|
||||||
|
* @returns The appropriately adjusted width and height.
|
||||||
|
*/
|
||||||
|
function adjustForVerticalTransform(
|
||||||
|
monitorWidth: number,
|
||||||
|
monitorHeight: number,
|
||||||
|
isVertical: boolean,
|
||||||
|
): TransformedDimensions {
|
||||||
|
if (!isVertical) {
|
||||||
|
return { finalWidth: monitorWidth, finalHeight: monitorHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { finalWidth: monitorHeight, finalHeight: monitorWidth };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the left and right margins required to place the dropdown in the correct position
|
||||||
|
* relative to the monitor width and the desired anchor X coordinate.
|
||||||
|
*
|
||||||
|
* @param monitorWidth - The width of the monitor (already scaled).
|
||||||
|
* @param dropdownWidth - The width of the dropdown widget.
|
||||||
|
* @param anchorX - The X coordinate (in scaled pixels) around which the dropdown should be placed.
|
||||||
|
* @returns An object containing `leftMargin` and `rightMargin`, ensuring they do not go below 0.
|
||||||
|
*/
|
||||||
|
function calculateHorizontalMargins(monitorWidth: number, dropdownWidth: number, anchorX: number): HorizontalMargins {
|
||||||
|
const minimumSpacing = 0;
|
||||||
|
|
||||||
|
let rightMarginSpacing = monitorWidth - dropdownWidth / 2;
|
||||||
|
rightMarginSpacing -= anchorX;
|
||||||
|
|
||||||
|
let leftMarginSpacing = monitorWidth - dropdownWidth - rightMarginSpacing;
|
||||||
|
|
||||||
|
if (rightMarginSpacing < minimumSpacing) {
|
||||||
|
rightMarginSpacing = minimumSpacing;
|
||||||
|
leftMarginSpacing = monitorWidth - dropdownWidth - minimumSpacing;
|
||||||
|
}
|
||||||
|
if (leftMarginSpacing < minimumSpacing) {
|
||||||
|
leftMarginSpacing = minimumSpacing;
|
||||||
|
rightMarginSpacing = monitorWidth - dropdownWidth - minimumSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { leftMargin: leftMarginSpacing, rightMargin: rightMarginSpacing };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Positions the dropdown vertically based on the global bar location setting.
|
||||||
|
* If the bar is positioned at the top, the dropdown is placed at the top (margin_top = 0).
|
||||||
|
* Otherwise, it's placed at the bottom.
|
||||||
|
*
|
||||||
|
* @param dropdownEventBox - The EventBox representing the dropdown.
|
||||||
|
* @param monitorHeight - The scaled (and possibly swapped) monitor height.
|
||||||
|
* @param dropdownHeight - The height of the dropdown widget.
|
||||||
|
*/
|
||||||
|
function setVerticalPosition(dropdownEventBox: EventBox, monitorHeight: number, dropdownHeight: number): void {
|
||||||
|
if (location.get() === 'top') {
|
||||||
|
dropdownEventBox.set_margin_top(0);
|
||||||
|
dropdownEventBox.set_margin_bottom(monitorHeight);
|
||||||
|
} else {
|
||||||
|
dropdownEventBox.set_margin_bottom(0);
|
||||||
|
dropdownEventBox.set_margin_top(monitorHeight - dropdownHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the position of a dropdown menu (event box) based on the focused monitor, scaling preferences,
|
||||||
|
* and the bar location setting. It ensures the dropdown is accurately placed either at the top or bottom
|
||||||
|
* of the screen within monitor boundaries, respecting both GDK scaling and Hyprland scaling.
|
||||||
|
*
|
||||||
|
* @param positionCoordinates - An array of `[x, y]` values representing the anchor position at which
|
||||||
|
* the dropdown should ideally appear (only the X coordinate is used here).
|
||||||
|
* @param windowName - A string that identifies the window in the globalEventBoxes map.
|
||||||
|
*
|
||||||
|
* @returns A Promise that resolves once the dropdown position is fully calculated and set.
|
||||||
|
*/
|
||||||
|
export const calculateMenuPosition = async (positionCoordinates: number[], windowName: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const self = globalEventBoxes.get()[windowName] as EventBox;
|
const dropdownEventBox = getDropdownEventBox(windowName);
|
||||||
|
|
||||||
const curHyprlandMonitor = hyprland.get_monitors().find((m) => m.id === hyprland.focusedMonitor.id);
|
if (!dropdownEventBox) {
|
||||||
|
|
||||||
const dropdownWidth = self.get_child()?.get_allocation().width ?? 0;
|
|
||||||
const dropdownHeight = self.get_child()?.get_allocation().height ?? 0;
|
|
||||||
|
|
||||||
let hyprScaling = 1;
|
|
||||||
const monitorInfo = await bash('hyprctl monitors -j');
|
|
||||||
const parsedMonitorInfo = JSON.parse(monitorInfo);
|
|
||||||
|
|
||||||
const foundMonitor = parsedMonitorInfo.find(
|
|
||||||
(monitor: AstalHyprland.Monitor) => monitor.id === hyprland.focusedMonitor.id,
|
|
||||||
);
|
|
||||||
hyprScaling = foundMonitor?.scale || 1;
|
|
||||||
|
|
||||||
let monWidth = curHyprlandMonitor?.width;
|
|
||||||
let monHeight = curHyprlandMonitor?.height;
|
|
||||||
|
|
||||||
if (monWidth === undefined || monHeight === undefined || hyprScaling === undefined) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If GDK Scaling is applied, then get divide width by scaling
|
const focusedHyprlandMonitor = getFocusedHyprlandMonitor();
|
||||||
// to get the proper coordinates.
|
|
||||||
// Ex: On a 2860px wide monitor... if scaling is set to 2, then the right
|
|
||||||
// end of the monitor is the 1430th pixel.
|
|
||||||
const gdkScale = GLib.getenv('GDK_SCALE') || '1';
|
|
||||||
|
|
||||||
if (scalingPriority.get() === 'both') {
|
if (!focusedHyprlandMonitor) {
|
||||||
const scale = parseFloat(gdkScale);
|
return;
|
||||||
monWidth = monWidth / scale;
|
|
||||||
monHeight = monHeight / scale;
|
|
||||||
|
|
||||||
monWidth = monWidth / hyprScaling;
|
|
||||||
monHeight = monHeight / hyprScaling;
|
|
||||||
} else if (/^\d+(.\d+)?$/.test(gdkScale) && scalingPriority.get() === 'gdk') {
|
|
||||||
const scale = parseFloat(gdkScale);
|
|
||||||
monWidth = monWidth / scale;
|
|
||||||
monHeight = monHeight / scale;
|
|
||||||
} else {
|
|
||||||
monWidth = monWidth / hyprScaling;
|
|
||||||
monHeight = monHeight / hyprScaling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If monitor is vertical (transform = 1 || 3) swap height and width
|
const dropdownWidth = dropdownEventBox.get_child()?.get_allocation().width ?? 0;
|
||||||
const isVertical = curHyprlandMonitor?.transform !== undefined ? curHyprlandMonitor.transform % 2 !== 0 : false;
|
const dropdownHeight = dropdownEventBox.get_child()?.get_allocation().height ?? 0;
|
||||||
|
|
||||||
if (isVertical) {
|
const monitorScaling = focusedHyprlandMonitor.scale || 1;
|
||||||
[monWidth, monHeight] = [monHeight, monWidth];
|
const { width: rawMonitorWidth, height: rawMonitorHeight, transform } = focusedHyprlandMonitor;
|
||||||
|
|
||||||
|
if (!rawMonitorWidth || !rawMonitorHeight) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let marginRight = monWidth - dropdownWidth / 2;
|
const { adjustedWidth, adjustedHeight } = applyMonitorScaling(
|
||||||
marginRight = marginRight - pos[0];
|
rawMonitorWidth,
|
||||||
let marginLeft = monWidth - dropdownWidth - marginRight;
|
rawMonitorHeight,
|
||||||
|
monitorScaling,
|
||||||
|
);
|
||||||
|
|
||||||
const minimumMargin = 0;
|
const isVertical = transform !== undefined ? transform % 2 !== 0 : false;
|
||||||
|
const { finalWidth, finalHeight } = adjustForVerticalTransform(adjustedWidth, adjustedHeight, isVertical);
|
||||||
|
|
||||||
if (marginRight < minimumMargin) {
|
const { leftMargin, rightMargin } = calculateHorizontalMargins(
|
||||||
marginRight = minimumMargin;
|
finalWidth,
|
||||||
marginLeft = monWidth - dropdownWidth - minimumMargin;
|
dropdownWidth,
|
||||||
}
|
positionCoordinates[0],
|
||||||
|
);
|
||||||
|
|
||||||
if (marginLeft < minimumMargin) {
|
dropdownEventBox.set_margin_left(leftMargin);
|
||||||
marginLeft = minimumMargin;
|
dropdownEventBox.set_margin_right(rightMargin);
|
||||||
marginRight = monWidth - dropdownWidth - minimumMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_margin_left(marginLeft);
|
setVerticalPosition(dropdownEventBox, finalHeight, dropdownHeight);
|
||||||
self.set_margin_right(marginRight);
|
} catch (caughtError) {
|
||||||
|
console.error(`Error getting menu position: ${caughtError}`);
|
||||||
if (location.get() === 'top') {
|
|
||||||
self.set_margin_top(0);
|
|
||||||
self.set_margin_bottom(monHeight);
|
|
||||||
} else {
|
|
||||||
self.set_margin_bottom(0);
|
|
||||||
self.set_margin_top(monHeight - dropdownHeight);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error getting menu position: ${error}`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type HorizontalMargins = {
|
||||||
|
leftMargin: number;
|
||||||
|
rightMargin: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MonitorScaling = {
|
||||||
|
adjustedWidth: number;
|
||||||
|
adjustedHeight: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TransformedDimensions = {
|
||||||
|
finalWidth: number;
|
||||||
|
finalHeight: number;
|
||||||
|
};
|
||||||
|
|||||||
@@ -140,12 +140,71 @@ export const BarSettings = (): JSX.Element => {
|
|||||||
{/* Workspaces Section */}
|
{/* Workspaces Section */}
|
||||||
<Header title="Workspaces" />
|
<Header title="Workspaces" />
|
||||||
<Option opt={options.theme.bar.buttons.workspaces.enableBorder} title="Button Border" type="boolean" />
|
<Option opt={options.theme.bar.buttons.workspaces.enableBorder} title="Button Border" type="boolean" />
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.monitorSpecific}
|
||||||
|
title="Monitor Specific"
|
||||||
|
subtitle="Only workspaces of the monitor are shown."
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<Option opt={options.bar.workspaces.show_icons} title="Show Workspace Icons" type="boolean" />
|
||||||
|
<Option opt={options.bar.workspaces.show_numbered} title="Show Workspace Numbers" type="boolean" />
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.workspaceMask}
|
||||||
|
title="Zero-Based Workspace Numbers"
|
||||||
|
subtitle={
|
||||||
|
'Start all workspace numbers from 0 on each monitor.\n' +
|
||||||
|
"Requires 'Show Workspace Numbers' to be enabled."
|
||||||
|
}
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.showWsIcons}
|
||||||
|
title="Map Workspaces to Icons"
|
||||||
|
subtitle="https://hyprpanel.com/configuration/panel.html#show-workspace-icons"
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.showApplicationIcons}
|
||||||
|
title="Map Workspaces to Application Icons"
|
||||||
|
subtitle="Requires 'Map Workspace to Icons' enabled. See docs."
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.applicationIconOncePerWorkspace}
|
||||||
|
title="Hide Duplicate App Icons"
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.workspaces.showAllActive}
|
opt={options.bar.workspaces.showAllActive}
|
||||||
title="Mark Active Workspace On All Monitors"
|
title="Mark Active Workspace On All Monitors"
|
||||||
subtitle="Marks the currently active workspace on each monitor."
|
subtitle="Marks the currently active workspace on each monitor."
|
||||||
type="boolean"
|
type="boolean"
|
||||||
/>
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.bar.workspaces.numbered_active_indicator}
|
||||||
|
title="Numbered Workspace Identifier"
|
||||||
|
subtitle="Only applicable if Workspace Numbers are enabled"
|
||||||
|
type="enum"
|
||||||
|
enums={['underline', 'highlight', 'color']}
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.theme.bar.buttons.workspaces.smartHighlight}
|
||||||
|
title="Smart Highlight"
|
||||||
|
subtitle="Automatically determines highlight color for mapped icons."
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.theme.bar.buttons.workspaces.numbered_active_highlight_border}
|
||||||
|
title="Highlight Radius"
|
||||||
|
subtitle="Only applicable if Workspace Numbers are enabled"
|
||||||
|
type="string"
|
||||||
|
/>
|
||||||
|
<Option
|
||||||
|
opt={options.theme.bar.buttons.workspaces.numbered_active_highlight_padding}
|
||||||
|
title="Highlight Padding"
|
||||||
|
subtitle="Only applicable if Workspace Numbers are enabled"
|
||||||
|
type="string"
|
||||||
|
/>
|
||||||
<Option
|
<Option
|
||||||
opt={options.theme.bar.buttons.workspaces.pill.radius}
|
opt={options.theme.bar.buttons.workspaces.pill.radius}
|
||||||
title="Pill Radius"
|
title="Pill Radius"
|
||||||
@@ -176,53 +235,9 @@ export const BarSettings = (): JSX.Element => {
|
|||||||
subtitle="Only applicable to numbered workspaces and mapped icons. Adjust carefully."
|
subtitle="Only applicable to numbered workspaces and mapped icons. Adjust carefully."
|
||||||
type="string"
|
type="string"
|
||||||
/>
|
/>
|
||||||
<Option opt={options.bar.workspaces.show_icons} title="Show Workspace Icons" type="boolean" />
|
|
||||||
<Option opt={options.bar.workspaces.icons.available} title="Workspace Available" type="string" />
|
<Option opt={options.bar.workspaces.icons.available} title="Workspace Available" type="string" />
|
||||||
<Option opt={options.bar.workspaces.icons.active} title="Workspace Active" type="string" />
|
<Option opt={options.bar.workspaces.icons.active} title="Workspace Active" type="string" />
|
||||||
<Option opt={options.bar.workspaces.icons.occupied} title="Workspace Occupied" type="string" />
|
<Option opt={options.bar.workspaces.icons.occupied} title="Workspace Occupied" type="string" />
|
||||||
<Option opt={options.bar.workspaces.show_numbered} title="Show Workspace Numbers" type="boolean" />
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.numbered_active_indicator}
|
|
||||||
title="Numbered Workspace Identifier"
|
|
||||||
subtitle="Only applicable if Workspace Numbers are enabled"
|
|
||||||
type="enum"
|
|
||||||
enums={['underline', 'highlight', 'color']}
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.theme.bar.buttons.workspaces.smartHighlight}
|
|
||||||
title="Smart Highlight"
|
|
||||||
subtitle="Automatically determines highlight color for mapped icons."
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.theme.bar.buttons.workspaces.numbered_active_highlight_border}
|
|
||||||
title="Highlight Radius"
|
|
||||||
subtitle="Only applicable if Workspace Numbers are enabled"
|
|
||||||
type="string"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.theme.bar.buttons.workspaces.numbered_active_highlight_padding}
|
|
||||||
title="Highlight Padding"
|
|
||||||
subtitle="Only applicable if Workspace Numbers are enabled"
|
|
||||||
type="string"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.showWsIcons}
|
|
||||||
title="Map Workspaces to Icons"
|
|
||||||
subtitle="https://hyprpanel.com/configuration/panel.html#show-workspace-icons"
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.showApplicationIcons}
|
|
||||||
title="Map Workspaces to Application Icons"
|
|
||||||
subtitle="Requires 'Map Workspace to Icons' enabled. See docs."
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.applicationIconOncePerWorkspace}
|
|
||||||
title="Hide Duplicate App Icons"
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.workspaces.applicationIconMap}
|
opt={options.bar.workspaces.applicationIconMap}
|
||||||
title="App Icon Mappings"
|
title="App Icon Mappings"
|
||||||
@@ -254,28 +269,10 @@ export const BarSettings = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.workspaces.workspaces}
|
opt={options.bar.workspaces.workspaces}
|
||||||
title="Total Workspaces"
|
title="Persistent Workspaces"
|
||||||
subtitle="Minimum number of workspaces to always show."
|
subtitle="Requires workspace rules to be defined if 'Monitor Specific' is selected."
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.monitorSpecific}
|
|
||||||
title="Monitor Specific"
|
|
||||||
subtitle="Only workspaces of the monitor are shown."
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.hideUnoccupied}
|
|
||||||
title="Hide Unoccupied"
|
|
||||||
subtitle="Only show occupied or active workspaces"
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
|
||||||
opt={options.bar.workspaces.workspaceMask}
|
|
||||||
title="Mask Workspace Numbers On Monitors"
|
|
||||||
subtitle="For monitor-specific numbering"
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.workspaces.reverse_scroll}
|
opt={options.bar.workspaces.reverse_scroll}
|
||||||
title="Invert Scroll"
|
title="Invert Scroll"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Gtk } from 'astal/gtk3';
|
import { Gtk } from 'astal/gtk3';
|
||||||
import FileChooserButton from 'src/components/shared/FileChooseButton';
|
import FileChooserButton from 'src/components/shared/FileChooserButton';
|
||||||
import { Opt } from 'src/lib/option';
|
import { Opt } from 'src/lib/option';
|
||||||
|
|
||||||
const handleFileSet =
|
const handleFileSet =
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import FileChooserButton from 'src/components/shared/FileChooseButton';
|
import FileChooserButton from 'src/components/shared/FileChooserButton';
|
||||||
import { Opt } from 'src/lib/option';
|
import { Opt } from 'src/lib/option';
|
||||||
import Wallpaper from 'src/services/Wallpaper';
|
import Wallpaper from 'src/services/Wallpaper';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import { EventBox } from 'astal/gtk3/widget';
|
||||||
import Variable from 'astal/variable';
|
import Variable from 'astal/variable';
|
||||||
|
|
||||||
type GlobalEventBoxes = {
|
type GlobalEventBoxes = {
|
||||||
[key: string]: unknown;
|
[key: string]: EventBox;
|
||||||
};
|
};
|
||||||
export const globalEventBoxes: Variable<GlobalEventBoxes> = Variable({});
|
export const globalEventBoxes: Variable<GlobalEventBoxes> = Variable({});
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import { execAsync } from 'astal';
|
|
||||||
import { hyprlandService } from '../constants/services';
|
import { hyprlandService } from '../constants/services';
|
||||||
|
|
||||||
const floatSettingsDialog = (): void => {
|
const floatSettingsDialog = (): void => {
|
||||||
execAsync(['bash', '-c', 'hyprctl keyword windowrulev2 "float, title:^(hyprpanel-settings)$"']);
|
hyprlandService.message(`hyprctl keyword windowrulev2 "float, title:^(hyprpanel-settings)$"`);
|
||||||
|
|
||||||
hyprlandService.connect('config-reloaded', () => {
|
hyprlandService.connect('config-reloaded', () => {
|
||||||
execAsync(['bash', '-c', 'hyprctl keyword windowrulev2 "float, title:^(hyprpanel-settings)$"']);
|
hyprlandService.message(`hyprctl keyword windowrulev2 "float, title:^(hyprpanel-settings)$"`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const floatFilePicker = (): void => {
|
const floatFilePicker = (): void => {
|
||||||
execAsync(['bash', '-c', 'hyprctl keyword windowrulev2 "float, title:^((Save|Import) Hyprpanel.*)$"']);
|
hyprlandService.message(`hyprctl keyword windowrulev2 "float, title:^((Save|Import) Hyprpanel.*)$"`);
|
||||||
|
|
||||||
hyprlandService.connect('config-reloaded', () => {
|
hyprlandService.connect('config-reloaded', () => {
|
||||||
execAsync(['bash', '-c', 'hyprctl keyword windowrulev2 "float, title:^((Save|Import) Hyprpanel.*)$"']);
|
hyprlandService.message(`hyprctl keyword windowrulev2 "float, title:^((Save|Import) Hyprpanel.*)$"`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
src/lib/types/workspace.d.ts
vendored
2
src/lib/types/workspace.d.ts
vendored
@@ -3,7 +3,7 @@ export type WorkspaceRule = {
|
|||||||
monitor: string;
|
monitor: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceMap = {
|
export type WorkspaceMonitorMap = {
|
||||||
[key: string]: number[];
|
[key: string]: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -959,7 +959,6 @@ const options = mkOptions(CONFIG, {
|
|||||||
workspaces: opt(5),
|
workspaces: opt(5),
|
||||||
spacing: opt(1),
|
spacing: opt(1),
|
||||||
monitorSpecific: opt(true),
|
monitorSpecific: opt(true),
|
||||||
hideUnoccupied: opt(true),
|
|
||||||
workspaceMask: opt(false),
|
workspaceMask: opt(false),
|
||||||
reverse_scroll: opt(false),
|
reverse_scroll: opt(false),
|
||||||
scroll_speed: opt(5),
|
scroll_speed: opt(5),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { dependencies, sh } from '../lib/utils';
|
|||||||
import options from '../options';
|
import options from '../options';
|
||||||
import { execAsync } from 'astal/process';
|
import { execAsync } from 'astal/process';
|
||||||
import { monitorFile } from 'astal/file';
|
import { monitorFile } from 'astal/file';
|
||||||
|
import { hyprlandService } from 'src/lib/constants/services';
|
||||||
|
|
||||||
const WP = `${GLib.get_home_dir()}/.config/background`;
|
const WP = `${GLib.get_home_dir()}/.config/background`;
|
||||||
|
|
||||||
@@ -14,8 +15,8 @@ class Wallpaper extends GObject.Object {
|
|||||||
#wallpaper(): void {
|
#wallpaper(): void {
|
||||||
if (!dependencies('swww')) return;
|
if (!dependencies('swww')) return;
|
||||||
|
|
||||||
sh('hyprctl cursorpos')
|
try {
|
||||||
.then((pos) => {
|
const cursorPosition = hyprlandService.message('cursorpos');
|
||||||
const transitionCmd = [
|
const transitionCmd = [
|
||||||
'swww',
|
'swww',
|
||||||
'img',
|
'img',
|
||||||
@@ -27,7 +28,7 @@ class Wallpaper extends GObject.Object {
|
|||||||
'--transition-fps',
|
'--transition-fps',
|
||||||
'30',
|
'30',
|
||||||
'--transition-pos',
|
'--transition-pos',
|
||||||
pos.replace(' ', ''),
|
cursorPosition.replace(' ', ''),
|
||||||
WP,
|
WP,
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
@@ -39,10 +40,9 @@ class Wallpaper extends GObject.Object {
|
|||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('Error setting wallpaper:', err);
|
console.error('Error setting wallpaper:', err);
|
||||||
});
|
});
|
||||||
})
|
} catch (err) {
|
||||||
.catch((err) => {
|
|
||||||
console.error('Error getting cursor position:', err);
|
console.error('Error getting cursor position:', err);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #setWallpaper(path: string): Promise<void> {
|
async #setWallpaper(path: string): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user