21 changed files with 0 additions and 1777 deletions
@ -1,31 +0,0 @@ |
|||||||
"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), |
|
||||||
}); |
|
||||||
|
|
||||||
@ -1,121 +0,0 @@ |
|||||||
|
|
||||||
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; |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
|
|
||||||
export function clamp(x, min, max) { |
|
||||||
return Math.min(Math.max(x, min), max); |
|
||||||
} |
|
||||||
@ -1,54 +0,0 @@ |
|||||||
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'; |
|
||||||
} |
|
||||||
@ -1,86 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,32 +0,0 @@ |
|||||||
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, |
|
||||||
}), |
|
||||||
}); |
|
||||||
} |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
import Cairo from 'gi://cairo?version=1.0'; |
|
||||||
|
|
||||||
export const dummyRegion = new Cairo.Region(); |
|
||||||
export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); |
|
||||||
@ -1,57 +0,0 @@ |
|||||||
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); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@ -1,25 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,28 +0,0 @@ |
|||||||
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)); |
|
||||||
} |
|
||||||
@ -1,18 +0,0 @@ |
|||||||
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(), |
|
||||||
] |
|
||||||
}), |
|
||||||
}) |
|
||||||
@ -1,155 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,423 +0,0 @@ |
|||||||
// 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, |
|
||||||
}) |
|
||||||
) |
|
||||||
}), |
|
||||||
}); |
|
||||||
} |
|
||||||
@ -1,65 +0,0 @@ |
|||||||
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; |
|
||||||
}) |
|
||||||
, |
|
||||||
}); |
|
||||||
} |
|
||||||
@ -1,192 +0,0 @@ |
|||||||
*: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 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: alpha(@color7, 0.9); |
|
||||||
caret-color: transparent; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
.overview-search-box selection { |
|
||||||
background-color: #DEBCDF; |
|
||||||
color: #402843; |
|
||||||
} |
|
||||||
|
|
||||||
.overview-search-box-extended { |
|
||||||
min-width: 25.909rem; |
|
||||||
caret-color: #FDD9FD; |
|
||||||
} |
|
||||||
|
|
||||||
.overview-search-prompt { |
|
||||||
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 @color9; |
|
||||||
margin: 0.476rem; |
|
||||||
min-width: 28.773rem; |
|
||||||
padding: 0.682rem; |
|
||||||
background-color: alpha(@color2, 0.5); |
|
||||||
color: alpha(@color7, 1.5); |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
.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: alpha(@color7, 0.4); |
|
||||||
color: alpha(@color0, 0.7); |
|
||||||
} |
|
||||||
|
|
||||||
.overview-search-result-btn:active { |
|
||||||
background-color: alpha(@color7, 0.4); |
|
||||||
color: @color4; |
|
||||||
} |
|
||||||
|
|
||||||
.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(@color0, 0.6); |
|
||||||
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: alpha(@color9, 0.8); |
|
||||||
} |
|
||||||
|
|
||||||
.overview-tasks-window:active { |
|
||||||
background-color: alpha(@color9, 0.8); |
|
||||||
} |
|
||||||
|
|
||||||
.overview-tasks-window-selected { |
|
||||||
background-color: alpha(@color9, 0.8); |
|
||||||
} |
|
||||||
|
|
||||||
.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 */ |
|
||||||
@ -1,21 +0,0 @@ |
|||||||
|
|
||||||
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; |
|
||||||
@ -1,21 +0,0 @@ |
|||||||
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'; |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue