From 09aefa9c00761b24d5b2c2c338804a5ecae9edf9 Mon Sep 17 00:00:00 2001 From: Elias Renman Date: Thu, 30 Nov 2023 00:46:04 +0100 Subject: [PATCH] feat: wofi --- wofi/config | 32 +++++++++++ wofi/config.screenshot | 9 ++++ wofi/style.css | 61 +++++++++++++++++++++ wofi/style.widgets.css | 49 +++++++++++++++++ wofi/windows.py | 119 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+) create mode 100644 wofi/config create mode 100644 wofi/config.screenshot create mode 100644 wofi/style.css create mode 100644 wofi/style.widgets.css create mode 100755 wofi/windows.py diff --git a/wofi/config b/wofi/config new file mode 100644 index 0000000..ffe064e --- /dev/null +++ b/wofi/config @@ -0,0 +1,32 @@ +# hide_scroll=true +# show=drun +# width=35% +# lines=15 +# line_wrap=word +# term=alacritty +# allow_markup=true +# always_parse_args=true +# show_all=true +# print_command=true +# layer=overlay +# allow_images=true +# insensitive=true +# prompt= +# image_size=15 +# display_generic=true +# location=center + +width=700 +height=350 +show=drun +prompt=Search... +filter_rate=100 +allow_markup=true +no_actions=true +halign=fill +orientation=vertical +content_halign=fill +insensitive=true +allow_images=true +image_size=30 +gtk_dark=true diff --git a/wofi/config.screenshot b/wofi/config.screenshot new file mode 100644 index 0000000..d18a3c8 --- /dev/null +++ b/wofi/config.screenshot @@ -0,0 +1,9 @@ +hide_search=true +hide_scroll=true +insensitive=true +width=1 +show=dmenu +lines=5 +location=centre +x=870 +y=455 diff --git a/wofi/style.css b/wofi/style.css new file mode 100644 index 0000000..ea202ca --- /dev/null +++ b/wofi/style.css @@ -0,0 +1,61 @@ +/* +Arc-Dark Color Scheme +*/ + +/* @define-color highlight #e30b9b; */ +@define-color highlight #f7768e; +@define-color base1 rgba(0,0,0,0.2); +@define-color base2 rgba(0,0,0,0.8); +@define-color base3 rgba(0,0,0,0.1); + +*{ + font-family: JetBrains Mono Nerd Font; +} + +window { + /* border: 1px solid @highlight; */ + border-radius: 18px; + /* background: rgba(0,0,0,0); */ + color: #bb9af7; + position: relative; + background: linear-gradient(to bottom right, #73daca, #f7768e); + padding: 1px; +} + +#input { + margin-bottom: 15px; + padding:3px; + border-radius: 5px; + border:none; + outline:none; + /* color: white; */ + color: #bb9af7; + /* background: @base1; */ + background-color: #0a0a0a; +} + +#inner-box { + /* background: @base3; */ +} + +#outer-box { + margin: 3px; + padding:15px; + background-color: #0a0a0a; + border-radius: 8px; +} + +#text { + padding: 5px; +} + +/* #entry:nth-child(even) { */ +/* background-color: @base1; */ +/* } */ + +#entry:selected { + color: black; + background-color: @highlight; + border:none; + outline:none; +} diff --git a/wofi/style.widgets.css b/wofi/style.widgets.css new file mode 100644 index 0000000..222d0bb --- /dev/null +++ b/wofi/style.widgets.css @@ -0,0 +1,49 @@ +/* +Arc-Dark Color Scheme +*/ + +@define-color highlight #f7768e; +@define-color base1 rgba(0,0,0,0.6); +@define-color base2 rgba(0,0,0,0.8); +@define-color base3 rgba(0,0,0,0.1); + +*{ + font-family: JetBrainsMono; + /* height: fit-content; */ +} + +#window { + border: 2px solid @highlight; + border-radius: 4px; + background-color: rgba(0,0,0,0); + color: #bb9af7; + padding: 5px; +} + +#inner-box { + background-color: @base1; + /* background-color: @base2; */ +} + +#outer-box { + /*margin: 5px; + padding:5px 10px; + */ + border-radius: 4px; + margin-top: -32px; + +} + +#text { + padding: 5px; + /* color: white; */ +} + +#entry:nth-child(even) { + /* background-color: pink; */ +} + +#entry:selected { +color: black; + background-color: @highlight; +} diff --git a/wofi/windows.py b/wofi/windows.py new file mode 100755 index 0000000..6b41a60 --- /dev/null +++ b/wofi/windows.py @@ -0,0 +1,119 @@ +#!/bin/python3 +import json +import subprocess +from argparse import ArgumentParser + +ENTER = "\n" + + +def get_windows(): + + command = "swaymsg -t get_tree" + process = subprocess.Popen( + command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + process = subprocess.Popen( + command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + data = json.loads(process.communicate()[0]) + + # Select outputs that are active + windows = [] + for output in data["nodes"]: + + # The scratchpad (under __i3) is not supported + if output.get("name") != "__i3" and output.get("type") == "output": + workspaces = output.get("nodes", []) + for ws in workspaces: + if ws.get("type") == "workspace": + windows.extend(extract_nodes_iterative(ws)) + return windows + + +# Extracts all windows from a sway workspace json object +def extract_nodes_iterative(workspace): + all_nodes = [] + + floating_nodes = workspace.get("floating_nodes", []) + + for floating_node in floating_nodes: + all_nodes.append(floating_node) + + nodes = workspace.get("nodes", []) + + for node in nodes: + + # Leaf node + if not node.get("nodes"): + all_nodes.append(node) + # Nested node, handled iterative + else: + for inner_node in node.get("nodes"): + nodes.append(inner_node) + + return all_nodes + + +# Returns an array of all windows +def parse_windows(windows): + return [window.get("name") for window in windows] + + +# Returns a newline seperated UFT-8 encoded string of all windows for wofi +def build_wofi_string(windows): + return ENTER.join(windows).encode("UTF-8") + + +# Executes wofi with the given input string +def show_wofi(windows): + + command = 'wofi -c ~/.config/wofi/menu -s ~/.config/wofi/style.css -p "Windows: " -d -i --hide-scroll' + + process = subprocess.Popen( + command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + return process.communicate(input=windows)[0] + + +# Returns the sway window id of the window that was selected by the user inside wofi +def parse_id(windows, parsed_windows, selected): + if not selected: + return None + else: + selected = (selected.decode("UTF-8"))[:-1] # Remove new line character + window_index = int( + parsed_windows.index(selected) + ) # Get index of selected window in the parsed window array + return str( + windows[window_index].get("id") + ) # Get sway window id based on the index + + +# Switches the focus to the given id +def switch_window(id): + command = "swaymsg [con_id={}] focus".format(id) + + process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + process.communicate()[0] + + +# Entry point +if __name__ == "__main__": + + parser = ArgumentParser(description="Wofi based window switcher") + + windows = get_windows() + + parsed_windows = parse_windows(windows) + + wofi_string = build_wofi_string(parsed_windows) + + selected = show_wofi(wofi_string) + + # Otherwise no point in running + if selected: + + selected_id = parse_id(windows, parsed_windows, selected) + + switch_window(selected_id)