Feature: Added lazy loading of settings dialog (#983)
* Feat: Added configurable lazy loading for the settings dialog. * Allow lazyloading to be configurable
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { App, Gdk } from 'astal/gtk3';
|
||||
import { SettingsDialogLoader } from 'src/components/settings/lazyLoader';
|
||||
|
||||
export const SettingsButton = (): JSX.Element => {
|
||||
return (
|
||||
@@ -6,7 +7,7 @@ export const SettingsButton = (): JSX.Element => {
|
||||
className={'dashboard-button'}
|
||||
tooltipText={'HyprPanel Configuration'}
|
||||
vexpand
|
||||
onButtonPressEvent={(_, event) => {
|
||||
onButtonPressEvent={async (_, event) => {
|
||||
const buttonClicked = event.get_button()[1];
|
||||
|
||||
if (buttonClicked !== Gdk.BUTTON_PRIMARY) {
|
||||
@@ -14,7 +15,8 @@ export const SettingsButton = (): JSX.Element => {
|
||||
}
|
||||
|
||||
App.get_window('dashboardmenu')?.set_visible(false);
|
||||
App.toggle_window('settings-dialog');
|
||||
const loader = SettingsDialogLoader.getInstance();
|
||||
await loader.toggle();
|
||||
}}
|
||||
>
|
||||
<label className={'button-label txt-icon'} label={''} />
|
||||
|
||||
91
src/components/settings/lazyLoader.ts
Normal file
91
src/components/settings/lazyLoader.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { App } from 'astal/gtk3';
|
||||
import { Timer } from 'src/lib/performance/timer';
|
||||
|
||||
/**
|
||||
* Manages lazy loading of the settings dialog to improve startup performance.
|
||||
* The dialog is only created when first accessed, not during app initialization.
|
||||
*/
|
||||
export class SettingsDialogLoader {
|
||||
private static _instance: SettingsDialogLoader | null = null;
|
||||
private _settingsDialog: JSX.Element | null = null;
|
||||
private _loadPromise: Promise<JSX.Element> | null = null;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* Gets the singleton instance of the settings dialog loader
|
||||
*/
|
||||
public static getInstance(): SettingsDialogLoader {
|
||||
if (!SettingsDialogLoader._instance) {
|
||||
SettingsDialogLoader._instance = new SettingsDialogLoader();
|
||||
}
|
||||
return SettingsDialogLoader._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads the settings dialog
|
||||
*/
|
||||
public static async preload(): Promise<void> {
|
||||
const instance = SettingsDialogLoader.getInstance();
|
||||
await instance._getDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and returns the settings dialog, creating it if necessary.
|
||||
* Multiple concurrent calls will share the same loading promise.
|
||||
*/
|
||||
private async _getDialog(): Promise<JSX.Element> {
|
||||
if (this._settingsDialog) {
|
||||
return this._settingsDialog;
|
||||
}
|
||||
|
||||
if (this._loadPromise) {
|
||||
return this._loadPromise;
|
||||
}
|
||||
|
||||
this._loadPromise = this._loadSettingsDialog();
|
||||
|
||||
try {
|
||||
this._settingsDialog = await this._loadPromise;
|
||||
return this._settingsDialog;
|
||||
} finally {
|
||||
this._loadPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual loading of the settings dialog module
|
||||
*/
|
||||
private async _loadSettingsDialog(): Promise<JSX.Element> {
|
||||
const { default: options } = await import('src/configuration');
|
||||
const isLazyLoading = options.hyprpanel.useLazyLoading.get();
|
||||
const timerLabel = isLazyLoading ? 'Lazy loading settings dialog' : 'Preloading settings dialog';
|
||||
const timer = new Timer(timerLabel);
|
||||
|
||||
try {
|
||||
const { default: SettingsDialog } = await import('./index');
|
||||
const dialog = SettingsDialog();
|
||||
timer.end();
|
||||
return dialog;
|
||||
} catch (error) {
|
||||
timer.end();
|
||||
throw new Error(`Failed to load settings dialog: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the settings dialog visibility, loading it if necessary
|
||||
*/
|
||||
public async toggle(): Promise<void> {
|
||||
await this._getDialog();
|
||||
App.toggle_window('settings-dialog');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to toggle the settings dialog
|
||||
*/
|
||||
export async function toggleSettingsDialog(): Promise<void> {
|
||||
const loader = SettingsDialogLoader.getInstance();
|
||||
await loader.toggle();
|
||||
}
|
||||
@@ -48,6 +48,15 @@ export const BarGeneral = (): JSX.Element => {
|
||||
subtitle="Command executed when restarting. Use '-b busName' flag if needed."
|
||||
type="string"
|
||||
/>
|
||||
<Option
|
||||
opt={options.hyprpanel.useLazyLoading}
|
||||
title="Lazy Load Settings Dialog"
|
||||
subtitle={
|
||||
'Only loads the settings dialog when first opened, rather than at startup. (Requires restart)\n' +
|
||||
'Improves launch speed and reduces memory usage until the dialog is accessed.'
|
||||
}
|
||||
type="boolean"
|
||||
/>
|
||||
<Option
|
||||
opt={options.terminal}
|
||||
title="Terminal"
|
||||
|
||||
@@ -16,6 +16,7 @@ export default {
|
||||
hyprpanel: {
|
||||
restartAgs: opt(true),
|
||||
restartCommand: opt('hyprpanel -q; hyprpanel'),
|
||||
useLazyLoading: opt(true),
|
||||
},
|
||||
dummy: opt(true),
|
||||
bar,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { execAsync } from 'astal';
|
||||
import { Bar } from 'src/components/bar';
|
||||
import Notifications from 'src/components/notifications';
|
||||
import SettingsDialog from 'src/components/settings/index';
|
||||
import OSD from 'src/components/osd/index';
|
||||
import { handleRealization } from 'src/components/menus/shared/dropdown/helpers/helpers';
|
||||
import { isDropdownMenu } from 'src/components/settings/constants.js';
|
||||
@@ -12,6 +11,8 @@ import { BarRefreshManager } from 'src/services/display/bar/refreshManager';
|
||||
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
||||
import { Timer } from 'src/lib/performance/timer';
|
||||
import { JSXElement } from 'src/core/types';
|
||||
import { SettingsDialogLoader } from 'src/components/settings/lazyLoader';
|
||||
import options from 'src/configuration';
|
||||
|
||||
/**
|
||||
* Manages the complete initialization sequence for HyprPanel.
|
||||
@@ -36,11 +37,14 @@ export class InitializationService {
|
||||
return bars;
|
||||
});
|
||||
|
||||
Timer.measureSync('Settings dialog', () => SettingsDialog());
|
||||
Timer.measureSync('Menus', () => this._initializeMenus());
|
||||
Timer.measureSync('System behaviors', () => initializeSystemBehaviors());
|
||||
Timer.measureSync('Monitor handlers', () => this._setupMonitorHandlers());
|
||||
|
||||
if (!options.hyprpanel.useLazyLoading.get()) {
|
||||
await Timer.measureAsync('Settings dialog preload', () => SettingsDialogLoader.preload());
|
||||
}
|
||||
|
||||
overallTimer.end();
|
||||
} catch (error) {
|
||||
console.error('Error during application initialization:', error);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { App } from 'astal/gtk3';
|
||||
import { isWindowVisible } from 'src/lib/window/visibility';
|
||||
import { BarVisibility } from 'src/services/display/bar';
|
||||
import { errorHandler } from 'src/core/errors/handler';
|
||||
import { SettingsDialogLoader } from 'src/components/settings/lazyLoader';
|
||||
|
||||
export const windowManagementCommands: Command[] = [
|
||||
{
|
||||
@@ -35,9 +36,21 @@ export const windowManagementCommands: Command[] = [
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
handler: (args: Record<string, unknown>): string => {
|
||||
handler: async (args: Record<string, unknown>): Promise<string> => {
|
||||
try {
|
||||
const windowName = args['window'] as string;
|
||||
|
||||
if (windowName === 'settings-dialog') {
|
||||
const loader = SettingsDialogLoader.getInstance();
|
||||
await loader.toggle();
|
||||
const foundWindow = App.get_window(windowName);
|
||||
const windowStatus = foundWindow?.visible ? 'visible' : 'hidden';
|
||||
|
||||
BarVisibility.set(windowName, windowStatus === 'visible');
|
||||
|
||||
return windowStatus;
|
||||
}
|
||||
|
||||
const foundWindow = App.get_window(windowName);
|
||||
|
||||
if (!foundWindow) {
|
||||
|
||||
Reference in New Issue
Block a user