sidebar: configure: save settings

This commit is contained in:
end-4
2025-03-28 22:05:04 +01:00
parent 2cded1cd38
commit 1028a02b0e
7 changed files with 233 additions and 70 deletions
@@ -7,8 +7,9 @@ const { Box, Button, Label, Revealer, SpinButton } = Widget;
export const ConfigToggle = ({ export const ConfigToggle = ({
icon, name, desc = '', initValue, icon, name, desc = '', initValue,
expandWidget = true, expandWidget = true, resetButton = false,
onChange = () => { }, extraSetup = () => { }, onChange = () => { }, extraSetup = () => { },
onReset = () => { },
...rest ...rest
}) => { }) => {
const enabled = Variable(initValue); const enabled = Variable(initValue);
@@ -48,7 +49,7 @@ export const ConfigToggle = ({
}); });
const widgetContent = Box({ const widgetContent = Box({
tooltipText: desc, tooltipText: desc,
className: 'txt spacing-h-5 configtoggle-box', className: 'txt spacing-h-5',
children: [ children: [
...(icon !== undefined ? [MaterialIcon(icon, 'norm')] : []), ...(icon !== undefined ? [MaterialIcon(icon, 'norm')] : []),
...(name !== undefined ? [Label({ ...(name !== undefined ? [Label({
@@ -85,7 +86,18 @@ export const ConfigToggle = ({
...rest, ...rest,
}); });
interactionWrapper.enabled = enabled; interactionWrapper.enabled = enabled;
return interactionWrapper; return Box({
className: 'configtoggle-box spacing-h-5',
children: [
interactionWrapper,
...(resetButton ? [Button({
className: 'spinbutton-reset',
onClicked: onReset,
child: MaterialIcon('settings_backup_restore', 'small'),
setup: setupCursorHover,
})] : []),
]
});
} }
export const ConfigSegmentedSelection = ({ export const ConfigSegmentedSelection = ({
@@ -187,8 +199,9 @@ export const ConfigGap = ({ vertical = true, size = 5, ...rest }) => Box({
export const ConfigSpinButton = ({ export const ConfigSpinButton = ({
icon, name, desc = '', initValue, icon, name, desc = '', initValue,
minValue = 0, maxValue = 100, step = 1, minValue = 0, maxValue = 100, step = 1,
expandWidget = true, expandWidget = true, resetButton = false,
onChange = () => { }, extraSetup = () => { }, onChange = () => { }, extraSetup = () => { },
onReset = () => { },
...rest ...rest
}) => { }) => {
const value = Variable(initValue); const value = Variable(initValue);
@@ -213,6 +226,12 @@ export const ConfigSpinButton = ({
})] : []), })] : []),
...(expandWidget ? [Box({ hexpand: true })] : []), ...(expandWidget ? [Box({ hexpand: true })] : []),
spinButton, spinButton,
...(resetButton ? [Button({
className: 'spinbutton-reset',
onClicked: onReset,
child: MaterialIcon('settings_backup_restore', 'small'),
setup: setupCursorHover,
})] : []),
], ],
setup: (self) => { setup: (self) => {
extraSetup(self); extraSetup(self);
@@ -2,31 +2,69 @@ const { GLib } = imports.gi;
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, Button, Icon, Label, Scrollable, Slider, Stack } = Widget; const { Box, Button, Icon, Label, Scrollable, Slider, Stack, Overlay } = Widget;
const { execAsync, exec } = Utils; const { execAsync, exec } = Utils;
import { MaterialIcon } from '../../.commonwidgets/materialicon.js'; import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import { setupCursorHover } from '../../.widgetutils/cursorhover.js'; import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
import { ConfigGap, ConfigSpinButton, ConfigToggle } from '../../.commonwidgets/configwidgets.js'; import { ConfigGap, ConfigSpinButton, ConfigToggle } from '../../.commonwidgets/configwidgets.js';
const HyprlandToggle = ({ icon, name, desc = null, option, enableValue = 1, disableValue = 0, extraOnChange = () => { } }) => ConfigToggle({ const HyprlandToggle = ({ icon, name, desc = null, option, enableValue = 1, disableValue = 0, extraOnChange = () => { }, extraOnReset = () => { }, save = true }) => ConfigToggle({
icon: icon, icon: icon,
name: name, name: name,
desc: desc, desc: desc,
resetButton: true,
initValue: JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"] != 0, initValue: JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"] != 0,
onChange: (self, newValue) => { onChange: (self, newValue) => {
if (save)
execAsync(['bash', '-c', `${App.configDir}/scripts/hyprland/hyprconfigurator.py \
--key ${option} \
--value ${newValue ? enableValue : disableValue} \
--file \${XDG_CONFIG_HOME:-$HOME/.config}/hypr/custom/general.conf`
]).catch(print);
else
execAsync(['hyprctl', 'keyword', option, `${newValue ? enableValue : disableValue}`]).catch(print); execAsync(['hyprctl', 'keyword', option, `${newValue ? enableValue : disableValue}`]).catch(print);
// scripts/hyprland/hyprconfigurator.py --key decoration:rounding --value 12 --file '/home/end/.config/hypr/custom/general.conf'
extraOnChange(self, newValue); extraOnChange(self, newValue);
} },
onReset: (self, newValue) => {
if (save)
execAsync(['bash', '-c', `${App.configDir}/scripts/hyprland/hyprconfigurator.py \
--key ${option} \
--reset \
--file \${XDG_CONFIG_HOME:-$HOME/.config}/hypr/custom/general.conf`
]).catch(print);
else
execAsync(['bash', '-c', `hyprctl reload`]).catch(print);
extraOnReset(self, newValue);
},
}); });
const HyprlandSpinButton = ({ icon, name, desc = null, option, ...rest }) => ConfigSpinButton({ const HyprlandSpinButton = ({ icon, name, desc = null, option, save = true, ...rest }) => ConfigSpinButton({
icon: icon, icon: icon,
name: name, name: name,
desc: desc, desc: desc,
resetButton: true,
initValue: Number(JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"]), initValue: Number(JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"]),
onChange: (self, newValue) => { onChange: (self, newValue) => {
if (save)
execAsync(['bash', '-c', `${App.configDir}/scripts/hyprland/hyprconfigurator.py \
--key ${option} \
--value ${newValue} \
--file \${XDG_CONFIG_HOME:-$HOME/.config}/hypr/custom/general.conf`
]).catch(print);
else
execAsync(['hyprctl', 'keyword', option, `${newValue}`]).catch(print); execAsync(['hyprctl', 'keyword', option, `${newValue}`]).catch(print);
}, },
onReset: (self, newValue) => {
if (save)
execAsync(['bash', '-c', `${App.configDir}/scripts/hyprland/hyprconfigurator.py \
--key ${option} \
--reset \
--file \${XDG_CONFIG_HOME:-$HOME/.config}/hypr/custom/general.conf`
]).catch(print);
else
execAsync(['bash', '-c', `hyprctl reload`]).catch(print);
},
...rest, ...rest,
}); });
@@ -53,11 +91,13 @@ export default (props) => {
}) })
] ]
}) })
const mainContent = Scrollable({ const mainContent = Overlay({
passThrough: true,
child: Scrollable({
vexpand: true, vexpand: true,
child: Box({ child: Box({
vertical: true, vertical: true,
className: 'spacing-v-10', className: 'spacing-v-10 sidebar-centermodules-scrollgradient-bottom-contentmargin',
children: [ children: [
ConfigSection({ ConfigSection({
name: getString('Effects'), children: [ name: getString('Effects'), children: [
@@ -83,7 +123,8 @@ export default (props) => {
ConfigGap({}), ConfigGap({}),
HyprlandToggle({ HyprlandToggle({
icon: 'animation', name: getString('Animations'), desc: getString('[Hyprland] [GTK]\nEnable animations'), option: 'animations:enabled', icon: 'animation', name: getString('Animations'), desc: getString('[Hyprland] [GTK]\nEnable animations'), option: 'animations:enabled',
extraOnChange: (self, newValue) => execAsync(['gsettings', 'set', 'org.gnome.desktop.interface', 'enable-animations', `${newValue}`]) extraOnChange: (self, newValue) => execAsync(['gsettings', 'set', 'org.gnome.desktop.interface', 'enable-animations', `${newValue}`]),
extraOnReset: (self, newValue) => execAsync(['gsettings', 'set', 'org.gnome.desktop.interface', 'enable-animations', 'true']),
}), }),
Subcategory([ Subcategory([
ConfigSpinButton({ ConfigSpinButton({
@@ -101,21 +142,25 @@ export default (props) => {
}), }),
ConfigSection({ ConfigSection({
name: getString('Developer'), children: [ name: getString('Developer'), children: [
HyprlandToggle({ icon: 'speed', name: getString('Show FPS'), desc: getString("[Hyprland]\nShow FPS overlay on top-left corner"), option: "debug:overlay" }), HyprlandToggle({ icon: 'speed', name: getString('Show FPS'), desc: getString("[Hyprland]\nShow FPS overlay on top-left corner"), option: "debug:overlay", save: false }),
HyprlandToggle({ icon: 'sort', name: getString('Log to stdout'), desc: getString("[Hyprland]\nPrint LOG, ERR, WARN, etc. messages to the console"), option: "debug:enable_stdout_logs" }), HyprlandToggle({ icon: 'sort', name: getString('Log to stdout'), desc: getString("[Hyprland]\nPrint LOG, ERR, WARN, etc. messages to the console"), option: "debug:enable_stdout_logs" }),
HyprlandToggle({ icon: 'motion_sensor_active', name: getString('Damage tracking'), desc: getString("[Hyprland]\nEnable damage tracking\nGenerally, leave it on.\nTurn off only when a shader doesn't work"), option: "debug:damage_tracking", enableValue: 2 }), HyprlandToggle({ icon: 'motion_sensor_active', name: getString('Damage tracking'), desc: getString("[Hyprland]\nEnable damage tracking\nGenerally, leave it on.\nTurn off only when a shader doesn't work"), option: "debug:damage_tracking", enableValue: 2, save: false }),
HyprlandToggle({ icon: 'destruction', name: getString('Damage blink'), desc: getString("[Hyprland] [Epilepsy warning!]\nShow screen damage flashes"), option: "debug:damage_blink" }), HyprlandToggle({ icon: 'destruction', name: getString('Damage blink'), desc: getString("[Hyprland] [Epilepsy warning!]\nShow screen damage flashes"), option: "debug:damage_blink", save: false }),
] ]
}), }),
] ]
}) })
}),
overlays: [Box({
className: 'sidebar-centermodules-scrollgradient-bottom'
})]
}); });
const footNote = Box({ const footNote = Box({
homogeneous: true, homogeneous: true,
children: [Label({ children: [Label({
hpack: 'center', hpack: 'center',
className: 'txt txt-italic txt-subtext margin-5', className: 'txt txt-italic txt-subtext margin-5',
label: getString('Not all changes are saved'), label: getString('AGS-related changes aren\'t saved'),
})] })]
}) })
return Box({ return Box({
@@ -356,7 +356,7 @@ export default (props) => {
}, },
}, },
vertical: true, vertical: true,
className: 'spacing-v-5 margin-bottom-15', className: 'spacing-v-5 sidebar-centermodules-scrollgradient-bottom-contentmargin',
setup: (self) => self.hook(Network, self.attribute.updateNetworks), setup: (self) => self.hook(Network, self.attribute.updateNetworks),
}), }),
}), }),
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env -S\_/bin/sh\_-xc\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
import argparse
import re
import os
def edit_hyprland_config(file_path, key, value, reset):
try:
with open(file_path, 'r') as file:
lines = file.readlines()
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
return
key_pattern = re.compile(rf'^\s*{re.escape(key)}\s*=')
new_lines = []
found = False
for line in lines:
if key_pattern.match(line):
found = True
if reset:
continue # Skip this line to remove the key
line = f"{key} = {value}\n"
new_lines.append(line)
if not found and not reset:
new_lines.append(f"{key} = {value}\n")
with open(file_path, 'w') as file:
file.writelines(new_lines)
if reset:
print(f"Removed '{key}' from '{file_path}'")
else:
print(f"Updated '{file_path}' with {key} = {value}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Edit a Hyprland config file.")
parser.add_argument("--file", default="~/.config/hypr/hyprland.conf", help="Path to the Hyprland config file (default: ~/.config/hypr/hyprland.conf).")
parser.add_argument("--key", required=True, help="Configuration key to modify or remove.")
parser.add_argument("--value", help="New value for the configuration key (optional).", default=None)
parser.add_argument("--reset", action="store_true", help="Remove the specified key from the config file.")
args = parser.parse_args()
file_path = os.path.expanduser(args.file)
if args.reset and args.value:
print("Error: --reset and --value cannot be used together.")
else:
edit_hyprland_config(file_path, args.key, args.value or "", args.reset)
+43 -4
View File
@@ -306,14 +306,53 @@ widget {
.spinbutton { .spinbutton {
@include small-rounding; @include small-rounding;
color: $onLayer2;
background-color: $layer2; background-color: $layer2;
padding: 0.341rem; min-width: 2.045rem;
min-height: 2.045rem;
caret-color: $onLayer2;
entry { entry {
color: $onLayer2; color: $onLayer2;
margin: 0.136rem 0.273rem; margin: 0.477rem 0.614rem;
} }
button { button {
margin-left: 0.205rem; @include unsharpen-rounding;
padding: 0.136rem; min-width: 2.045rem;
min-height: 2.045rem;
-gtk-outline-radius: $rounding_small;
}
button.up {
// Only apply rounding to (+) button
border-top-right-radius: $rounding_small;
border-bottom-right-radius: $rounding_small;
}
// button:focus, // Looks weird after clicking cuz it'll highlight both + and -
button:hover {
background-color: $layer2Hover;
}
button:active {
background-color: $layer2Active;
} }
} }
.spinbutton-reset {
@include small-rounding;
color: $onLayer2;
background-color: $layer2;
min-width: 2.045rem;
min-height: 2.045rem;
}
.spinbutton-reset:focus,
.spinbutton-reset:hover {
background-color: $layer2Hover;
}
.spinbutton-reset:active {
background-color: $layer2Active;
}
+5
View File
@@ -1,6 +1,7 @@
// Common colors // Common colors
$hovercolor: $surfaceContainerHigh; $hovercolor: $surfaceContainerHigh;
$activecolor: $surfaceContainerHighest; $activecolor: $surfaceContainerHighest;
$rounding_unsharpen: 0.136rem;
$rounding_verysmall: 0.477rem; $rounding_verysmall: 0.477rem;
$rounding_small: 0.818rem; $rounding_small: 0.818rem;
$rounding_mediumsmall: 0.955rem; $rounding_mediumsmall: 0.955rem;
@@ -9,6 +10,10 @@ $rounding_mediumlarge: 1.364rem;
$rounding_large: 1.705rem; $rounding_large: 1.705rem;
// Common rules // Common rules
@mixin unsharpen-rounding {
border-radius: $rounding_unsharpen;
}
@mixin small-rounding { @mixin small-rounding {
border-radius: $rounding_small; border-radius: $rounding_small;
-gtk-outline-radius: $rounding_small; -gtk-outline-radius: $rounding_small;
+4
View File
@@ -1041,6 +1041,10 @@ $waifu_image_overlay_transparency: 0.7;
background: linear-gradient(to top, $layer1 0%, transparentize($layer1, 1) 1.023rem); background: linear-gradient(to top, $layer1 0%, transparentize($layer1, 1) 1.023rem);
} }
.sidebar-centermodules-scrollgradient-bottom-contentmargin {
margin-bottom: 1.023rem;
}
.sidebar-wifinetworks-network-button { .sidebar-wifinetworks-network-button {
@include full-rounding; @include full-rounding;
@include element_decel; @include element_decel;