From dc9f0cd58454b4cb33a704b11e4cc8a7c6594e67 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Wed, 24 Apr 2024 22:19:05 +0530 Subject: [PATCH 1/2] Added ags overview widget Updated search iscon for overview search result suggested action Removed excluded site from overview web search Updated calculator icon for overview Updated overview search action icon spacing Added initial pywal integration to overview search and updated seach icon --- config/ags/config.js | 31 ++ .../modules/.configuration/user_options.js | 121 +++++ config/ags/modules/.miscutils/icons.js | 13 + config/ags/modules/.miscutils/mathfuncs.js | 4 + config/ags/modules/.miscutils/system.js | 54 +++ .../modules/.widgethacks/advancedrevealers.js | 86 ++++ .../ags/modules/.widgethacks/popupwindow.js | 32 ++ .../ags/modules/.widgetutils/clickthrough.js | 4 + .../ags/modules/.widgetutils/cursorhover.js | 57 +++ config/ags/modules/.widgetutils/keybind.js | 25 ++ config/ags/modules/overview/actions.js | 28 ++ config/ags/modules/overview/main.js | 18 + config/ags/modules/overview/miscfunctions.js | 155 +++++++ .../ags/modules/overview/overview_hyprland.js | 423 ++++++++++++++++++ config/ags/modules/overview/searchbuttons.js | 163 +++++++ config/ags/modules/overview/searchitem.js | 65 +++ config/ags/modules/overview/windowcontent.js | 262 +++++++++++ config/ags/user/style.css | 190 ++++++++ config/ags/user_options.js | 21 + config/ags/variables.js | 21 + config/hypr/UserConfigs/Startup_Apps.conf | 1 + config/hypr/configs/Keybinds.conf | 1 + config/hypr/scripts/Refresh.sh | 7 +- 23 files changed, 1781 insertions(+), 1 deletion(-) create mode 100644 config/ags/config.js create mode 100644 config/ags/modules/.configuration/user_options.js create mode 100644 config/ags/modules/.miscutils/icons.js create mode 100644 config/ags/modules/.miscutils/mathfuncs.js create mode 100644 config/ags/modules/.miscutils/system.js create mode 100644 config/ags/modules/.widgethacks/advancedrevealers.js create mode 100644 config/ags/modules/.widgethacks/popupwindow.js create mode 100644 config/ags/modules/.widgetutils/clickthrough.js create mode 100644 config/ags/modules/.widgetutils/cursorhover.js create mode 100644 config/ags/modules/.widgetutils/keybind.js create mode 100644 config/ags/modules/overview/actions.js create mode 100644 config/ags/modules/overview/main.js create mode 100644 config/ags/modules/overview/miscfunctions.js create mode 100644 config/ags/modules/overview/overview_hyprland.js create mode 100644 config/ags/modules/overview/searchbuttons.js create mode 100644 config/ags/modules/overview/searchitem.js create mode 100644 config/ags/modules/overview/windowcontent.js create mode 100644 config/ags/user/style.css create mode 100644 config/ags/user_options.js create mode 100644 config/ags/variables.js diff --git a/config/ags/config.js b/config/ags/config.js new file mode 100644 index 0000000..278bf35 --- /dev/null +++ b/config/ags/config.js @@ -0,0 +1,31 @@ +"use strict"; +import GLib from 'gi://GLib'; +import App from 'resource:///com/github/Aylur/ags/app.js' +import userOptions from './modules/.configuration/user_options.js'; +import Overview from './modules/overview/main.js'; + +const COMPILED_STYLE_DIR = `${GLib.get_user_config_dir()}/ags/user/` + +async function applyStyle() { + + App.resetCss(); + App.applyCss(`${COMPILED_STYLE_DIR}/style.css`); + console.log('[LOG] Styles loaded') +} +applyStyle().catch(print); + +const Windows = () => [ + Overview() +]; +const CLOSE_ANIM_TIME = 210; +App.config({ + css: `${COMPILED_STYLE_DIR}/style.css`, + stackTraceOnError: true, + closeWindowDelay: { + 'sideright': CLOSE_ANIM_TIME, + 'sideleft': CLOSE_ANIM_TIME, + 'osk': CLOSE_ANIM_TIME, + }, + windows: Windows().flat(1), +}); + diff --git a/config/ags/modules/.configuration/user_options.js b/config/ags/modules/.configuration/user_options.js new file mode 100644 index 0000000..0c76a54 --- /dev/null +++ b/config/ags/modules/.configuration/user_options.js @@ -0,0 +1,121 @@ + +import userOverrides from '../../user_options.js'; + +// Defaults +let configOptions = { + // General stuff + 'ai': { + 'defaultGPTProvider': "openai", + 'defaultTemperature': 0.9, + 'enhancements': true, + 'useHistory': true, + 'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering + }, + 'animations': { + 'choreographyDelay': 35, + 'durationSmall': 110, + 'durationLarge': 180, + }, + 'appearance': { + 'keyboardUseFlag': false, // Use flag emoji instead of abbreviation letters + }, + 'apps': { + 'imageViewer': "loupe", + 'terminal': "foot", // This is only for shell actions + }, + 'battery': { + 'low': 20, + 'critical': 10, + }, + 'music': { + 'preferredPlayer': "plasma-browser-integration", + }, + 'onScreenKeyboard': { + 'layout': "qwerty_full", // See modules/onscreenkeyboard/onscreenkeyboard.js for available layouts + }, + 'overview': { + 'scale': 0.18, // Relative to screen size + 'numOfRows': 2, + 'numOfCols': 5, + 'wsNumScale': 0.09, + 'wsNumMarginScale': 0.07, + }, + 'sidebar': { + 'imageColumns': 2, + 'imageBooruCount': 20, + 'imageAllowNsfw': false, + }, + 'search': { + 'engineBaseUrl': "https://www.google.com/search?q=", + 'excludedSites': [], //add site to exclude from result. eg: "quora.com" + }, + 'time': { + // See https://docs.gtk.org/glib/method.DateTime.format.html + // Here's the 12h format: "%I:%M%P" + // For seconds, add "%S" and set interval to 1000 + 'format': "%H:%M", + 'interval': 5000, + 'dateFormatLong': "%A, %d/%m", // On bar + 'dateInterval': 5000, + 'dateFormat': "%d/%m", // On notif time + }, + 'weather': { + 'city': "", + }, + 'workspaces': { + 'shown': 10, + }, + // Longer stuff + 'icons': { + substitutions: { + 'code-url-handler': "visual-studio-code", + 'Code': "visual-studio-code", + 'GitHub Desktop': "github-desktop", + 'Minecraft* 1.20.1': "minecraft", + 'gnome-tweaks': "org.gnome.tweaks", + 'pavucontrol-qt': "pavucontrol", + 'wps': "wps-office2019-kprometheus", + 'wpsoffice': "wps-office2019-kprometheus", + '': "image-missing", + } + }, + 'keybinds': { + // Format: Mod1+Mod2+key. CaSe SeNsItIvE! + // Modifiers: Shift Ctrl Alt Hyper Meta + // See https://docs.gtk.org/gdk3/index.html#constants for the other keys (they are listed as KEY_key) + 'overview': { + 'altMoveLeft': "Ctrl+b", + 'altMoveRight': "Ctrl+f", + 'deleteToEnd': "Ctrl+k", + }, + 'sidebar': { + 'apis': { + 'nextTab': "Page_Down", + 'prevTab': "Page_Up", + }, + 'options': { // Right sidebar + 'nextTab': "Page_Down", + 'prevTab': "Page_Up", + }, + 'pin': "Ctrl+p", + 'cycleTab': "Ctrl+Tab", + 'nextTab': "Ctrl+Page_Down", + 'prevTab': "Ctrl+Page_Up", + }, + }, +} + +// Override defaults with user's options +function overrideConfigRecursive(userOverrides, configOptions = {}) { + for (const [key, value] of Object.entries(userOverrides)) { + if (typeof value === 'object') { + overrideConfigRecursive(value, configOptions[key]); + } else { + configOptions[key] = value; + } + } +} +overrideConfigRecursive(userOverrides, configOptions); + +globalThis['userOptions'] = configOptions; +export default configOptions; \ No newline at end of file diff --git a/config/ags/modules/.miscutils/icons.js b/config/ags/modules/.miscutils/icons.js new file mode 100644 index 0000000..fb1e20d --- /dev/null +++ b/config/ags/modules/.miscutils/icons.js @@ -0,0 +1,13 @@ +const { Gtk } = imports.gi; + +export function iconExists(iconName) { + let iconTheme = Gtk.IconTheme.get_default(); + return iconTheme.has_icon(iconName); +} + +export function substitute(str) { + if(userOptions.icons.substitutions[str]) return userOptions.icons.substitutions[str]; + + if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case + return str; +} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/mathfuncs.js b/config/ags/modules/.miscutils/mathfuncs.js new file mode 100644 index 0000000..ba1c0b5 --- /dev/null +++ b/config/ags/modules/.miscutils/mathfuncs.js @@ -0,0 +1,4 @@ + +export function clamp(x, min, max) { + return Math.min(Math.max(x, min), max); +} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/system.js b/config/ags/modules/.miscutils/system.js new file mode 100644 index 0000000..ef7d15c --- /dev/null +++ b/config/ags/modules/.miscutils/system.js @@ -0,0 +1,54 @@ +const { GLib } = imports.gi; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; + +export const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2 | sed "s/\\"//g"'`).trim(); +export const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'popos' || distroID == 'raspbian' || distroID == 'kali'); +export const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros' || distroID == 'cachyos'); +export const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`); + +const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/colormode.txt`; +const colorMode = Utils.exec('bash -c "sed -n \'1p\' $HOME/.cache/ags/user/colormode.txt"'); +export let darkMode = Variable(!(Utils.readFile(LIGHTDARK_FILE_LOCATION).split('\n')[0].trim() == 'light')); +export const hasPlasmaIntegration = !!Utils.exec('bash -c "command -v plasma-browser-integration-host"'); + +export const getDistroIcon = () => { + // Arches + if(distroID == 'arch') return 'arch-symbolic'; + if(distroID == 'endeavouros') return 'endeavouros-symbolic'; + if(distroID == 'cachyos') return 'cachyos-symbolic'; + // Funny flake + if(distroID == 'nixos') return 'nixos-symbolic'; + // Cool thing + if(distroID == 'fedora') return 'fedora-symbolic'; + // Debians + if(distroID == 'linuxmint') return 'ubuntu-symbolic'; + if(distroID == 'ubuntu') return 'ubuntu-symbolic'; + if(distroID == 'debian') return 'debian-symbolic'; + if(distroID == 'zorin') return 'ubuntu-symbolic'; + if(distroID == 'popos') return 'ubuntu-symbolic'; + if(distroID == 'raspbian') return 'debian-symbolic'; + if(distroID == 'kali') return 'debian-symbolic'; + return 'linux-symbolic'; +} + +export const getDistroName = () => { + // Arches + if(distroID == 'arch') return 'Arch Linux'; + if(distroID == 'endeavouros') return 'EndeavourOS'; + if(distroID == 'cachyos') return 'CachyOS'; + // Funny flake + if(distroID == 'nixos') return 'NixOS'; + // Cool thing + if(distroID == 'fedora') return 'Fedora'; + // Debians + if(distroID == 'linuxmint') return 'Linux Mint'; + if(distroID == 'ubuntu') return 'Ubuntu'; + if(distroID == 'debian') return 'Debian'; + if(distroID == 'zorin') return 'Zorin'; + if(distroID == 'popos') return 'Pop!_OS'; + if(distroID == 'raspbian') return 'Raspbian'; + if(distroID == 'kali') return 'Kali Linux'; + return 'Linux'; +} diff --git a/config/ags/modules/.widgethacks/advancedrevealers.js b/config/ags/modules/.widgethacks/advancedrevealers.js new file mode 100644 index 0000000..3f12793 --- /dev/null +++ b/config/ags/modules/.widgethacks/advancedrevealers.js @@ -0,0 +1,86 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; + +const { Revealer, Scrollable } = Widget; + +export const MarginRevealer = ({ + transition = 'slide_down', + child, + revealChild, + showClass = 'element-show', // These are for animation curve, they don't really hide + hideClass = 'element-hide', // Don't put margins in these classes! + extraSetup = () => { }, + ...rest +}) => { + const widget = Scrollable({ + ...rest, + attribute: { + 'revealChild': true, // It'll be set to false after init if it's supposed to hide + 'transition': transition, + 'show': () => { + if (widget.attribute.revealChild) return; + widget.hscroll = 'never'; + widget.vscroll = 'never'; + child.toggleClassName(hideClass, false); + child.toggleClassName(showClass, true); + widget.attribute.revealChild = true; + child.css = 'margin: 0px;'; + }, + 'hide': () => { + if (!widget.attribute.revealChild) return; + child.toggleClassName(hideClass, true); + child.toggleClassName(showClass, false); + widget.attribute.revealChild = false; + if (widget.attribute.transition == 'slide_left') + child.css = `margin-right: -${child.get_allocated_width()}px;`; + else if (widget.attribute.transition == 'slide_right') + child.css = `margin-left: -${child.get_allocated_width()}px;`; + else if (widget.attribute.transition == 'slide_up') + child.css = `margin-bottom: -${child.get_allocated_height()}px;`; + else if (widget.attribute.transition == 'slide_down') + child.css = `margin-top: -${child.get_allocated_height()}px;`; + }, + 'toggle': () => { + if (widget.attribute.revealChild) widget.attribute.hide(); + else widget.attribute.show(); + }, + }, + child: child, + hscroll: `${revealChild ? 'never' : 'always'}`, + vscroll: `${revealChild ? 'never' : 'always'}`, + setup: (self) => { + extraSetup(self); + } + }); + child.toggleClassName(`${revealChild ? showClass : hideClass}`, true); + return widget; +} + +// TODO: Allow reveal update. Currently this just helps at declaration +export const DoubleRevealer = ({ + transition1 = 'slide_right', + transition2 = 'slide_left', + duration1 = 150, + duration2 = 150, + child, + revealChild, + ...rest +}) => { + const r2 = Revealer({ + transition: transition2, + transitionDuration: duration2, + revealChild: revealChild, + child: child, + }); + const r1 = Revealer({ + transition: transition1, + transitionDuration: duration1, + revealChild: revealChild, + child: r2, + ...rest, + }) + r1.toggleRevealChild = (value) => { + r1.revealChild = value; + r2.revealChild = value; + } + return r1; +} diff --git a/config/ags/modules/.widgethacks/popupwindow.js b/config/ags/modules/.widgethacks/popupwindow.js new file mode 100644 index 0000000..26dad59 --- /dev/null +++ b/config/ags/modules/.widgethacks/popupwindow.js @@ -0,0 +1,32 @@ +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +const { Box, Window } = Widget; + + +export default ({ + name, + child, + showClassName = "", + hideClassName = "", + ...props +}) => { + return Window({ + name, + visible: false, + layer: 'overlay', + ...props, + + child: Box({ + setup: (self) => { + self.hook(App, (self, currentName, visible) => { + if (currentName === name) { + self.toggleClassName(hideClassName, !visible); + } + }).keybind("Escape", () => App.closeWindow(name)) + if (showClassName !== "" && hideClassName !== "") + self.className = `${showClassName} ${hideClassName}`; + }, + child: child, + }), + }); +} \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/clickthrough.js b/config/ags/modules/.widgetutils/clickthrough.js new file mode 100644 index 0000000..505f141 --- /dev/null +++ b/config/ags/modules/.widgetutils/clickthrough.js @@ -0,0 +1,4 @@ +import Cairo from 'gi://cairo?version=1.0'; + +export const dummyRegion = new Cairo.Region(); +export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/cursorhover.js b/config/ags/modules/.widgetutils/cursorhover.js new file mode 100644 index 0000000..89be913 --- /dev/null +++ b/config/ags/modules/.widgetutils/cursorhover.js @@ -0,0 +1,57 @@ +const { Gdk } = imports.gi; + +export function setupCursorHover(button) { // Hand pointing cursor on hover + const display = Gdk.Display.get_default(); + button.connect('enter-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'pointer'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); + +} + +export function setupCursorHoverAim(button) { // Crosshair cursor on hover + button.connect('enter-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'crosshair'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + +export function setupCursorHoverGrab(button) { // Hand ready to grab on hover + button.connect('enter-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'grab'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + +export function setupCursorHoverInfo(button) { // "?" mark cursor on hover + const display = Gdk.Display.get_default(); + button.connect('enter-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'help'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + diff --git a/config/ags/modules/.widgetutils/keybind.js b/config/ags/modules/.widgetutils/keybind.js new file mode 100644 index 0000000..eda7877 --- /dev/null +++ b/config/ags/modules/.widgetutils/keybind.js @@ -0,0 +1,25 @@ +const { Gdk } = imports.gi; + +const MODS = { + 'Shift': Gdk.ModifierType.SHIFT_MASK, + 'Ctrl': Gdk.ModifierType.CONTROL_MASK, + 'Alt': Gdk.ModifierType.ALT_MASK, + 'Hyper': Gdk.ModifierType.HYPER_MASK, + 'Meta': Gdk.ModifierType.META_MASK +} + +export const checkKeybind = (event, keybind) => { + const pressedModMask = event.get_state()[1]; + const pressedKey = event.get_keyval()[1]; + const keys = keybind.split('+'); + for (let i = 0; i < keys.length; i++) { + if (keys[i] in MODS) { + if (!(pressedModMask & MODS[keys[i]])) { + return false; + } + } else if (pressedKey !== Gdk[`KEY_${keys[i]}`]) { + return false; + } + } + return true; +} diff --git a/config/ags/modules/overview/actions.js b/config/ags/modules/overview/actions.js new file mode 100644 index 0000000..766cf45 --- /dev/null +++ b/config/ags/modules/overview/actions.js @@ -0,0 +1,28 @@ +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + +function moveClientToWorkspace(address, workspace) { + Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]); +} + +export function dumpToWorkspace(from, to) { + if (from == to) return; + Hyprland.clients.forEach(client => { + if (client.workspace.id == from) { + moveClientToWorkspace(client.address, to); + } + }); +} + +export function swapWorkspace(workspaceA, workspaceB) { + if (workspaceA == workspaceB) return; + const clientsA = []; + const clientsB = []; + Hyprland.clients.forEach(client => { + if (client.workspace.id == workspaceA) clientsA.push(client.address); + if (client.workspace.id == workspaceB) clientsB.push(client.address); + }); + + clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB)); + clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA)); +} \ No newline at end of file diff --git a/config/ags/modules/overview/main.js b/config/ags/modules/overview/main.js new file mode 100644 index 0000000..1f5348d --- /dev/null +++ b/config/ags/modules/overview/main.js @@ -0,0 +1,18 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import { SearchAndWindows } from "./windowcontent.js"; +import PopupWindow from '../.widgethacks/popupwindow.js'; + +export default (id = '') => PopupWindow({ + name: `overview${id}`, + exclusivity: 'ignore', + keymode: 'exclusive', + visible: false, + // anchor: ['middle'], + layer: 'overlay', + child: Widget.Box({ + vertical: true, + children: [ + SearchAndWindows(), + ] + }), +}) diff --git a/config/ags/modules/overview/miscfunctions.js b/config/ags/modules/overview/miscfunctions.js new file mode 100644 index 0000000..187ee6e --- /dev/null +++ b/config/ags/modules/overview/miscfunctions.js @@ -0,0 +1,155 @@ +const { Gio, GLib } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; +// import Todo from "../../services/todo.js"; +import { darkMode } from '../.miscutils/system.js'; + +export function hasUnterminatedBackslash(inputString) { + // Use a regular expression to match a trailing odd number of backslashes + const regex = /\\+$/; + return regex.test(inputString); +} + +export function launchCustomCommand(command) { + const args = command.toLowerCase().split(' '); + if (args[0] == '>raw') { // Mouse raw input + Utils.execAsync('hyprctl -j getoption input:accel_profile') + .then((output) => { + const value = JSON.parse(output)["str"].trim(); + if (value != "[[EMPTY]]" && value != "") { + execAsync(['bash', '-c', `hyprctl keyword input:accel_profile '[[EMPTY]]'`]).catch(print); + } + else { + execAsync(['bash', '-c', `hyprctl keyword input:accel_profile flat`]).catch(print); + } + }) + } + else if (args[0] == '>img') { // Change wallpaper + execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchwall.sh`, `&`]).catch(print); + } + else if (args[0] == '>color') { // Generate colorscheme from color picker + execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh --pick`, `&`]).catch(print); + } + else if (args[0] == '>light') { // Light mode + darkMode.value = false; + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/light/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>dark') { // Dark mode + darkMode.value = true; + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/dark/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>badapple') { // Black and white + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "3s/.*/monochrome/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>material') { // Use material colors + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "material" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) + .catch(print); + } + else if (args[0] == '>pywal') { // Use Pywal (ik it looks shit but I'm not removing) + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "pywal" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) + .catch(print); + } + else if (args[0] == '>todo') { // Todo + Todo.add(args.slice(1).join(' ')); + } + else if (args[0] == '>shutdown') { // Shut down + execAsync([`bash`, `-c`, `systemctl poweroff || loginctl poweroff`]).catch(print); + } + else if (args[0] == '>reboot') { // Reboot + execAsync([`bash`, `-c`, `systemctl reboot || loginctl reboot`]).catch(print); + } + else if (args[0] == '>sleep') { // Sleep + execAsync([`bash`, `-c`, `systemctl suspend || loginctl suspend`]).catch(print); + } + else if (args[0] == '>logout') { // Log out + execAsync([`bash`, `-c`, `pkill Hyprland || pkill sway`]).catch(print); + } +} + +export function execAndClose(command, terminal) { + App.closeWindow('overview'); + if (terminal) { + execAsync([`bash`, `-c`, `${userOptions.apps.terminal} fish -C "${command}"`, `&`]).catch(print); + } + else + execAsync(command).catch(print); +} + +export function couldBeMath(str) { + const regex = /^[0-9.+*/-]/; + return regex.test(str); +} + +export function expandTilde(path) { + if (path.startsWith('~')) { + return GLib.get_home_dir() + path.slice(1); + } else { + return path; + } +} + +function getFileIcon(fileInfo) { + let icon = fileInfo.get_icon(); + if (icon) { + // Get the icon's name + return icon.get_names()[0]; + } else { + // Default icon for files + return 'text-x-generic'; + } +} + +export function ls({ path = '~', silent = false }) { + let contents = []; + try { + let expandedPath = expandTilde(path); + if (expandedPath.endsWith('/')) + expandedPath = expandedPath.slice(0, -1); + let folder = Gio.File.new_for_path(expandedPath); + + let enumerator = folder.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); + let fileInfo; + while ((fileInfo = enumerator.next_file(null)) !== null) { + let fileName = fileInfo.get_display_name(); + let fileType = fileInfo.get_file_type(); + + let item = { + parentPath: expandedPath, + name: fileName, + type: fileType === Gio.FileType.DIRECTORY ? 'folder' : 'file', + icon: getFileIcon(fileInfo), + }; + + // Add file extension for files + if (fileType === Gio.FileType.REGULAR) { + let fileExtension = fileName.split('.').pop(); + item.type = `${fileExtension}`; + } + + contents.push(item); + contents.sort((a, b) => { + const aIsFolder = a.type.startsWith('folder'); + const bIsFolder = b.type.startsWith('folder'); + if (aIsFolder && !bIsFolder) { + return -1; + } else if (!aIsFolder && bIsFolder) { + return 1; + } else { + return a.name.localeCompare(b.name); // Sort alphabetically within folders and files + } + }); + } + } catch (e) { + if (!silent) console.log(e); + } + return contents; +} diff --git a/config/ags/modules/overview/overview_hyprland.js b/config/ags/modules/overview/overview_hyprland.js new file mode 100644 index 0000000..7a5b55c --- /dev/null +++ b/config/ags/modules/overview/overview_hyprland.js @@ -0,0 +1,423 @@ +// TODO +// - Make client destroy/create not destroy and recreate the whole thing +// - Active ws hook optimization: only update when moving to next group +// +const { Gdk, Gtk } = imports.gi; +const { Gravity } = imports.gi.Gdk; +import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../variables.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +const { execAsync, exec } = Utils; +import { setupCursorHoverGrab } from '../.widgetutils/cursorhover.js'; +import { dumpToWorkspace, swapWorkspace } from "./actions.js"; +import { substitute } from "../.miscutils/icons.js"; + +const NUM_OF_WORKSPACES_SHOWN = userOptions.overview.numOfCols * userOptions.overview.numOfRows; +const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; +const POPUP_CLOSE_TIME = 100; // ms + +const overviewTick = Variable(false); + +export default () => { + const clientMap = new Map(); + let workspaceGroup = 0; + const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({ + label: `${label}`, + setup: (menuItem) => { + let submenu = new Gtk.Menu(); + submenu.className = 'menu'; + + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + const startWorkspace = offset + 1; + const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1; + for (let i = startWorkspace; i <= endWorkspace; i++) { + let button = new Gtk.MenuItem({ + label: `Workspace ${i}` + }); + button.connect("activate", () => { + // execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print); + actionFunc(thisWorkspace, i); + overviewTick.setValue(!overviewTick.value); + }); + submenu.append(button); + } + menuItem.set_reserve_indicator(true); + menuItem.set_submenu(submenu); + } + }) + + const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, title, xwayland }, screenCoords) => { + const revealInfoCondition = (Math.min(w, h) * userOptions.overview.scale > 70); + if (w <= 0 || h <= 0 || (c === '' && title === '')) return null; + // Non-primary monitors + if (screenCoords.x != 0) x -= screenCoords.x; + if (screenCoords.y != 0) y -= screenCoords.y; + // Other offscreen adjustments + if (x + w <= 0) x += (Math.floor(x / SCREEN_WIDTH) * SCREEN_WIDTH); + else if (x < 0) { w = x + w; x = 0; } + if (y + h <= 0) x += (Math.floor(y / SCREEN_HEIGHT) * SCREEN_HEIGHT); + else if (y < 0) { h = y + h; y = 0; } + // Truncate if offscreen + if (x + w > SCREEN_WIDTH) w = SCREEN_WIDTH - x; + if (y + h > SCREEN_HEIGHT) h = SCREEN_HEIGHT - y; + + const appIcon = Widget.Icon({ + icon: substitute(c), + size: Math.min(w, h) * userOptions.overview.scale / 2.5, + }); + return Widget.Button({ + attribute: { + address, x, y, w, h, ws: id, + updateIconSize: (self) => { + appIcon.size = Math.min(self.attribute.w, self.attribute.h) * userOptions.overview.scale / 2.5; + }, + }, + className: 'overview-tasks-window', + hpack: 'start', + vpack: 'start', + css: ` + margin-left: ${Math.round(x * userOptions.overview.scale)}px; + margin-top: ${Math.round(y * userOptions.overview.scale)}px; + margin-right: -${Math.round((x + w) * userOptions.overview.scale)}px; + margin-bottom: -${Math.round((y + h) * userOptions.overview.scale)}px; + `, + onClicked: (self) => { + App.closeWindow('overview'); + Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch focuswindow address:${address}`)); + }, + onMiddleClickRelease: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), + onSecondaryClick: (button) => { + button.toggleClassName('overview-tasks-window-selected', true); + const menu = Widget.Menu({ + className: 'menu', + children: [ + Widget.MenuItem({ + child: Widget.Label({ + xalign: 0, + label: "Close (Middle-click)", + }), + onActivate: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), + }), + ContextMenuWorkspaceArray({ + label: "Dump windows to workspace", + actionFunc: dumpToWorkspace, + thisWorkspace: Number(id) + }), + ContextMenuWorkspaceArray({ + label: "Swap windows with workspace", + actionFunc: swapWorkspace, + thisWorkspace: Number(id) + }), + ], + }); + menu.connect("deactivate", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.connect("selection-done", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.popup_at_widget(button.get_parent(), Gravity.SOUTH, Gravity.NORTH, null); // Show menu below the button + button.connect("destroy", () => menu.destroy()); + }, + child: Widget.Box({ + homogeneous: true, + child: Widget.Box({ + vertical: true, + vpack: 'center', + className: 'spacing-v-5', + children: [ + appIcon, + // TODO: Add xwayland tag instead of just having italics + Widget.Revealer({ + transition: 'slide_down', + revealChild: revealInfoCondition, + child: Widget.Label({ + maxWidthChars: 10, // Doesn't matter what number + truncate: 'end', + className: `${xwayland ? 'txt txt-italic' : 'txt'}`, + css: ` + font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 14.6}px; + margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 10}px; + `, + // If the title is too short, include the class + label: (title.length <= 1 ? `${c}: ${title}` : title), + }) + }) + ] + }) + }), + tooltipText: `${c}: ${title}`, + setup: (button) => { + setupCursorHoverGrab(button); + + button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE); + button.drag_source_set_icon_name(substitute(c)); + // button.drag_source_set_icon_gicon(icon); + + button.connect('drag-begin', (button) => { // On drag start, add the dragging class + button.toggleClassName('overview-tasks-window-dragging', true); + }); + button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address + data.set_text(address, address.length); + button.toggleClassName('overview-tasks-window-dragging', false); + }); + }, + }); + } + + const Workspace = (index) => { + // const fixed = Widget.Fixed({ + // attribute: { + // put: (widget, x, y) => { + // fixed.put(widget, x, y); + // }, + // move: (widget, x, y) => { + // fixed.move(widget, x, y); + // }, + // } + // }); + const fixed = Widget.Box({ + attribute: { + put: (widget, x, y) => { + if (!widget.attribute) return; + // Note: x and y are already multiplied by userOptions.overview.scale + const newCss = ` + margin-left: ${Math.round(x)}px; + margin-top: ${Math.round(y)}px; + margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; + margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; + `; + widget.css = newCss; + fixed.pack_start(widget, false, false, 0); + }, + move: (widget, x, y) => { + if (!widget) return; + if (!widget.attribute) return; + // Note: x and y are already multiplied by userOptions.overview.scale + const newCss = ` + margin-left: ${Math.round(x)}px; + margin-top: ${Math.round(y)}px; + margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; + margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; + `; + widget.css = newCss; + }, + } + }) + const WorkspaceNumber = ({ index, ...rest }) => Widget.Label({ + className: 'overview-tasks-workspace-number', + label: `${index}`, + css: ` + margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale * userOptions.overview.wsNumMarginScale}px; + font-size: ${SCREEN_HEIGHT * userOptions.overview.scale * userOptions.overview.wsNumScale}px; + `, + setup: (self) => self.hook(Hyprland.active.workspace, (self) => { + // Update when going to new ws group + const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); + self.label = `${currentGroup * NUM_OF_WORKSPACES_SHOWN + index}`; + }), + ...rest, + }) + const widget = Widget.Box({ + className: 'overview-tasks-workspace', + vpack: 'center', + css: ` + min-width: ${SCREEN_WIDTH * userOptions.overview.scale}px; + min-height: ${SCREEN_HEIGHT * userOptions.overview.scale}px; + `, + children: [Widget.EventBox({ + hexpand: true, + vexpand: true, + onPrimaryClick: () => { + App.closeWindow('overview'); + Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch workspace ${index}`)); + }, + setup: (eventbox) => { + eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); + eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + Hyprland.messageAsync(`dispatch movetoworkspacesilent ${index + offset},address:${data.get_text()}`) + overviewTick.setValue(!overviewTick.value); + }); + }, + child: Widget.Overlay({ + child: Widget.Box({}), + overlays: [ + WorkspaceNumber({ index: index, hpack: 'start', vpack: 'start' }), + fixed + ] + }), + })], + }); + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + fixed.attribute.put(WorkspaceNumber(offset + index), 0, 0); + widget.clear = () => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + clientMap.forEach((client, address) => { + if (!client) return; + if ((client.attribute.ws <= offset || client.attribute.ws > offset + NUM_OF_WORKSPACES_SHOWN) || + (client.attribute.ws == offset + index)) { + client.destroy(); + client = null; + clientMap.delete(address); + } + }); + } + widget.set = (clientJson, screenCoords) => { + let c = clientMap.get(clientJson.address); + if (c) { + if (c.attribute?.ws !== clientJson.workspace.id) { + c.destroy(); + c = null; + clientMap.delete(clientJson.address); + } + else if (c) { + c.attribute.w = clientJson.size[0]; + c.attribute.h = clientJson.size[1]; + c.attribute.updateIconSize(c); + fixed.attribute.move(c, + Math.max(0, clientJson.at[0] * userOptions.overview.scale), + Math.max(0, clientJson.at[1] * userOptions.overview.scale) + ); + return; + } + } + const newWindow = Window(clientJson, screenCoords); + if (newWindow === null) return; + // clientMap.set(clientJson.address, newWindow); + fixed.attribute.put(newWindow, + Math.max(0, newWindow.attribute.x * userOptions.overview.scale), + Math.max(0, newWindow.attribute.y * userOptions.overview.scale) + ); + clientMap.set(clientJson.address, newWindow); + }; + widget.unset = (clientAddress) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + let c = clientMap.get(clientAddress); + if (!c) return; + c.destroy(); + c = null; + clientMap.delete(clientAddress); + }; + widget.show = () => { + fixed.show_all(); + } + return widget; + }; + + const arr = (s, n) => { + const array = []; + for (let i = 0; i < n; i++) + array.push(s + i); + + return array; + }; + + const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({ + children: arr(startWorkspace, workspaces).map(Workspace), + attribute: { + monitorMap: [], + getMonitorMap: (box) => { + execAsync('hyprctl -j monitors').then(monitors => { + box.attribute.monitorMap = JSON.parse(monitors).reduce((acc, item) => { + acc[item.id] = { x: item.x, y: item.y }; + return acc; + }, {}); + }); + }, + update: (box) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + if (!App.getWindow(windowName).visible) return; + Hyprland.messageAsync('j/clients').then(clients => { + const allClients = JSON.parse(clients); + const kids = box.get_children(); + kids.forEach(kid => kid.clear()); + for (let i = 0; i < allClients.length; i++) { + const client = allClients[i]; + const childID = client.workspace.id - (offset + startWorkspace); + if (offset + startWorkspace <= client.workspace.id && + client.workspace.id <= offset + startWorkspace + workspaces) { + const screenCoords = box.attribute.monitorMap[client.monitor]; + if (kids[childID]) { + kids[childID].set(client, screenCoords); + } + continue; + } + } + kids.forEach(kid => kid.show()); + }).catch(print); + }, + updateWorkspace: (box, id) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + if (!( // Not in range, ignore + offset + startWorkspace <= id && + id <= offset + startWorkspace + workspaces + )) return; + // if (!App.getWindow(windowName).visible) return; + Hyprland.messageAsync('j/clients').then(clients => { + const allClients = JSON.parse(clients); + const kids = box.get_children(); + for (let i = 0; i < allClients.length; i++) { + const client = allClients[i]; + if (client.workspace.id != id) continue; + const screenCoords = box.attribute.monitorMap[client.monitor]; + kids[id - (offset + startWorkspace)]?.set(client, screenCoords); + } + kids[id - (offset + startWorkspace)]?.show(); + }).catch(print); + }, + }, + setup: (box) => { + box.attribute.getMonitorMap(box); + box + .hook(overviewTick, (box) => box.attribute.update(box)) + .hook(Hyprland, (box, clientAddress) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + const kids = box.get_children(); + const client = Hyprland.getClient(clientAddress); + if (!client) return; + const id = client.workspace.id; + + box.attribute.updateWorkspace(box, id); + kids[id - (offset + startWorkspace)]?.unset(clientAddress); + }, 'client-removed') + .hook(Hyprland, (box, clientAddress) => { + const client = Hyprland.getClient(clientAddress); + if (!client) return; + box.attribute.updateWorkspace(box, client.workspace.id); + }, 'client-added') + .hook(Hyprland.active.workspace, (box) => { + // Full update when going to new ws group + const previousGroup = box.attribute.workspaceGroup; + const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); + if (currentGroup !== previousGroup) { + box.attribute.update(box); + box.attribute.workspaceGroup = currentGroup; + } + }) + .hook(App, (box, name, visible) => { // Update on open + if (name == 'overview' && visible) box.attribute.update(box); + }) + }, + }); + + return Widget.Revealer({ + revealChild: true, + transition: 'slide_down', + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Box({ + vertical: true, + className: 'overview-tasks', + children: Array.from({ length: userOptions.overview.numOfRows }, (_, index) => + OverviewRow({ + startWorkspace: 1 + index * userOptions.overview.numOfCols, + workspaces: userOptions.overview.numOfCols, + }) + ) + }), + }); +} \ No newline at end of file diff --git a/config/ags/modules/overview/searchbuttons.js b/config/ags/modules/overview/searchbuttons.js new file mode 100644 index 0000000..f5892f3 --- /dev/null +++ b/config/ags/modules/overview/searchbuttons.js @@ -0,0 +1,163 @@ +const { Gtk } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; +import { searchItem } from './searchitem.js'; +import { execAndClose, couldBeMath, launchCustomCommand } from './miscfunctions.js'; + +export const DirectoryButton = ({ parentPath, name, type, icon }) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: 'Open', + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }); + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: () => { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${parentPath}/${name}'`, `&`]).catch(print); + }, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Box({ + className: 'overview-search-results-icon', + homogeneous: true, + child: Widget.Icon({ + icon: icon, + }), + }), + Widget.Label({ + className: 'overview-search-results-txt txt txt-norm', + label: name, + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ] + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }) +} + +export const CalculationResultButton = ({ result, text }) => searchItem({ + materialIconName: '󱖦 ', + name: `Math result`, + actionName: "Copy", + content: `${result}`, + onActivate: () => { + App.closeWindow('overview'); + execAsync(['wl-copy', `${result}`]).catch(print); + }, +}); + +export const DesktopEntryButton = (app) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: 'Launch', + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }); + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: () => { + App.closeWindow('overview'); + app.launch(); + }, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Box({ + className: 'overview-search-results-icon', + homogeneous: true, + child: Widget.Icon({ + icon: app.iconName, + }), + }), + Widget.Label({ + className: 'overview-search-results-txt txt txt-norm', + label: app.name, + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ] + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }) +} + +export const ExecuteCommandButton = ({ command, terminal = false }) => searchItem({ + materialIconName: `${terminal ? 'terminal' : ' '}`, + name: `Run command`, + actionName: `Execute ${terminal ? 'in terminal' : ''}`, + content: `${command}`, + onActivate: () => execAndClose(command, terminal), + extraClassName: 'techfont', +}) + +export const CustomCommandButton = ({ text = '' }) => searchItem({ + materialIconName: ' ', + name: 'Action', + actionName: 'Run', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + launchCustomCommand(text); + }, +}); + +export const SearchButton = ({ text = '' }) => searchItem({ + materialIconName: '󰜏 ', + name: 'Search the web', + actionName: 'Go', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); + }, +}); \ No newline at end of file diff --git a/config/ags/modules/overview/searchitem.js b/config/ags/modules/overview/searchitem.js new file mode 100644 index 0000000..2a3303a --- /dev/null +++ b/config/ags/modules/overview/searchitem.js @@ -0,0 +1,65 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; + +export const searchItem = ({ materialIconName, name, actionName, content, onActivate, extraClassName = '', ...rest }) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: `${actionName}`, + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }) + return Widget.Button({ + className: `overview-search-result-btn txt ${extraClassName}`, + onClicked: onActivate, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Label({ + className: `icon-material overview-search-results-icon`, + label: `${materialIconName}`, + }), + Widget.Box({ + vertical: true, + children: [ + Widget.Label({ + hpack: 'start', + className: 'overview-search-results-txt txt-smallie txt-subtext', + label: `${name}`, + truncate: "end", + }), + Widget.Label({ + hpack: 'start', + className: 'overview-search-results-txt txt-norm', + label: `${content}`, + truncate: "end", + }), + ] + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ], + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }); +} diff --git a/config/ags/modules/overview/windowcontent.js b/config/ags/modules/overview/windowcontent.js new file mode 100644 index 0000000..7a19dd3 --- /dev/null +++ b/config/ags/modules/overview/windowcontent.js @@ -0,0 +1,262 @@ +const { Gdk, Gtk } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; +const { execAsync, exec } = Utils; +import { execAndClose, expandTilde, hasUnterminatedBackslash, couldBeMath, launchCustomCommand, ls } from './miscfunctions.js'; +import { + CalculationResultButton, CustomCommandButton, DirectoryButton, + DesktopEntryButton, ExecuteCommandButton, SearchButton +} from './searchbuttons.js'; +import { checkKeybind } from '../.widgetutils/keybind.js'; + +// Add math funcs +const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math; +const pi = Math.PI; +// trigonometric funcs for deg +const sind = x => sin(x * pi / 180); +const cosd = x => cos(x * pi / 180); +const tand = x => tan(x * pi / 180); +const cotd = x => cot(x * pi / 180); +const asind = x => asin(x) * 180 / pi; +const acosd = x => acos(x) * 180 / pi; +const atand = x => atan(x) * 180 / pi; +const acotd = x => acot(x) * 180 / pi; + +const MAX_RESULTS = 10; +const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size +const OVERVIEW_WS_NUM_SCALE = 0.0; +const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07; +const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; + +function iconExists(iconName) { + let iconTheme = Gtk.IconTheme.get_default(); + return iconTheme.has_icon(iconName); +} + +const OptionalOverview = async () => { + try { + return (await import('./overview_hyprland.js')).default(); + } catch { + return Widget.Box({}); + // return (await import('./overview_hyprland.js')).default(); + } +}; + +const overviewContent = await OptionalOverview(); + +export const SearchAndWindows = () => { + var _appSearchResults = []; + + const ClickToClose = ({ ...props }) => Widget.EventBox({ + ...props, + onPrimaryClick: () => App.closeWindow('overview'), + onSecondaryClick: () => App.closeWindow('overview'), + onMiddleClick: () => App.closeWindow('overview'), + }); + const resultsBox = Widget.Box({ + className: 'overview-search-results', + vertical: true, + vexpand: true, + }); + const resultsRevealer = Widget.Revealer({ + transitionDuration: userOptions.animations.durationLarge, + revealChild: false, + transition: 'slide_down', + // duration: 200, + hpack: 'center', + child: resultsBox, + }); + const entryPromptRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: userOptions.animations.durationLarge, + revealChild: true, + hpack: 'center', + child: Widget.Label({ + className: 'overview-search-prompt txt-small txt', + label: 'Type to search' + }), + }); + + const entryIconRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: userOptions.animations.durationLarge, + revealChild: false, + hpack: 'end', + child: Widget.Label({ + className: 'txt txt-large icon-material overview-search-icon', + label: ' ', + }), + }); + + const entryIcon = Widget.Box({ + className: 'overview-search-prompt-box', + setup: box => box.pack_start(entryIconRevealer, true, true, 0), + }); + + const entry = Widget.Entry({ + className: 'overview-search-box txt-small txt', + hpack: 'center', + onAccept: (self) => { // This is when you hit Enter + const text = self.text; + if (text.length == 0) return; + const isAction = text.startsWith('>'); + const isDir = (['/', '~'].includes(entry.text[0])); + + if (couldBeMath(text)) { // Eval on typing is dangerous, this is a workaround + try { + const fullResult = eval(text.replace(/\^/g, "**")); + // copy + execAsync(['wl-copy', `${fullResult}`]).catch(print); + App.closeWindow('overview'); + return; + } catch (e) { + // console.log(e); + } + } + if (isDir) { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print); + return; + } + if (_appSearchResults.length > 0) { + App.closeWindow('overview'); + _appSearchResults[0].launch(); + return; + } + else if (text[0] == '>') { // Custom commands + App.closeWindow('overview'); + launchCustomCommand(text); + return; + } + // Fallback: Execute command + if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + if (text.startsWith('sudo')) + execAndClose(text, true); + else + execAndClose(text, false); + } + + else { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); + } + }, + onChange: (entry) => { // this is when you type + const isAction = entry.text[0] == '>'; + const isDir = (['/', '~'].includes(entry.text[0])); + resultsBox.get_children().forEach(ch => ch.destroy()); + + // check empty if so then dont do stuff + if (entry.text == '') { + resultsRevealer.revealChild = false; + overviewContent.revealChild = true; + entryPromptRevealer.revealChild = true; + entryIconRevealer.revealChild = false; + entry.toggleClassName('overview-search-box-extended', false); + return; + } + const text = entry.text; + resultsRevealer.revealChild = true; + overviewContent.revealChild = false; + entryPromptRevealer.revealChild = false; + entryIconRevealer.revealChild = true; + entry.toggleClassName('overview-search-box-extended', true); + _appSearchResults = Applications.query(text); + + // Calculate + if (couldBeMath(text)) { // Eval on typing is dangerous; this is a small workaround. + try { + const fullResult = eval(text.replace(/\^/g, "**")); + resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); + } catch (e) { + // console.log(e); + } + } + if (isDir) { + var contents = []; + contents = ls({ path: text, silent: true }); + contents.forEach((item) => { + resultsBox.add(DirectoryButton(item)); + }) + } + if (isAction) { // Eval on typing is dangerous, this is a workaround. + resultsBox.add(CustomCommandButton({ text: entry.text })); + } + // Add application entries + let appsToAdd = MAX_RESULTS; + _appSearchResults.forEach(app => { + if (appsToAdd == 0) return; + resultsBox.add(DesktopEntryButton(app)); + appsToAdd--; + }); + + // Fallbacks + // if the first word is an actual command + if (!isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') })); + } + + // Add fallback: search + resultsBox.add(SearchButton({ text: entry.text })); + resultsBox.show_all(); + }, + }); + return Widget.Box({ + vertical: true, + children: [ + ClickToClose({ // Top margin. Also works as a click-outside-to-close thing + child: Widget.Box({ + className: 'bar-height', + }) + }), + Widget.Box({ + hpack: 'center', + children: [ + entry, + Widget.Box({ + className: 'overview-search-icon-box', + setup: (box) => { + box.pack_start(entryPromptRevealer, true, true, 0) + }, + }), + entryIcon, + ] + }), + overviewContent, + resultsRevealer, + ], + setup: (self) => self + .hook(App, (_b, name, visible) => { + if (name == 'overview' && !visible) { + resultsBox.children = []; + entry.set_text(''); + } + }) + .on('key-press-event', (widget, event) => { // Typing + const keyval = event.get_keyval()[1]; + const modstate = event.get_state()[1]; + if (checkKeybind(event, userOptions.keybinds.overview.altMoveLeft)) + entry.set_position(Math.max(entry.get_position() - 1, 0)); + else if (checkKeybind(event, userOptions.keybinds.overview.altMoveRight)) + entry.set_position(Math.min(entry.get_position() + 1, entry.get_text().length)); + else if (checkKeybind(event, userOptions.keybinds.overview.deleteToEnd)) { + const text = entry.get_text(); + const pos = entry.get_position(); + const newText = text.slice(0, pos); + entry.set_text(newText); + entry.set_position(newText.length); + } + else if (!(modstate & Gdk.ModifierType.CONTROL_MASK)) { // Ctrl not held + if (keyval >= 32 && keyval <= 126 && widget != entry) { + Utils.timeout(1, () => entry.grab_focus()); + entry.set_text(entry.text + String.fromCharCode(keyval)); + entry.set_position(-1); + } + } + }) + , + }); +}; diff --git a/config/ags/user/style.css b/config/ags/user/style.css new file mode 100644 index 0000000..7941573 --- /dev/null +++ b/config/ags/user/style.css @@ -0,0 +1,190 @@ +*:not(popover) { + all: unset; +} + +@import '../../../.cache/wal/colors-waybar.css'; +/* @import '../../../.cache/wal/colors-waybar-rgba.css'; */ + + +widget { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; +} + +.overview-window { + margin-top: 2.727rem; +} + +.overview-search-box { + transition: 300ms cubic-bezier(0, 0.55, 0.45, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid @color7; + border-left: 1px solid @color7; + border-right: 1px solid @color7; + border-bottom: 1px solid @color7; + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 0.476rem; + min-width: 13.636rem; + min-height: 3.409rem; + padding: 0rem 1.364rem; + padding-right: 2.864rem; + background-color: alpha(@color0, 0.5); + /* color: #EBDFED; */ + color: alpha(@color7, 0.9); + caret-color: transparent; +} +.overview-search-box selection { + background-color: #DEBCDF; + color: #402843; +} + +.overview-search-box-extended { + min-width: 25.909rem; + caret-color: #FDD9FD; +} + +.overview-search-prompt { + /* color: #988D9D; */ + color: alpha(@color7, 0.9); +} + +.overview-search-icon { + margin: 0rem 1.023rem; +} + +.overview-search-prompt-box { + margin-left: -18.545rem; + margin-right: 0.544rem; +} + +.overview-search-icon-box { + margin-left: -18.545rem; + margin-right: 0.544rem; +} + +.overview-search-results { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid @color7; + border-left: 1px solid @color7; + border-right: 1px solid @color7; + border-bottom: 1px solid @color7; + box-shadow: 0px 2px 3px @color8; + margin: 0.476rem; + min-width: 28.773rem; + padding: 0.682rem; + background-color: @color2; + color: #EBDFED; +} + +.overview-search-results-icon { + margin: 0rem 0.682rem; + font-size: 2.386rem; + min-width: 2.386rem; + min-height: 2.386rem; +} + +.overview-search-results-txt { + margin-right: 0.682rem; +} + +.overview-search-results-txt-cmd { + margin-right: 0.682rem; + font-family: "JetBrains Mono NF", "JetBrains Mono Nerd Font", "JetBrains Mono NL", "SpaceMono NF", "SpaceMono Nerd Font", monospace; + font-size: 1.227rem; +} + +.overview-search-result-btn { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + padding: 0.341rem; + min-width: 2.386rem; + min-height: 2.386rem; + caret-color: transparent; +} + +.overview-search-result-btn:hover, +.overview-search-result-btn:focus { + background-color: #2E2832; +} + +.overview-search-result-btn:active { + background-color: #39323D; +} + +.overview-tasks { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid @color7; + border-left: 1px solid @color7; + border-right: 1px solid @color7; + border-bottom: 1px solid @color7; + box-shadow: 0px 2px 3px @color5; + margin: 0.476rem; + padding: 0.341rem; + /* background-color: rgba(49, 50, 68, 0.8); */ + background-color: alpha(@color9, 0.4); + color: #EBDFED; +} + +.overview-tasks-workspace { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + margin: 0.341rem; + /* background-color: #26233A; */ + background-image: url('../../rofi/.current_wallpaper'); + background-size: cover; + background-position: center; + border: 0.068rem solid alpha(@color4, 0.5); +} + +.overview-tasks-workspace-number { + font-family: "Open Sans", "Noto Sans", sans-serif; + color: #CFC2D3; +} + +.overview-tasks-window { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + transition: 300ms cubic-bezier(0.1, 1, 0, 1); + background-color: alpha(@color3, .7); + /* background-color: @color_a3; */ + /* background-color: rgba(46, 40, 50, 0.8); */ + color: #EBDFED; + border: 0.068rem solid @color7; +} + +.overview-tasks-window:hover, +.overview-tasks-window:focus { + background-color: @color9; +} + +.overview-tasks-window:active { + background-color: @color9; +} + +.overview-tasks-window-selected { + background-color: @color9; +} + +.overview-tasks-window-dragging { + opacity: 0.2; +} + +.growingRadial { + transition: 300ms cubic-bezier(0.2, 0, 0, 1); +} + +.fadingRadial { + transition: 50ms cubic-bezier(0.2, 0, 0, 1); +} + +.sidebar-pinned { + margin: 0rem; + border-radius: 0rem; + border-bottom-right-radius: 1.705rem; + border: 0rem solid; +} + +/*# sourceMappingURL=style.css.map */ diff --git a/config/ags/user_options.js b/config/ags/user_options.js new file mode 100644 index 0000000..9105dec --- /dev/null +++ b/config/ags/user_options.js @@ -0,0 +1,21 @@ + +const userConfigOptions = { + // For every option, see ~/.config/ags/modules/.configuration/user_options.js + // (vscode users ctrl+click this: file://./modules/.configuration/user_options.js) + // (vim users: `:vsp` to split window, move cursor to this path, press `gf`. `Ctrl-w` twice to switch between) + // options listed in this file will override the default ones in the above file + // Here's an example + 'overview':{ + 'scale': 0.15, + 'numOfRows': 2 + }, + 'keybinds': { + 'sidebar': { + 'pin': "Ctrl+p", + 'nextTab': "Ctrl+Page_Down", + 'prevTab': "Ctrl+Page_Up", + }, + }, +} + +export default userConfigOptions; diff --git a/config/ags/variables.js b/config/ags/variables.js new file mode 100644 index 0000000..ec4aede --- /dev/null +++ b/config/ags/variables.js @@ -0,0 +1,21 @@ +const { Gtk } = imports.gi; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { exec, execAsync } = Utils; + +Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`); + +// Screen size +export const SCREEN_WIDTH = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f1 | head -1" | awk '{print $1}'`)); +export const SCREEN_HEIGHT = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1" | awk '{print $1}'`)); + +// Mode switching +export const currentShellMode = Variable('normal', {}) // normal, focus +globalThis['currentMode'] = currentShellMode; +globalThis['cycleMode'] = () => { + if (currentShellMode.value === 'normal') { + currentShellMode.value = 'focus'; + } else { + currentShellMode.value = 'normal'; + } +} diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf index ee387d8..c6b96ee 100644 --- a/config/hypr/UserConfigs/Startup_Apps.conf +++ b/config/hypr/UserConfigs/Startup_Apps.conf @@ -25,6 +25,7 @@ exec-once = nm-applet --indicator & exec-once = swaync & #exec-once = blueman-applet & #exec-once = rog-control-center & +exec-once = ags & #clipboard manager diff --git a/config/hypr/configs/Keybinds.conf b/config/hypr/configs/Keybinds.conf index 1946cc2..1cc8e2a 100644 --- a/config/hypr/configs/Keybinds.conf +++ b/config/hypr/configs/Keybinds.conf @@ -30,6 +30,7 @@ bind = $mainMod ALT, L, exec, $scriptsDir/ChangeLayout.sh # Toggle Master or Dwi bind = $mainMod ALT, V, exec, $scriptsDir/ClipManager.sh # Clipboard Manager bind = $mainMod SHIFT, N, exec, swaync-client -t -sw # swayNC panel bind = SHIFT, ALT_L, exec, $scriptsDir/SwitchKeyboardLayout.sh # Switch Keyboard Layout +bind = $mainMod, A, exec, ags -t 'overview' # FEATURES / EXTRAS (UserScripts) bind = $mainMod, E, exec, $UserScripts/QuickEdit.sh # Quick Edit Hyprland Settings diff --git a/config/hypr/scripts/Refresh.sh b/config/hypr/scripts/Refresh.sh index 15516df..bf806e0 100755 --- a/config/hypr/scripts/Refresh.sh +++ b/config/hypr/scripts/Refresh.sh @@ -15,13 +15,15 @@ file_exists() { } # Kill already running processes -_ps=(waybar rofi swaync) +_ps=(waybar rofi swaync ags) for _prs in "${_ps[@]}"; do if pidof "${_prs}" >/dev/null; then pkill "${_prs}" fi done +ags -q + sleep 0.3 # Relaunch waybar waybar & @@ -30,6 +32,9 @@ waybar & sleep 0.5 swaync > /dev/null 2>&1 & +# relaunch ags +ags & + # Relaunching rainbow borders if the script exists sleep 1 if file_exists "${UserScripts}/RainbowBorders.sh"; then From 6f5cd04f60601be888c09581210deba091f8949a Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sat, 4 May 2024 14:16:12 +0530 Subject: [PATCH 2/2] Improved colour scheme for light and dark wallpapers in overview search and added blur --- config/ags/user/style.css | 26 +++++++++++++----------- config/hypr/UserConfigs/WindowRules.conf | 3 +++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/config/ags/user/style.css b/config/ags/user/style.css index 7941573..b149668 100644 --- a/config/ags/user/style.css +++ b/config/ags/user/style.css @@ -23,16 +23,16 @@ widget { border-left: 1px solid @color7; border-right: 1px solid @color7; border-bottom: 1px solid @color7; - box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + /* box-shadow: 0px 2px 3px alpha(@color0, 0.45); */ margin: 0.476rem; min-width: 13.636rem; min-height: 3.409rem; padding: 0rem 1.364rem; padding-right: 2.864rem; background-color: alpha(@color0, 0.5); - /* color: #EBDFED; */ color: alpha(@color7, 0.9); caret-color: transparent; + font-weight: bold; } .overview-search-box selection { background-color: #DEBCDF; @@ -45,7 +45,6 @@ widget { } .overview-search-prompt { - /* color: #988D9D; */ color: alpha(@color7, 0.9); } @@ -70,12 +69,13 @@ widget { border-left: 1px solid @color7; border-right: 1px solid @color7; border-bottom: 1px solid @color7; - box-shadow: 0px 2px 3px @color8; + box-shadow: 0px 2px 3px @color9; margin: 0.476rem; min-width: 28.773rem; padding: 0.682rem; - background-color: @color2; - color: #EBDFED; + background-color: alpha(@color2, 0.5); + color: alpha(@color7, 1.5); + font-weight: bold; } .overview-search-results-icon { @@ -106,11 +106,13 @@ widget { .overview-search-result-btn:hover, .overview-search-result-btn:focus { - background-color: #2E2832; + background-color: alpha(@color7, 0.4); + color: alpha(@color0, 0.7); } .overview-search-result-btn:active { - background-color: #39323D; + background-color: alpha(@color7, 0.4); + color: @color4; } .overview-tasks { @@ -124,7 +126,7 @@ widget { margin: 0.476rem; padding: 0.341rem; /* background-color: rgba(49, 50, 68, 0.8); */ - background-color: alpha(@color9, 0.4); + background-color: alpha(@color0, 0.6); color: #EBDFED; } @@ -157,15 +159,15 @@ widget { .overview-tasks-window:hover, .overview-tasks-window:focus { - background-color: @color9; + background-color: alpha(@color9, 0.8); } .overview-tasks-window:active { - background-color: @color9; + background-color: alpha(@color9, 0.8); } .overview-tasks-window-selected { - background-color: @color9; + background-color: alpha(@color9, 0.8); } .overview-tasks-window-dragging { diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index 38d4122..0d87044 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -83,6 +83,9 @@ windowrulev2 = opacity 0.9 0.8, class:^(seahorse)$ # gnome-keyring gui #layerrule = blur,class:^([Rr]ofi)$ #layerrule = ignorezero, +layerrule = ignorezero, overview +layerrule = blur, overview + #windowrulev2 = bordercolor rgb(EE4B55) rgb(880808), fullscreen:1 #windowrulev2 = bordercolor rgb(282737) rgb(1E1D2D), floating:1 #windowrulev2 = opacity 0.8 0.8, pinned:1