OSDs are now click through and can have borders. (#674)

This commit is contained in:
Jas Singh
2024-12-30 04:09:16 -08:00
committed by GitHub
parent d49a750bfd
commit d2e02f553a
7 changed files with 121 additions and 167 deletions

View File

@@ -551,6 +551,7 @@ in
theme.osd.opacity = mkIntOption 100; theme.osd.opacity = mkIntOption 100;
theme.osd.orientation = mkStrOption "vertical"; theme.osd.orientation = mkStrOption "vertical";
theme.osd.radius = mkStrOption "0.4em"; theme.osd.radius = mkStrOption "0.4em";
theme.osd.border.size = mkStrOption "0em";
theme.osd.scaling = mkIntOption 100; theme.osd.scaling = mkIntOption 100;
theme.tooltip.scaling = mkIntOption 100; theme.tooltip.scaling = mkIntOption 100;
wallpaper.enable = mkBoolOption true; wallpaper.enable = mkBoolOption true;

View File

@@ -1,4 +1,3 @@
import { Binding } from 'astal';
import { bind, timeout, Variable } from 'astal'; import { bind, timeout, Variable } from 'astal';
import { Widget } from 'astal/gtk3'; import { Widget } from 'astal/gtk3';
import { audioService, brightnessService, hyprlandService } from 'src/lib/constants/services'; import { audioService, brightnessService, hyprlandService } from 'src/lib/constants/services';
@@ -17,16 +16,19 @@ timeout(3000, () => {
}); });
/** /**
* Handles the reveal state of a Widget.Revealer. * Handles the reveal state of a Widget.Revealer or Widget.Window.
* *
* This function sets the `reveal_child` property of the Widget.Revealer to true if the OSD is enabled and the property is 'revealChild'. * This function delegates the reveal handling to either `handleRevealRevealer` or `handleRevealWindow` based on the type of the widget.
* It also manages a timeout to reset the `reveal_child` property after the specified duration.
* *
* @param self The Widget.Revealer instance. * @param self The Widget.Revealer or Widget.Window instance.
* @param property The property to check, either 'revealChild' or 'visible'. * @param property The property to check, either 'revealChild' or 'visible'.
*/ */
export const handleRevealRevealer = (self: Widget.Revealer, property: 'revealChild' | 'visible'): void => { export const handleReveal = (self: Widget.Revealer): void => {
if (!enable.get() || property !== 'revealChild') { if (isStartingUp) {
return;
}
if (!enable.get()) {
return; return;
} }
@@ -42,60 +44,14 @@ export const handleRevealRevealer = (self: Widget.Revealer, property: 'revealChi
}); });
}; };
/**
* Handles the reveal state of a Widget.Window.
*
* This function sets the `visible` property of the Widget.Window to true if the OSD is enabled and the property is 'visible'.
* It also manages a timeout to reset the `visible` property after the specified duration.
*
* @param self The Widget.Window instance.
* @param property The property to check, either 'revealChild' or 'visible'.
*/
export const handleRevealWindow = (self: Widget.Window, property: 'revealChild' | 'visible'): void => {
if (!enable.get() || property !== 'visible') {
return;
}
self.visible = true;
count++;
timeout(duration.get(), () => {
count--;
if (count === 0) {
self.visible = false;
}
});
};
/**
* Handles the reveal state of a Widget.Revealer or Widget.Window.
*
* This function delegates the reveal handling to either `handleRevealRevealer` or `handleRevealWindow` based on the type of the widget.
*
* @param self The Widget.Revealer or Widget.Window instance.
* @param property The property to check, either 'revealChild' or 'visible'.
*/
export const handleReveal = (self: Widget.Revealer | Widget.Window, property: 'revealChild' | 'visible'): void => {
if (isStartingUp) {
return;
}
if (self instanceof Widget.Revealer) {
handleRevealRevealer(self, property);
} else if (self instanceof Widget.Window) {
handleRevealWindow(self, property);
}
};
/** /**
* Retrieves the monitor index for the OSD. * Retrieves the monitor index for the OSD.
* *
* This function derives the monitor index for the OSD based on the focused monitor, default monitor, and active monitor settings. * This function derives the monitor index for the OSD based on the focused monitor, default monitor, and active monitor settings.
* *
* @returns A Binding<number> representing the monitor index for the OSD. * @returns A Variable<number> representing the monitor index for the OSD.
*/ */
export const getOsdMonitor = (): Binding<number> => { export const getOsdMonitor = (): Variable<number> => {
return Variable.derive( return Variable.derive(
[bind(hyprlandService, 'focusedMonitor'), bind(monitor), bind(active_monitor)], [bind(hyprlandService, 'focusedMonitor'), bind(monitor), bind(active_monitor)],
(currentMonitor, defaultMonitor, followMonitor) => { (currentMonitor, defaultMonitor, followMonitor) => {
@@ -105,39 +61,7 @@ export const getOsdMonitor = (): Binding<number> => {
return defaultMonitor; return defaultMonitor;
}, },
)();
};
/**
* Sets up the window for OSD.
*
* This function hooks various services and settings to the window to handle its visibility based on the OSD configuration.
*
* @param self The Widget.Window instance to set up.
*/
export const windowSetup = (self: Widget.Window): void => {
self.hook(enable, () => {
handleReveal(self, 'visible');
});
self.hook(brightnessService, 'notify::screen', () => {
handleReveal(self, 'visible');
});
self.hook(brightnessService, 'notify::kbd', () => {
handleReveal(self, 'visible');
});
Variable.derive(
[bind(audioService.defaultMicrophone, 'volume'), bind(audioService.defaultMicrophone, 'mute')],
() => {
handleReveal(self, 'visible');
},
); );
Variable.derive([bind(audioService.defaultSpeaker, 'volume'), bind(audioService.defaultSpeaker, 'mute')], () => {
handleReveal(self, 'visible');
});
}; };
/** /**
@@ -149,25 +73,25 @@ export const windowSetup = (self: Widget.Window): void => {
*/ */
export const revealerSetup = (self: Widget.Revealer): void => { export const revealerSetup = (self: Widget.Revealer): void => {
self.hook(enable, () => { self.hook(enable, () => {
handleReveal(self, 'revealChild'); handleReveal(self);
}); });
self.hook(brightnessService, 'notify::screen', () => { self.hook(brightnessService, 'notify::screen', () => {
handleReveal(self, 'revealChild'); handleReveal(self);
}); });
self.hook(brightnessService, 'notify::kbd', () => { self.hook(brightnessService, 'notify::kbd', () => {
handleReveal(self, 'revealChild'); handleReveal(self);
}); });
Variable.derive( Variable.derive(
[bind(audioService.defaultMicrophone, 'volume'), bind(audioService.defaultMicrophone, 'mute')], [bind(audioService.defaultMicrophone, 'volume'), bind(audioService.defaultMicrophone, 'mute')],
() => { () => {
handleReveal(self, 'revealChild'); handleReveal(self);
}, },
); );
Variable.derive([bind(audioService.defaultSpeaker, 'volume'), bind(audioService.defaultSpeaker, 'mute')], () => { Variable.derive([bind(audioService.defaultSpeaker, 'volume'), bind(audioService.defaultSpeaker, 'mute')], () => {
handleReveal(self, 'revealChild'); handleReveal(self);
}); });
}; };

View File

@@ -2,7 +2,7 @@ import options from 'src/options';
import { getPosition } from 'src/lib/utils'; import { getPosition } from 'src/lib/utils';
import { bind } from 'astal'; import { bind } from 'astal';
import { Astal } from 'astal/gtk3'; import { Astal } from 'astal/gtk3';
import { getOsdMonitor, windowSetup } from './helpers'; import { getOsdMonitor } from './helpers';
import { OsdRevealer } from './OsdRevealer'; import { OsdRevealer } from './OsdRevealer';
const { location } = options.theme.osd; const { location } = options.theme.osd;
@@ -10,14 +10,18 @@ const { location } = options.theme.osd;
export default (): JSX.Element => { export default (): JSX.Element => {
return ( return (
<window <window
monitor={getOsdMonitor()} monitor={getOsdMonitor()()}
name={'indicator'} name={'indicator'}
namespace={'indicator'} namespace={'indicator'}
className={'indicator'} className={'indicator'}
visible={false} visible={true}
layer={bind(options.tear).as((tear) => (tear ? Astal.Layer.TOP : Astal.Layer.OVERLAY))} layer={bind(options.tear).as((tear) => (tear ? Astal.Layer.TOP : Astal.Layer.OVERLAY))}
anchor={bind(location).as((anchorPoint) => getPosition(anchorPoint))} anchor={bind(location).as((anchorPoint) => getPosition(anchorPoint))}
setup={windowSetup} setup={(self) => {
getOsdMonitor().subscribe(() => {
self.set_click_through(true);
});
}}
clickThrough clickThrough
> >
<OsdRevealer /> <OsdRevealer />

View File

@@ -42,13 +42,14 @@ export const OSDSettings = (): JSX.Element => {
subtitle="OSD follows monitor of cursor" subtitle="OSD follows monitor of cursor"
type="boolean" type="boolean"
/> />
<Option opt={options.theme.osd.radius} title="Radius" subtitle="Radius of the OSD" type="string" />
<Option <Option
opt={options.theme.osd.margins} opt={options.theme.osd.margins}
title="Margins" title="Margins"
subtitle="Format: top right bottom left" subtitle="Format: top right bottom left"
type="string" type="string"
/> />
<Option opt={options.theme.osd.border.size} title="Border Size" type="string" />
<Option opt={options.theme.osd.radius} title="Radius" subtitle="Radius of the OSD" type="string" />
<Option <Option
opt={options.theme.osd.muted_zero} opt={options.theme.osd.muted_zero}
title="Mute Volume as Zero" title="Mute Volume as Zero"

View File

@@ -24,6 +24,7 @@ export const OsdTheme = (): JSX.Element => {
min={0} min={0}
max={100} max={100}
/> />
<Option opt={options.theme.osd.border.color} title="Border" type="color" />
<Option opt={options.theme.osd.bar_color} title="Bar" type="color" /> <Option opt={options.theme.osd.bar_color} title="Bar" type="color" />
<Option <Option
opt={options.theme.osd.bar_overflow_color} opt={options.theme.osd.bar_overflow_color}

View File

@@ -134,6 +134,10 @@ const options = mkOptions(CONFIG, {
enable: opt(true), enable: opt(true),
orientation: opt<OSDOrientation>('vertical'), orientation: opt<OSDOrientation>('vertical'),
opacity: opt(100), opacity: opt(100),
border: {
size: opt('0em'),
color: opt(colors.lavender),
},
bar_container: opt(colors.crust), bar_container: opt(colors.crust),
icon_container: opt(tertiary_colors.lavender), icon_container: opt(tertiary_colors.lavender),
bar_color: opt(tertiary_colors.lavender), bar_color: opt(tertiary_colors.lavender),

View File

@@ -9,14 +9,22 @@
} }
.osd-label-container { .osd-label-container {
border-top: $osd-border-size solid $osd-border-color;
border-bottom: if($osd-orientation == 'horizontal', $osd-border-size solid $osd-border-color, 0em);
border-left: if($osd-orientation == 'vertical', $osd-border-size solid $osd-border-color, 0em);
border-right: $osd-border-size solid $osd-border-color;
background: $osd-bar_container; background: $osd-bar_container;
border-radius: if($osd-orientation =="vertical", $osd-radius $osd-radius 0em 0em, 0em $osd-radius $osd-radius 0em); border-radius: if(
$osd-orientation == 'vertical',
$osd-radius $osd-radius 0em 0em,
0em $osd-radius $osd-radius 0em
);
.osd-label { .osd-label {
font-size: 1em; font-size: 1em;
padding-top: if($osd-orientation =="vertical", 1em, 0em); padding-top: if($osd-orientation == 'vertical', 1em, 0em);
padding-right: if($osd-orientation =="horizontal", 1em, 0em); padding-right: if($osd-orientation == 'horizontal', 1em, 0em);
color: $osd-label; color: $osd-label;
&.overflow { &.overflow {
@@ -26,18 +34,30 @@
} }
.osd-icon-container { .osd-icon-container {
border-top: if($osd-orientation == 'horizontal', $osd-border-size solid $osd-border-color, 0em);
border-bottom: $osd-border-size solid $osd-border-color;
border-left: $osd-border-size solid $osd-border-color;
border-right: if($osd-orientation == 'vertical', $osd-border-size solid $osd-border-color, 0em);
background: $osd-icon_container; background: $osd-icon_container;
border-radius: if($osd-orientation =="vertical", 0em 0em $osd-radius $osd-radius, $osd-radius 0em 0em $osd-radius ); border-radius: if(
$osd-orientation == 'vertical',
0em 0em $osd-radius $osd-radius,
$osd-radius 0em 0em $osd-radius
);
.osd-icon { .osd-icon {
font-size: 2.1em; font-size: 2.1em;
padding: if($osd-orientation =="vertical", 0.2em 0em, 0em 0.4em); padding: if($osd-orientation == 'vertical', 0.2em 0em, 0em 0.4em);
color: $osd-icon; color: $osd-icon;
} }
} }
.osd-bar-container { .osd-bar-container {
border-top: if($osd-orientation == 'horizontal', $osd-border-size solid $osd-border-color, 0em);
border-bottom: if($osd-orientation == 'horizontal', $osd-border-size solid $osd-border-color, 0em);
border-left: if($osd-orientation == 'vertical', $osd-border-size solid $osd-border-color, 0em);
border-right: if($osd-orientation == 'vertical', $osd-border-size solid $osd-border-color, 0em);
padding: 1.25em; padding: 1.25em;
background: $osd-bar_container; background: $osd-bar_container;
@@ -47,8 +67,8 @@
} }
trough { trough {
min-height: if($osd-orientation =="vertical", 10em, 0); min-height: if($osd-orientation == 'vertical', 10em, 0);
min-width: if($osd-orientation =="horizontal", 10em, 0); min-width: if($osd-orientation == 'horizontal', 10em, 0);
} }
block { block {
@@ -56,7 +76,6 @@
&.empty { &.empty {
background: $osd-bar_empty_color; background: $osd-bar_empty_color;
} }
&.filled { &.filled {