Minor: Refactor the code-base for better organization and compartmentalization. (#934)
* Clean up unused code * Fix media player formatting issue for labels with new line characteres. * Refactor the media player handlers into a class. * More code cleanup and organize shared weather utils into distinct classes. * Flatten some nesting. * Move weather manager in dedicated class and build HTTP Utility class for Rest API calling. * Remove logs * Rebase master merge * Reorg code (WIP) * More reorg * Delete utility scripts * Reorg options * Finish moving all options over * Fix typescript issues * Update options imports to default * missed update * Screw barrel files honestly, work of the devil. * Only initialize power profiles if power-profiles-daemon is running. * Fix window positioning and weather service naming * style dir * More organization * Restructure types to be closer to their source * Remove lib types and constants * Update basic weather object to be saner with extensibility. * Service updates * Fix initialization strategy for services. * Fix Config Manager to only emit changed objects and added missing temp converters. * Update storage service to handle unit changes. * Added cpu temp sensor auto-discovery * Added missing JSDocs to services * remove unused * Migrate to network service. * Fix network password issue. * Move out password input into helper * Rename password mask constant to be less double-negativey. * Dropdown menu rename * Added a component to edit JSON in the settings dialog (rough/WIP) * Align settings * Add and style JSON Editor. * Adjust padding * perf(shortcuts): ⚡ avoid unnecessary polling when shortcuts are disabled Stops the recording poller when shortcuts are disabled, preventing redundant polling and reducing resource usage. * Fix types and return value if shortcut not enabled. * Move the swww daemon checking process outside of the wallpaper service into a dedicated deamon lifecyle processor. * Add more string formatters and use title case for weather status (as it was). * Fix startup errors. * Rgba fix * Remove zod from dependencies --------- Co-authored-by: KernelDiego <gonzalezdiego.contact@gmail.com>
This commit is contained in:
185
src/components/bar/layout/BarLayout.tsx
Normal file
185
src/components/bar/layout/BarLayout.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
import { App, Gtk } from 'astal/gtk3';
|
||||
import Astal from 'gi://Astal?version=3.0';
|
||||
import { bind, Binding, Variable } from 'astal';
|
||||
import { idleInhibit } from 'src/lib/window/visibility';
|
||||
import { WidgetRegistry } from './WidgetRegistry';
|
||||
import { getLayoutForMonitor, isLayoutEmpty } from '../utils/monitors';
|
||||
import options from 'src/configuration';
|
||||
|
||||
/**
|
||||
* Responsible for the bar UI layout and positioning
|
||||
*/
|
||||
export class BarLayout {
|
||||
private _hyprlandMonitor: number;
|
||||
private _gdkMonitor: number;
|
||||
private _widgetRegistry: WidgetRegistry;
|
||||
|
||||
private _visibilityVar: Variable<boolean>;
|
||||
private _classNameVar: Variable<string>;
|
||||
private _anchorVar: Variable<Astal.WindowAnchor>;
|
||||
private _layerVar: Variable<Astal.Layer>;
|
||||
private _borderLocationVar: Binding<string>;
|
||||
private _barSectionsVar: {
|
||||
left: Variable<JSX.Element[]>;
|
||||
middle: Variable<JSX.Element[]>;
|
||||
right: Variable<JSX.Element[]>;
|
||||
};
|
||||
|
||||
constructor(gdkMonitor: number, hyprlandMonitor: number, widgetRegistry: WidgetRegistry) {
|
||||
this._gdkMonitor = gdkMonitor;
|
||||
this._hyprlandMonitor = hyprlandMonitor;
|
||||
this._widgetRegistry = widgetRegistry;
|
||||
|
||||
this._visibilityVar = Variable(true);
|
||||
this._classNameVar = Variable('bar');
|
||||
this._anchorVar = Variable(
|
||||
Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
|
||||
);
|
||||
this._layerVar = Variable(Astal.Layer.TOP);
|
||||
this._borderLocationVar = Variable('bar-panel')();
|
||||
this._barSectionsVar = {
|
||||
left: Variable([]),
|
||||
middle: Variable([]),
|
||||
right: Variable([]),
|
||||
};
|
||||
|
||||
this._initializeReactiveVariables();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<window
|
||||
inhibit={bind(idleInhibit)}
|
||||
name={`bar-${this._hyprlandMonitor}`}
|
||||
namespace={`bar-${this._hyprlandMonitor}`}
|
||||
className={this._classNameVar()}
|
||||
application={App}
|
||||
monitor={this._gdkMonitor}
|
||||
visible={this._visibilityVar()}
|
||||
anchor={this._anchorVar()}
|
||||
layer={this._layerVar()}
|
||||
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||
onDestroy={() => this._cleanup()}
|
||||
>
|
||||
<box className="bar-panel-container">
|
||||
<centerbox
|
||||
css="padding: 1px;"
|
||||
hexpand
|
||||
className={this._borderLocationVar}
|
||||
startWidget={
|
||||
<box className="box-left" hexpand>
|
||||
{this._barSectionsVar.left()}
|
||||
</box>
|
||||
}
|
||||
centerWidget={
|
||||
<box className="box-center" halign={Gtk.Align.CENTER}>
|
||||
{this._barSectionsVar.middle()}
|
||||
</box>
|
||||
}
|
||||
endWidget={
|
||||
<box className="box-right" halign={Gtk.Align.END}>
|
||||
{this._barSectionsVar.right()}
|
||||
</box>
|
||||
}
|
||||
/>
|
||||
</box>
|
||||
</window>
|
||||
);
|
||||
}
|
||||
|
||||
private _initializeReactiveVariables(): void {
|
||||
this._initializeVisibilityVariables();
|
||||
this._initializePositionVariables();
|
||||
this._initializeAppearanceVariables();
|
||||
this._initializeSectionVariables();
|
||||
}
|
||||
|
||||
private _initializeVisibilityVariables(): void {
|
||||
const { layouts } = options.bar;
|
||||
|
||||
this._visibilityVar = Variable.derive([bind(layouts)], (currentLayouts) => {
|
||||
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
|
||||
return !isLayoutEmpty(foundLayout);
|
||||
});
|
||||
|
||||
this._classNameVar = Variable.derive([bind(layouts)], (currentLayouts) => {
|
||||
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
|
||||
return !isLayoutEmpty(foundLayout) ? 'bar' : '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize variables related to bar positioning
|
||||
*/
|
||||
private _initializePositionVariables(): void {
|
||||
const { location } = options.theme.bar;
|
||||
|
||||
this._anchorVar = Variable.derive([bind(location)], (loc) => {
|
||||
if (loc === 'bottom') {
|
||||
return Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
|
||||
}
|
||||
return Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
|
||||
});
|
||||
}
|
||||
|
||||
private _initializeAppearanceVariables(): void {
|
||||
const { location: borderLocation } = options.theme.bar.border;
|
||||
|
||||
this._layerVar = this._createLayerVariable();
|
||||
|
||||
this._borderLocationVar = bind(borderLocation).as((brdrLcn) =>
|
||||
brdrLcn !== 'none' ? 'bar-panel withBorder' : 'bar-panel',
|
||||
);
|
||||
}
|
||||
|
||||
private _createLayerVariable(): Variable<Astal.Layer> {
|
||||
return Variable.derive([bind(options.theme.bar.layer), bind(options.tear)], (barLayer, tear) => {
|
||||
if (tear && barLayer === 'overlay') {
|
||||
return Astal.Layer.TOP;
|
||||
}
|
||||
|
||||
return this._getLayerFromConfig(barLayer);
|
||||
});
|
||||
}
|
||||
|
||||
private _getLayerFromConfig(barLayer: string): Astal.Layer {
|
||||
const layerMap: Record<string, Astal.Layer> = {
|
||||
overlay: Astal.Layer.OVERLAY,
|
||||
top: Astal.Layer.TOP,
|
||||
bottom: Astal.Layer.BOTTOM,
|
||||
background: Astal.Layer.BACKGROUND,
|
||||
};
|
||||
|
||||
return layerMap[barLayer] ?? Astal.Layer.TOP;
|
||||
}
|
||||
|
||||
private _initializeSectionVariables(): void {
|
||||
this._barSectionsVar = {
|
||||
left: this._createSectionBinding('left'),
|
||||
middle: this._createSectionBinding('middle'),
|
||||
right: this._createSectionBinding('right'),
|
||||
};
|
||||
}
|
||||
|
||||
private _createSectionBinding(section: 'left' | 'middle' | 'right'): Variable<JSX.Element[]> {
|
||||
const { layouts } = options.bar;
|
||||
|
||||
return Variable.derive([bind(layouts)], (currentLayouts) => {
|
||||
const foundLayout = getLayoutForMonitor(this._hyprlandMonitor, currentLayouts);
|
||||
return foundLayout[section]
|
||||
.filter((mod) => this._widgetRegistry.hasWidget(mod))
|
||||
.map((widget) => this._widgetRegistry.createWidget(widget, this._hyprlandMonitor));
|
||||
});
|
||||
}
|
||||
|
||||
private _cleanup(): void {
|
||||
this._visibilityVar.drop();
|
||||
this._classNameVar.drop();
|
||||
this._anchorVar.drop();
|
||||
this._layerVar.drop();
|
||||
|
||||
this._barSectionsVar.left.drop();
|
||||
this._barSectionsVar.middle.drop();
|
||||
this._barSectionsVar.right.drop();
|
||||
}
|
||||
}
|
||||
55
src/components/bar/layout/WidgetRegistry.tsx
Normal file
55
src/components/bar/layout/WidgetRegistry.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { CustomModules } from '../customModules';
|
||||
|
||||
export type WidgetFactory = (monitor: number) => JSX.Element;
|
||||
|
||||
/**
|
||||
* Manages registration and creation of widgets
|
||||
*/
|
||||
export class WidgetRegistry {
|
||||
private _widgets: Record<string, WidgetFactory> = {};
|
||||
private _initialized = false;
|
||||
|
||||
constructor(coreWidgets: Record<string, WidgetFactory>) {
|
||||
this._widgets = { ...coreWidgets };
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the registry with core and custom widgets
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
if (this._initialized) return;
|
||||
|
||||
try {
|
||||
const customWidgets = await CustomModules.build();
|
||||
|
||||
this._widgets = {
|
||||
...this._widgets,
|
||||
...customWidgets,
|
||||
};
|
||||
|
||||
this._initialized = true;
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize widget registry:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a widget is registered
|
||||
*/
|
||||
public hasWidget(name: string): boolean {
|
||||
return Object.keys(this._widgets).includes(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a widget
|
||||
*/
|
||||
public createWidget(name: string, monitor: number): JSX.Element {
|
||||
if (!this.hasWidget(name)) {
|
||||
console.error(`Widget "${name}" not found`);
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return this._widgets[name](monitor);
|
||||
}
|
||||
}
|
||||
61
src/components/bar/layout/coreWidgets.tsx
Normal file
61
src/components/bar/layout/coreWidgets.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { BatteryLabel } from '../modules/battery';
|
||||
import { Bluetooth } from '../modules/bluetooth';
|
||||
import { Cava } from '../modules/cava';
|
||||
import { Clock } from '../modules/clock';
|
||||
import { Cpu } from '../modules/cpu';
|
||||
import { CpuTemp } from '../modules/cputemp';
|
||||
import { Hypridle } from '../modules/hypridle';
|
||||
import { Hyprsunset } from '../modules/hyprsunset';
|
||||
import { KbInput } from '../modules/kblayout';
|
||||
import { Media } from '../modules/media';
|
||||
import { Menu } from '../modules/menu';
|
||||
import { Microphone } from '../modules/microphone';
|
||||
import { Netstat } from '../modules/netstat';
|
||||
import { Network } from '../modules/network';
|
||||
import { Notifications } from '../modules/notifications';
|
||||
import { Power } from '../modules/power';
|
||||
import { Ram } from '../modules/ram';
|
||||
import { ModuleSeparator } from '../modules/separator';
|
||||
import { Storage } from '../modules/storage';
|
||||
import { Submap } from '../modules/submap';
|
||||
import { SysTray } from '../modules/systray';
|
||||
import { Updates } from '../modules/updates';
|
||||
import { Volume } from '../modules/volume';
|
||||
import { Weather } from '../modules/weather';
|
||||
import { ClientTitle } from '../modules/window_title';
|
||||
import { Workspaces } from '../modules/workspaces';
|
||||
import { WorldClock } from '../modules/worldclock';
|
||||
import { WidgetContainer } from '../shared/widgetContainer';
|
||||
import { WidgetFactory } from './WidgetRegistry';
|
||||
|
||||
export function getCoreWidgets(): Record<string, WidgetFactory> {
|
||||
return {
|
||||
battery: () => WidgetContainer(BatteryLabel()),
|
||||
dashboard: () => WidgetContainer(Menu()),
|
||||
workspaces: (monitor: number) => WidgetContainer(Workspaces(monitor)),
|
||||
windowtitle: () => WidgetContainer(ClientTitle()),
|
||||
media: () => WidgetContainer(Media()),
|
||||
notifications: () => WidgetContainer(Notifications()),
|
||||
volume: () => WidgetContainer(Volume()),
|
||||
network: () => WidgetContainer(Network()),
|
||||
bluetooth: () => WidgetContainer(Bluetooth()),
|
||||
clock: () => WidgetContainer(Clock()),
|
||||
systray: () => WidgetContainer(SysTray()),
|
||||
microphone: () => WidgetContainer(Microphone()),
|
||||
ram: () => WidgetContainer(Ram()),
|
||||
cpu: () => WidgetContainer(Cpu()),
|
||||
cputemp: () => WidgetContainer(CpuTemp()),
|
||||
storage: () => WidgetContainer(Storage()),
|
||||
netstat: () => WidgetContainer(Netstat()),
|
||||
kbinput: () => WidgetContainer(KbInput()),
|
||||
updates: () => WidgetContainer(Updates()),
|
||||
submap: () => WidgetContainer(Submap()),
|
||||
weather: () => WidgetContainer(Weather()),
|
||||
power: () => WidgetContainer(Power()),
|
||||
hyprsunset: () => WidgetContainer(Hyprsunset()),
|
||||
hypridle: () => WidgetContainer(Hypridle()),
|
||||
cava: () => WidgetContainer(Cava()),
|
||||
worldclock: () => WidgetContainer(WorldClock()),
|
||||
separator: () => ModuleSeparator(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user