Cava Module (#662)
* First version of the cava module * Update cava stuff * Update themes for cava * Update themes * Handle cava visibility when null * Add bar characters in options --------- Co-authored-by: Ed Bennett <ed@dodimead.com> Co-authored-by: Jas Singh <jaskiratpal.singh@outlook.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import { Weather } from '../../components/bar/modules/weather/index';
|
||||
import { Power } from '../../components/bar/modules/power/index';
|
||||
import { Hyprsunset } from '../../components/bar/modules/hyprsunset/index';
|
||||
import { Hypridle } from '../../components/bar/modules/hypridle/index';
|
||||
import { Cava } from '../../components/bar/modules/cava/index';
|
||||
|
||||
export {
|
||||
Menu,
|
||||
@@ -50,4 +51,5 @@ export {
|
||||
Power,
|
||||
Hyprsunset,
|
||||
Hypridle,
|
||||
Cava,
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
Power,
|
||||
Hyprsunset,
|
||||
Hypridle,
|
||||
Cava,
|
||||
} from './exports';
|
||||
|
||||
import { WidgetContainer } from './shared/WidgetContainer';
|
||||
@@ -62,6 +63,7 @@ const widget = {
|
||||
power: (): JSX.Element => WidgetContainer(Power()),
|
||||
hyprsunset: (): JSX.Element => WidgetContainer(Hyprsunset()),
|
||||
hypridle: (): JSX.Element => WidgetContainer(Hypridle()),
|
||||
cava: (): JSX.Element => WidgetContainer(Cava()),
|
||||
};
|
||||
|
||||
export const Bar = (() => {
|
||||
|
||||
64
src/components/bar/modules/cava/helpers.ts
Normal file
64
src/components/bar/modules/cava/helpers.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { bind, Variable } from 'astal';
|
||||
import { cavaService, mprisService } from 'src/lib/constants/services';
|
||||
import options from 'src/options';
|
||||
|
||||
const {
|
||||
showActiveOnly,
|
||||
bars,
|
||||
autoSensitivity,
|
||||
lowCutoff,
|
||||
highCutoff,
|
||||
noiseReduction,
|
||||
stereo,
|
||||
channels,
|
||||
framerate,
|
||||
samplerate,
|
||||
} = options.bar.customModules.cava;
|
||||
|
||||
/**
|
||||
* Initializes a visibility tracker that updates the visibility status based on the active state and the presence of players.
|
||||
*
|
||||
* @param isVis - A variable that holds the visibility status.
|
||||
*/
|
||||
export function initVisibilityTracker(isVis: Variable<boolean>): void {
|
||||
Variable.derive([bind(showActiveOnly), bind(mprisService, 'players')], (showActive, players) => {
|
||||
isVis.set(cavaService !== null && (!showActive || players?.length > 0));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a settings tracker that updates the CAVA service settings based on the provided options.
|
||||
*/
|
||||
export function initSettingsTracker(): void {
|
||||
const cava = cavaService;
|
||||
|
||||
if (!cava) {
|
||||
return;
|
||||
}
|
||||
|
||||
Variable.derive(
|
||||
[
|
||||
bind(bars),
|
||||
bind(channels),
|
||||
bind(framerate),
|
||||
bind(samplerate),
|
||||
bind(autoSensitivity),
|
||||
bind(lowCutoff),
|
||||
bind(highCutoff),
|
||||
bind(noiseReduction),
|
||||
bind(stereo),
|
||||
],
|
||||
(bars, channels, framerate, samplerate, autoSens, lCutoff, hCutoff, noiseRed, isStereo) => {
|
||||
cava.set_autosens(autoSens);
|
||||
cava.set_low_cutoff(lCutoff);
|
||||
cava.set_high_cutoff(hCutoff);
|
||||
cava.set_noise_reduction(noiseRed);
|
||||
cava.set_source('auto');
|
||||
cava.set_stereo(isStereo);
|
||||
cava.set_bars(bars);
|
||||
cava.set_channels(channels);
|
||||
cava.set_framerate(framerate);
|
||||
cava.set_samplerate(samplerate);
|
||||
},
|
||||
);
|
||||
}
|
||||
77
src/components/bar/modules/cava/index.tsx
Normal file
77
src/components/bar/modules/cava/index.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Variable, bind } from 'astal';
|
||||
import { Astal } from 'astal/gtk3';
|
||||
import { cavaService } from 'src/lib/constants/services';
|
||||
import { BarBoxChild } from 'src/lib/types/bar';
|
||||
import { Module } from '../../shared/Module';
|
||||
import { inputHandler } from '../../utils/helpers';
|
||||
import options from 'src/options';
|
||||
import { initSettingsTracker, initVisibilityTracker } from './helpers';
|
||||
|
||||
const {
|
||||
icon,
|
||||
showIcon: label,
|
||||
showActiveOnly,
|
||||
barCharacters,
|
||||
spaceCharacter,
|
||||
leftClick,
|
||||
rightClick,
|
||||
middleClick,
|
||||
scrollUp,
|
||||
scrollDown,
|
||||
} = options.bar.customModules.cava;
|
||||
|
||||
const isVis = Variable(!showActiveOnly.get());
|
||||
|
||||
initVisibilityTracker(isVis);
|
||||
initSettingsTracker();
|
||||
|
||||
export const Cava = (): BarBoxChild => {
|
||||
let labelBinding: Variable<string> = Variable('');
|
||||
|
||||
if (cavaService) {
|
||||
labelBinding = Variable.derive(
|
||||
[bind(cavaService, 'values'), bind(spaceCharacter), bind(barCharacters)],
|
||||
(values, spacing, blockCharacters) => {
|
||||
const valueMap = values
|
||||
.map((v: number) => {
|
||||
const index = Math.floor(v * blockCharacters.length);
|
||||
return blockCharacters[Math.min(index, blockCharacters.length - 1)];
|
||||
})
|
||||
.join(spacing);
|
||||
return valueMap;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Module({
|
||||
isVis,
|
||||
label: labelBinding(),
|
||||
showIconBinding: bind(label),
|
||||
textIcon: bind(icon),
|
||||
boxClass: 'cava',
|
||||
props: {
|
||||
setup: (self: Astal.Button) => {
|
||||
inputHandler(self, {
|
||||
onPrimaryClick: {
|
||||
cmd: leftClick,
|
||||
},
|
||||
onSecondaryClick: {
|
||||
cmd: rightClick,
|
||||
},
|
||||
onMiddleClick: {
|
||||
cmd: middleClick,
|
||||
},
|
||||
onScrollUp: {
|
||||
cmd: scrollUp,
|
||||
},
|
||||
onScrollDown: {
|
||||
cmd: scrollDown,
|
||||
},
|
||||
});
|
||||
},
|
||||
onDestroy: () => {
|
||||
labelBinding.drop();
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -354,6 +354,43 @@ export const CustomModuleSettings = (): JSX.Element => {
|
||||
<Option opt={options.bar.customModules.hypridle.scrollUp} title="Scroll Up" type="string" />
|
||||
<Option opt={options.bar.customModules.hypridle.scrollDown} title="Scroll Down" type="string" />
|
||||
|
||||
{/* Cava Section */}
|
||||
<Header title="Cava" />
|
||||
<Option
|
||||
opt={options.theme.bar.buttons.modules.cava.enableBorder}
|
||||
title="Button Border"
|
||||
type="boolean"
|
||||
/>
|
||||
<Option opt={options.bar.customModules.cava.icon} title="Icon" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.showIcon} title="Show Icon" type="boolean" />
|
||||
<Option opt={options.theme.bar.buttons.modules.cava.spacing} title="Spacing" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.barCharacters} title="Bar Characters" type="object" />
|
||||
<Option opt={options.bar.customModules.cava.spaceCharacter} title="Bar Separator" type="string" />
|
||||
<Option
|
||||
opt={options.bar.customModules.cava.showActiveOnly}
|
||||
title="Auto Hide"
|
||||
subtitle="Hide if no media detected."
|
||||
type="boolean"
|
||||
/>
|
||||
<Option opt={options.bar.customModules.cava.bars} title="Bars" type="number" />
|
||||
<Option opt={options.bar.customModules.cava.channels} title="Channels" type="number" />
|
||||
<Option opt={options.bar.customModules.cava.framerate} title="Framerate" type="number" />
|
||||
<Option opt={options.bar.customModules.cava.samplerate} title="Sample Rate" type="number" />
|
||||
<Option
|
||||
opt={options.bar.customModules.cava.autoSensitivity}
|
||||
title="Automatic Sensitivity"
|
||||
type="boolean"
|
||||
/>
|
||||
<Option opt={options.bar.customModules.cava.lowCutoff} title="Low Cutoff" type="number" />
|
||||
<Option opt={options.bar.customModules.cava.highCutoff} title="High Cutoff" type="number" />
|
||||
<Option opt={options.bar.customModules.cava.noiseReduction} title="Noise Reduction" type="float" />
|
||||
<Option opt={options.bar.customModules.cava.stereo} title="Stereo" type="boolean" />
|
||||
<Option opt={options.bar.customModules.cava.leftClick} title="Left Click" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.rightClick} title="Right Click" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.middleClick} title="Middle Click" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.scrollUp} title="Scroll Up" type="string" />
|
||||
<Option opt={options.bar.customModules.cava.scrollDown} title="Scroll Down" type="string" />
|
||||
|
||||
{/* Power Section */}
|
||||
<Header title="Power" />
|
||||
<Option
|
||||
|
||||
@@ -193,6 +193,19 @@ export const CustomModuleTheme = (): JSX.Element => {
|
||||
/>
|
||||
<Option opt={options.theme.bar.buttons.modules.hypridle.border} title="Border" type="color" />
|
||||
|
||||
{/* Cava Module Section */}
|
||||
<Header title="Cava" />
|
||||
<Option opt={options.theme.bar.buttons.modules.cava.text} title="Bars" type="color" />
|
||||
<Option opt={options.theme.bar.buttons.modules.cava.icon} title="Icon" type="color" />
|
||||
<Option opt={options.theme.bar.buttons.modules.cava.background} title="Label Background" type="color" />
|
||||
<Option
|
||||
opt={options.theme.bar.buttons.modules.cava.icon_background}
|
||||
title="Icon Background"
|
||||
subtitle="Applies a background color to the icon section of the button.\nRequires 'split' button styling."
|
||||
type="color"
|
||||
/>
|
||||
<Option opt={options.theme.bar.buttons.modules.cava.border} title="Border" type="color" />
|
||||
|
||||
{/* Power Module Section */}
|
||||
<Header title="Power" />
|
||||
<Option opt={options.theme.bar.buttons.modules.power.icon} title="Icon" type="color" />
|
||||
|
||||
@@ -5,8 +5,6 @@ import options from 'src/options';
|
||||
|
||||
const { style } = options.theme.bar.buttons;
|
||||
|
||||
const undefinedVar = Variable(undefined);
|
||||
|
||||
export const Module = ({
|
||||
icon,
|
||||
textIcon,
|
||||
@@ -16,7 +14,8 @@ export const Module = ({
|
||||
boxClass,
|
||||
isVis,
|
||||
props = {},
|
||||
showLabelBinding = bind(undefinedVar),
|
||||
showLabelBinding = bind(Variable(true)),
|
||||
showIconBinding = bind(Variable(true)),
|
||||
showLabel,
|
||||
labelHook,
|
||||
hook,
|
||||
@@ -48,12 +47,12 @@ export const Module = ({
|
||||
);
|
||||
|
||||
const componentChildren = Variable.derive(
|
||||
[showLabelBinding, useTextIcon],
|
||||
(showLabel: boolean, forceTextIcon: boolean): JSX.Element[] => {
|
||||
[showLabelBinding, showIconBinding, useTextIcon],
|
||||
(showLabel: boolean, showIcon: boolean, forceTextIcon: boolean): JSX.Element[] => {
|
||||
const childrenArray = [];
|
||||
const iconWidget = getIconWidget(forceTextIcon);
|
||||
|
||||
if (iconWidget !== undefined) {
|
||||
if (showIcon && iconWidget !== undefined) {
|
||||
childrenArray.push(iconWidget);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import options from 'src/options';
|
||||
import { Gdk } from 'astal/gtk3';
|
||||
import { GtkWidget } from 'src/lib/types/widget';
|
||||
import { onMiddleClick, onPrimaryClick, onSecondaryClick } from 'src/lib/shared/eventHandlers';
|
||||
import { isScrollDown, isScrollUp } from 'src/lib/utils';
|
||||
|
||||
const { scrollSpeed } = options.bar.customModules;
|
||||
|
||||
@@ -45,7 +46,7 @@ export const runAsyncCommand: RunAsyncCommand = (cmd, events, fn, postInputUpdat
|
||||
return;
|
||||
}
|
||||
|
||||
execAsync(`bash -c "${cmd}"`)
|
||||
execAsync(['bash', '-c', cmd])
|
||||
.then((output) => {
|
||||
handlePostInputUpdater(postInputUpdater);
|
||||
if (fn !== undefined) {
|
||||
@@ -152,29 +153,18 @@ export const inputHandler = (
|
||||
});
|
||||
|
||||
const id = self.connect('scroll-event', (self: GtkWidget, event: Gdk.Event) => {
|
||||
const [directionSuccess, direction] = event.get_scroll_direction();
|
||||
const [deltaSuccess, , yScroll] = event.get_scroll_deltas();
|
||||
|
||||
const handleScroll = (input?: { cmd: Variable<string>; fn: (output: string) => void }): void => {
|
||||
if (input) {
|
||||
throttledHandler(sanitizeInput(input.cmd), { clicked: self, event }, input.fn, postInputUpdater);
|
||||
}
|
||||
};
|
||||
|
||||
if (directionSuccess) {
|
||||
if (direction === Gdk.ScrollDirection.UP) {
|
||||
handleScroll(onScrollUpInput);
|
||||
} else if (direction === Gdk.ScrollDirection.DOWN) {
|
||||
handleScroll(onScrollDownInput);
|
||||
}
|
||||
if (isScrollUp(event)) {
|
||||
handleScroll(onScrollUpInput);
|
||||
}
|
||||
|
||||
if (deltaSuccess) {
|
||||
if (yScroll > 0) {
|
||||
handleScroll(onScrollUpInput);
|
||||
} else if (yScroll < 0) {
|
||||
handleScroll(onScrollDownInput);
|
||||
}
|
||||
if (isScrollDown(event)) {
|
||||
handleScroll(onScrollDownInput);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user