Upgrade to Agsv2 + Astal (#533)
* migrate to astal * Reorganize project structure. * progress * Migrate Dashboard and Window Title modules. * Migrate clock and notification bar modules. * Remove unused code * Media menu * Rework network and volume modules * Finish custom modules. * Migrate battery bar module. * Update battery module and organize helpers. * Migrate workspace module. * Wrap up bar modules. * Checkpoint before I inevitbly blow something up. * Updates * Fix event propagation logic. * Type fixes * More type fixes * Fix padding for event boxes. * Migrate volume menu and refactor scroll event handlers. * network module WIP * Migrate network service. * Migrate bluetooth menu * Updates * Migrate notifications * Update scrolling behavior for custom modules. * Improve popup notifications and add timer functionality. * Migration notifications menu header/controls. * Migrate notifications menu and consolidate notifications menu code. * Migrate power menu. * Dashboard progress * Migrate dashboard * Migrate media menu. * Reduce media menu nesting. * Finish updating media menu bindings to navigate active player. * Migrate battery menu * Consolidate code * Migrate calendar menu * Fix workspace logic to update on client add/change/remove and consolidate code. * Migrate osd * Consolidate hyprland service connections. * Implement startup dropdown menu position allocation. * Migrate settings menu (WIP) * Settings dialo menu fixes * Finish Dashboard menu * Type updates * update submoldule for types * update github ci * ci * Submodule update * Ci updates * Remove type checking for now. * ci fix * Fix a bunch of stuff, losing track... need rest. Brb coffee * Validate dropdown menu before render. * Consolidate code and add auto-hide functionality. * Improve auto-hide behavior. * Consolidate audio menu code * Organize bluetooth code * Improve active player logic * Properly dismiss a notification on action button resolution. * Implement CLI command engine and migrate CLI commands. * Handle variable disposal * Bar component fixes and add hyprland startup rules. * Handle potentially null bindings network and bluetooth bindings. * Handle potentially null wired adapter. * Fix GPU stats * Handle poller for GPU * Fix gpu bar logic. * Clean up logic for stat bars. * Handle wifi and wired bar icon bindings. * Fix battery percentages * Fix switch behavior * Wifi staging fixes * Reduce redundant hyprland service calls. * Code cleanup * Document the option code and reduce redundant calls to optimize performance. * Remove outdated comment. * Add JSDocs * Add meson to build hyprpanel * Consistency updates * Organize commands * Fix images not showing up on notifications. * Remove todo * Move hyprpanel configuration to the ~/.config/hyprpanel directory and add utility commands. * Handle SRC directory for the bundled/built hyprpanel. * Add namespaces to all windows * Migrate systray * systray updates * Update meson to include ts, tsx and scss files. * Remove log from meson * Fix file choose path and make it float. * Added a command to check the dependency status * Update dep names. * Get scale directly from env * Add todo
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { bind, execAsync, Variable } from 'astal';
|
||||
import { App, Gdk, Gtk } from 'astal/gtk3';
|
||||
import Menu from 'src/components/shared/Menu';
|
||||
import MenuItem from 'src/components/shared/MenuItem';
|
||||
import { hyprlandService } from 'src/lib/constants/services';
|
||||
import { isRecording } from '../helpers';
|
||||
|
||||
const monitorList = Variable(hyprlandService.monitors);
|
||||
|
||||
hyprlandService.connect('monitor-added', () => monitorList.set(hyprlandService.monitors));
|
||||
hyprlandService.connect('monitor-removed', () => monitorList.set(hyprlandService.monitors));
|
||||
|
||||
const MonitorListDropdown = (): JSX.Element => {
|
||||
return (
|
||||
<Menu className={'dropdown recording'} halign={Gtk.Align.FILL} hexpand>
|
||||
{bind(monitorList).as((monitors) => {
|
||||
return monitors.map((monitor) => (
|
||||
<MenuItem
|
||||
label={`Display ${monitor.name}`}
|
||||
onButtonPressEvent={(_, event) => {
|
||||
const buttonClicked = event.get_button()[1];
|
||||
|
||||
if (buttonClicked !== Gdk.BUTTON_PRIMARY) {
|
||||
return;
|
||||
}
|
||||
|
||||
App.get_window('dashboardmenu')?.set_visible(false);
|
||||
|
||||
execAsync(`${SRC_DIR}/scripts/screen_record.sh start ${monitor.name}`).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export const RecordingButton = (): JSX.Element => {
|
||||
return (
|
||||
<button
|
||||
className={`dashboard-button record ${isRecording.get() ? 'active' : ''}`}
|
||||
tooltipText={'Record Screen'}
|
||||
vexpand
|
||||
onButtonPressEvent={(_, event) => {
|
||||
const buttonClicked = event.get_button()[1];
|
||||
|
||||
if (buttonClicked !== Gdk.BUTTON_PRIMARY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRecording.get() === true) {
|
||||
App.get_window('dashboardmenu')?.set_visible(false);
|
||||
return execAsync(`${SRC_DIR}/scripts/screen_record.sh stop`).catch((err) => console.error(err));
|
||||
} else {
|
||||
const monitorDropdownList = MonitorListDropdown() as Gtk.Menu;
|
||||
monitorDropdownList.popup_at_pointer(event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<label className={'button-label txt-icon'} label={''} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { App, Gdk } from 'astal/gtk3';
|
||||
|
||||
export const SettingsButton = (): JSX.Element => {
|
||||
return (
|
||||
<button
|
||||
className={'dashboard-button'}
|
||||
tooltipText={'HyprPanel Configuration'}
|
||||
vexpand
|
||||
onButtonPressEvent={(_, event) => {
|
||||
const buttonClicked = event.get_button()[1];
|
||||
|
||||
if (buttonClicked !== Gdk.BUTTON_PRIMARY) {
|
||||
return;
|
||||
}
|
||||
|
||||
App.get_window('dashboardmenu')?.set_visible(false);
|
||||
App.toggle_window('settings-dialog');
|
||||
}}
|
||||
>
|
||||
<label className={'button-label txt-icon'} label={''} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Widget } from 'astal/gtk3';
|
||||
import { ShortcutVariable } from 'src/lib/types/dashboard';
|
||||
import { isPrimaryClick } from 'src/lib/utils';
|
||||
import { handleClick, hasCommand } from '../helpers';
|
||||
import options from 'src/options';
|
||||
|
||||
const { left, right } = options.menus.dashboard.shortcuts;
|
||||
|
||||
const ShortcutButton = ({ shortcut, ...props }: ShortcutButtonProps): JSX.Element => {
|
||||
return (
|
||||
<button
|
||||
vexpand
|
||||
tooltipText={shortcut.tooltip.get()}
|
||||
onClick={(_, event) => {
|
||||
if (isPrimaryClick(event)) {
|
||||
handleClick(shortcut.command.get());
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<label className={'button-label txt-icon'} label={shortcut.icon.get()} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const LeftShortcut1 = (): JSX.Element => {
|
||||
return (
|
||||
<ShortcutButton
|
||||
shortcut={left.shortcut1}
|
||||
className={`dashboard-button top-button ${hasCommand(left.shortcut1) ? 'paired' : ''}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const LeftShortcut2 = (): JSX.Element => {
|
||||
return <ShortcutButton shortcut={left.shortcut2} className={`dashboard-button`} />;
|
||||
};
|
||||
|
||||
export const LeftShortcut3 = (): JSX.Element => {
|
||||
return (
|
||||
<ShortcutButton
|
||||
shortcut={left.shortcut3}
|
||||
className={`dashboard-button top-button ${hasCommand(left.shortcut3) ? 'paired' : ''}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const LeftShortcut4 = (): JSX.Element => {
|
||||
return <ShortcutButton shortcut={left.shortcut4} className={`dashboard-button `} />;
|
||||
};
|
||||
|
||||
export const RightShortcut1 = (): JSX.Element => {
|
||||
return <ShortcutButton shortcut={right.shortcut1} className={`dashboard-button top-button paired`} />;
|
||||
};
|
||||
|
||||
export const RightShortcut3 = (): JSX.Element => {
|
||||
return <ShortcutButton shortcut={right.shortcut3} className={`dashboard-button top-button paired`} />;
|
||||
};
|
||||
|
||||
interface ShortcutButtonProps extends Widget.ButtonProps {
|
||||
shortcut: ShortcutVariable;
|
||||
}
|
||||
93
src/components/menus/dashboard/shortcuts/helpers.ts
Normal file
93
src/components/menus/dashboard/shortcuts/helpers.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { bind, execAsync, timeout, Variable } from 'astal';
|
||||
import { App } from 'astal/gtk3';
|
||||
import { BashPoller } from 'src/lib/poller/BashPoller';
|
||||
import { ShortcutVariable } from 'src/lib/types/dashboard';
|
||||
import options from 'src/options';
|
||||
|
||||
const { left } = options.menus.dashboard.shortcuts;
|
||||
|
||||
/**
|
||||
* Handles the recorder status based on the command output.
|
||||
*
|
||||
* This function checks if the command output indicates that recording is in progress.
|
||||
*
|
||||
* @param commandOutput The output of the command to check.
|
||||
*
|
||||
* @returns True if the command output is 'recording', false otherwise.
|
||||
*/
|
||||
export const handleRecorder = (commandOutput: string): boolean => {
|
||||
if (commandOutput === 'recording') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the click action for a shortcut.
|
||||
*
|
||||
* This function hides the dashboard menu and executes the specified action after an optional timeout.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param tOut The timeout in milliseconds before executing the action. Defaults to 0.
|
||||
*/
|
||||
export const handleClick = (action: string, tOut: number = 0): void => {
|
||||
App.get_window('dashboardmenu')?.set_visible(false);
|
||||
|
||||
timeout(tOut, () => {
|
||||
execAsync(`bash -c "${action}"`)
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a shortcut has a command.
|
||||
*
|
||||
* This function determines if the provided shortcut has a command defined.
|
||||
*
|
||||
* @param shortCut The shortcut to check.
|
||||
*
|
||||
* @returns True if the shortcut has a command, false otherwise.
|
||||
*/
|
||||
export const hasCommand = (shortCut: ShortcutVariable): boolean => {
|
||||
return shortCut.command.get().length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A variable indicating whether the left card is hidden.
|
||||
*
|
||||
* This variable is set to true if none of the left shortcuts have commands defined.
|
||||
*/
|
||||
export const leftCardHidden = Variable(
|
||||
!(
|
||||
hasCommand(left.shortcut1) ||
|
||||
hasCommand(left.shortcut2) ||
|
||||
hasCommand(left.shortcut3) ||
|
||||
hasCommand(left.shortcut4)
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* A variable representing the polling interval in milliseconds.
|
||||
*/
|
||||
export const pollingInterval = Variable(1000);
|
||||
|
||||
/**
|
||||
* A variable indicating whether recording is in progress.
|
||||
*/
|
||||
export const isRecording = Variable(false);
|
||||
|
||||
/**
|
||||
* A poller for checking the recording status.
|
||||
*
|
||||
* This poller periodically checks the recording status by executing a bash command and updates the `isRecording` variable.
|
||||
*/
|
||||
export const recordingPoller = new BashPoller<boolean, []>(
|
||||
isRecording,
|
||||
[],
|
||||
bind(pollingInterval),
|
||||
`${SRC_DIR}/scripts/screen_record.sh status`,
|
||||
handleRecorder,
|
||||
);
|
||||
22
src/components/menus/dashboard/shortcuts/index.tsx
Normal file
22
src/components/menus/dashboard/shortcuts/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Gtk } from 'astal/gtk3';
|
||||
import { LeftShortcuts, RightShortcuts } from './sections/Section';
|
||||
import { recordingPoller } from './helpers';
|
||||
|
||||
export const Shortcuts = ({ isEnabled }: ShortcutsProps): JSX.Element => {
|
||||
recordingPoller.initialize();
|
||||
|
||||
if (!isEnabled) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return (
|
||||
<box className={'shortcuts-container'} halign={Gtk.Align.FILL} hexpand>
|
||||
<LeftShortcuts />
|
||||
<RightShortcuts />
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
interface ShortcutsProps {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
34
src/components/menus/dashboard/shortcuts/sections/Column.tsx
Normal file
34
src/components/menus/dashboard/shortcuts/sections/Column.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { BindableChild } from 'astal/gtk3/astalify';
|
||||
|
||||
export const LeftColumn = ({ visibleClass, children }: LeftColumnProps): JSX.Element => {
|
||||
return (
|
||||
<box className={`card-button-section-container ${visibleClass ? 'visible' : ''}`}>
|
||||
{visibleClass ? (
|
||||
<box vertical hexpand vexpand>
|
||||
{children}
|
||||
</box>
|
||||
) : (
|
||||
<box />
|
||||
)}
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
export const RightColumn = ({ children }: RightColumnProps): JSX.Element => {
|
||||
return (
|
||||
<box className={`card-button-section-container`}>
|
||||
<box vertical hexpand vexpand>
|
||||
{children}
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
interface LeftColumnProps {
|
||||
visibleClass?: boolean;
|
||||
children?: BindableChild | BindableChild[];
|
||||
}
|
||||
|
||||
interface RightColumnProps {
|
||||
children?: BindableChild | BindableChild[];
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { bind, Variable } from 'astal';
|
||||
import options from 'src/options';
|
||||
import { hasCommand, isRecording, leftCardHidden } from '../helpers';
|
||||
import {
|
||||
LeftShortcut1,
|
||||
LeftShortcut2,
|
||||
LeftShortcut3,
|
||||
LeftShortcut4,
|
||||
RightShortcut1,
|
||||
RightShortcut3,
|
||||
} from '../buttons/ShortcutButtons';
|
||||
import { LeftColumn, RightColumn } from './Column';
|
||||
import { SettingsButton } from '../buttons/SettingsButton';
|
||||
import { RecordingButton } from '../buttons/RecordingButton';
|
||||
|
||||
const { left, right } = options.menus.dashboard.shortcuts;
|
||||
|
||||
const leftBindings = [
|
||||
bind(left.shortcut1.command),
|
||||
bind(left.shortcut1.tooltip),
|
||||
bind(left.shortcut1.icon),
|
||||
bind(left.shortcut2.command),
|
||||
bind(left.shortcut2.tooltip),
|
||||
bind(left.shortcut2.icon),
|
||||
bind(left.shortcut3.command),
|
||||
bind(left.shortcut3.tooltip),
|
||||
bind(left.shortcut3.icon),
|
||||
bind(left.shortcut4.command),
|
||||
bind(left.shortcut4.tooltip),
|
||||
bind(left.shortcut4.icon),
|
||||
];
|
||||
|
||||
const rightBindings = [
|
||||
bind(right.shortcut1.command),
|
||||
bind(right.shortcut1.tooltip),
|
||||
bind(right.shortcut1.icon),
|
||||
bind(right.shortcut3.command),
|
||||
bind(right.shortcut3.tooltip),
|
||||
bind(right.shortcut3.icon),
|
||||
bind(leftCardHidden),
|
||||
bind(isRecording),
|
||||
];
|
||||
|
||||
export const LeftShortcuts = (): JSX.Element => {
|
||||
return (
|
||||
<box>
|
||||
{Variable.derive(leftBindings, () => {
|
||||
const isVisibleLeft = hasCommand(left.shortcut1) || hasCommand(left.shortcut2);
|
||||
const isVisibleRight = hasCommand(left.shortcut3) || hasCommand(left.shortcut4);
|
||||
|
||||
if (!isVisibleLeft && !isVisibleRight) {
|
||||
leftCardHidden.set(true);
|
||||
return <box />;
|
||||
}
|
||||
|
||||
leftCardHidden.set(false);
|
||||
|
||||
return (
|
||||
<box className={'container most-used dashboard-card'}>
|
||||
<LeftColumn visibleClass={isVisibleRight && isVisibleLeft}>
|
||||
<LeftShortcut1 />
|
||||
<LeftShortcut2 />
|
||||
</LeftColumn>
|
||||
<RightColumn>
|
||||
<LeftShortcut3 />
|
||||
<LeftShortcut4 />
|
||||
</RightColumn>
|
||||
</box>
|
||||
);
|
||||
})()}
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
export const RightShortcuts = (): JSX.Element => {
|
||||
return (
|
||||
<box>
|
||||
{Variable.derive(rightBindings, () => {
|
||||
return (
|
||||
<box className={`container utilities dashboard-card ${!leftCardHidden.get() ? 'paired' : ''}`}>
|
||||
<LeftColumn visibleClass={!leftCardHidden.get()}>
|
||||
<RightShortcut1 />
|
||||
<SettingsButton />
|
||||
</LeftColumn>
|
||||
<RightColumn>
|
||||
<RightShortcut3 />
|
||||
<RecordingButton />
|
||||
</RightColumn>
|
||||
</box>
|
||||
);
|
||||
})()}
|
||||
</box>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user