hyprpanel

This commit is contained in:
wt committed 2024-10-23 21:12:15 +07:00
1 parent 10080a83ee
commit 5151ac24da
322 files changed
+38276

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
import Gtk from 'gi://Gtk?version=3.0';
export default Widget.subclass<typeof Gtk.Window, Gtk.Window.ConstructorProperties>(Gtk.Window);
@@ -0,0 +1,89 @@
import RegularWindow from 'widget/RegularWindow';
import icons from 'lib/icons';
import options from 'options';
import { ThemesMenu } from './pages/theme/index';
import { SettingsMenu } from './pages/config/index';
import './side_effects';
import { GBox, GCenterBox } from 'lib/types/widget';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
type Page = 'Configuration' | 'Theming';
const CurrentPage = Variable<Page>('Configuration');
const pagerMap: Page[] = ['Configuration', 'Theming'];
const Header = (): GCenterBox =>
Widget.CenterBox({
class_name: 'header',
start_widget: Widget.Button({
class_name: 'reset',
on_clicked: options.reset,
hpack: 'start',
vpack: 'start',
child: Widget.Icon(icons.ui.refresh),
tooltip_text: 'Reset',
}),
center_widget: Widget.Box({}),
end_widget: Widget.Button({
class_name: 'close',
hpack: 'end',
vpack: 'start',
child: Widget.Icon(icons.ui.close),
on_clicked: () => App.closeWindow('settings-dialog'),
}),
});
const PageContainer = (): GBox => {
return Widget.Box({
hpack: 'fill',
hexpand: true,
vertical: true,
children: CurrentPage.bind('value').as((v) => {
return [
Widget.Box({
class_name: 'option-pages-container',
hpack: 'center',
hexpand: true,
children: pagerMap.map((page) => {
return Widget.Button({
xalign: 0,
hpack: 'center',
class_name: `pager-button ${v === page ? 'active' : ''} category`,
label: page,
on_primary_click: () => (CurrentPage.value = page),
});
}),
}),
Widget.Stack({
vexpand: false,
class_name: 'themes-menu-stack',
children: {
Configuration: SettingsMenu(),
Theming: ThemesMenu(),
},
shown: CurrentPage.bind('value'),
}),
];
}),
});
};
export default (): Gtk.Window =>
RegularWindow({
name: 'settings-dialog',
class_name: 'settings-dialog',
title: 'Settings',
setup(win) {
win.on('delete-event', () => {
win.hide();
return true;
});
win.set_default_size(200, 300);
},
child: Widget.Box({
class_name: 'settings-dialog-box',
vertical: true,
children: [Header(), PageContainer()],
}),
});
@@ -0,0 +1,781 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0';
export const BarSettings = (): Scrollable<Gtk.Widget, Gtk.Widget> => {
return Widget.Scrollable({
vscroll: 'always',
hscroll: 'automatic',
class_name: 'menu-theme-page paged-container',
child: Widget.Box({
vertical: true,
children: [
/*
******************************
* LAYOUTS *
******************************
*/
Header('Layouts'),
Option(
{
opt: options.bar.layouts,
title: 'Bar Layouts for Monitors',
subtitle: 'Wiki Link: https://hyprpanel.com/configuration/panel.html#layouts',
type: 'object',
subtitleLink: 'https://hyprpanel.com/configuration/panel.html#layouts',
},
'bar-layout-input',
),
Option({
opt: options.theme.bar.floating,
title: 'Floating Bar',
type: 'boolean',
}),
Option({
opt: options.theme.bar.location,
title: 'Location',
type: 'enum',
enums: ['top', 'bottom'],
}),
Option({
opt: options.theme.bar.buttons.enableBorders,
title: 'Enable Button Borders',
subtitle: 'Enables button borders for all buttons in the bar.',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.borderSize,
title: 'Button Border Size',
subtitle: 'Button border for the individual modules must be enabled first',
type: 'string',
}),
/*
******************************
* SPACING *
******************************
*/
Header('Spacing'),
Option({
opt: options.theme.bar.outer_spacing,
title: 'Outer Spacing',
subtitle: 'Spacing on the outer left and right edges of the bar.',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.y_margins,
title: 'Vertical Margins',
subtitle: 'Spacing above/below the buttons in the bar.',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.spacing,
title: 'Button Spacing',
subtitle: 'Spacing between the buttons in the bar.',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.padding_x,
title: 'Button Horizontal Padding',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.padding_y,
title: 'Button Vertical Padding',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.radius,
title: 'Button Radius',
type: 'string',
}),
Option({
opt: options.theme.bar.layer,
title: 'Layer',
type: 'enum',
subtitle: 'Layer determines the Z index of your bar.',
enums: ['top', 'bottom', 'overlay', 'background'],
}),
Option({
opt: options.theme.bar.dropdownGap,
title: 'Dropdown Gap',
subtitle: 'The gap between the dropdown and the bar',
type: 'string',
}),
Option({
opt: options.theme.bar.margin_top,
title: 'Margin Top',
subtitle: 'Only applies if floating is enabled',
type: 'string',
}),
Option({
opt: options.theme.bar.margin_bottom,
title: 'Margin Bottom',
subtitle: 'Only applies if floating is enabled',
type: 'string',
}),
Option({
opt: options.theme.bar.margin_sides,
title: 'Margin Sides',
subtitle: 'Only applies if floating is enabled',
type: 'string',
}),
Option({
opt: options.theme.bar.border_radius,
title: 'Border Radius',
subtitle: 'Only applies if floating is enabled',
type: 'string',
}),
/*
******************************
* Actions *
******************************
*/
Header('Actions'),
Option({
opt: options.bar.scrollSpeed,
title: 'Scrolling Speed',
subtitle: 'The speed at which the commands assigned to the scroll event will trigger',
type: 'number',
}),
/*
******************************
* DASHBOARD *
******************************
*/
Header('Dashboard'),
Option({
opt: options.bar.launcher.icon,
title: 'Dashboard Menu Icon',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.dashboard.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.launcher.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.launcher.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.launcher.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.launcher.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* WORKSPACES *
******************************
*/
Header('Workspaces'),
Option({
opt: options.theme.bar.buttons.workspaces.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.showAllActive,
title: 'Mark Active Workspace On All Monitors',
subtitle: 'Marks the currently active workspace on each monitor.',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.workspaces.fontSize,
title: 'Indicator Size',
subtitle:
'Only applicable to numbered workspaces and mapped icons\n' +
'Adjust with caution as it may cause the bar to expand',
type: 'string',
}),
Option({
opt: options.bar.workspaces.show_icons,
title: 'Show Workspace Icons',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.icons.available,
title: 'Workspace Available',
type: 'string',
}),
Option({
opt: options.bar.workspaces.icons.active,
title: 'Workspace Active',
type: 'string',
}),
Option({
opt: options.bar.workspaces.icons.occupied,
title: 'Workspace Occupied',
type: 'string',
}),
Option({
opt: options.bar.workspaces.show_numbered,
title: 'Show Workspace Numbers',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.numbered_active_indicator,
title: 'Numbered Workspace Identifier',
subtitle: 'Only applicable if Workspace Numbers are enabled',
type: 'enum',
enums: ['underline', 'highlight', 'color'],
}),
Option({
opt: options.theme.bar.buttons.workspaces.smartHighlight,
title: 'Smart Highlight',
subtitle:
'Automatically determines the highlight color of the workspace icon.\n' +
'Only compatible with mapped icons.',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_border,
title: 'Highlight Radius',
subtitle: 'Only applicable if Workspace Numbers are enabled',
type: 'string',
}),
Option({
opt: options.theme.bar.buttons.workspaces.numbered_active_highlight_padding,
title: 'Highlight Padding',
subtitle: 'Only applicable if Workspace Numbers are enabled',
type: 'string',
}),
Option({
opt: options.bar.workspaces.showWsIcons,
title: 'Map Workspaces to Icons',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.workspaceIconMap,
title: 'Workspace Icon Mappings',
type: 'object',
}),
Option({
opt: options.bar.workspaces.spacing,
title: 'Spacing',
subtitle: 'Spacing between workspace icons',
type: 'float',
}),
Option({
opt: options.bar.workspaces.workspaces,
title: 'Total Workspaces',
subtitle: 'The least amount of workspaces to always show.',
type: 'number',
}),
Option({
opt: options.bar.workspaces.monitorSpecific,
title: 'Monitor Specific',
subtitle:
'Only workspaces applicable to the monitor will be displayed.\n' +
"Works in conjuction with 'Total Workspaces'.",
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.hideUnoccupied,
title: 'Hide Unoccupied',
subtitle: 'Only show workspaces that are occupied or active',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.workspaceMask,
title: 'Mask Workspace Numbers On Monitors',
subtitle:
'Only applicable if Workspace Numbers and Monitor Specific are enabled.\n' +
"Forces each Monitor's Workspace labels to start from 1.",
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.reverse_scroll,
title: 'Invert Scroll',
subtitle: 'Scrolling up will go to the previous workspace rather than the next.',
type: 'boolean',
}),
Option({
opt: options.bar.workspaces.scroll_speed,
title: 'Scrolling Speed',
type: 'number',
}),
Option({
opt: options.bar.workspaces.ignored,
title: 'Ignored Workspaces',
subtitle: 'A regex that defines workspaces to ignore',
type: 'string',
}),
/*
******************************
* WINDOW TITLES *
******************************
*/
Header('Window Titles'),
Option({
opt: options.theme.bar.buttons.windowtitle.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.custom_title,
title: 'Use Custom Title',
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.title_map,
title: 'Window Title Mappings',
subtitle:
'Only applicable if Show Custom Title is enabled\nWiki Link: https://hyprpanel.com/configuration/panel.html#window-title-mappings',
type: 'object',
subtitleLink: 'https://hyprpanel.com/configuration/panel.html#window-title-mappings',
}),
Option({
opt: options.bar.windowtitle.class_name,
title: 'Use Class Name',
subtitle:
"Only applicable if Show Custom Title is disabled\nDisplays the window's class name instead of its title.",
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.label,
title: 'Show Window Title Label',
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.icon,
title: 'Show Icon',
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.truncation,
title: 'Truncate Window Title',
subtitle: 'Will truncate the window title to the specified size below.',
type: 'boolean',
}),
Option({
opt: options.bar.windowtitle.truncation_size,
title: 'Truncation Size',
type: 'number',
min: 10,
}),
Option({
opt: options.theme.bar.buttons.windowtitle.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.windowtitle.leftClick,
title: 'Left Click',
type: 'string',
}),
Option({
opt: options.bar.windowtitle.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.windowtitle.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.windowtitle.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.windowtitle.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* VOLUME *
******************************
*/
Header('Volume'),
Option({
opt: options.theme.bar.buttons.volume.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.volume.label,
title: 'Show Volume Percentage',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.volume.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.volume.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.volume.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.volume.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.volume.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* NETWORK *
******************************
*/
Header('Network'),
Option({
opt: options.theme.bar.buttons.network.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.network.label,
title: 'Show Network Name',
type: 'boolean',
}),
Option({
opt: options.bar.network.truncation,
title: 'Truncate Network Name',
subtitle: 'Will truncate the network name to the specified size below.',
type: 'boolean',
}),
Option({
opt: options.bar.network.truncation_size,
title: 'Truncation Size',
type: 'number',
}),
Option({
opt: options.theme.bar.buttons.network.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.network.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.network.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.network.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.network.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* BLUETOOTH *
******************************
*/
Header('Bluetooth'),
Option({
opt: options.theme.bar.buttons.bluetooth.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.bluetooth.label,
title: 'Show Bluetooth Label',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.bluetooth.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.bluetooth.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.bluetooth.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.bluetooth.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.bluetooth.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* BATTERY *
******************************
*/
Header('Battery'),
Option({
opt: options.theme.bar.buttons.battery.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.battery.label,
title: 'Show Battery Percentage',
type: 'boolean',
}),
Option({
opt: options.bar.battery.hideLabelWhenFull,
title: 'Hide Battery Percentage When Full',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.battery.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.battery.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.battery.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.battery.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.battery.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* SYSTEM TRAY *
******************************
*/
Header('System Tray'),
Option({
opt: options.theme.bar.buttons.systray.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.systray.ignore,
title: 'Ignore List',
subtitle:
'An array of applications to prevent from showing in the system tray.\n' +
'Wiki: https://hyprpanel.com/configuration/panel.html#system-tray',
subtitleLink: 'https://hyprpanel.com/configuration/panel.html#system-tray',
type: 'object',
}),
/*
******************************
* CLOCK *
******************************
*/
Header('Clock'),
Option({
opt: options.theme.bar.buttons.clock.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.clock.format,
title: 'Clock Format',
type: 'string',
}),
Option({
opt: options.bar.clock.icon,
title: 'Icon',
type: 'string',
}),
Option({
opt: options.bar.clock.showIcon,
title: 'Show Icon',
type: 'boolean',
}),
Option({
opt: options.bar.clock.showTime,
title: 'Show Time',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.clock.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.clock.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.clock.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.clock.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.clock.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
/*
******************************
* MEDIA *
******************************
*/
Header('Media'),
Option({
opt: options.theme.bar.buttons.media.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.media.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.media.show_artist,
title: 'Show Track Artist',
type: 'boolean',
}),
Option({
opt: options.bar.media.show_label,
title: 'Toggle Media Label',
type: 'boolean',
}),
Option({
opt: options.bar.media.truncation,
title: 'Truncate Media Label',
subtitle: 'Only applicable if Toggle Media Label is enabled',
type: 'boolean',
}),
Option({
opt: options.bar.media.truncation_size,
title: 'Truncation Size',
subtitle: 'Only applicable if Toggle Media Label is enabled',
type: 'number',
min: 10,
}),
Option({
opt: options.bar.media.show_active_only,
title: 'Auto Hide',
subtitle: 'Button will automatically hide if no media is detected.',
type: 'boolean',
}),
Option({
opt: options.bar.media.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.media.middleClick,
title: 'Middle Click',
type: 'string',
}),
/*
******************************
* NOTIFICATIONS *
******************************
*/
Header('Notifications'),
Option({
opt: options.theme.bar.buttons.notifications.enableBorder,
title: 'Button Border',
type: 'boolean',
}),
Option({
opt: options.bar.notifications.show_total,
title: 'Show Total # of notifications',
type: 'boolean',
}),
Option({
opt: options.bar.notifications.hideCountWhenZero,
title: 'Auto Hide Label',
subtitle: 'Hide Total # of notifications when zero',
type: 'boolean',
}),
Option({
opt: options.theme.bar.buttons.notifications.spacing,
title: 'Inner Spacing',
subtitle: 'Spacing between the icon and the label inside the buttons.',
type: 'string',
}),
Option({
opt: options.bar.notifications.rightClick,
title: 'Right Click',
type: 'string',
}),
Option({
opt: options.bar.notifications.middleClick,
title: 'Middle Click',
type: 'string',
}),
Option({
opt: options.bar.notifications.scrollUp,
title: 'Scroll Up',
type: 'string',
}),
Option({
opt: options.bar.notifications.scrollDown,
title: 'Scroll Down',
type: 'string',
}),
],
}),
});
};
@@ -0,0 +1,180 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const BarGeneral = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
class_name: 'bar-theme-page paged-container',
vscroll: 'automatic',
child: Widget.Box({
vertical: true,
children: [
Header('General Settings'),
Option({ opt: options.theme.font.name, title: 'Font', type: 'font' }),
Option({ opt: options.theme.font.size, title: 'Font Size', type: 'string' }),
Option({
opt: options.theme.font.weight,
title: 'Font Weight',
subtitle: '100, 200, 300, etc.',
type: 'number',
increment: 100,
min: 100,
max: 900,
}),
Option({
opt: options.dummy,
title: 'Config',
subtitle: 'WARNING: Importing a configuration will replace your current configuration settings.',
type: 'config_import',
exportData: {
filePath: OPTIONS,
themeOnly: false,
},
}),
Option({
opt: options.terminal,
title: 'Terminal',
subtitle: "Tools such as 'btop' will open in this terminal",
type: 'string',
}),
Option({
opt: options.tear,
title: 'Tearing Compatible',
subtitle:
'Makes HyprPanel compatible with Hyprland tearing.\n' +
"Enabling this will change all overlays (Notifications, OSDs, Bar) to the 'top' layer instead the 'overlay' layer.",
type: 'boolean',
}),
Option({
opt: options.menus.transition,
title: 'Menu Transition',
type: 'enum',
enums: ['none', 'crossfade'],
}),
Option({
opt: options.menus.transitionTime,
title: 'Menu Transition Duration',
type: 'number',
min: 100,
max: 10000,
increment: 100,
}),
Header('Scaling'),
Option({
opt: options.scalingPriority,
title: 'Scaling Priority',
type: 'enum',
enums: ['both', 'gdk', 'hyprland'],
}),
Option({
opt: options.theme.bar.scaling,
title: 'Bar',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.notification.scaling,
title: 'Notifications',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.osd.scaling,
title: 'OSD',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.scaling,
title: 'Dashboard Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.confirmation_scaling,
title: 'Confirmation Dialog',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.media.scaling,
title: 'Media Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.volume.scaling,
title: 'Volume Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.network.scaling,
title: 'Network Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.bluetooth.scaling,
title: 'Bluetooth Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.battery.scaling,
title: 'Battery Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.clock.scaling,
title: 'Clock Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.notifications.scaling,
title: 'Notifications Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
Option({
opt: options.theme.bar.menus.menu.power.scaling,
title: 'Power Menu',
type: 'number',
min: 1,
max: 100,
increment: 5,
}),
],
}),
});
};
@@ -0,0 +1,70 @@
import { BarGeneral } from './general/index';
import { BarSettings } from './bar/index';
import { ClockMenuSettings } from './menus/clock';
import { DashboardMenuSettings } from './menus/dashboard';
import { NotificationSettings } from './notifications/index';
import { OSDSettings } from './osd/index';
import { CustomModuleSettings } from 'customModules/config';
import { PowerMenuSettings } from './menus/power';
import { GBox } from 'lib/types/widget';
type Page =
| 'General'
| 'Bar'
| 'Clock Menu'
| 'Dashboard Menu'
| 'Power Menu'
| 'Notifications'
| 'OSD'
| 'Custom Modules';
const CurrentPage = Variable<Page>('General');
const pagerMap: Page[] = [
'General',
'Bar',
'Notifications',
'OSD',
'Power Menu',
'Clock Menu',
'Dashboard Menu',
'Custom Modules',
];
export const SettingsMenu = (): GBox => {
return Widget.Box({
vertical: true,
children: CurrentPage.bind('value').as((v) => {
return [
Widget.Box({
class_name: 'option-pages-container',
hpack: 'center',
hexpand: true,
children: pagerMap.map((page) => {
return Widget.Button({
hpack: 'center',
class_name: `pager-button ${v === page ? 'active' : ''}`,
label: page,
on_primary_click: () => (CurrentPage.value = page),
});
}),
}),
Widget.Stack({
vexpand: true,
class_name: 'themes-menu-stack',
children: {
General: BarGeneral(),
Bar: BarSettings(),
Notifications: NotificationSettings(),
OSD: OSDSettings(),
'Clock Menu': ClockMenuSettings(),
'Dashboard Menu': DashboardMenuSettings(),
'Custom Modules': CustomModuleSettings(),
'Power Menu': PowerMenuSettings(),
},
shown: CurrentPage.bind('value'),
}),
];
}),
});
};
@@ -0,0 +1,48 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const ClockMenuSettings = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
child: Widget.Box({
class_name: 'bar-theme-page paged-container',
vertical: true,
children: [
Header('Time'),
Option({ opt: options.menus.clock.time.military, title: 'Use 24hr time', type: 'boolean' }),
Option({ opt: options.menus.clock.time.hideSeconds, title: 'Hide seconds', type: 'boolean' }),
Header('Weather'),
Option({ opt: options.menus.clock.weather.enabled, title: 'Enabled', type: 'boolean' }),
Option({
opt: options.menus.clock.weather.location,
title: 'Location',
subtitle: 'Zip Code, Postal Code, City, etc.',
type: 'string',
}),
Option({
opt: options.menus.clock.weather.key,
title: 'Weather API Key',
subtitle: 'May require AGS restart. https://weatherapi.com/',
type: 'string',
}),
Option({
opt: options.menus.clock.weather.unit,
title: 'Units',
type: 'enum',
enums: ['imperial', 'metric'],
}),
Option({
opt: options.menus.clock.weather.interval,
title: 'Weather Fetching Interval (ms)',
subtitle: 'May require AGS restart.',
type: 'number',
}),
],
}),
});
};
@@ -0,0 +1,214 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const DashboardMenuSettings = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
class_name: 'bar-theme-page paged-container',
vscroll: 'always',
hscroll: 'automatic',
vexpand: true,
overlayScrolling: true,
child: Widget.Box({
vertical: true,
children: [
Header('Power Menu'),
Option({ opt: options.menus.dashboard.powermenu.avatar.image, title: 'Profile Image', type: 'img' }),
Option({
opt: options.menus.dashboard.powermenu.avatar.name,
title: 'Profile Name',
subtitle: "Use 'system' to automatically set system name",
type: 'string',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.profile.size,
title: 'Profile Image Size',
type: 'string',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.profile.radius,
title: 'Profile Image Radius',
type: 'string',
}),
Option({
opt: options.menus.dashboard.powermenu.confirmation,
title: 'Show Confirmation Dialogue',
type: 'boolean',
}),
Option({ opt: options.menus.dashboard.powermenu.shutdown, title: 'Shutdown Command', type: 'string' }),
Option({ opt: options.menus.dashboard.powermenu.reboot, title: 'Reboot Command', type: 'string' }),
Option({ opt: options.menus.dashboard.powermenu.logout, title: 'Logout Command', type: 'string' }),
Option({ opt: options.menus.dashboard.powermenu.sleep, title: 'Sleep Command', type: 'string' }),
Header('Controls'),
Option({ opt: options.menus.dashboard.controls.enabled, title: 'Enabled', type: 'boolean' }),
Header('Resource Usage Metrics'),
Option({ opt: options.menus.dashboard.stats.enabled, title: 'Enabled', type: 'boolean' }),
Option({
opt: options.menus.dashboard.stats.enable_gpu,
title: 'Track GPU',
subtitle: "NOTE: This is currently only available for NVidia GPUs and requires 'python-gpustat'.",
type: 'boolean',
}),
Header('Shortcuts'),
Option({ opt: options.menus.dashboard.shortcuts.enabled, title: 'Enabled', type: 'boolean' }),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut1.icon,
title: 'Left - Shortcut 1 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut1.command,
title: 'Left - Shortcut 1 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut1.tooltip,
title: 'Left - Shortcut 1 (Tooltip)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut2.icon,
title: 'Left - Shortcut 2 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut2.command,
title: 'Left - Shortcut 2 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut2.tooltip,
title: 'Left - Shortcut 2 (Tooltip)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut3.icon,
title: 'Left - Shortcut 3 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut3.command,
title: 'Left - Shortcut 3 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut3.tooltip,
title: 'Left - Shortcut 3 (Tooltip)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut4.icon,
title: 'Left - Shortcut 4 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut4.command,
title: 'Left - Shortcut 4 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.left.shortcut4.tooltip,
title: 'Left - Shortcut 4 (Tooltip)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut1.icon,
title: 'Right - Shortcut 1 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut1.command,
title: 'Right - Shortcut 1 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut1.tooltip,
title: 'Right - Shortcut 1 (Tooltip)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut3.icon,
title: 'Right - Shortcut 3 (Icon)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut3.command,
title: 'Right - Shortcut 3 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.shortcuts.right.shortcut3.tooltip,
title: 'Right - Shortcut 3 (Tooltip)',
type: 'string',
}),
Header('Directories'),
Option({ opt: options.menus.dashboard.directories.enabled, title: 'Enabled', type: 'boolean' }),
Option({
opt: options.menus.dashboard.directories.left.directory1.label,
title: 'Left - Directory 1 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.left.directory1.command,
title: 'Left - Directory 1 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.left.directory2.label,
title: 'Left - Directory 2 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.left.directory2.command,
title: 'Left - Directory 2 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.left.directory3.label,
title: 'Left - Directory 3 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.left.directory3.command,
title: 'Left - Directory 3 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory1.label,
title: 'Right - Directory 1 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory1.command,
title: 'Right - Directory 1 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory2.label,
title: 'Right - Directory 2 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory2.command,
title: 'Right - Directory 2 (Command)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory3.label,
title: 'Right - Directory 3 (Label)',
type: 'string',
}),
Option({
opt: options.menus.dashboard.directories.right.directory3.command,
title: 'Right - Directory 3 (Command)',
type: 'string',
}),
],
}),
});
};
@@ -0,0 +1,28 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import { Attribute, Child } from 'lib/types/widget';
import Scrollable from 'types/widgets/scrollable';
export const PowerMenuSettings = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
class_name: 'bar-theme-page paged-container',
vscroll: 'always',
hscroll: 'automatic',
vexpand: true,
overlayScrolling: true,
child: Widget.Box({
vertical: true,
children: [
Header('Power Menu'),
Option({ opt: options.menus.power.showLabel, title: 'Show Label', type: 'boolean' }),
Option({ opt: options.menus.power.confirmation, title: 'Confirmation Dialog', type: 'boolean' }),
Option({ opt: options.menus.power.shutdown, title: 'Shutdown Command', type: 'string' }),
Option({ opt: options.menus.power.reboot, title: 'Reboot Command', type: 'string' }),
Option({ opt: options.menus.power.logout, title: 'Logout Command', type: 'string' }),
Option({ opt: options.menus.power.sleep, title: 'Sleep Command', type: 'string' }),
],
}),
});
};
@@ -0,0 +1,104 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const NotificationSettings = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
child: Widget.Box({
class_name: 'bar-theme-page paged-container',
vertical: true,
children: [
Header('Notification Settings'),
Option({
opt: options.notifications.ignore,
title: 'Ignored Applications',
subtitle:
'Applications to ignore.\n' +
'Wiki: https://hyprpanel.com/configuration/notifications.html#ignored-applications',
subtitleLink: 'https://hyprpanel.com/configuration/notifications.html#ignored-applications',
type: 'object',
}),
Option({
opt: options.notifications.position,
title: 'Notification Location',
type: 'enum',
enums: ['top left', 'top', 'top right', 'right', 'bottom right', 'bottom', 'bottom left', 'left'],
}),
Option({
opt: options.theme.notification.border_radius,
title: 'Border Radius',
type: 'string',
}),
Option({
opt: options.notifications.monitor,
title: 'Monitor',
subtitle: 'The ID of the monitor on which to display the notification',
type: 'number',
}),
Option({
opt: options.notifications.active_monitor,
title: 'Follow Cursor',
subtitle: 'The notification will follow the monitor of your cursor',
type: 'boolean',
}),
Option({
opt: options.notifications.clearDelay,
title: 'Clear Delay',
subtitle:
'The delay in milliseconds before a notification is cleared' +
'\nWARNING: Setting this value too low may crash AGS depending on your system.',
type: 'number',
increment: 20,
}),
Option({
opt: options.notifications.timeout,
title: 'Notification Timeout',
subtitle: 'How long notification popups will last (in milliseconds).',
type: 'number',
}),
Option({
opt: options.notifications.cache_actions,
title: 'Preserve Actions',
subtitle: 'This will persist the action buttons of a notification after rebooting.',
type: 'boolean',
}),
Header('Notification Menu Settings'),
Option({
opt: options.theme.bar.menus.menu.notifications.height,
title: 'Notification Menu Height',
type: 'string',
}),
Option({
opt: options.notifications.displayedTotal,
title: 'Displayed Total',
subtitle:
'How many notifications to show in the menu at once.\n' +
'Newer notifications will display towards the top.',
type: 'number',
min: 1,
}),
Option({
opt: options.theme.bar.menus.menu.notifications.pager.show,
title: 'Show Pager',
subtitle: 'Shows the pagination footer at the bottom of the menu.',
type: 'boolean',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.scrollbar.width,
title: 'Scrollbar Width',
type: 'string',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.scrollbar.radius,
title: 'Scrollbar Radius',
type: 'string',
}),
],
}),
});
};
@@ -0,0 +1,71 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const OSDSettings = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
child: Widget.Box({
class_name: 'bar-theme-page paged-container',
vertical: true,
children: [
Header('On Screen Display'),
Option({ opt: options.theme.osd.enable, title: 'Enabled', type: 'boolean' }),
Option({
opt: options.theme.osd.duration,
title: 'Duration',
type: 'number',
min: 100,
max: 10000,
increment: 500,
}),
Option({
opt: options.theme.osd.orientation,
title: 'Orientation',
type: 'enum',
enums: ['horizontal', 'vertical'],
}),
Option({
opt: options.theme.osd.location,
title: 'Position',
subtitle: 'Position of the OSD on the screen',
type: 'enum',
enums: ['top left', 'top', 'top right', 'right', 'bottom right', 'bottom', 'bottom left', 'left'],
}),
Option({
opt: options.theme.osd.monitor,
title: 'Monitor',
subtitle: 'The ID of the monitor on which to display the OSD',
type: 'number',
}),
Option({
opt: options.theme.osd.active_monitor,
title: 'Follow Cursor',
subtitle: 'The OSD will follow the monitor of your cursor',
type: 'boolean',
}),
Option({
opt: options.theme.osd.radius,
title: 'Radius',
subtitle: 'Radius of the on-screen-display that indicates volume/brightness change',
type: 'string',
}),
Option({
opt: options.theme.osd.margins,
title: 'Margins',
subtitle: 'Margins in the following format: top right bottom left',
type: 'string',
}),
Option({
opt: options.theme.osd.muted_zero,
title: 'Mute Volume as Zero',
subtitle: 'Display volume as 0 when muting, instead of previous device volume',
type: 'boolean',
}),
],
}),
});
};
@@ -0,0 +1,227 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const BarTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'always',
hscroll: 'automatic',
class_name: 'bar-theme-page paged-container',
child: Widget.Box({
vertical: true,
children: [
Header('General'),
Option({ opt: options.theme.bar.transparent, title: 'Transparent', type: 'boolean' }),
Option({ opt: options.theme.bar.background, title: 'Background Color', type: 'color' }),
Option({
opt: options.theme.bar.buttons.style,
title: 'Button Style',
type: 'enum',
enums: ['default', 'split', 'wave', 'wave2'],
}),
Option({
opt: options.theme.bar.opacity,
title: 'Background Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({
opt: options.theme.bar.buttons.opacity,
title: 'Button Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({
opt: options.theme.bar.buttons.background_opacity,
title: 'Button Background Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({
opt: options.theme.bar.buttons.background_hover_opacity,
title: 'Button Background Hover Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({
opt: options.theme.bar.buttons.monochrome,
title: 'Use Global Colors',
type: 'boolean',
disabledBinding: options.theme.matugen,
}),
Option({ opt: options.theme.bar.buttons.background, title: 'Button Background', type: 'color' }),
Option({ opt: options.theme.bar.buttons.hover, title: 'Button Hover', type: 'color' }),
Option({ opt: options.theme.bar.buttons.text, title: 'Button Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.icon, title: 'Button Icon', type: 'color' }),
Option({
opt: options.theme.bar.buttons.icon_background,
title: 'Button Icon Background',
subtitle:
"Applies a background color to the icon section of the button.\nRequires 'split' button styling.",
type: 'color',
}),
Header('Dashboard Button'),
Option({ opt: options.theme.bar.buttons.dashboard.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.buttons.dashboard.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.bar.buttons.dashboard.border, title: 'Border', type: 'color' }),
Header('Workspaces'),
Option({ opt: options.theme.bar.buttons.workspaces.background, title: 'Background', type: 'color' }),
Option({
opt: options.theme.bar.buttons.workspaces.hover,
title: 'Workspace Hover Color',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.workspaces.available,
title: 'Workspace Available Color',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.workspaces.occupied,
title: 'Workspace Occupied Color',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.workspaces.active,
title: 'Workspace Active Color',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.workspaces.numbered_active_highlighted_text_color,
title: 'Highlighted Workspace Text Color',
type: 'color',
}),
Option({
opt: options.theme.bar.buttons.workspaces.numbered_active_underline_color,
title: 'Workspace Underline Color',
type: 'color',
}),
Option({ opt: options.theme.bar.buttons.workspaces.border, title: 'Border', type: 'color' }),
Header('Window Title'),
Option({ opt: options.theme.bar.buttons.windowtitle.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.buttons.windowtitle.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.windowtitle.icon, title: 'Icon', type: 'color' }),
Option({
opt: options.theme.bar.buttons.windowtitle.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.windowtitle.border, title: 'Border', type: 'color' }),
Header('Media'),
Option({ opt: options.theme.bar.buttons.media.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.buttons.media.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.media.icon, title: 'Icon', type: 'color' }),
Option({
opt: options.theme.bar.buttons.media.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.media.border, title: 'Border', type: 'color' }),
Header('Volume'),
Option({ opt: options.theme.bar.buttons.volume.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.buttons.volume.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.buttons.volume.icon, title: 'Icon', type: 'color' }),
Option({
opt: options.theme.bar.buttons.volume.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.volume.border, title: 'Border', type: 'color' }),
Header('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' }),
Header('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' }),
Header('System Tray'),
Option({ opt: options.theme.bar.buttons.systray.background, title: 'Background', type: 'color' }),
Header('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' }),
Header('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' }),
Header('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' }),
],
}),
});
};
@@ -0,0 +1,111 @@
import { BarTheme } from './bar/index';
import { NotificationsTheme } from './notifications/index';
import { BatteryMenuTheme } from './menus/battery';
import { BluetoothMenuTheme } from './menus/bluetooth';
import { ClockMenuTheme } from './menus/clock';
import { DashboardMenuTheme } from './menus/dashboard';
import { MenuTheme } from './menus/index';
import { MediaMenuTheme } from './menus/media';
import { NetworkMenuTheme } from './menus/network';
import { NotificationsMenuTheme } from './menus/notifications';
import { SystrayMenuTheme } from './menus/systray';
import { VolumeMenuTheme } from './menus/volume';
import { OsdTheme } from './osd/index';
import { Matugen } from './menus/matugen';
import { CustomModuleTheme } from 'customModules/theme';
import { PowerMenuTheme } from './menus/power';
import { GBox } from 'lib/types/widget';
type Page =
| 'General Settings'
| 'Matugen Settings'
| 'Bar'
| 'Notifications'
| 'OSD'
| 'Battery Menu'
| 'Bluetooth Menu'
| 'Clock Menu'
| 'Dashboard Menu'
| 'Media Menu'
| 'Network Menu'
| 'Notifications Menu'
| 'System Tray'
| 'Volume Menu'
| 'Power Menu'
| 'Custom Modules';
const CurrentPage = Variable<Page>('General Settings');
const pagerMap: Page[] = [
'General Settings',
'Matugen Settings',
'Bar',
'Notifications',
'OSD',
'Battery Menu',
'Bluetooth Menu',
'Clock Menu',
'Dashboard Menu',
'Media Menu',
'Network Menu',
'Notifications Menu',
'System Tray',
'Volume Menu',
'Power Menu',
'Custom Modules',
];
export const ThemesMenu = (): GBox => {
return Widget.Box({
vertical: true,
children: CurrentPage.bind('value').as((v) => {
return [
Widget.Box({
class_name: 'option-pages-container',
hpack: 'center',
hexpand: true,
vertical: true,
children: [0, 1, 2].map((section) => {
return Widget.Box({
children: pagerMap.map((page, index) => {
if (index >= section * 6 && index < section * 6 + 6) {
return Widget.Button({
hpack: 'center',
xalign: 0,
class_name: `pager-button ${v === page ? 'active' : ''}`,
label: page,
on_primary_click: () => (CurrentPage.value = page),
});
}
return Widget.Box();
}),
});
}),
}),
Widget.Stack({
vexpand: true,
class_name: 'themes-menu-stack',
children: {
'General Settings': MenuTheme(),
'Matugen Settings': Matugen(),
Bar: BarTheme(),
Notifications: NotificationsTheme(),
OSD: OsdTheme(),
'Battery Menu': BatteryMenuTheme(),
'Bluetooth Menu': BluetoothMenuTheme(),
'Clock Menu': ClockMenuTheme(),
'Dashboard Menu': DashboardMenuTheme(),
'Media Menu': MediaMenuTheme(),
'Network Menu': NetworkMenuTheme(),
'Notifications Menu': NotificationsMenuTheme(),
'System Tray': SystrayMenuTheme(),
'Volume Menu': VolumeMenuTheme(),
'Power Menu': PowerMenuTheme(),
'Custom Modules': CustomModuleTheme(),
},
shown: CurrentPage.bind('value'),
}),
];
}),
});
};
@@ -0,0 +1,68 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const BatteryMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page battery paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Battery Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.battery.text, title: 'Text', type: 'color' }),
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.battery.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.battery.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.battery.border.color, title: 'Border', type: 'color' }),
Header('Label'),
Option({ opt: options.theme.bar.menus.menu.battery.label.color, title: 'Label', type: 'color' }),
Header('List Items'),
Option({
opt: options.theme.bar.menus.menu.battery.listitems.active,
title: 'Active/Hover',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.battery.listitems.passive,
title: 'Passive',
type: 'color',
}),
Header('Icons'),
Option({ opt: options.theme.bar.menus.menu.battery.icons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.battery.icons.passive, title: 'Passive', type: 'color' }),
Header('Slider'),
Option({ opt: options.theme.bar.menus.menu.battery.slider.primary, title: 'Primary', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.battery.slider.background,
title: 'Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.battery.slider.backgroundhover,
title: 'Background (Hover)',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.battery.slider.puck, title: 'Puck', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,89 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const BluetoothMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page bluetooth paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Bluetooth Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.text, title: 'Text', type: 'color' }),
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.bluetooth.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.border.color, title: 'Border', type: 'color' }),
Header('Label'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.label.color, title: 'Label', type: 'color' }),
Header('Status'),
Option({
opt: options.theme.bar.menus.menu.bluetooth.status,
title: 'Connection Status',
type: 'color',
}),
Header('List Items'),
Option({
opt: options.theme.bar.menus.menu.bluetooth.listitems.active,
title: 'Active/Hover',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.bluetooth.listitems.passive,
title: 'Passive',
type: 'color',
}),
Header('Icons'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.icons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.bluetooth.icons.passive, title: 'Passive', type: 'color' }),
Header('Icon Buttons'),
Option({
opt: options.theme.bar.menus.menu.bluetooth.iconbutton.active,
title: 'Active',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.bluetooth.iconbutton.passive,
title: 'Passive',
type: 'color',
}),
Header('Switch'),
Option({ opt: options.theme.bar.menus.menu.bluetooth.switch.enabled, title: 'Enabled', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.bluetooth.switch.disabled,
title: 'Disabled',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.bluetooth.switch.puck, title: 'Puck', type: 'color' }),
Header('Switch Divider'),
Option({
opt: options.theme.bar.menus.menu.bluetooth.switch_divider,
title: 'Switch Divider',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,130 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const ClockMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page clock paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Clock Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.clock.text, title: 'Text', type: 'color' }),
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.clock.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.clock.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.clock.border.color, title: 'Border', type: 'color' }),
Header('Time'),
Option({ opt: options.theme.bar.menus.menu.clock.time.time, title: 'Time', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.clock.time.timeperiod,
title: 'Period',
subtitle: 'AM/PM',
type: 'color',
}),
Header('Calendar'),
Option({
opt: options.theme.bar.menus.menu.clock.calendar.yearmonth,
title: 'Year/Month',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.clock.calendar.weekdays, title: 'Weekdays', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.clock.calendar.paginator,
title: 'Navigation Arrows (Hover)',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.calendar.currentday,
title: 'Current Day',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.clock.calendar.days, title: 'Days', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.clock.calendar.contextdays,
title: 'Trailing/Leading Days',
type: 'color',
}),
Header('Weather'),
Option({
opt: options.theme.bar.menus.menu.clock.weather.icon,
title: 'Current Weather Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.temperature,
title: 'Current Temperature',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.status,
title: 'Current Status',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.stats,
title: 'Current Stats',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelyhot,
title: 'Thermometer - Extremely Hot',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.thermometer.hot,
title: 'Thermometer - Hot',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.thermometer.moderate,
title: 'Thermometer - Moderate',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.thermometer.cold,
title: 'Thermometer - Cold',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.thermometer.extremelycold,
title: 'Thermometer - Extremely Cold',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.hourly.time,
title: 'Hourly Weather Time',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.hourly.icon,
title: 'Hourly Weather Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.clock.weather.hourly.temperature,
title: 'Hourly Weather Temperature',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,268 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const DashboardMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'always',
hscroll: 'automatic',
class_name: 'menu-theme-page dashboard paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.dashboard.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.dashboard.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.dashboard.border.color, title: 'Border', type: 'color' }),
Header('Profile'),
Option({
opt: options.theme.bar.menus.menu.dashboard.profile.name,
title: 'Profile Name',
type: 'color',
}),
Header('Power Menu'),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.shutdown,
title: 'Shutdown',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.restart,
title: 'Restart',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.logout,
title: 'Log Out',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.dashboard.powermenu.sleep, title: 'Sleep', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.card,
title: 'Confirmation Dialog Card',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.background,
title: 'Confirmation Dialog Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.border,
title: 'Confirmation Dialog Border',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.label,
title: 'Confirmation Dialog Label',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.body,
title: 'Confirmation Dialog Description',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.confirm,
title: 'Confirmation Dialog Confirm Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.deny,
title: 'Confirmation Dialog Cancel Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.powermenu.confirmation.button_text,
title: 'Confirmation Dialog Button Text',
type: 'color',
}),
Header('Shortcuts'),
Option({
opt: options.theme.bar.menus.menu.dashboard.shortcuts.background,
title: 'Primary',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.dashboard.shortcuts.text, title: 'Text', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.dashboard.shortcuts.recording,
title: 'Recording',
subtitle: 'Color of the Record button when recording is in progress',
type: 'color',
}),
Header('Controls'),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.disabled,
title: 'Module Off',
subtitle: 'Button color when element is disabled',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.wifi.background,
title: 'Wifi Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.wifi.text,
title: 'Wifi Button Text',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.background,
title: 'Bluetooth Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.bluetooth.text,
title: 'Bluetooth Button Text',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.notifications.background,
title: 'Notifications Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.notifications.text,
title: 'Notifications Button Text',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.volume.background,
title: 'Volume Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.volume.text,
title: 'Volume Button Text',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.input.background,
title: 'Input Button',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.controls.input.text,
title: 'Input Button Text',
type: 'color',
}),
Header('Directories'),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.left.top.color,
title: 'Directory: Left - Top',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.left.middle.color,
title: 'Directory: Left - Middle',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.left.bottom.color,
title: 'Directory: Left - Bottom',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.right.top.color,
title: 'Directory: Right - Top',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.right.middle.color,
title: 'Directory: Right - Middle',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.directories.right.bottom.color,
title: 'Directory: Right - Bottom',
type: 'color',
}),
Header('System Stats'),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.bar_background,
title: 'Bar Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.icon,
title: 'CPU Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.bar,
title: 'CPU Bar',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.cpu.label,
title: 'CPU Label',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.ram.icon,
title: 'RAM Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.ram.bar,
title: 'RAM Bar',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.ram.label,
title: 'RAM Label',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.icon,
title: 'GPU Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.bar,
title: 'GPU Bar',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.gpu.label,
title: 'GPU Label',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.disk.icon,
title: 'Disk Icon',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.disk.bar,
title: 'Disk Bar',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.dashboard.monitors.disk.label,
title: 'Disk Label',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,139 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const MenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('General'),
Option({
opt: options.dummy,
title: 'Theme',
subtitle: 'WARNING: Importing a theme will replace your current theme color settings.',
type: 'config_import',
exportData: {
filePath: OPTIONS,
themeOnly: true,
},
}),
Option({
opt: options.theme.bar.menus.monochrome,
title: 'Use Global Colors',
type: 'boolean',
disabledBinding: options.theme.matugen,
}),
Option({
opt: options.wallpaper.pywal,
title: 'Generate Pywal Colors',
subtitle: 'Whether to also generate pywal colors with chosen wallpaper',
type: 'boolean',
}),
Option({
opt: options.wallpaper.enable,
title: 'Apply Wallpapers',
subtitle: 'Whether to apply the wallpaper or to only use it for Matugen color generation.',
type: 'boolean',
}),
Option({
opt: options.wallpaper.image,
title: 'Wallpaper',
subtitle: options.wallpaper.image.bind('value'),
type: 'wallpaper',
}),
Option({ opt: options.theme.bar.menus.background, title: 'Background Color', type: 'color' }),
Option({
opt: options.theme.bar.menus.opacity,
title: 'Menu Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({ opt: options.theme.bar.menus.cards, title: 'Cards', type: 'color' }),
Option({ opt: options.theme.bar.menus.card_radius, title: 'Card Radius', type: 'string' }),
Option({ opt: options.theme.bar.menus.text, title: 'Primary Text', type: 'color' }),
Option({ opt: options.theme.bar.menus.dimtext, title: 'Dim Text', type: 'color' }),
Option({ opt: options.theme.bar.menus.feinttext, title: 'Feint Text', type: 'color' }),
Option({ opt: options.theme.bar.menus.label, title: 'Label Color', type: 'color' }),
Header('Border'),
Option({ opt: options.theme.bar.menus.border.size, title: 'Border Width', type: 'string' }),
Option({ opt: options.theme.bar.menus.border.radius, title: 'Border Radius', type: 'string' }),
Option({ opt: options.theme.bar.menus.border.color, title: 'Border Color', type: 'color' }),
Header('Popover'),
Option({ opt: options.theme.bar.menus.popover.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.menus.popover.background, title: 'Background', type: 'color' }),
Header('List Items'),
Option({
opt: options.theme.bar.menus.listitems.active,
title: 'Active',
subtitle:
'Items of a list (network name, bluetooth device name, playback device, etc.) when active or hovered.',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.listitems.passive, title: 'Passive', type: 'color' }),
Header('Icons'),
Option({ opt: options.theme.bar.menus.icons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.icons.passive, title: 'Passive', type: 'color' }),
Header('Switch'),
Option({ opt: options.theme.bar.menus.switch.enabled, title: 'Enabled', type: 'color' }),
Option({ opt: options.theme.bar.menus.switch.disabled, title: 'Disabled', type: 'color' }),
Option({ opt: options.theme.bar.menus.switch.puck, title: 'Puck', type: 'color' }),
Header('Check/Radio Buttons'),
Option({
opt: options.theme.bar.menus.check_radio_button.background,
title: 'Background',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.check_radio_button.active, title: 'Active', type: 'color' }),
Header('Buttons'),
Option({ opt: options.theme.bar.menus.buttons.default, title: 'Primary', type: 'color' }),
Option({ opt: options.theme.bar.menus.buttons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.buttons.disabled, title: 'Disabled', type: 'color' }),
Option({ opt: options.theme.bar.menus.buttons.text, title: 'Text', type: 'color' }),
Header('Icon Buttons'),
Option({ opt: options.theme.bar.menus.iconbuttons.passive, title: 'Primary', type: 'color' }),
Option({ opt: options.theme.bar.menus.iconbuttons.active, title: 'Active/Hovered', type: 'color' }),
Header('Progress Bar'),
Option({ opt: options.theme.bar.menus.progressbar.foreground, title: 'Primary', type: 'color' }),
Option({ opt: options.theme.bar.menus.progressbar.background, title: 'Background', type: 'color' }),
Header('Slider'),
Option({ opt: options.theme.bar.menus.slider.primary, title: 'Primary', type: 'color' }),
Option({ opt: options.theme.bar.menus.slider.background, title: 'Background', type: 'color' }),
Option({
opt: options.theme.bar.menus.slider.backgroundhover,
title: 'Background (Hover)',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.slider.puck, title: 'Puck', type: 'color' }),
Header('Dropdown Menu'),
Option({ opt: options.theme.bar.menus.dropdownmenu.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.menus.dropdownmenu.text, title: 'Text', type: 'color' }),
Option({ opt: options.theme.bar.menus.dropdownmenu.divider, title: 'Divider', type: 'color' }),
Header('Tooltips'),
Option({ opt: options.theme.bar.menus.tooltip.background, title: 'Background', type: 'color' }),
Option({ opt: options.theme.bar.menus.tooltip.text, title: 'Text', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,71 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const Matugen = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Matugen Settings'),
Option({
opt: options.theme.matugen,
title: 'Enable Matugen',
subtitle: 'WARNING: THIS WILL REPLACE YOUR CURRENT COLOR SCHEME!!!',
type: 'boolean',
dependencies: ['matugen', 'swww'],
}),
Option({
opt: options.theme.matugen_settings.mode,
title: 'Matugen Theme',
type: 'enum',
enums: ['light', 'dark'],
}),
Option({
opt: options.theme.matugen_settings.scheme_type,
title: 'Matugen Scheme',
type: 'enum',
enums: [
'content',
'expressive',
'fidelity',
'fruit-salad',
'monochrome',
'neutral',
'rainbow',
'tonal-spot',
],
}),
Option({
opt: options.theme.matugen_settings.variation,
title: 'Matugen Variation',
type: 'enum',
enums: [
'standard_1',
'standard_2',
'standard_3',
'monochrome_1',
'monochrome_2',
'monochrome_3',
'vivid_1',
'vivid_2',
'vivid_3',
],
}),
Option({
opt: options.theme.matugen_settings.contrast,
title: 'Contrast',
subtitle: 'Range: -1 to 1 (Default: 0)',
type: 'float',
}),
],
}),
});
};
@@ -0,0 +1,83 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const MediaMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page media paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Media Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.media.song, title: 'Song', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.media.artist, title: 'Artist', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.media.album, title: 'Album', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.media.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.media.border.color, title: 'Border', type: 'color' }),
Header('Card/Album Art'),
Option({ opt: options.theme.bar.menus.menu.media.card.color, title: 'Color', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.media.card.tint,
title: 'Tint',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Header('Buttons'),
Option({
opt: options.theme.bar.menus.menu.media.buttons.inactive,
title: 'Unavailable',
subtitle: "Disabled button when media control isn't available.",
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.media.buttons.enabled,
title: 'Enabled',
subtitle: 'Ex: Button color when shuffle/loop is enabled.',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.media.buttons.background,
title: 'Background',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.media.buttons.text, title: 'Text', type: 'color' }),
Header('Slider'),
Option({
opt: options.theme.bar.menus.menu.media.slider.primary,
title: 'Primary Color',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.media.slider.background,
title: 'Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.media.slider.backgroundhover,
title: 'Backround (Hover)',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.media.slider.puck, title: 'Puck', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,86 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const NetworkMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page network paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Network Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.network.text, title: 'Text', type: 'color' }),
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.network.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.network.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.network.border.color, title: 'Border', type: 'color' }),
Header('Label'),
Option({ opt: options.theme.bar.menus.menu.network.label.color, title: 'Label', type: 'color' }),
Header('Status'),
Option({
opt: options.theme.bar.menus.menu.network.status.color,
title: 'Connection Status',
type: 'color',
}),
Header('Switch'),
Option({
opt: options.theme.bar.menus.menu.network.switch.enabled,
title: 'Enabled',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.network.switch.disabled,
title: 'Disabled',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.network.switch.puck, title: 'Puck', type: 'color' }),
Header('List Items'),
Option({
opt: options.theme.bar.menus.menu.network.listitems.active,
title: 'Active/Hover',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.network.listitems.passive,
title: 'Passive',
type: 'color',
}),
Header('Icons'),
Option({ opt: options.theme.bar.menus.menu.network.icons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.network.icons.passive, title: 'Passive', type: 'color' }),
Header('Icon Buttons'),
Option({
opt: options.theme.bar.menus.menu.network.iconbuttons.active,
title: 'Active',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.network.iconbuttons.passive,
title: 'Passive',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,81 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const NotificationsMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page notifications paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Notifications Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.notifications.label, title: 'Menu Label', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.notifications.card, title: 'Card', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.notifications.background,
title: 'Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.no_notifications_label,
title: 'Empty Notifications Backdrop',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.notifications.border, title: 'Border', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.notifications.switch_divider,
title: 'Switch Divider',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.clear,
title: 'Clear Notifications Button',
type: 'color',
}),
Header('Switch'),
Option({
opt: options.theme.bar.menus.menu.notifications.switch.enabled,
title: 'Enabled',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.switch.disabled,
title: 'Disabled',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.notifications.switch.puck, title: 'Puck', type: 'color' }),
Header('Scrollbar'),
Option({
opt: options.theme.bar.menus.menu.notifications.scrollbar.color,
title: 'Scrollbar Color',
type: 'color',
}),
Header('Pagination'),
Option({
opt: options.theme.bar.menus.menu.notifications.pager.background,
title: 'Pager Footer Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.pager.button,
title: 'Pager Button Color',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.notifications.pager.label,
title: 'Pager Label Color',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,101 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const PowerMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page power paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.power.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.power.border.color, title: 'Border', type: 'color' }),
Header('Shutdown Button'),
Option({
opt: options.theme.bar.menus.menu.power.buttons.shutdown.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.shutdown.icon_background,
title: 'Icon Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.shutdown.text,
title: 'Label Text',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.power.buttons.shutdown.icon, title: 'Icon', type: 'color' }),
Header('Reboot Button'),
Option({
opt: options.theme.bar.menus.menu.power.buttons.restart.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.restart.icon_background,
title: 'Icon Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.restart.text,
title: 'Label Text',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.power.buttons.restart.icon, title: 'Icon', type: 'color' }),
Header('Logout Button'),
Option({
opt: options.theme.bar.menus.menu.power.buttons.logout.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.logout.icon_background,
title: 'Icon Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.logout.text,
title: 'Label Text',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.power.buttons.logout.icon, title: 'Icon', type: 'color' }),
Header('Sleep Button'),
Option({
opt: options.theme.bar.menus.menu.power.buttons.sleep.background,
title: 'Label Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.sleep.icon_background,
title: 'Icon Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.power.buttons.sleep.text,
title: 'Label Text',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.power.buttons.sleep.icon, title: 'Icon', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,32 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const SystrayMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page systray paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Dropdown Menu'),
Option({
opt: options.theme.bar.menus.menu.systray.dropdownmenu.background,
title: 'Background',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.systray.dropdownmenu.text, title: 'Text', type: 'color' }),
Option({
opt: options.theme.bar.menus.menu.systray.dropdownmenu.divider,
title: 'Section Divider',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,98 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const VolumeMenuTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'menu-theme-page volume paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Volume Menu Theme Settings'),
Option({ opt: options.theme.bar.menus.menu.volume.text, title: 'Text', type: 'color' }),
Header('Card'),
Option({ opt: options.theme.bar.menus.menu.volume.card.color, title: 'Card', type: 'color' }),
Header('Background'),
Option({
opt: options.theme.bar.menus.menu.volume.background.color,
title: 'Background',
type: 'color',
}),
Header('Border'),
Option({ opt: options.theme.bar.menus.menu.volume.border.color, title: 'Border', type: 'color' }),
Header('Label'),
Option({ opt: options.theme.bar.menus.menu.volume.label.color, title: 'Label', type: 'color' }),
Header('List Items'),
Option({
opt: options.theme.bar.menus.menu.volume.listitems.active,
title: 'Active/Hover',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.volume.listitems.passive, title: 'Passive', type: 'color' }),
Header('Icon Button'),
Option({
opt: options.theme.bar.menus.menu.volume.iconbutton.active,
title: 'Active/Hover',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.volume.iconbutton.passive,
title: 'Passive',
type: 'color',
}),
Header('Icons'),
Option({ opt: options.theme.bar.menus.menu.volume.icons.active, title: 'Active', type: 'color' }),
Option({ opt: options.theme.bar.menus.menu.volume.icons.passive, title: 'Passive', type: 'color' }),
Header('Audio Slider'),
Option({
opt: options.theme.bar.menus.menu.volume.audio_slider.primary,
title: 'Primary',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.volume.audio_slider.background,
title: 'Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.volume.audio_slider.backgroundhover,
title: 'Background (Hover)',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.volume.audio_slider.puck, title: 'Puck', type: 'color' }),
Header('Input Slider'),
Option({
opt: options.theme.bar.menus.menu.volume.input_slider.primary,
title: 'Primary',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.volume.input_slider.background,
title: 'Background',
type: 'color',
}),
Option({
opt: options.theme.bar.menus.menu.volume.input_slider.backgroundhover,
title: 'Background (Hover)',
type: 'color',
}),
Option({ opt: options.theme.bar.menus.menu.volume.input_slider.puck, title: 'Puck', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,61 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const NotificationsTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'notifications-theme-page paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('Notifications Theme Settings'),
Option({ opt: options.theme.notification.background, title: 'Notification Background', type: 'color' }),
Option({
opt: options.theme.notification.opacity,
title: 'Notification Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({
opt: options.theme.notification.actions.background,
title: 'Action Button Background',
subtitle: 'Buttons that perform actions within a notification',
type: 'color',
}),
Option({
opt: options.theme.notification.actions.text,
title: 'Action Button Text Color',
type: 'color',
}),
Option({ opt: options.theme.notification.label, title: 'Label', type: 'color' }),
Option({ opt: options.theme.notification.border, title: 'Border', type: 'color' }),
Option({ opt: options.theme.notification.time, title: 'Time Stamp', type: 'color' }),
Option({ opt: options.theme.notification.text, title: 'Body Text', type: 'color' }),
Option({
opt: options.theme.notification.labelicon,
title: 'Label Icon',
subtitle: "Icon that accompanies the label. Doesn't apply if icon is an app icon.",
type: 'color',
}),
Option({
opt: options.theme.notification.close_button.background,
title: 'Dismiss Button',
type: 'color',
}),
Option({
opt: options.theme.notification.close_button.label,
title: 'Dismiss Button Text',
type: 'color',
}),
],
}),
});
};
@@ -0,0 +1,41 @@
import { Option } from 'widget/settings/shared/Option';
import { Header } from 'widget/settings/shared/Header';
import options from 'options';
import Scrollable from 'types/widgets/scrollable';
import { Attribute, Child } from 'lib/types/widget';
export const OsdTheme = (): Scrollable<Child, Attribute> => {
return Widget.Scrollable({
vscroll: 'automatic',
hscroll: 'automatic',
class_name: 'osd-theme-page paged-container',
vexpand: true,
child: Widget.Box({
vertical: true,
children: [
Header('On Screen Display Settings'),
Option({
opt: options.theme.osd.opacity,
title: 'OSD Opacity',
type: 'number',
increment: 5,
min: 0,
max: 100,
}),
Option({ opt: options.theme.osd.bar_color, title: 'Bar', type: 'color' }),
Option({
opt: options.theme.osd.bar_overflow_color,
title: 'Bar Overflow',
subtitle: 'Overflow color is for when the volume goes over a 100',
type: 'color',
}),
Option({ opt: options.theme.osd.bar_empty_color, title: 'Bar Background', type: 'color' }),
Option({ opt: options.theme.osd.bar_container, title: 'Bar Container', type: 'color' }),
Option({ opt: options.theme.osd.icon, title: 'Icon', type: 'color' }),
Option({ opt: options.theme.osd.icon_container, title: 'Icon Container', type: 'color' }),
Option({ opt: options.theme.osd.label, title: 'Value Text', type: 'color' }),
],
}),
});
};
@@ -0,0 +1,251 @@
import Gtk from 'gi://Gtk?version=3.0';
import Gio from 'gi://Gio';
import { bash, Notify } from 'lib/utils';
import icons from 'lib/icons';
import { Config } from 'lib/types/filechooser';
import { hexColorPattern } from 'globals/useTheme';
import { isHexColor } from 'globals/variables';
const whiteListedThemeProp = ['theme.bar.buttons.style'];
export const loadJsonFile = (filePath: string): Config | null => {
const file = Gio.File.new_for_path(filePath as string);
const [success, content] = file.load_contents(null);
if (!success) {
console.error(`Failed to import: ${filePath}`);
return null;
}
const jsonString = new TextDecoder('utf-8').decode(content);
return JSON.parse(jsonString);
};
export const saveConfigToFile = (config: object, filePath: string): void => {
const file = Gio.File.new_for_path(filePath);
const outputStream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
const dataOutputStream = new Gio.DataOutputStream({ base_stream: outputStream });
const jsonString = JSON.stringify(config, null, 2);
dataOutputStream.put_string(jsonString, null);
dataOutputStream.close(null);
};
export const filterConfigForThemeOnly = (config: Config): Config => {
const filteredConfig: Config = {};
for (const key in config) {
const value = config[key];
if (typeof value === 'string' && hexColorPattern.test(value)) {
filteredConfig[key] = config[key];
} else if (whiteListedThemeProp.includes(key)) {
filteredConfig[key] = config[key];
}
}
return filteredConfig;
};
export const filterConfigForNonTheme = (config: Config): Config => {
const filteredConfig: Config = {};
for (const key in config) {
if (whiteListedThemeProp.includes(key)) {
continue;
}
const value = config[key];
if (!(typeof value === 'string' && hexColorPattern.test(value))) {
filteredConfig[key] = config[key];
}
}
return filteredConfig;
};
export const saveFileDialog = (filePath: string, themeOnly: boolean): void => {
const original_file_path = filePath;
const file = Gio.File.new_for_path(original_file_path);
const [success, content] = file.load_contents(null);
if (!success) {
console.error(`Could not find 'config.json' at ${TMP}`);
return;
}
const jsonString = new TextDecoder('utf-8').decode(content);
const jsonObject = JSON.parse(jsonString);
// Function to filter hex color pairs
const filterHexColorPairs = (jsonObject: Config): Config => {
const filteredObject: Config = {};
for (const key in jsonObject) {
const value = jsonObject[key];
if (typeof value === 'string' && isHexColor(value)) {
filteredObject[key] = jsonObject[key];
} else if (whiteListedThemeProp.includes(key)) {
filteredObject[key] = jsonObject[key];
}
}
return filteredObject;
};
// Function to filter out hex color pairs (keep only non-hex color value)
const filterOutHexColorPairs = (jsonObject: Config): Config => {
const filteredObject: Config = {};
for (const key in jsonObject) {
// do not add key-value pair if its in whiteListedThemeProp
if (whiteListedThemeProp.includes(key)) {
continue;
}
const value = jsonObject[key];
if (!(typeof value === 'string' && isHexColor(value))) {
filteredObject[key] = jsonObject[key];
}
}
return filteredObject;
};
// Filter the JSON object based on the themeOnly flag
const filteredJsonObject = themeOnly ? filterHexColorPairs(jsonObject) : filterOutHexColorPairs(jsonObject);
const filteredContent = JSON.stringify(filteredJsonObject, null, 2);
const dialog = new Gtk.FileChooserDialog({
title: 'Save File As',
action: Gtk.FileChooserAction.SAVE,
});
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
dialog.add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT);
dialog.set_current_name(themeOnly ? 'hyprpanel_theme.json' : 'hyprpanel_config.json');
const response = dialog.run();
if (response === Gtk.ResponseType.ACCEPT) {
const file_path = dialog.get_filename();
console.info(`Original file path: ${file_path}`);
const getIncrementedFilePath = (filePath: string): string => {
let increment = 1;
const baseName = filePath.replace(/(\.\w+)$/, '');
const match = filePath.match(/(\.\w+)$/);
const extension = match ? match[0] : '';
let newFilePath = filePath;
let file = Gio.File.new_for_path(newFilePath);
while (file.query_exists(null)) {
newFilePath = `${baseName}_${increment}${extension}`;
file = Gio.File.new_for_path(newFilePath);
increment++;
}
return newFilePath;
};
const finalFilePath = getIncrementedFilePath(file_path as string);
console.info(`File will be saved at: ${finalFilePath}`);
try {
const save_file = Gio.File.new_for_path(finalFilePath);
const outputStream = save_file.replace(null, false, Gio.FileCreateFlags.NONE, null);
const dataOutputStream = new Gio.DataOutputStream({
base_stream: outputStream,
});
dataOutputStream.put_string(filteredContent, null);
dataOutputStream.close(null);
Notify({
summary: 'File Saved Successfully',
body: `At ${finalFilePath}.`,
iconName: icons.ui.info,
timeout: 5000,
});
} catch (e) {
if (e instanceof Error) {
console.error('Failed to write to file:', e.message);
}
}
}
dialog.destroy();
};
export const importFiles = (themeOnly: boolean = false): void => {
const dialog = new Gtk.FileChooserDialog({
title: `Import ${themeOnly ? 'Theme' : 'Config'}`,
action: Gtk.FileChooserAction.OPEN,
});
dialog.set_current_folder(`${App.configDir}/themes`);
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
dialog.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT);
const response = dialog.run();
if (response === Gtk.ResponseType.CANCEL) {
dialog.destroy();
return;
}
if (response === Gtk.ResponseType.ACCEPT) {
const filePath: string | null = dialog.get_filename();
if (filePath === null) {
Notify({
summary: 'Failed to import',
body: 'No file selected.',
iconName: icons.ui.warning,
timeout: 5000,
});
return;
}
const importedConfig = loadJsonFile(filePath);
if (!importedConfig) {
dialog.destroy();
return;
}
Notify({
summary: `Importing ${themeOnly ? 'Theme' : 'Config'}`,
body: `Importing: ${filePath}`,
iconName: icons.ui.info,
timeout: 7000,
});
const tmpConfigFile = Gio.File.new_for_path(`${TMP}/config.json`);
const optionsConfigFile = Gio.File.new_for_path(OPTIONS);
const [tmpSuccess, tmpContent] = tmpConfigFile.load_contents(null);
const [optionsSuccess, optionsContent] = optionsConfigFile.load_contents(null);
if (!tmpSuccess || !optionsSuccess) {
console.error('Failed to read existing configuration files.');
dialog.destroy();
return;
}
let tmpConfig = JSON.parse(new TextDecoder('utf-8').decode(tmpContent));
let optionsConfig = JSON.parse(new TextDecoder('utf-8').decode(optionsContent));
if (themeOnly) {
const filteredConfig = filterConfigForThemeOnly(importedConfig);
tmpConfig = { ...tmpConfig, ...filteredConfig };
optionsConfig = { ...optionsConfig, ...filteredConfig };
} else {
const filteredConfig = filterConfigForNonTheme(importedConfig);
tmpConfig = { ...tmpConfig, ...filteredConfig };
optionsConfig = { ...optionsConfig, ...filteredConfig };
}
saveConfigToFile(tmpConfig, `${TMP}/config.json`);
saveConfigToFile(optionsConfig, OPTIONS);
}
dialog.destroy();
bash('pkill ags && ags');
};
@@ -0,0 +1,18 @@
import { GBox } from 'lib/types/widget';
export const Header = (headerName: string): GBox => {
return Widget.Box({
class_name: 'options-header',
children: [
Widget.Label({
class_name: 'label-name',
label: headerName,
}),
Widget.Separator({
vpack: 'center',
hexpand: true,
class_name: 'menu-separator',
}),
],
});
};
@@ -0,0 +1,63 @@
import { RowProps } from 'lib/types/options';
import { Variable } from 'types/variable';
import { BoxWidget } from 'lib/types/widget';
import { numberInputter } from './components/number';
import { objectInputter } from './components/object';
import { stringInputter } from './components/string';
import { booleanInputter } from './components/boolean';
import { imageInputter } from './components/image';
import { importInputter } from './components/import';
import { wallpaperInputter } from './components/wallpaper';
import { colorInputter } from './components/color';
import { enumInputter } from './components/enum';
import { fontInputter } from './components/font';
export const Inputter = <T extends string | number | boolean | object>(
{
opt,
type = typeof opt.value as RowProps<T>['type'],
enums,
disabledBinding,
dependencies,
exportData,
min = 0,
max = 1000000,
increment = 1,
}: RowProps<T>,
className: string,
isUnsaved: Variable<boolean>,
): BoxWidget => {
return Widget.Box({
vpack: 'center',
class_name: /export|import/.test(type || '') ? '' : 'inputter-container',
setup: (self) => {
switch (type) {
case 'number':
return numberInputter(self, opt, min, max, increment, isUnsaved);
case 'float':
case 'object':
return objectInputter(self, opt, isUnsaved, className);
case 'string':
return stringInputter(self, opt, isUnsaved);
case 'enum':
return enumInputter(self, opt, enums!);
case 'boolean':
return booleanInputter(self, opt, disabledBinding, dependencies);
case 'img':
return imageInputter(self, opt);
case 'config_import':
return importInputter(self, exportData);
case 'wallpaper':
return wallpaperInputter(self, opt);
case 'font':
return fontInputter(self, opt);
case 'color':
return colorInputter(self, opt);
default:
return (self.child = Widget.Label({
label: `No setter with type ${type}`,
}));
}
},
});
};
@@ -0,0 +1,34 @@
import { GBox, GButton, GLabel } from 'lib/types/widget';
export const Label = (name: string, sub = '', subtitleLink = ''): GBox => {
const subTitle = (): GButton | GLabel => {
if (subtitleLink.length) {
return Widget.Button({
hpack: 'start',
vpack: 'center',
class_name: 'options-sublabel-link',
label: sub,
on_primary_click: () => Utils.execAsync(`bash -c 'xdg-open ${subtitleLink}'`),
});
}
return Widget.Label({
hpack: 'start',
vpack: 'center',
class_name: 'options-sublabel',
label: sub,
});
};
return Widget.Box({
vertical: true,
hpack: 'start',
children: [
Widget.Label({
hpack: 'start',
vpack: 'center',
class_name: 'options-label',
label: name,
}),
subTitle(),
],
});
};
@@ -0,0 +1,33 @@
import { Label } from './Label';
import { Inputter } from './Inputter';
import icons from 'lib/icons';
import { RowProps } from 'lib/types/options';
import { GBox } from 'lib/types/widget';
export const Option = <T extends string | number | boolean | object>(
props: RowProps<T>,
className: string = '',
): GBox => {
const isUnsaved = Variable(false);
return Widget.Box({
class_name: 'option-item',
hexpand: true,
children: [
Widget.Box({
hpack: 'start',
vpack: 'center',
hexpand: true,
child: Label(props.title, props.subtitle || '', props.subtitleLink),
}),
Inputter(props, className, isUnsaved),
Widget.Button({
vpack: 'center',
class_name: 'reset-options',
child: Widget.Icon(icons.ui.refresh),
on_clicked: () => props.opt.reset(),
sensitive: props.opt.bind().as((v) => v !== props.opt.initial),
}),
],
});
};
@@ -0,0 +1,29 @@
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
import { Variable } from 'types/variable';
import { dependencies as checkDependencies } from 'lib/utils';
export const booleanInputter = <T>(
self: BoxWidget,
opt: Opt<T>,
disabledBinding: Variable<boolean> | undefined,
dependencies: string[] | undefined,
): Attribute => {
return (self.child = Widget.Switch({
sensitive: disabledBinding !== undefined ? disabledBinding.bind('value').as((disabled) => !disabled) : true,
})
.on('notify::active', (self) => {
if (disabledBinding !== undefined && disabledBinding.value) {
return;
}
if (self.active && dependencies !== undefined && !dependencies.every((d) => checkDependencies(d))) {
self.active = false;
return;
}
opt.value = self.active as T;
})
.hook(opt, (self) => {
self.active = opt.value as boolean;
}));
};
@@ -0,0 +1,19 @@
import Gdk from 'gi://Gdk';
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
export const colorInputter = <T>(self: BoxWidget, opt: Opt<T>): Attribute => {
return (self.child = Widget.ColorButton()
.hook(opt, (self) => {
const rgba = new Gdk.RGBA();
rgba.parse(opt.value as string);
self.rgba = rgba;
})
.on('color-set', ({ rgba: { red, green, blue } }) => {
const hex = (n: number): string => {
const c = Math.floor(255 * n).toString(16);
return c.length === 1 ? `0${c}` : c;
};
opt.value = `#${hex(red)}${hex(green)}${hex(blue)}` as T;
}));
};
@@ -0,0 +1,36 @@
import { Opt } from 'lib/option';
import { BoxWidget } from 'lib/types/widget';
import icons from 'lib/icons';
import { Box } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
export const enumInputter = <T extends string | number | boolean | object>(
self: BoxWidget,
opt: Opt<T>,
values: T[],
): Box => {
const lbl = Widget.Label({ label: opt.bind().as((v) => `${v}`) });
const step = (dir: 1 | -1): void => {
const i = values.findIndex((i) => i === lbl.label);
opt.setValue(
dir > 0
? i + dir > values.length - 1
? values[0]
: values[i + dir]
: i + dir < 0
? values[values.length - 1]
: values[i + dir],
);
};
const next = Widget.Button({
child: Widget.Icon(icons.ui.arrow.right),
on_clicked: () => step(+1),
});
const prev = Widget.Button({
child: Widget.Icon(icons.ui.arrow.left),
on_clicked: () => step(-1),
});
return (self.child = Widget.Box({
class_name: 'enum-setter',
children: [lbl, prev, next],
}));
};
@@ -0,0 +1,14 @@
import { Opt } from 'lib/option';
import { Attribute, BoxWidget, Child } from 'lib/types/widget';
import FontButton from 'types/widgets/fontbutton';
export const fontInputter = <T>(self: BoxWidget, opt: Opt<T>): FontButton<Child, Attribute> => {
return (self.child = Widget.FontButton({
show_size: false,
use_size: false,
setup: (self) =>
self
.hook(opt, () => (self.font = opt.value as string))
.on('font-set', ({ font }) => (opt.value = font!.split(' ').slice(0, -1).join(' ') as T)),
}));
};
@@ -0,0 +1,31 @@
import { Opt } from 'lib/option';
import { Attribute, BoxWidget, Child } from 'lib/types/widget';
import FileChooserButton from 'types/widgets/filechooserbutton';
export const imageInputter = <T>(self: BoxWidget, opt: Opt<T>): Attribute => {
self.child = createFileChooserButton(opt);
return self.child;
};
const createFileChooserButton = <T>(opt: Opt<T>): FileChooserButton<Child, Attribute> => {
return Widget.FileChooserButton({
class_name: 'image-chooser',
on_file_set: handleFileSet(opt),
});
};
const handleFileSet =
<T>(opt: Opt<T>) =>
({ uri }: { uri: string | null }): void => {
if (!uri) {
console.warn('No URI selected');
return;
}
try {
const decodedPath = decodeURIComponent(uri.replace('file://', ''));
opt.value = decodedPath as T;
} catch (error) {
console.error('Failed to decode URI:', error);
}
};
@@ -0,0 +1,24 @@
import { ThemeExportData } from 'lib/types/options';
import { Attribute, BoxWidget } from 'lib/types/widget';
import { importFiles, saveFileDialog } from '../FileChooser';
export const importInputter = (self: BoxWidget, exportData?: ThemeExportData): Attribute => {
return (self.child = Widget.Box({
children: [
Widget.Button({
class_name: 'options-import',
label: 'import',
on_clicked: () => {
importFiles(exportData?.themeOnly as boolean);
},
}),
Widget.Button({
class_name: 'options-export',
label: 'export',
on_clicked: () => {
saveFileDialog(exportData?.filePath as string, exportData?.themeOnly as boolean);
},
}),
],
}));
};
@@ -0,0 +1,45 @@
import icons from 'lib/icons';
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
import { Variable } from 'types/variable';
export const numberInputter = <T>(
self: BoxWidget,
opt: Opt<T>,
min: number,
max: number,
increment = 1,
isUnsaved: Variable<boolean>,
): Attribute => {
return (self.children = [
Widget.Box({
class_name: 'unsaved-icon-container',
child: isUnsaved.bind('value').as((unsvd) => {
if (unsvd) {
return Widget.Icon({
class_name: 'unsaved-icon',
icon: icons.ui.warning,
tooltipText: "Press 'Enter' to apply your changes.",
});
}
return Widget.Box();
}),
}),
Widget.SpinButton({
setup(self) {
self.set_range(min, max);
self.set_increments(1 * increment, 5 * increment);
self.on('value-changed', () => {
opt.value = self.value as T;
});
self.hook(opt, () => {
self.value = opt.value as number;
isUnsaved.value = Number(self.text) !== (opt.value as number);
});
self.connect('key-release-event', () => {
isUnsaved.value = Number(self.text) !== (opt.value as number);
});
},
}),
]);
};
@@ -0,0 +1,37 @@
import icons from 'lib/icons';
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
import { Variable } from 'types/variable';
export const objectInputter = <T>(
self: BoxWidget,
opt: Opt<T>,
isUnsaved: Variable<boolean>,
className: string,
): Attribute => {
return (self.children = [
Widget.Box({
class_name: 'unsaved-icon-container',
child: isUnsaved.bind('value').as((unsvd) => {
if (unsvd) {
return Widget.Icon({
class_name: 'unsaved-icon',
icon: icons.ui.warning,
tooltipText: "Press 'Enter' to apply your changes.",
});
}
return Widget.Box();
}),
}),
Widget.Entry({
class_name: className,
on_change: (self) => (isUnsaved.value = self.text !== JSON.stringify(opt.value)),
on_accept: (self) => (opt.value = JSON.parse(self.text || '')),
setup: (self) =>
self.hook(opt, () => {
self.text = JSON.stringify(opt.value);
isUnsaved.value = self.text !== JSON.stringify(opt.value);
}),
}),
]);
};
@@ -0,0 +1,34 @@
import icons from 'lib/icons';
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
import { Variable } from 'types/variable';
export const stringInputter = <T>(self: BoxWidget, opt: Opt<T>, isUnsaved: Variable<boolean>): Attribute => {
return (self.children = [
Widget.Box({
class_name: 'unsaved-icon-container',
child: isUnsaved.bind('value').as((unsvd) => {
if (unsvd) {
return Widget.Icon({
class_name: 'unsaved-icon',
icon: icons.ui.warning,
tooltipText: "Press 'Enter' to apply your changes.",
});
}
return Widget.Box();
}),
}),
Widget.Entry({
class_name: isUnsaved.bind('value').as((unsaved) => (unsaved ? 'unsaved' : '')),
on_change: (self) => (isUnsaved.value = self.text !== opt.value),
on_accept: (self) => {
opt.value = self.text as T;
},
setup: (self) =>
self.hook(opt, () => {
isUnsaved.value = self.text !== opt.value;
self.text = opt.value as string;
}),
}),
]);
};
@@ -0,0 +1,20 @@
import { Opt } from 'lib/option';
import { Attribute, BoxWidget } from 'lib/types/widget';
import Wallpaper from 'services/Wallpaper';
export const wallpaperInputter = <T extends string | number | boolean | object>(
self: BoxWidget,
opt: Opt<T>,
): Attribute | void => {
if (typeof opt.value === 'string') {
return (self.child = Widget.FileChooserButton({
on_file_set: ({ uri }) => {
const newValue: string = uri!.replace('file://', '');
opt.value = newValue as T;
if (options.wallpaper.enable.value) {
Wallpaper.set(newValue);
}
},
}));
}
};
@@ -0,0 +1,34 @@
import options from 'options';
const { show_numbered, show_icons, showWsIcons } = options.bar.workspaces;
const { monochrome: monoBar } = options.theme.bar.buttons;
const { monochrome: monoMenu } = options.theme.bar.menus;
const { matugen } = options.theme;
show_numbered.connect('changed', ({ value }) => {
if (value === true) {
show_icons.value = false;
showWsIcons.value = false;
}
});
show_icons.connect('changed', ({ value }) => {
if (value === true) {
show_numbered.value = false;
showWsIcons.value = false;
}
});
showWsIcons.connect('changed', ({ value }) => {
if (value === true) {
show_numbered.value = false;
show_icons.value = false;
}
});
matugen.connect('changed', ({ value }) => {
if (value === true) {
monoBar.value = false;
monoMenu.value = false;
}
});