Added the ability to adjust application specific audio levels. (#608)

* Added a playback volume module in audio menu.

* Finish playback source volume adjuster.
This commit is contained in:
Jas Singh
2024-12-23 14:03:01 -08:00
committed by GitHub
parent a3240e6c6d
commit af88c267f4
18 changed files with 368 additions and 46 deletions

View File

@@ -8,11 +8,11 @@ import AstalHyprland from 'gi://AstalHyprland?version=0.1';
* This function searches for a matching window title in the predefined `windowTitleMap` based on the class of the provided window. * This function searches for a matching window title in the predefined `windowTitleMap` based on the class of the provided window.
* If a match is found, it returns an object containing the icon and label for the window. If no match is found, it returns a default icon and label. * If a match is found, it returns an object containing the icon and label for the window. If no match is found, it returns a default icon and label.
* *
* @param windowtitle The window object containing the class information. * @param client The window object containing the class information.
* *
* @returns An object containing the icon and label for the window. * @returns An object containing the icon and label for the window.
*/ */
export const getWindowMatch = (windowtitle: AstalHyprland.Client): Record<string, string> => { export const getWindowMatch = (client: AstalHyprland.Client): Record<string, string> => {
const windowTitleMap = [ const windowTitleMap = [
// user provided values // user provided values
...options.bar.windowtitle.title_map.get(), ...options.bar.windowtitle.title_map.get(),
@@ -119,17 +119,17 @@ export const getWindowMatch = (windowtitle: AstalHyprland.Client): Record<string
['^$', '󰇄', 'Desktop'], ['^$', '󰇄', 'Desktop'],
// Fallback icon // Fallback icon
['(.+)', '󰣆', `${capitalizeFirstLetter(windowtitle?.class ?? 'Unknown')}`], ['(.+)', '󰣆', `${capitalizeFirstLetter(client?.class ?? 'Unknown')}`],
]; ];
if (!windowtitle?.class) { if (!client?.class) {
return { return {
icon: '󰇄', icon: '󰇄',
label: 'Desktop', label: 'Desktop',
}; };
} }
const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0]).test(windowtitle?.class.toLowerCase())); const foundMatch = windowTitleMap.find((wt) => RegExp(wt[0]).test(client?.class.toLowerCase()));
if (!foundMatch || foundMatch.length !== 3) { if (!foundMatch || foundMatch.length !== 3) {
return { return {
@@ -157,13 +157,12 @@ export const getWindowMatch = (windowtitle: AstalHyprland.Client): Record<string
* @returns The title of the window as a string. * @returns The title of the window as a string.
*/ */
export const getTitle = (client: AstalHyprland.Client, useCustomTitle: boolean, useClassName: boolean): string => { export const getTitle = (client: AstalHyprland.Client, useCustomTitle: boolean, useClassName: boolean): string => {
if (client === null) return getWindowMatch(client).label; if (client === null || useCustomTitle) return getWindowMatch(client).label;
if (useCustomTitle) return getWindowMatch(client).label;
if (useClassName) return client.class;
const title = client.title; const title = client.title;
// If the title is empty or only filled with spaces, fallback to the class name
if (!title || useClassName) return client.class;
if (title.length === 0 || title.match(/^ *$/)) { if (title.length === 0 || title.match(/^ *$/)) {
return client.class; return client.class;
} }

View File

@@ -152,7 +152,7 @@ export const WorkspaceModule = ({ monitor }: WorkspaceModuleProps): JSX.Element
self.toggleClassName('active', activeWorkspace === wsId); self.toggleClassName('active', activeWorkspace === wsId);
self.toggleClassName( self.toggleClassName(
'occupied', 'occupied',
(hyprlandService.get_workspace(wsId)?.get_clients().length || 0) > 0, (hyprlandService.get_workspace(wsId)?.get_clients()?.length || 0) > 0,
); );
}} }}
/> />

View File

@@ -140,7 +140,10 @@ export const CustomModuleSettings = (): JSX.Element => {
<Option <Option
opt={options.bar.customModules.netstat.networkInterface} opt={options.bar.customModules.netstat.networkInterface}
title="Network Interface" title="Network Interface"
subtitle="Wiki: https://hyprpanel.com/configuration/panel.html#custom-modules" subtitle={
'Name of the network interface to poll.\n' +
"HINT: Get a list of interfaces with 'cat /proc/net/dev"
}
type="string" type="string"
/> />
<Option <Option
@@ -280,6 +283,79 @@ export const CustomModuleSettings = (): JSX.Element => {
<Option opt={options.bar.customModules.weather.middleClick} title="Middle Click" type="string" /> <Option opt={options.bar.customModules.weather.middleClick} title="Middle Click" type="string" />
<Option opt={options.bar.customModules.weather.scrollUp} title="Scroll Up" type="string" /> <Option opt={options.bar.customModules.weather.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.weather.scrollDown} title="Scroll Down" type="string" /> <Option opt={options.bar.customModules.weather.scrollDown} title="Scroll Down" type="string" />
{/* Hyprsunset Section */}
<Header title="Hyprsunset" />
<Option
opt={options.bar.customModules.hyprsunset.temperature}
title="Temperature"
subtitle="Ex: 1000k, 2000k, 5000k, etc."
type="string"
/>
<Option
opt={options.theme.bar.buttons.modules.hyprsunset.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.hyprsunset.onIcon} title="Enabled Icon" type="string" />
<Option opt={options.bar.customModules.hyprsunset.offIcon} title="Disabled Icon" type="string" />
<Option opt={options.bar.customModules.hyprsunset.onLabel} title="Enabled Label" type="string" />
<Option opt={options.bar.customModules.hyprsunset.offLabel} title="Disabled Label" type="string" />
<Option opt={options.bar.customModules.hyprsunset.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.hyprsunset.spacing} title="Spacing" type="string" />
<Option
opt={options.bar.customModules.hyprsunset.pollingInterval}
title="Polling Interval"
type="number"
min={100}
max={60 * 24 * 1000}
increment={1000}
/>
<Option opt={options.bar.customModules.hyprsunset.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.hyprsunset.middleClick} title="Middle Click" type="string" />
<Option opt={options.bar.customModules.hyprsunset.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.hyprsunset.scrollDown} title="Scroll Down" type="string" />
{/* Hypridle Section */}
<Header title="Hypridle" />
<Option
opt={options.theme.bar.buttons.modules.hypridle.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.hypridle.onIcon} title="Enabled Icon" type="string" />
<Option opt={options.bar.customModules.hypridle.offIcon} title="Disabled Icon" type="string" />
<Option opt={options.bar.customModules.hypridle.onLabel} title="Enabled Label" type="string" />
<Option opt={options.bar.customModules.hypridle.offLabel} title="Disabled Label" type="string" />
<Option opt={options.bar.customModules.hypridle.label} title="Show Label" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.hypridle.spacing} title="Spacing" type="string" />
<Option
opt={options.bar.customModules.hypridle.pollingInterval}
title="Polling Interval"
type="number"
min={100}
max={60 * 24 * 1000}
increment={1000}
/>
<Option opt={options.bar.customModules.hypridle.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.hypridle.middleClick} title="Middle Click" type="string" />
<Option opt={options.bar.customModules.hypridle.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.hypridle.scrollDown} title="Scroll Down" type="string" />
{/* Power Section */}
<Header title="Power" />
<Option
opt={options.theme.bar.buttons.modules.power.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.theme.bar.buttons.modules.power.spacing} title="Spacing" type="string" />
<Option opt={options.bar.customModules.power.icon} title="Power Button Icon" type="string" />
<Option opt={options.bar.customModules.power.leftClick} title="Left Click" type="string" />
<Option opt={options.bar.customModules.power.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.power.middleClick} title="Middle Click" type="string" />
<Option opt={options.bar.customModules.power.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.power.scrollDown} title="Scroll Down" type="string" />
</box> </box>
</scrollable> </scrollable>
); );

View File

@@ -158,6 +158,56 @@ export const CustomModuleTheme = (): JSX.Element => {
type="color" type="color"
/> />
<Option opt={options.theme.bar.buttons.modules.weather.border} title="Border" type="color" /> <Option opt={options.theme.bar.buttons.modules.weather.border} title="Border" type="color" />
{/* Hyprsunset Module Section */}
<Header title="Hyprsunset" />
<Option opt={options.theme.bar.buttons.modules.hyprsunset.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.modules.hyprsunset.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.modules.hyprsunset.background}
title="Label Background"
type="color"
/>
<Option
opt={options.theme.bar.buttons.modules.hyprsunset.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.hyprsunset.border} title="Border" type="color" />
{/* Hypridle Module Section */}
<Header title="Hypridle" />
<Option opt={options.theme.bar.buttons.modules.hypridle.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.modules.hypridle.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.modules.hypridle.background}
title="Label Background"
type="color"
/>
<Option
opt={options.theme.bar.buttons.modules.hypridle.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.hypridle.border} title="Border" type="color" />
{/* Power Module Section */}
<Header title="Power" />
<Option opt={options.theme.bar.buttons.modules.power.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.modules.power.background}
title="Label Background"
type="color"
/>
<Option
opt={options.theme.bar.buttons.modules.power.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.power.border} title="Border" type="color" />
</box> </box>
</scrollable> </scrollable>
); );

View File

@@ -0,0 +1,25 @@
import { BindableChild } from 'astal/gtk3/astalify';
import { audioService } from 'src/lib/constants/services';
import { SliderItem } from '../sliderItem/SliderItem';
import { ActiveDeviceMenu } from '..';
const ActiveDeviceContainer = ({ children }: ActiveDeviceContainerProps): JSX.Element => {
return (
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.Devices} vertical>
{children}
</box>
);
};
export const ActiveDevices = (): JSX.Element => {
return (
<ActiveDeviceContainer>
<SliderItem type={'playback'} device={audioService.defaultSpeaker} />
<SliderItem type={'input'} device={audioService.defaultMicrophone} />
</ActiveDeviceContainer>
);
};
interface ActiveDeviceContainerProps {
children?: BindableChild | BindableChild[];
}

View File

@@ -1,34 +1,56 @@
import { Gtk } from 'astal/gtk3'; import { Gtk } from 'astal/gtk3';
import { ActiveDevice } from './device/index.js'; import { ActiveDevices } from './devices/index.js';
import { audioService } from 'src/lib/constants/services.js'; import Variable from 'astal/variable.js';
import { BindableChild } from 'astal/gtk3/astalify.js'; import { ActivePlaybacks } from './playbacks/index.js';
import { bind } from 'astal/binding.js';
import { isPrimaryClick } from 'src/lib/utils.js';
export enum ActiveDeviceMenu {
Devices = 'devices',
Playbacks = 'playbacks',
}
const activeMenu: Variable<ActiveDeviceMenu> = Variable(ActiveDeviceMenu.Devices);
export const SelectedDevices = (): JSX.Element => {
const Header = (): JSX.Element => ( const Header = (): JSX.Element => (
<box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}> <box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}>
<label className={'menu-label audio volume'} halign={Gtk.Align.START} hexpand label={'Volume'} /> <label className={'menu-label audio volume'} halign={Gtk.Align.START} hexpand label={'Volume'} />
<button
className={'menu-label slider-toggle'}
onClick={(_, event) => {
if (!isPrimaryClick(event)) {
return;
}
if (activeMenu.get() === ActiveDeviceMenu.Devices) {
activeMenu.set(ActiveDeviceMenu.Playbacks);
} else {
activeMenu.set(ActiveDeviceMenu.Devices);
}
}}
halign={Gtk.Align.END}
hexpand
label={bind(activeMenu).as((menu) => (menu === ActiveDeviceMenu.Devices ? '' : '󰤽'))}
/>
</box> </box>
); );
const ActiveDeviceContainer = ({ children }: ActiveDeviceContainerProps): JSX.Element => { export const VolumeSliders = (): JSX.Element => {
return (
<box className={'menu-items-section selected'} vertical>
{children}
</box>
);
};
return ( return (
<box className={'menu-section-container volume'} vertical> <box className={'menu-section-container volume'} vertical>
<Header /> <Header />
<ActiveDeviceContainer> <revealer
<ActiveDevice type={'playback'} device={audioService.defaultSpeaker} /> transitionType={Gtk.RevealerTransitionType.NONE}
<ActiveDevice type={'input'} device={audioService.defaultMicrophone} /> revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.Devices)}
</ActiveDeviceContainer> >
<ActiveDevices />
</revealer>
<revealer
transitionType={Gtk.RevealerTransitionType.NONE}
revealChild={bind(activeMenu).as((curMenu) => curMenu === ActiveDeviceMenu.Playbacks)}
>
<ActivePlaybacks />
</revealer>
</box> </box>
); );
}; };
interface ActiveDeviceContainerProps {
children?: BindableChild | BindableChild[];
}

