Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust space padding when no stacked windows present? #57

Open
jzelner opened this issue Dec 10, 2020 · 5 comments
Open

Adjust space padding when no stacked windows present? #57

jzelner opened this issue Dec 10, 2020 · 5 comments
Labels
enhancement New feature or request

Comments

@jzelner
Copy link

jzelner commented Dec 10, 2020

I'm wondering if there is any advice on how to configure .yabairc or .skhdrc i.e. with signals or keybindings to detect when a space has a window stack and open up the padding to accommodate stack icons and then go back to tighter padding when there are no stacks?

@AdamWagner AdamWagner added the enhancement New feature or request label Dec 12, 2020
@AdamWagner
Copy link
Owner

@jzelner I've definitely had this same thought!

I'm still working on refactoring for testability. I don't quite see the light at the end of the tunnel yet, but hope to be able to wrap this up shortly after the new year.

In parallel, I've been day-dreaming about rewriting stackline using a state-machine-esque component model:

  • Add a base component class that
    • has a registerHandler method that accepts an onChange function to be called with the old/new state whenever the component's state changes
    • uses lua's metamethod magic to automatically call all registered onChange handlers whenever the component's state changes (inspired by https://github.com/Phrogz/notifitable, but I was able to implement the desired behavior in a much simpler way)
  • Rewrite stackmanager stack, window, and indicator classes as subclasses of component
  • Simplify the scarily complicated logic here using rules inspired by state machines. (Of all the ideas so far, this has the highest risk of failing to meet expectations. I'm kind of assuming / hoping that reliable/omnipresent onChange handlers will make it possible to simplify that mess)
  • Expose a public API for end-users to easily register onSomethingChange handlers that perform an action when a) stackline state changes or b) key window management events (such as switching the active space) occur.

Let's tie that ramble together with your idea:

I think that the changes above would make it trivial to implement your suggestion:

  • On space change:
    • if stack length is zero, execute yabai command to set left & right padding to X
    • otherwise, execute yabai command to set left & right padding to stackling.config.appearance.size + X

Since waiting is no fun, below is a snippet that you can place in your ~/.hammerspoon/init.lua file to get an idea how this might work.

The result is pretty janky, but it should give you an idea.

If you're constantly switching spaces (like I am), the window jitter this causes isn't worth the extra real estate on spaces that don't have any stacked windows. You might be able to minimize the jank by independently maintaining state (e.g., {spaceHasStacks = { 1 = false, 2 = true, 3 = false}) that's only updated when necessary. I'm not even sure that would help much: I don't think yabai supports per-space padding, so you'll still need to call out to yabai when changing to a space with hasStack value that differs from the previous space.

local function yabai(cmd)
    local output

    -- NOTE: requires string:split() defined at /stackline/lib/utils.lua:16
    local args = cmd:split(' ')

    hs.task.new(
        stackline.config:get('paths.yabai'),
        function(_, stdOut) output = stdOut end,
        args
    ):start():waitUntilExit()

    return output
end

local function sidePadding(val)
    yabai(string.format('-m config left_padding %s', val))
    yabai(string.format('-m config right_padding %s', val))
end

-- See https://www.hammerspoon.org/docs/hs.spaces.watcher.html
hs.spaces.watcher.new(function()

    -- Set space padding for each case
    -- 'big' is dynamically determined based on current size of stack indicators
    -- NOTE: Assumes `showIcons = true`. Should probably factor out function
    -- that dynamically determines indicator width:/stackline/stackline/window.lua:45
    local small = 10
    local big = stackline.config:get('appearance.size') + small

    if stackline.manager then -- ensure stackline is initiated

        -- Hack: we need to wait ~0.5s before updating padding to ensure that
        --     stackline state has finished updating. Otherwise, we'll set the new
        --     space's padding based on whether the *previous* space had stacks ☹️
        -- The appropriate delay will depend on your personal system's speed.
        hs.timer.doAfter(0.4, function()

            -- Get number of stacks on current space
            -- Remember that running this on space change without a delay gives us outdated data
            local stackCount = #stackline.manager:get()

            -- DEBUG: uncomment to show notification w/ # of stacks on space change
            -- local msg = string.format('This space has %s stacks', stackCount)
            -- hs.notify.show('stackline', msg, '')

            -- Set the appropriate padding
            sidePadding(stackCount > 0 and big or small)
        end)
    end
end):start()

There might be a better way. Would love to hear any ideas you might have, @jzelner

@blefevre
Copy link

FYI Yabai does support per-space padding so you can adjust the sidePadding function of your script to:

local function sidePadding(val)
    local space = hs.json.decode(yabai('-m query --spaces --space'))
    local space_index = space['index']
    yabai(string.format('-m config --space %s left_padding %s', space_index, val))
    yabai(string.format('-m config --space %s right_padding %s', space_index, val))
end

This gets pretty good results, only downside I see is the required half a second delay.

@kpritam
Copy link

kpritam commented Jan 25, 2021

I am using following scripts to manage spaces between windows:

  1. when stacked, add left padding 40px
  2. When not stacked, use 8px padding

yabairc

yabai -m signal --add event=window_created action="~/.config/yabai/refresh.sh"
yabai -m signal --add event=window_destroyed action="~/.config/yabai/refresh.sh"

refresh.sh

#!/usr/bin/env bash

number_of_windows=$(yabai -m query --windows --space | ~/.nix-profile/bin/jq 'length')
number_of_stacked=$(yabai -m query --windows --space | ~/.nix-profile/bin/jq -c 'map(select(."stack-index" != 0)) | length')

