zakk4223 / hyprland-easymotion

Easymotion, for hyprland
BSD 3-Clause "New" or "Revised" License
78 stars 8 forks source link

Select Clients From An Overview Mode #3

Open modestbadger1773 opened 7 months ago

modestbadger1773 commented 7 months ago

Being able to press a key and have all clients in a grid view that could selected through easymotion would also be an awesome implementation for later on.

As I said in other request - fantastic work, does exactly what I wanted it to.

https://github.com/DreamMaoMao/hycov

modestbadger1773 commented 7 months ago

I've made some bash scripts that combined with innate functionality of this plugin and hyprland's keybinds allow for a functional overview mode.

Show All

#!/bin/sh

# Files to store mappings and fullscreen state
mapping_file="/tmp/client_workspace_mapping.txt"
fullscreen_file="/tmp/client_fullscreen_state.txt"

# Clear the mapping file and fullscreen state file or create if doesn't exist
: > $mapping_file
: > $fullscreen_file

# Workspace to move clients to
target_workspace=1

# Result string for hyptctl --batch
# Also switch to target workspace and first workspace for other monitor
result="dispatch workspace 1; dispatch workspace $target_workspace;"

# For every Hyprland client address
for client in $(hyprctl clients -j | jq -r ".[] | select(.workspace.id != $target_workspace and .workspace.id != -1) | @base64"); do
    workspace_id=$(echo $client | base64 --decode | jq -r ".workspace.id")
    address=$(echo $client | base64 --decode | jq -r ".address")
    fullscreen=$(echo $client | base64 --decode | jq "if .fullscreen == true then 1 elif .fullscreen == false then 0 else .fullscreen end")

    # Save mapping to file
    echo "$address $workspace_id" >> $mapping_file

    # If client is fullscreen, record this state and prepare to exit fullscreen
    if [ "$fullscreen" -eq 1 ]; then
        echo "$address" >> $fullscreen_file
        # Append command to exit fullscreen for this client
        result="$result dispatch fullscreen,address:$address;"
    fi

    # Append hyprctl command to execute with address
    result="$result dispatch movetoworkspacesilent $target_workspace,address:$address;"
done

# Execute commands
hyprctl --batch "$result"

Return All

#!/bin/sh

# Files where the client to workspace mappings and fullscreen states are stored
mapping_file="/tmp/client_workspace_mapping.txt"
fullscreen_file="/tmp/client_fullscreen_state.txt"

# Use hyprctl activewindow and extract the window ID
focused_window_id=$(hyprctl activewindow | grep 'Window' | awk '{print $2}')
echo "Focused Window ID: $focused_window_id"

# Initialize variable for the focused client's original workspace
focused_client_workspace=""

# Check if focused_window_id is not empty
if [ -n "$focused_window_id" ]; then
    # Look up the focused window's original workspace
    focused_client_workspace=$(grep "$focused_window_id" "$mapping_file" | awk '{print $2}')
fi

echo "Focused Client's Original Workspace: $focused_client_workspace"

# Initialize result string for hyprctl --batch
result=""

# Read each mapping from the file and prepare hyprctl --batch commands
while IFS= read -r line; do
    address=$(echo "$line" | awk '{print $1}')
    original_workspace=$(echo "$line" | awk '{print $2}')

    # Move client to its original workspace
    result="$result dispatch movetoworkspacesilent $original_workspace,address:$address;"

    # Check if client was fullscreen and append command if necessary
    if grep -q "$address" "$fullscreen_file"; then
        result="$result dispatch fullscreen,address:$address;"
    fi
done < "$mapping_file"

# If the focused client's workspace was found, use it; otherwise, default to workspace 1
if [ -z "$focused_client_workspace" ]; then
    echo "No focused client workspace found, defaulting to workspace 1."
    focused_client_workspace=1
else
    echo "Moving to focused client's original workspace: $focused_client_workspace"
fi

# Append the command to switch to the workspace of the originally focused client
result="$result dispatch workspace $focused_client_workspace;"

# Execute commands
echo "Executing command: $result"
hyprctl --batch "$result"

Workflow

I call these commands with:

bind = ALT, grave, exec, $UserScripts/showall.sh bindr = ALT, grave, easymotion, bgcolor:rgba(000000af), bordercolor:rgba(00ff00ff), textcolor:rgba(00ff00ff), action:hyprctl dispatch focuswindow address:{};bash $UserScripts/returnall.sh

Which brings all clients to Workspace 1. Then, once I choose the client I want, all the clients return to their original Workspaces and I am moved to the chosen client's workspace with it active.

If I want to work with all the clients on one Workspace, I can escape out of the previous keybind. If I later want to return all clients to their respective workspaces (and follow my chosen client) I can use:

bindr = $mainMod, grave, easymotion, bgcolor:rgba(000000af), bordercolor:rgba(00ff00ff), textcolor:rgba(00ff00ff), action:hyprctl dispatch focuswindow address:{};bash $UserScripts/returnall.sh

Hopefully these scripts can be helpful for someone other than just me. If anyone has any questions, I'm happy to give as much (of my admittedly meager) support as I can.

luravoid commented 7 months ago

Bind this script:

bind = ALT,tab, exec, ~/.config/hypr/scripts/hycov-easymotion
#!/bin/bash

workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')

if [ "$workspace_name" = "OVERVIEW" ]; then
    hyprctl dispatch hycov:toggleoverview
else
    hyprctl dispatch hycov:toggleoverview
    hyprctl dispatch 'easymotion action:hyprctl --batch "dispatch focuswindow address:{} ; dispatch hycov:toggleoverview"'
fi
DreamMaoMao commented 7 months ago

@luravoid

SO COOL!

https://github.com/zakk4223/hyprland-easymotion/assets/30348075/eba107fe-b0c7-488d-8d3f-dd7fb318ad1f

luravoid commented 7 months ago

I added a socket event to the script so that the labels also hide when selecting a window with the mouse:

#!/bin/bash

handle() {
  case $1 in
    workspace*)
      workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')
      if [ "$workspace_name" != "OVERVIEW" ]; then
        hyprctl dispatch easymotionexit
        exit 0
      fi
      ;;
  esac
}

workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')

if [ "$workspace_name" = "OVERVIEW" ]; then
    hyprctl dispatch hycov:toggleoverview
else
    hyprctl dispatch hycov:toggleoverview
    hyprctl dispatch "easymotion action:hyprctl --batch 'dispatch focuswindow address:{} ; dispatch hycov:toggleoverview'"
fi

socat -U - UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done

It would also be possible to show and hide the overview with labels using a single hotkey if it were possible to define submaps that work in easymode to execute the script with the same hotkey


if [ "$workspace_name" = "OVERVIEW" ]; then
    hyprctl dispatch easymotionexit
    hyprctl dispatch hycov:toggleoverview
else
    hyprctl dispatch hycov:toggleoverview
    hyprctl dispatch "easymotion action:hyprctl --batch 'dispatch focuswindow address:{} ; dispatch hycov:toggleoverview'"
fi
DreamMaoMao commented 7 months ago

@luravoid

hycov support leaveoverview and enteroverview dispatch. you should ues this replace toggleoverview.

workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')

if [ "$workspace_name" = "OVERVIEW" ]; then
    hyprctl dispatch hycov:leaveoverview
else
    hyprctl dispatch hycov:enteroverview
    hyprctl dispatch 'easymotion action:hyprctl --batch "dispatch focuswindow address:{} ; dispatch hycov:leaveoverview"'
fi
luravoid commented 7 months ago

Ok, now it's perfect. Single keybinding opens and closes hycov with labels

bind = ALT, Tab, exec, ~/.config/hypr/scripts/hycov-easymotion
submap=__easymotionsubmap__ 
bind = ALT, Tab, exec, ~/.config/hypr/scripts/hycov-easymotion
submap=reset
#!/bin/bash

handle() {
  case $1 in
    renameworkspace*)
      workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')
      if [ "$workspace_name" != "OVERVIEW" ]; then
        hyprctl dispatch easymotionexit
        exit 0
      fi
      ;;
  esac
}

workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')

if [ "$workspace_name" = "OVERVIEW" ]; then
    hyprctl dispatch hycov:leaveoverview
else
    hyprctl dispatch hycov:enteroverview
    hyprctl dispatch "easymotion action:hyprctl --batch 'dispatch focuswindow address:{} ; dispatch hycov:leaveoverview'"
fi

socat -U - UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done
thepenguinthatwants commented 5 months ago

This makes the whole plugin hundred times more awesome. Hopefully it can somehow get merged with the master. But then using separate custom scripts is also fine enough.

luravoid commented 5 days ago

This is the script I'm currently using. It allows not only to focus the selected window but also to move the window to the active workspace.

#!/bin/bash

# Plugins: hycov, hyprland-easymotion

oneshot=false
color=6e3a3a
mode="focus" # Default mode
store_file="/tmp/.win_store"

if [ ! -f "$store_file" ]; then
    touch "$store_file"
fi

handle() {
  case $1 in
    custom*)
      event_data=$(echo $1 | cut -d'>' -f3)
      if [[ "$event_data" == "changemode" ]]; then
        if [[ "$mode" == "focus" ]]; then
          mode="move"
          hyprctl dispatch easymotion "bgcolor:rgb($color)"
        else
          mode="focus"
          hyprctl dispatch easymotion
        fi
        echo "Changed mode to: $mode"
      fi
    ;;
    renameworkspace*)
      workspace_name=$(hyprctl -j monitors | jq -r '.[] | select(.activeWorkspace.name == "OVERVIEW") | .activeWorkspace.name')
      if [ "$workspace_name" = "OVERVIEW" ]; then
        if [ "$mode" = "focus" ]; then
          hyprctl dispatch easymotion
        elif [ "$mode" = "move" ]; then
          hyprctl dispatch easymotion "bgcolor:rgb($color)"
        fi
      else
        hyprctl dispatch easymotionexit
        mode="focus"
        if [[ "$oneshot" == true ]]; then
          exit
        fi
      fi
      ;;
    easymotionselect*)
      address=$(echo $1 | cut -d',' -f1 | cut -d'>' -f3)
      workspace_name=$(hyprctl -j monitors | jq -r '.[] | select(.activeWorkspace.name == "OVERVIEW") | .activeWorkspace.name')
      active_workspace=$(hyprctl -j activeworkspace | jq -r '.id')

      if [ "$workspace_name" = "OVERVIEW" ]; then
        if [ "$mode" = "focus" ]; then
          hyprctl dispatch focuswindow address:$address
          hyprctl dispatch hycov:leaveoverview
        elif [ "$mode" = "move" ]; then
          hyprctl dispatch hycov:leaveoverview
          original_workspace=$(hyprctl -j clients | jq -r --arg ADDRESS "$address" '.[] | select(.address == $ADDRESS) | .workspace.name')
          if grep -q "^$address" "$store_file"; then
            sed -i "/^$address/d" "$store_file"
          fi
          hyprctl dispatch movetoworkspace $active_workspace,address:$address
          echo "$address $original_workspace" >>"$store_file"
          mode="focus"
        fi
        if [[ "$oneshot" == true ]]; then
          exit
        fi
      fi
      ;;
    easymotionexit*)
      workspace_name=$(hyprctl -j monitors | jq -r '.[] | select(.activeWorkspace.name == "OVERVIEW") | .activeWorkspace.name')
      if [ "$workspace_name" = "OVERVIEW" ]; then
        hyprctl dispatch hycov:leaveoverview
        mode="focus"
      fi
      if [[ "$oneshot" == true ]]; then
        exit
      fi
      ;;
  esac
}

case $1 in
  -o|--oneshot)
    oneshot=true
    hyprctl dispatch hycov:enteroverview 
    case $2 in
      move)
        mode="move"
        hyprctl dispatch easymotion "bgcolor:rgb($color)"
        ;;
      focus)
        hyprctl dispatch easymotion 
        ;;
      *)
        hyprctl dispatch easymotion
        ;;
    esac
    ;;
  -h|--help)
    echo "Usage: $(basename $0) [OPTIONS]"
    echo "    -o, --oneshot [move|focus]   Run once with 'move' or 'focus' mode"
    echo "    -h, --help                   Show this help message"
    exit
    ;;
  *)
    echo "$(basename $0) started"
    ;;
esac

socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done

Bindings:

bind = ALT, Tab, hycov:toggleoverview

submap = __easymotionsubmap__

bind = ,Tab, event, changemode
bind = ALT, Tab, hycov:leaveoverview

submap = reset