View File

@@ -0,0 +1,30 @@
import { bind } from 'astal';
import { audioService } from 'src/lib/constants/services';
import { SliderItem } from '../sliderItem/SliderItem';
import { ActiveDeviceMenu } from '..';
const NoStreams = (): JSX.Element => {
return <label className={'no-playbacks dim'} label={'No active playbacks found.'} expand />;
};
export const ActivePlaybacks = (): JSX.Element => {
return (
<box className={'menu-items-section selected'} name={ActiveDeviceMenu.Playbacks} vertical>
<scrollable className={'menu-scroller active-playbacks-scrollable'}>
<box vertical>
{bind(audioService, 'streams').as((streams) => {
if (!streams || streams.length === 0) {
return <NoStreams />;
}
const currentStreams = streams;
return currentStreams.map((stream) => {
return <SliderItem type={'playback'} device={stream} />;
});
})}
</box>
</scrollable>
</box>
);
};

View File

@@ -1,6 +1,7 @@
import { bind } from 'astal'; import { bind } from 'astal';
import { Gtk } from 'astal/gtk3'; import { Gdk, Gtk } from 'astal/gtk3';
import AstalWp from 'gi://AstalWp?version=0.1'; import AstalWp from 'gi://AstalWp?version=0.1';
import { capitalizeFirstLetter } from 'src/lib/utils';
import options from 'src/options'; import options from 'src/options';
const { raiseMaximumVolume } = options.menus.volume; const { raiseMaximumVolume } = options.menus.volume;
@@ -12,9 +13,11 @@ export const Slider = ({ device, type }: SliderProps): JSX.Element => {
className={`menu-active ${type}`} className={`menu-active ${type}`}
halign={Gtk.Align.START} halign={Gtk.Align.START}
truncate truncate
expand hexpand
wrap wrap
label={bind(device, 'description').as((description) => description ?? `Unknown ${type} Device`)} label={bind(device, 'description').as((description) =>
capitalizeFirstLetter(description ?? `Unknown ${type} Device`),
)}
/> />
<slider <slider
value={bind(device, 'volume')} value={bind(device, 'volume')}
@@ -29,6 +32,20 @@ export const Slider = ({ device, type }: SliderProps): JSX.Element => {
device.mute = false; device.mute = false;
} }
}} }}
setup={(self) => {
self.connect('scroll-event', (_, event: Gdk.Event) => {
const [directionSuccess, direction] = event.get_scroll_direction();
const [deltasSuccess, , yScroll] = event.get_scroll_deltas();
if (directionSuccess) {
const newVolume = device.volume + (direction === Gdk.ScrollDirection.DOWN ? 0.05 : -0.05);
device.set_volume(Math.min(newVolume, 1));
} else if (deltasSuccess) {
const newVolume = device.volume - yScroll / 100;
device.set_volume(Math.min(newVolume, 1));
}
});
}}
/> />
</box> </box>
); );

View File

@@ -3,7 +3,7 @@ import { SliderIcon } from './SliderIcon';
import { Slider } from './Slider'; import { Slider } from './Slider';
import { SliderPercentage } from './SliderPercentage'; import { SliderPercentage } from './SliderPercentage';
export const ActiveDevice = ({ type, device }: ActiveDeviceProps): JSX.Element => { export const SliderItem = ({ type, device }: SliderItemProps): JSX.Element => {
return ( return (
<box className={`menu-active-container ${type}`} vertical> <box className={`menu-active-container ${type}`} vertical>
<box className={`menu-slider-container ${type}`}> <box className={`menu-slider-container ${type}`}>
@@ -15,7 +15,7 @@ export const ActiveDevice = ({ type, device }: ActiveDeviceProps): JSX.Element =
); );
}; };
interface ActiveDeviceProps { interface SliderItemProps {
type: 'playback' | 'input'; type: 'playback' | 'input';
device: AstalWp.Endpoint; device: AstalWp.Endpoint;
} }

View File

@@ -1,5 +1,5 @@
import DropdownMenu from '../shared/dropdown/index.js'; import DropdownMenu from '../shared/dropdown/index.js';
import { SelectedDevices } from './active/index.js'; import { VolumeSliders } from './active/index.js';
import options from 'src/options.js'; import options from 'src/options.js';
import { bind } from 'astal/binding.js'; import { bind } from 'astal/binding.js';
import { Gtk } from 'astal/gtk3'; import { Gtk } from 'astal/gtk3';
@@ -14,7 +14,7 @@ export default (): JSX.Element => {
> >
<box className={'menu-items audio'} halign={Gtk.Align.FILL} hexpand> <box className={'menu-items audio'} halign={Gtk.Align.FILL} hexpand>
<box className={'menu-items-container audio'} halign={Gtk.Align.FILL} vertical hexpand> <box className={'menu-items-container audio'} halign={Gtk.Align.FILL} vertical hexpand>
<SelectedDevices /> <VolumeSliders />
<AvailableDevices /> <AvailableDevices />
</box> </box>
</box> </box>

View File

@@ -145,6 +145,77 @@ export const BarTheme = (): JSX.Element => {
type="color" type="color"
/> />
<Option opt={options.theme.bar.buttons.volume.border} title="Border" type="color" /> <Option opt={options.theme.bar.buttons.volume.border} title="Border" type="color" />
{/* Network Section */}
<Header title="Network" />
<Option opt={options.theme.bar.buttons.network.background} title="Background" type="color" />
<Option opt={options.theme.bar.buttons.network.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.network.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.network.icon_background}
title="Button 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.network.border} title="Border" type="color" />
{/* Bluetooth Section */}
<Header title="Bluetooth" />
<Option opt={options.theme.bar.buttons.bluetooth.background} title="Background" type="color" />
<Option opt={options.theme.bar.buttons.bluetooth.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.bluetooth.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.bluetooth.icon_background}
title="Button 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.bluetooth.border} title="Border" type="color" />
{/* System Tray Section */}
<Header title="System Tray" />
<Option opt={options.theme.bar.buttons.systray.border} title="Border" type="color" />
<Option opt={options.theme.bar.buttons.systray.customIcon} title="Custom Icons" type="color" />
<Option opt={options.theme.bar.buttons.systray.background} title="Background" type="color" />
{/* Battery Section */}
<Header title="Battery" />
<Option opt={options.theme.bar.buttons.battery.background} title="Background" type="color" />
<Option opt={options.theme.bar.buttons.battery.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.battery.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.battery.icon_background}
title="Button 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.battery.border} title="Border" type="color" />
{/* Clock Section */}
<Header title="Clock" />
<Option opt={options.theme.bar.buttons.clock.background} title="Background" type="color" />
<Option opt={options.theme.bar.buttons.clock.text} title="Text" type="color" />
<Option opt={options.theme.bar.buttons.clock.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.clock.icon_background}
title="Button 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.clock.border} title="Border" type="color" />
{/* Notifications Section */}
<Header title="Notifications" />
<Option opt={options.theme.bar.buttons.notifications.background} title="Background" type="color" />
<Option opt={options.theme.bar.buttons.notifications.total} title="Notification Count" type="color" />
<Option opt={options.theme.bar.buttons.notifications.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.notifications.icon_background}
title="Button 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.notifications.border} title="Border" type="color" />
</box> </box>
</scrollable> </scrollable>
); );

View File

@@ -527,6 +527,9 @@ const options = mkOptions(CONFIG, {
color: opt(colors.maroon), color: opt(colors.maroon),
}, },
text: opt(colors.text), text: opt(colors.text),
scroller: {
color: opt(colors.maroon),
},
listitems: { listitems: {
passive: opt(colors.text), passive: opt(colors.text),
active: opt(secondary_colors.maroon), active: opt(secondary_colors.maroon),

View File

@@ -212,4 +212,33 @@
.menu-items-section.playback { .menu-items-section.playback {
border-radius: 0em; border-radius: 0em;
} }
.slider-toggle {
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-passive, $bar-menus-menu-volume-iconbutton-passive);
padding: 0em 1em;
label {
font-size: 1.25em;
}
&:hover {
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-volume-iconbutton-active);
}
}
.active-playbacks-scrollable {
min-height: 12.5em;
scrollbar contents trough slider {
background: $bar-menus-menu-volume-scroller-color;
}
.menu-active-percentage.playback,
.menu-active-percentage.input {
margin-right: 0.6em;
}
}
.no-playbacks.dim {
opacity: 0.5;
}
} }

View File

@@ -202,7 +202,7 @@
.menu-scroller.bluetooth { .menu-scroller.bluetooth {
min-height: 18em; min-height: 18em;
slider { scrollbar contents trough slider {
background: $bar-menus-menu-bluetooth-scroller-color; background: $bar-menus-menu-bluetooth-scroller-color;
} }
} }

View File

@@ -259,7 +259,7 @@
margin-right: 0em; margin-right: 0em;
min-width: 0.35em; min-width: 0.35em;
slider { scrollbar contents trough slider {
min-width: $bar-menus-scroller-width; min-width: $bar-menus-scroller-width;
border-radius: $bar-menus-scroller-radius; border-radius: $bar-menus-scroller-radius;
background: $bar-menus-text; background: $bar-menus-text;

View File

@@ -185,7 +185,7 @@
.menu-scroller.wap { .menu-scroller.wap {
min-height: 16em; min-height: 16em;
slider { scrollbar contents trough slider {
background: $bar-menus-menu-network-scroller-color; background: $bar-menus-menu-network-scroller-color;
} }
} }