padding=8
lpadding=40

[[ "$number_of_windows" -eq 1 ]] && padding=0
[[ "$number_of_stacked" = 0 ]] && lpadding=$padding

yabai -m config --space mouse top_padding $padding
yabai -m config --space mouse bottom_padding $padding
yabai -m config --space mouse left_padding $lpadding
yabai -m config --space mouse right_padding $padding
yabai -m config --space mouse window_gap $padding

@basbebe
Copy link

basbebe commented Jul 3, 2021

this works also for the current focused window – independent of where the mouse is at the point:

#!/usr/bin/env sh

number_of_windows=$(yabai -m query --windows --space | /usr/local/bin/jq 'length')
number_of_stacked=$(yabai -m query --windows --space | /usr/local/bin/jq -c 'map(select(."stack-index" != 0)) | length')
currspace=$(yabai -m query --spaces --space | /usr/local/bin/jq '.index')

padding=8
spadding=40

[[ "$number_of_windows" -eq 1 ]] && padding=0
[[ "$number_of_stacked" = 0 ]] && spadding=$padding

yabai -m config --space "$currspace" top_padding $padding
yabai -m config --space "$currspace" bottom_padding $padding
yabai -m config --space "$currspace" left_padding $spadding
yabai -m config --space "$currspace" right_padding $spadding
yabai -m config --space "$currspace" window_gap $padding

@AJAYK-01
Copy link

AJAYK-01 commented Nov 4, 2021

@jzelner I've definitely had this same thought!

I'm still working on refactoring for testability. I don't quite see the light at the end of the tunnel yet, but hope to be able to wrap this up shortly after the new year.

In parallel, I've been day-dreaming about rewriting stackline using a state-machine-esque component model:

  • Add a base component class that

    • has a registerHandler method that accepts an onChange function to be called with the old/new state whenever the component's state changes
    • uses lua's metamethod magic to automatically call all registered onChange handlers whenever the component's state changes (inspired by https://github.com/Phrogz/notifitable, but I was able to implement the desired behavior in a much simpler way)
  • Rewrite stackmanager stack, window, and indicator classes as subclasses of component

  • Simplify the scarily complicated logic here using rules inspired by state machines. (Of all the ideas so far, this has the highest risk of failing to meet expectations. I'm kind of assuming / hoping that reliable/omnipresent onChange handlers will make it possible to simplify that mess)

  • Expose a public API for end-users to easily register onSomethingChange handlers that perform an action when a) stackline state changes or b) key window management events (such as switching the active space) occur.

Let's tie that ramble together with your idea:

I think that the changes above would make it trivial to implement your suggestion:

  • On space change:

    • if stack length is zero, execute yabai command to set left & right padding to X
    • otherwise, execute yabai command to set left & right padding to stackling.config.appearance.size + X

Since waiting is no fun, below is a snippet that you can place in your ~/.hammerspoon/init.lua file to get an idea how this might work.

The result is pretty janky, but it should give you an idea.

If you're constantly switching spaces (like I am), the window jitter this causes isn't worth the extra real estate on spaces that don't have any stacked windows. You might be able to minimize the jank by independently maintaining state (e.g., {spaceHasStacks = { 1 = false, 2 = true, 3 = false}) that's only updated when necessary. I'm not even sure that would help much: I don't think yabai supports per-space padding, so you'll still need to call out to yabai when changing to a space with hasStack value that differs from the previous space.

local function yabai(cmd)
    local output

    -- NOTE: requires string:split() defined at /stackline/lib/utils.lua:16
    local args = cmd:split(' ')

    hs.task.new(
        stackline.config:get('paths.yabai'),
        function(_, stdOut) output = stdOut end,
        args
    ):start():waitUntilExit()

    return output
end

local function sidePadding(val)
    yabai(string.format('-m config left_padding %s', val))
    yabai(string.format('-m config right_padding %s', val))
end

-- See https://www.hammerspoon.org/docs/hs.spaces.watcher.html
hs.spaces.watcher.new(function()

    -- Set space padding for each case
    -- 'big' is dynamically determined based on current size of stack indicators
    -- NOTE: Assumes `showIcons = true`. Should probably factor out function
    -- that dynamically determines indicator width:/stackline/stackline/window.lua:45
    local small = 10
    local big = stackline.config:get('appearance.size') + small

    if stackline.manager then -- ensure stackline is initiated

        -- Hack: we need to wait ~0.5s before updating padding to ensure that
        --     stackline state has finished updating. Otherwise, we'll set the new
        --     space's padding based on whether the *previous* space had stacks ☹️
        -- The appropriate delay will depend on your personal system's speed.
        hs.timer.doAfter(0.4, function()

            -- Get number of stacks on current space
            -- Remember that running this on space change without a delay gives us outdated data
            local stackCount = #stackline.manager:get()

            -- DEBUG: uncomment to show notification w/ # of stacks on space change
            -- local msg = string.format('This space has %s stacks', stackCount)
            -- hs.notify.show('stackline', msg, '')

            -- Set the appropriate padding
            sidePadding(stackCount > 0 and big or small)
        end)
    end
end):start()

There might be a better way. Would love to hear any ideas you might have, @jzelner

Hey, I was wondering if there was a way to just add a top padding in the stack column, as most of the time it's just the window control buttons being blocked. I don't really want a big left padding as my screen is not very big.

image

Thanks for this cool project btw!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants