sidebar: add volume mixer

This commit is contained in:
end-4
2024-03-24 21:37:06 +07:00
parent bf2920a76d
commit 0fd91e18fc
12 changed files with 401 additions and 42 deletions
@@ -0,0 +1,49 @@
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
const { Gtk } = imports.gi;
const Lang = imports.lang;
export const AnimatedSlider = ({
className,
value,
...rest
}) => {
return Widget.DrawingArea({
className: `${className}`,
setup: (self) => {
self.connect('draw', Lang.bind(self, (self, cr) => {
const styleContext = self.get_style_context();
const allocatedWidth = self.get_allocated_width();
const allocatedHeight = self.get_allocated_height();
console.log(allocatedHeight, allocatedWidth)
const minWidth = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
const radius = styleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
const bg = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
const fg = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
const value = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100;
self.set_size_request(-1, minHeight);
const width = allocatedHeight;
const height = minHeight;
cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
cr.arc(width - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
cr.arc(width - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
cr.closePath();
cr.fill();
// const valueWidth = width * value;
// cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
// cr.arc(valueWidth - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
// cr.arc(valueWidth - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
// cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
// cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
// cr.closePath();
// cr.fill();
}));
},
...rest,
})
}
@@ -4,6 +4,7 @@ const { Box, Button, EventBox, Label, Overlay, Stack } = Widget;
import { MaterialIcon } from './materialicon.js';
import { NavigationIndicator } from './cairo_navigationindicator.js';
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
import { DoubleRevealer } from '../.widgethacks/advancedrevealers.js';
export const TabContainer = ({ icons, names, children, className = '', setup = () => { }, ...rest }) => {
const shownIndex = Variable(0);
@@ -167,3 +168,107 @@ export const IconTabContainer = ({
return mainBox;
}
export const ExpandingIconTabContainer = ({
icons, names, children, className = '',
setup = () => { }, onChange = () => { },
tabsHpack = 'center', tabSwitcherClassName = '',
...rest
}) => {
const shownIndex = Variable(0);
let previousShownIndex = 0;
const count = Math.min(icons.length, names.length, children.length);
const tabs = Box({
hpack: tabsHpack,
className: `spacing-h-5 ${tabSwitcherClassName}`,
children: icons.map((icon, i) => {
const tabIcon = MaterialIcon(icon, 'norm', { hexpand: true });
const tabName = DoubleRevealer({
transition1: 'slide_right',
transition2: 'crossfade',
duration1: 0,
duration2: 0,
// duration1: userOptions.animations.durationSmall,
// duration2: userOptions.animations.durationSmall,
child: Label({
className: 'margin-left-5 txt-small',
label: names[i],
}),
revealChild: i === shownIndex.value,
})
const button = Button({
className: 'tab-icon-expandable',
tooltipText: names[i],
child: Box({
homogeneous: true,
children: [Box({
hpack: 'center',
children: [
tabIcon,
tabName,
]
})],
}),
setup: setupCursorHover,
onClicked: () => shownIndex.value = i,
});
button.toggleFocus = (value) => {
tabIcon.hexpand = !value;
button.toggleClassName('tab-icon-expandable-active', value);
tabName.toggleRevealChild(value);
}
return button;
}),
setup: (self) => self.hook(shownIndex, (self) => {
self.children[previousShownIndex].toggleFocus(false);
self.children[shownIndex.value].toggleFocus(true);
previousShownIndex = shownIndex.value;
}),
});
const tabSection = Box({
homogeneous: true,
children: [EventBox({
onScrollUp: () => mainBox.prevTab(),
onScrollDown: () => mainBox.nextTab(),
child: Box({
vertical: true,
hexpand: true,
children: [
tabs,
]
})
})]
});
const contentStack = Stack({
transition: 'slide_left_right',
children: children.reduce((acc, currentValue, index) => {
acc[index] = currentValue;
return acc;
}, {}),
setup: (self) => self.hook(shownIndex, (self) => {
self.shown = `${shownIndex.value}`;
}),
});
const mainBox = Box({
attribute: {
children: children,
shown: shownIndex,
names: names,
},
vertical: true,
className: `spacing-v-5 ${className}`,
setup: (self) => {
self.pack_start(tabSection, false, false, 0);
self.pack_end(contentStack, true, true, 0);
setup(self);
self.hook(shownIndex, (self) => onChange(self, shownIndex.value));
},
...rest,
});
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
mainBox.shown = shownIndex;
return mainBox;
}
@@ -89,6 +89,10 @@ let configOptions = {
'nextTab': "Page_Down",
'prevTab': "Page_Up",
},
'options': { // Right sidebar
'nextTab': "Page_Down",
'prevTab': "Page_Up",
},
'pin': "Ctrl+p",
'cycleTab': "Ctrl+Tab",
'nextTab': "Ctrl+Page_Down",
@@ -65,16 +65,22 @@ export const DoubleRevealer = ({
revealChild,
...rest
}) => {
return Revealer({
const r2 = Revealer({
transition: transition2,
transitionDuration: duration2,
revealChild: revealChild,
child: child,
});
const r1 = Revealer({
transition: transition1,
transitionDuration: duration1,
revealChild: revealChild,
child: Revealer({
transition: transition2,
transitionDuration: duration2,
revealChild: revealChild,
child: child,
}),
child: r2,
...rest,
})
r1.toggleRevealChild = (value) => {
r1.revealChild = value;
r2.revealChild = value;
}
return r1;
}
+1 -1
View File
@@ -185,7 +185,7 @@ const apiCommandStack = Stack({
})
export const apiContentStack = IconTabContainer({
tabSwitcherClassName: 'sidebar-chat-apiswitcher',
tabSwitcherClassName: 'sidebar-icontabswitcher',
className: 'margin-top-5',
iconWidgets: APIS.map((api) => api.tabIcon),
names: APIS.map((api) => api.name),
@@ -4,9 +4,9 @@
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
const { Box, Button, Label, Scrollable, Stack } = Widget;
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
import Notification from '../.commonwidgets/notification.js';
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
import Notification from '../../.commonwidgets/notification.js';
export default (props) => {
const notifEmptyContent = Box({
@@ -18,7 +18,7 @@ export default (props) => {
children: [
Box({
vertical: true,
className: 'spacing-v-5',
className: 'spacing-v-5 txt-subtext',
children: [
MaterialIcon('notifications_active', 'gigantic'),
Label({ label: 'No notifications', className: 'txt-small' }),
@@ -88,18 +88,29 @@ export default (props) => {
Notifications.clear();
notificationList.get_children().forEach(ch => ch.attribute.destroyWithAnims())
});
const notifCount = Label({
attribute: {
updateCount: (self) => {
const count = Notifications.notifications.length;
if (count > 0) self.label = `${count} notifications`;
else self.label = '';
},
},
hexpand: true,
xalign: 0,
className: 'txt-small margin-left-10',
label: `${Notifications.notifications.length}`,
setup: (self) => self
.hook(Notifications, (box, id) => self.attribute.updateCount(self), 'notified')
.hook(Notifications, (box, id) => self.attribute.updateCount(self), 'dismissed')
.hook(Notifications, (box, id) => self.attribute.updateCount(self), 'closed')
,
});
const listTitle = Box({
vpack: 'start',
className: 'sidebar-group-invisible txt spacing-h-5',
children: [
Label({
hexpand: true,
xalign: 0,
className: 'txt-title-small margin-left-10',
// ^ (extra margin on the left so that it looks similarly spaced
// when compared to borderless "Clear" button on the right)
label: 'Notifications',
}),
notifCount,
silenceButton,
clearButton,
]
@@ -131,11 +142,11 @@ export default (props) => {
});
return Box({
...props,
className: 'sidebar-group spacing-v-5',
className: 'spacing-v-5',
vertical: true,
children: [
listTitle,
listContents,
listTitle,
]
});
}
@@ -0,0 +1,94 @@
// This file is for the notification list on the sidebar
// For the popup notifications, see onscreendisplay.js
// The actual widget for each single notification is in ags/modules/.commonwidgets/notification.js
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
const { Box, Button, Icon, Label, Scrollable, Slider, Stack } = Widget;
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
import { AnimatedSlider } from '../../.commonwidgets/cairo_slider.js';
const appVolume = (stream) => {
console.log(stream)
return Box({
className: 'sidebar-volmixer-stream spacing-h-10',
children: [
Icon({
className: 'sidebar-volmixer-stream-appicon',
vpack: 'center',
icon: stream.stream.name.toLowerCase(),
}),
Box({
hexpand: true,
vpack: 'center',
vertical: true,
className: 'spacing-v-5',
children: [
Label({
xalign: 0,
maxWidthChars: 10,
truncate: 'end',
label: stream.description,
className: 'txt-small',
setup: (self) => {
self.hook(stream, (self) => {
self.label = `${stream.description}`
})
}
}),
Slider({
drawValue: false,
hpack: 'fill',
className: 'sidebar-volmixer-stream-slider',
value: stream.volume,
min: 0, max: 1,
onChange: ({value}) => {
stream.volume = value;
},
setup: (self) => {
self.hook(stream, (self) => {
self.value = stream.volume;
})
}
}),
// Box({
// homogeneous: true,
// className: 'test',
// children: [AnimatedSlider({
// className: 'sidebar-volmixer-stream-slider',
// value: stream.volume,
// })],
// })
]
})
]
})
}
export default (props) => {
const appList = Scrollable({
vexpand: true,
child: Box({
attribute: {
'updateStreams': (self) => {
const streams = Audio.apps;
self.children = streams.map(stream => appVolume(stream));
},
},
vertical: true,
className: 'spacing-v-5',
setup: (self) => self
.hook(Audio, self.attribute.updateStreams, 'stream-added')
.hook(Audio, self.attribute.updateStreams, 'stream-removed')
,
})
})
return Box({
...props,
className: 'spacing-v-5',
vertical: true,
children: [
appList,
]
});
}
+46 -3
View File
@@ -15,9 +15,26 @@ import {
ModulePowerIcon,
ModuleRawInput
} from "./quicktoggles.js";
import ModuleNotificationList from "./notificationlist.js";
import ModuleNotificationList from "./centermodules/notificationlist.js";
import ModuleVolumeMixer from "./centermodules/volumemixer.js";
import { ModuleCalendar } from "./calendar.js";
import { getDistroIcon } from '../.miscutils/system.js';
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
import { ExpandingIconTabContainer } from '../.commonwidgets/tabcontainer.js';
import { checkKeybind } from '../.widgetutils/keybind.js';
const centerWidgets = [
{
name: 'Notifications',
materialIcon: 'notifications',
contentWidget: ModuleNotificationList(),
},
{
name: 'Volume mixer',
materialIcon: 'volume_up',
contentWidget: ModuleVolumeMixer(),
},
];
const timeRow = Box({
className: 'spacing-h-10 sidebar-group-invisible-morehorizpad',
@@ -60,6 +77,17 @@ const togglesBox = Widget.Box({
]
})
export const sidebarOptionsStack = ExpandingIconTabContainer({
tabsHpack: 'center',
tabSwitcherClassName: 'sidebar-icontabswitcher',
icons: centerWidgets.map((api) => api.materialIcon),
names: centerWidgets.map((api) => api.name),
children: centerWidgets.map((api) => api.contentWidget),
onChange: (self, id) => {
self.shown = centerWidgets[id].name;
}
});
export default () => Box({
vexpand: true,
hexpand: true,
@@ -84,9 +112,24 @@ export default () => Box({
togglesBox,
]
}),
ModuleNotificationList({ vexpand: true, }),
Box({
className: 'sidebar-group',
children: [
sidebarOptionsStack,
],
}),
ModuleCalendar(),
]
}),
]
],
setup: (self) => self
.on('key-press-event', (widget, event) => { // Handle keybinds
if (checkKeybind(event, userOptions.keybinds.sidebar.options.nextTab)) {
sidebarOptionsStack.nextTab();
}
else if (checkKeybind(event, userOptions.keybinds.sidebar.options.prevTab)) {
sidebarOptionsStack.prevTab();
}
})
,
});
+21 -9
View File
@@ -1,7 +1,3 @@
// * {
// border: 1px solid $onSurfaceVariant; // Debugging
// }
* {
selection {
background-color: $secondary;
@@ -197,8 +193,7 @@ popover {
color: $onSecondaryContainer;
}
.multipleselection-container {
}
.multipleselection-container {}
.multipleselection-btn {
@include small-rounding;
@@ -277,8 +272,8 @@ popover {
.tab-icon {
@include element_decel;
@include full-rounding;
min-width: 2.182rem;
min-height: 2.182rem;
min-width: 2.25rem;
min-height: 2.25rem;
font-size: 1.406rem;
color: $onSurface;
}
@@ -288,6 +283,23 @@ popover {
color: $onSecondaryContainer;
}
.tab-icon-expandable {
transition: 0ms;
@include full-rounding;
min-width: 2.25rem;
min-height: 2.25rem;
font-size: 1.406rem;
color: $onSurface;
padding: 0rem;
}
.tab-icon-expandable-active {
background-color: $secondaryContainer;
color: $onSecondaryContainer;
padding: 0rem 0.545rem;
min-width: 9.545rem;
}
widget {
@include small-rounding;
}
}
+1
View File
@@ -1,6 +1,7 @@
// Common colors
$hovercolor: $surfaceContainerHigh;
$activecolor: $surfaceContainerHighest;
$rounding_verysmall: 0.477rem;
$rounding_small: 0.818rem;
$rounding_mediumsmall: 0.955rem;
$rounding_medium: 1.159rem;
+41 -7
View File
@@ -260,7 +260,7 @@ $sidebar_chat_textboxareaColor: mix($onSurfaceVariant, $surfaceVariant, 40%);
@include full-rounding;
@include element_decel;
padding: 0rem 0.682rem;
background-color: $surfaceContainerHigh;
background-color: $layer2;
color: $onSurface;
}
@@ -278,7 +278,7 @@ $sidebar_chat_textboxareaColor: mix($onSurfaceVariant, $surfaceVariant, 40%);
@include element_decel;
min-width: 2.045rem;
min-height: 2.045rem;
background-color: $surfaceContainerHigh;
background-color: $layer2;
color: $outline;
}
@@ -326,7 +326,7 @@ $sidebar_chat_textboxareaColor: mix($onSurfaceVariant, $surfaceVariant, 40%);
.sidebar-todo-new {
@include full-rounding;
@include element_decel;
background-color: $surfaceContainerHigh;
background-color: $layer2;
color: $onSurfaceVariant;
margin: 0.341rem;
padding: 0.205rem 0.545rem;
@@ -492,7 +492,7 @@ $colorpicker_rounding: 0.341rem;
padding: 0.341rem;
}
.sidebar-chat-apiswitcher {
.sidebar-icontabswitcher {
@include full-rounding;
@include group-padding;
background-color: $layer1;
@@ -517,7 +517,7 @@ $colorpicker_rounding: 0.341rem;
.sidebar-chat-textarea {
@include normal-rounding;
background-color: $surfaceContainerHigh;
background-color: $layer1;
color: $onSurfaceVariant;
padding: 0.682rem;
}
@@ -709,7 +709,7 @@ $colorpicker_rounding: 0.341rem;
.sidebar-chat-chip-action {
@include element_decel;
background-color: $surfaceContainerHigh;
background-color: $layer2;
color: $onSurfaceVariant;
}
@@ -731,7 +731,7 @@ $colorpicker_rounding: 0.341rem;
@include element_decel;
@include small-rounding;
padding: 0.341rem 0.477rem;
background-color: $surfaceContainerHigh;
background-color: $layer3;
color: $onSurfaceVariant;
}
@@ -845,3 +845,37 @@ $waifu_image_overlay_transparency: 0.7;
@include element_decel;
margin: 0.545rem;
}
.sidebar-volmixer-stream {
// @include normal-rounding;
// background-color: $layer2;
// color: $onLayer2;
border-bottom: 0.068rem solid $outlineVariant;
padding: 0.682rem;
}
.sidebar-volmixer-stream-appicon {
font-size: 3.273rem;
}
.sidebar-volmixer-stream-slider {
trough {
border-radius: $rounding_verysmall;
min-height: 1.364rem;
min-width: 1.364rem;
background-color: $secondaryContainer;
}
highlight {
border-radius: $rounding_verysmall;
min-height: 1.364rem;
min-width: 1.364rem;
background-color: $primary;
}
slider {
border-radius: $rounding_verysmall;
min-height: 1.364rem;
min-width: 1.364rem;
}
}
+1 -1
View File
@@ -38,4 +38,4 @@
border-radius: 0rem;
border-bottom-right-radius: $rounding_large;
border: 0rem solid;
}
}