Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 114 additions & 12 deletions dotfiles/.config/ml4w/scripts/sidepad.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,73 @@ launcher=$(cat $HOME/.config/ml4w/settings/launcher)
# Configuration
SIDEPAD_PATH="$HOME/.config/sidepad/sidepad"
SIDEPAD_DATA="$HOME/.config/ml4w/settings/sidepad-active"
SIDEPAD_POS_FILE="$HOME/.config/ml4w/settings/sidepad-position"
SIDEPAD_PADS_FOLDER="$HOME/.config/sidepad/pads"
SIDEPAD_SELECT="$HOME/.config/sidepad/scripts/select.sh"

# Load active sidepad
SIDEPAD_OPTIONS=""
SIDEPAD_OPTIONS="--width 900 --width-max 1200"

# Load active sidepad with error handling
if [ ! -f "$SIDEPAD_DATA" ]; then
echo "Error: Sidepad data file not found at $SIDEPAD_DATA"
exit 1
fi
SIDEPAD_ACTIVE=$(cat "$SIDEPAD_DATA")
source $SIDEPAD_PADS_FOLDER/$(cat "$SIDEPAD_DATA")
source $SIDEPAD_PADS_FOLDER/$SIDEPAD_ACTIVE

# Load position
if [ -f "$SIDEPAD_POS_FILE" ]; then
SIDEPAD_POS=$(cat "$SIDEPAD_POS_FILE")
else
SIDEPAD_POS="left"
fi

source "$SIDEPAD_PADS_FOLDER/$SIDEPAD_ACTIVE"

echo ":: Current sidepad: $SIDEPAD_ACTIVE"
echo ":: Current sidepad app: $SIDEPAD_APP"
echo ":: Current sidepad class: $SIDEPAD_CLASS"
echo ":: Current sidepad position: $SIDEPAD_POS"

# Check if sidepad window exists
is_sidepad_running() {
hyprctl clients -j | jq -e --arg class "$SIDEPAD_CLASS" '.[] | select(.class == $class)' > /dev/null
return $?
}

# Check if sidepad is visible (works for both left and right positions)
is_sidepad_visible() {
local window_info=$(hyprctl clients -j | jq --arg class "$SIDEPAD_CLASS" '.[] | select(.class == $class)')

if [ -z "$window_info" ]; then
return 1 # Window doesn't exist
fi

local window_x=$(echo "$window_info" | jq -r '.at[0]')
local window_width=$(echo "$window_info" | jq -r '.size[0]')
local monitor_info=$(hyprctl monitors -j | jq '.[] | select(.focused == true)')
local monitor_x=$(echo "$monitor_info" | jq -r '.x')
local monitor_width=$(echo "$monitor_info" | jq -r '.width')

if [ -z "$window_x" ] || [ "$window_x" == "null" ]; then
return 1
fi

# Check visibility based on position
if [[ "$SIDEPAD_POS" == "right" ]]; then
# For right position: visible if right edge is on screen
if (( window_x <= monitor_x + monitor_width - window_width )); then
return 0 # visible
fi
else
# For left position: visible if left edge is on screen
if (( window_x >= monitor_x )); then
return 0 # visible
fi
fi

return 1 # hidden
}

# Select new sidepad with rofi
select_sidepad() {
Expand All @@ -40,30 +96,76 @@ select_sidepad() {
echo ":: New sidepad: $pad"

# Kill existing sidepad
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --kill"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --kill"

# Write pad into active data file
echo "$pad" > "$SIDEPAD_DATA"
SIDEPAD_ACTIVE=$(cat "$SIDEPAD_DATA")

# Init sidepad
source $SIDEPAD_PADS_FOLDER/$pad
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"
source "$SIDEPAD_PADS_FOLDER/$pad"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"
echo ":: Sidepad switched"
fi
}

# Switch sidepad position (left <-> right)
switch_side() {
local new_pos
if [[ "$SIDEPAD_POS" == "left" ]]; then
new_pos="right"
else
new_pos="left"
fi

echo ":: Switching sidepad from $SIDEPAD_POS to $new_pos"

# If sidepad is running, smoothly move it to the other side
if is_sidepad_running; then
eval "$SIDEPAD_PATH --position '$new_pos' --class '$SIDEPAD_CLASS' $SIDEPAD_OPTIONS"
fi

# Update position file for future sessions
echo "$new_pos" > "$SIDEPAD_POS_FILE"

echo ":: Sidepad switched to $new_pos"
}

# Dispatch parameters
if [[ "$1" == "--init" ]]; then
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"

elif [[ "$1" == "--hide" ]]; then
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --hide"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --hide"

elif [[ "$1" == "--toggle" ]]; then
if ! is_sidepad_running; then
echo ":: Sidepad not running, initializing..."
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"
elif is_sidepad_visible; then
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --hide"
else
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' $SIDEPAD_OPTIONS"
fi

elif [[ "$1" == "--test" ]]; then
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --test"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --test"

elif [[ "$1" == "--kill" ]]; then
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' --kill"
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --kill"

elif [[ "$1" == "--switch-side" ]]; then
switch_side

elif [[ "$1" == "--select" ]]; then
select_sidepad

else
eval "$SIDEPAD_PATH --class '$SIDEPAD_CLASS' $SIDEPAD_OPTIONS"
fi
# Default action: init if not running, otherwise show
if ! is_sidepad_running; then
echo ":: Sidepad not running, initializing..."
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' --init '$SIDEPAD_APP'"
else
eval "$SIDEPAD_PATH --position '$SIDEPAD_POS' --class '$SIDEPAD_CLASS' $SIDEPAD_OPTIONS"
fi
fi
158 changes: 143 additions & 15 deletions dotfiles/.config/sidepad/sidepad
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

# --- Configuration ---
WINDOW_CLASS="dotfiles-sidepad"
HIDDEN_LEFT_GAP=10
VISIBLE_LEFT_GAP=10
POSITION="left"
HIDDEN_GAP=10
VISIBLE_GAP=10
TARGET_WIDTH=700
TARGET_WIDTH_MAX=1000
TOP_GAP=100
BOTTOM_GAP=100
PREV_FOCUS_FILE="/tmp/sidepad-prev-focus"

# --- Script Variables ---
HIDE_REQUESTED=0
Expand All @@ -26,8 +28,9 @@ show_help() {
echo ""
echo "Options:"
echo " --class <name> Override the window class (Default: $WINDOW_CLASS)"
echo " --hidden-gap <px> Override the hidden left gap (Default: $HIDDEN_LEFT_GAP)"
echo " --visible-gap <px> Override the visible left gap (Default: $VISIBLE_LEFT_GAP)"
echo " --position <pos> Override the position 'left' or 'right' (Default: $POSITION)"
echo " --hidden-gap <px> Override the hidden gap (Default: $HIDDEN_GAP)"
echo " --visible-gap <px> Override the visible gap (Default: $VISIBLE_GAP)"
echo " --width <px> Override the target width (Default: $TARGET_WIDTH)"
echo " --width-max <px> Override the maximum target width (Default: $TARGET_WIDTH_MAX)"
echo " --top-gap <px> Override the top gap (Default: $TOP_GAP)"
Expand Down Expand Up @@ -88,13 +91,22 @@ while [[ $# -gt 0 ]]; do
shift; shift
;;

--position)
if [[ "$2" != "left" && "$2" != "right" ]]; then
echo "Error: --position must be either 'left' or 'right' (got '$2')." >&2
exit 1
fi
POSITION="$2"
shift; shift
;;

--hidden-gap)
HIDDEN_LEFT_GAP="$2"
HIDDEN_GAP="$2"
shift; shift
;;

--visible-gap)
VISIBLE_LEFT_GAP="$2"
VISIBLE_GAP="$2"
shift; shift
;;

Expand Down Expand Up @@ -165,20 +177,109 @@ WINDOW_HEIGHT=$(echo "$WINDOW_INFO" | jq -r '.size[1]')
WINDOW_X=$(echo "$WINDOW_INFO" | jq -r '.at[0]')
WINDOW_Y=$(echo "$WINDOW_INFO" | jq -r '.at[1]')
MONITOR_HEIGHT=$(echo "$MONITOR_INFO" | jq -r '.height')
MONITOR_WIDTH=$(echo "$MONITOR_INFO" | jq -r '.width')
MONITOR_X=$(echo "$MONITOR_INFO" | jq -r '.x')
MONITOR_Y=$(echo "$MONITOR_INFO" | jq -r '.y')

# --- Detect current position based on window location ---
CURRENT_POSITION="left"
# If window is on the right half of the monitor, it's currently right-positioned
if (( WINDOW_X > MONITOR_X + MONITOR_WIDTH / 2 )); then
CURRENT_POSITION="right"
fi

# --- Main Logic ---

# Determine if window is hidden
IS_HIDDEN=0
if [[ "$CURRENT_POSITION" == "right" ]]; then
if (( WINDOW_X > MONITOR_X + MONITOR_WIDTH - TARGET_WIDTH || WINDOW_X < MONITOR_X )); then
IS_HIDDEN=1
fi
else
if (( WINDOW_X < MONITOR_X || WINDOW_X > MONITOR_X + MONITOR_WIDTH )); then
IS_HIDDEN=1
fi
fi

# Check if we're switching sides (position mismatch)
SWITCHING_SIDES=0
if [[ "$CURRENT_POSITION" != "$POSITION" ]]; then
SWITCHING_SIDES=1
echo "--- Switching sides from $CURRENT_POSITION to $POSITION ---"
fi

# Case 0: Switching sides - move window to opposite side while keeping visibility
if [[ "$SWITCHING_SIDES" -eq 1 && "$IS_HIDDEN" -eq 0 ]]; then
echo "--- Moving visible window to $POSITION side ---"

# Calculate new position
if [[ "$POSITION" == "right" ]]; then
TARGET_X=$(( MONITOR_X + MONITOR_WIDTH - WINDOW_WIDTH - VISIBLE_GAP ))
else
TARGET_X=$(( MONITOR_X + VISIBLE_GAP ))
fi

PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
PIXELS_TO_MOVE_Y=$(( MONITOR_Y + TOP_GAP - WINDOW_Y ))
TARGET_HEIGHT=$(( MONITOR_HEIGHT - TOP_GAP - BOTTOM_GAP ))
HEIGHT_CHANGE=$(( TARGET_HEIGHT - WINDOW_HEIGHT ))

hyprctl --batch "dispatch resizewindowpixel 0 $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS"
echo "Operation completed."
exit 0
fi

# If switching sides while hidden, just update the hidden position
if [[ "$SWITCHING_SIDES" -eq 1 && "$IS_HIDDEN" -eq 1 ]]; then
echo "--- Repositioning hidden window to $POSITION side ---"

if [[ "$POSITION" == "right" ]]; then
TARGET_X=$(( MONITOR_X + MONITOR_WIDTH - HIDDEN_GAP ))
else
TARGET_X=$(( MONITOR_X - TARGET_WIDTH + HIDDEN_GAP ))
fi

PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
WIDTH_CHANGE=$(( TARGET_WIDTH - WINDOW_WIDTH ))
PIXELS_TO_MOVE_Y=$(( MONITOR_Y + TOP_GAP - WINDOW_Y ))
TARGET_HEIGHT=$(( MONITOR_HEIGHT - TOP_GAP - BOTTOM_GAP ))
HEIGHT_CHANGE=$(( TARGET_HEIGHT - WINDOW_HEIGHT ))

hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS"
echo "Operation completed."
exit 0
fi

# Case 1: --hide flag is used, unconditionally hide the window.
if [[ "$HIDE_REQUESTED" -eq 1 ]]; then
if (( WINDOW_X >= 0 )); then # Only act if it is not already hidden
if [[ "$IS_HIDDEN" -eq 0 ]]; then # Only act if it is not already hidden
echo "--- Hiding window (--hide) ---"
PIXELS_TO_MOVE_X=$(( (WINDOW_X * -1) - TARGET_WIDTH + HIDDEN_LEFT_GAP ))
if [[ "$POSITION" == "right" ]]; then
TARGET_X=$(( MONITOR_X + MONITOR_WIDTH - HIDDEN_GAP ))
else
TARGET_X=$(( MONITOR_X - TARGET_WIDTH + HIDDEN_GAP ))
fi
PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
WIDTH_CHANGE=$(( TARGET_WIDTH - WINDOW_WIDTH ))
PIXELS_TO_MOVE_Y=$(( TOP_GAP - WINDOW_Y ))
PIXELS_TO_MOVE_Y=$(( MONITOR_Y + TOP_GAP - WINDOW_Y ))
TARGET_HEIGHT=$(( MONITOR_HEIGHT - TOP_GAP - BOTTOM_GAP ))
HEIGHT_CHANGE=$(( TARGET_HEIGHT - WINDOW_HEIGHT ))

hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS"

# Restore focus
if [ -f "$PREV_FOCUS_FILE" ]; then
PREV_FOCUS=$(cat "$PREV_FOCUS_FILE")
if [[ -n "$PREV_FOCUS" ]]; then
# Verify the window still exists before focusing
if hyprctl clients -j | jq -e --arg addr "$PREV_FOCUS" '.[] | select(.address == $addr)' > /dev/null 2>&1; then
hyprctl dispatch focuswindow address:"$PREV_FOCUS" 2>/dev/null
fi
fi
rm "$PREV_FOCUS_FILE"
fi

echo "Operation completed."
else
echo "Window is already hidden."
Expand All @@ -187,21 +288,34 @@ if [[ "$HIDE_REQUESTED" -eq 1 ]]; then
fi

# Case 2: Window is hidden, so show it.
if (( WINDOW_X < 0 )); then
if [[ "$IS_HIDDEN" -eq 1 ]]; then
echo "--- Showing window ---"
PIXELS_TO_MOVE_X=$(( VISIBLE_LEFT_GAP - WINDOW_X ))
# Save current focus
CURRENT_FOCUS=$(hyprctl activewindow -j | jq -r '.address')
if [[ -n "$CURRENT_FOCUS" && "$CURRENT_FOCUS" != "null" && "$CURRENT_FOCUS" != "$WINDOW_ADDRESS" ]]; then
echo "$CURRENT_FOCUS" > "$PREV_FOCUS_FILE"
fi

if [[ "$POSITION" == "right" ]]; then
TARGET_X=$(( MONITOR_X + MONITOR_WIDTH - TARGET_WIDTH - VISIBLE_GAP ))
else
TARGET_X=$(( MONITOR_X + VISIBLE_GAP ))
fi

PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
WIDTH_CHANGE=$(( TARGET_WIDTH - WINDOW_WIDTH ))
PIXELS_TO_MOVE_Y=$(( TOP_GAP - WINDOW_Y ))
PIXELS_TO_MOVE_Y=$(( MONITOR_Y + TOP_GAP - WINDOW_Y ))
TARGET_HEIGHT=$(( MONITOR_HEIGHT - TOP_GAP - BOTTOM_GAP ))
HEIGHT_CHANGE=$(( TARGET_HEIGHT - WINDOW_HEIGHT ))

hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS"
hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS; dispatch focuswindow address:$WINDOW_ADDRESS"

echo "Operation completed."

# Case 3: Window is visible, so toggle its width and correct its position.
else
# Ensure vertical position and height are correct
PIXELS_TO_MOVE_Y=$(( TOP_GAP - WINDOW_Y ))
PIXELS_TO_MOVE_Y=$(( MONITOR_Y + TOP_GAP - WINDOW_Y ))
TARGET_HEIGHT=$(( MONITOR_HEIGHT - TOP_GAP - BOTTOM_GAP ))
HEIGHT_CHANGE=$(( TARGET_HEIGHT - WINDOW_HEIGHT ))

Expand All @@ -212,11 +326,25 @@ else
if (( WINDOW_WIDTH == TARGET_WIDTH )); then
echo "--- Expanding width to max ---"
WIDTH_CHANGE=$(( TARGET_WIDTH_MAX - WINDOW_WIDTH ))
NEW_WIDTH=$TARGET_WIDTH_MAX
else
echo "--- Shrinking width to default ---"
WIDTH_CHANGE=$(( TARGET_WIDTH - WINDOW_WIDTH ))
NEW_WIDTH=$TARGET_WIDTH
fi

hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS"
# Calculate horizontal move
if [[ "$POSITION" == "right" ]]; then
# If right aligned, we need to move X to keep right edge constant
TARGET_X=$(( MONITOR_X + MONITOR_WIDTH - NEW_WIDTH - VISIBLE_GAP ))
PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
else
# If left aligned, X stays same (or we enforce VISIBLE_GAP)
TARGET_X=$(( MONITOR_X + VISIBLE_GAP ))
PIXELS_TO_MOVE_X=$(( TARGET_X - WINDOW_X ))
fi

hyprctl --batch "dispatch resizewindowpixel $WIDTH_CHANGE $HEIGHT_CHANGE,address:$WINDOW_ADDRESS; dispatch movewindowpixel $PIXELS_TO_MOVE_X $PIXELS_TO_MOVE_Y,address:$WINDOW_ADDRESS; dispatch focuswindow address:$WINDOW_ADDRESS"

echo "Operation completed."
fi