diff --git a/.config/ags/modules/.commonwidgets/configwidgets.js b/.config/ags/modules/.commonwidgets/configwidgets.js index 2f67755c..7bbeb0a4 100644 --- a/.config/ags/modules/.commonwidgets/configwidgets.js +++ b/.config/ags/modules/.commonwidgets/configwidgets.js @@ -7,8 +7,9 @@ const { Box, Button, Label, Revealer, SpinButton } = Widget; export const ConfigToggle = ({ icon, name, desc = '', initValue, - expandWidget = true, + expandWidget = true, resetButton = false, onChange = () => { }, extraSetup = () => { }, + onReset = () => { }, ...rest }) => { const enabled = Variable(initValue); @@ -48,7 +49,7 @@ export const ConfigToggle = ({ }); const widgetContent = Box({ tooltipText: desc, - className: 'txt spacing-h-5 configtoggle-box', + className: 'txt spacing-h-5', children: [ ...(icon !== undefined ? [MaterialIcon(icon, 'norm')] : []), ...(name !== undefined ? [Label({ @@ -85,7 +86,18 @@ export const ConfigToggle = ({ ...rest, }); 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 = ({ @@ -187,8 +199,9 @@ export const ConfigGap = ({ vertical = true, size = 5, ...rest }) => Box({ export const ConfigSpinButton = ({ icon, name, desc = '', initValue, minValue = 0, maxValue = 100, step = 1, - expandWidget = true, + expandWidget = true, resetButton = false, onChange = () => { }, extraSetup = () => { }, + onReset = () => { }, ...rest }) => { const value = Variable(initValue); @@ -213,6 +226,12 @@ export const ConfigSpinButton = ({ })] : []), ...(expandWidget ? [Box({ hexpand: true })] : []), spinButton, + ...(resetButton ? [Button({ + className: 'spinbutton-reset', + onClicked: onReset, + child: MaterialIcon('settings_backup_restore', 'small'), + setup: setupCursorHover, + })] : []), ], setup: (self) => { extraSetup(self); diff --git a/.config/ags/modules/sideright/centermodules/configure.js b/.config/ags/modules/sideright/centermodules/configure.js index f78608aa..abb2fbdd 100644 --- a/.config/ags/modules/sideright/centermodules/configure.js +++ b/.config/ags/modules/sideright/centermodules/configure.js @@ -2,30 +2,68 @@ const { GLib } = imports.gi; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.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; import { MaterialIcon } from '../../.commonwidgets/materialicon.js'; import { setupCursorHover } from '../../.widgetutils/cursorhover.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, name: name, desc: desc, + resetButton: true, initValue: JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"] != 0, onChange: (self, newValue) => { - execAsync(['hyprctl', 'keyword', option, `${newValue ? enableValue : disableValue}`]).catch(print); + 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); + // scripts/hyprland/hyprconfigurator.py --key decoration:rounding --value 12 --file '/home/end/.config/hypr/custom/general.conf' 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, name: name, desc: desc, + resetButton: true, initValue: Number(JSON.parse(exec(`hyprctl getoption -j ${option}`))["int"]), onChange: (self, newValue) => { - execAsync(['hyprctl', 'keyword', option, `${newValue}`]).catch(print); + 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); + }, + 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, }); @@ -53,69 +91,76 @@ export default (props) => { }) ] }) - const mainContent = Scrollable({ - vexpand: true, - child: Box({ - vertical: true, - className: 'spacing-v-10', - children: [ - ConfigSection({ - name: getString('Effects'), children: [ - ConfigToggle({ - icon: 'border_clear', - name: getString('Transparency'), - desc: getString('[AGS]\nMake shell elements transparent\nBlur is also recommended if you enable this'), - initValue: exec(`bash -c "sed -n \'2p\' ${GLib.get_user_state_dir()}/ags/user/colormode.txt"`) == "transparent", - onChange: (self, newValue) => { - const transparency = newValue == 0 ? "opaque" : "transparent"; - console.log(transparency); - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "2s/.*/${transparency}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`]) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) - .catch(print); - }, - }), - HyprlandToggle({ icon: 'blur_on', name: getString('Blur'), desc: getString("[Hyprland]\nEnable blur on transparent elements\nDoesn't affect performance/power consumption unless you have transparent windows."), option: "decoration:blur:enabled" }), - Subcategory([ - HyprlandToggle({ icon: 'stack_off', name: getString('X-ray'), desc: getString("[Hyprland]\nMake everything behind a window/layer except the wallpaper not rendered on its blurred surface\nRecommended to improve performance (if you don't abuse transparency/blur) "), option: "decoration:blur:xray" }), - HyprlandSpinButton({ icon: 'target', name: getString('Size'), desc: getString('[Hyprland]\nAdjust the blur radius. Generally doesn\'t affect performance\nHigher = more color spread'), option: 'decoration:blur:size', minValue: 1, maxValue: 1000 }), - HyprlandSpinButton({ icon: 'repeat', name: getString('Passes'), desc: getString('[Hyprland] Adjust the number of runs of the blur algorithm\nMore passes = more spread and power consumption\n4 is recommended\n2- would look weird and 6+ would look lame.'), option: 'decoration:blur:passes', minValue: 1, maxValue: 10 }), - ]), - ConfigGap({}), - HyprlandToggle({ - 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}`]) - }), - Subcategory([ - ConfigSpinButton({ - icon: 'clear_all', - name: getString('Choreography delay'), - desc: getString('In milliseconds, the delay between animations of a series'), - initValue: userOptions.animations.choreographyDelay, - step: 10, minValue: 0, maxValue: 1000, + const mainContent = Overlay({ + passThrough: true, + child: Scrollable({ + vexpand: true, + child: Box({ + vertical: true, + className: 'spacing-v-10 sidebar-centermodules-scrollgradient-bottom-contentmargin', + children: [ + ConfigSection({ + name: getString('Effects'), children: [ + ConfigToggle({ + icon: 'border_clear', + name: getString('Transparency'), + desc: getString('[AGS]\nMake shell elements transparent\nBlur is also recommended if you enable this'), + initValue: exec(`bash -c "sed -n \'2p\' ${GLib.get_user_state_dir()}/ags/user/colormode.txt"`) == "transparent", onChange: (self, newValue) => { - userOptions.animations.choreographyDelay = newValue + const transparency = newValue == 0 ? "opaque" : "transparent"; + console.log(transparency); + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "2s/.*/${transparency}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); }, - }) - ]), - ] - }), - ConfigSection({ - 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: '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: 'destruction', name: getString('Damage blink'), desc: getString("[Hyprland] [Epilepsy warning!]\nShow screen damage flashes"), option: "debug:damage_blink" }), - ] - }), - ] - }) + }), + HyprlandToggle({ icon: 'blur_on', name: getString('Blur'), desc: getString("[Hyprland]\nEnable blur on transparent elements\nDoesn't affect performance/power consumption unless you have transparent windows."), option: "decoration:blur:enabled" }), + Subcategory([ + HyprlandToggle({ icon: 'stack_off', name: getString('X-ray'), desc: getString("[Hyprland]\nMake everything behind a window/layer except the wallpaper not rendered on its blurred surface\nRecommended to improve performance (if you don't abuse transparency/blur) "), option: "decoration:blur:xray" }), + HyprlandSpinButton({ icon: 'target', name: getString('Size'), desc: getString('[Hyprland]\nAdjust the blur radius. Generally doesn\'t affect performance\nHigher = more color spread'), option: 'decoration:blur:size', minValue: 1, maxValue: 1000 }), + HyprlandSpinButton({ icon: 'repeat', name: getString('Passes'), desc: getString('[Hyprland] Adjust the number of runs of the blur algorithm\nMore passes = more spread and power consumption\n4 is recommended\n2- would look weird and 6+ would look lame.'), option: 'decoration:blur:passes', minValue: 1, maxValue: 10 }), + ]), + ConfigGap({}), + HyprlandToggle({ + 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}`]), + extraOnReset: (self, newValue) => execAsync(['gsettings', 'set', 'org.gnome.desktop.interface', 'enable-animations', 'true']), + }), + Subcategory([ + ConfigSpinButton({ + icon: 'clear_all', + name: getString('Choreography delay'), + desc: getString('In milliseconds, the delay between animations of a series'), + initValue: userOptions.animations.choreographyDelay, + step: 10, minValue: 0, maxValue: 1000, + onChange: (self, newValue) => { + userOptions.animations.choreographyDelay = newValue + }, + }) + ]), + ] + }), + ConfigSection({ + name: getString('Developer'), children: [ + 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: '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", save: false }), + ] + }), + ] + }) + }), + overlays: [Box({ + className: 'sidebar-centermodules-scrollgradient-bottom' + })] }); const footNote = Box({ homogeneous: true, children: [Label({ hpack: 'center', 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({ diff --git a/.config/ags/modules/sideright/centermodules/wifinetworks.js b/.config/ags/modules/sideright/centermodules/wifinetworks.js index 90be10b9..0f41108d 100644 --- a/.config/ags/modules/sideright/centermodules/wifinetworks.js +++ b/.config/ags/modules/sideright/centermodules/wifinetworks.js @@ -356,7 +356,7 @@ export default (props) => { }, }, 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), }), }), diff --git a/.config/ags/scripts/hyprland/hyprconfigurator.py b/.config/ags/scripts/hyprland/hyprconfigurator.py new file mode 100755 index 00000000..1a9855eb --- /dev/null +++ b/.config/ags/scripts/hyprland/hyprconfigurator.py @@ -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) + \ No newline at end of file diff --git a/.config/ags/scss/_common.scss b/.config/ags/scss/_common.scss index f8d3b62f..b086a150 100644 --- a/.config/ags/scss/_common.scss +++ b/.config/ags/scss/_common.scss @@ -306,14 +306,53 @@ widget { .spinbutton { @include small-rounding; + color: $onLayer2; background-color: $layer2; - padding: 0.341rem; + min-width: 2.045rem; + min-height: 2.045rem; + caret-color: $onLayer2; + entry { color: $onLayer2; - margin: 0.136rem 0.273rem; + margin: 0.477rem 0.614rem; } + button { - margin-left: 0.205rem; - padding: 0.136rem; + @include unsharpen-rounding; + 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; } \ No newline at end of file diff --git a/.config/ags/scss/_lib_mixins.scss b/.config/ags/scss/_lib_mixins.scss index 548909ce..36a458bc 100644 --- a/.config/ags/scss/_lib_mixins.scss +++ b/.config/ags/scss/_lib_mixins.scss @@ -1,6 +1,7 @@ // Common colors $hovercolor: $surfaceContainerHigh; $activecolor: $surfaceContainerHighest; +$rounding_unsharpen: 0.136rem; $rounding_verysmall: 0.477rem; $rounding_small: 0.818rem; $rounding_mediumsmall: 0.955rem; @@ -9,6 +10,10 @@ $rounding_mediumlarge: 1.364rem; $rounding_large: 1.705rem; // Common rules +@mixin unsharpen-rounding { + border-radius: $rounding_unsharpen; +} + @mixin small-rounding { border-radius: $rounding_small; -gtk-outline-radius: $rounding_small; diff --git a/.config/ags/scss/_sidebars.scss b/.config/ags/scss/_sidebars.scss index 5d01d06e..aab969bb 100644 --- a/.config/ags/scss/_sidebars.scss +++ b/.config/ags/scss/_sidebars.scss @@ -1041,6 +1041,10 @@ $waifu_image_overlay_transparency: 0.7; 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 { @include full-rounding; @include element_decel;