├── LICENSE ├── config-example ├── i3-goto-workspace ├── i3-moveto-workspace ├── i3-rename-workspace ├── i3-renumber-workspace ├── i3-workspacer.py └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 cameronleger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config-example: -------------------------------------------------------------------------------- 1 | # custom workspace commander 2 | set $workspacer ~/.config/i3/i3-workspacer.py 3 | 4 | # go to new workspace blocks 5 | bindsym $mod+Tab exec --no-startup-id $workspacer go -d up 6 | bindsym $mod+w exec --no-startup-id $workspacer go -d down 7 | 8 | # move to new workspace blocks 9 | bindsym $mod+Shift+Tab exec --no-startup-id $workspacer move -d up 10 | bindsym $mod+Shift+w exec --no-startup-id $workspacer move -d down 11 | 12 | # switch to workspace 13 | bindsym $mod+grave exec --no-startup-id ~/.config/i3/i3-goto-workspace 14 | bindsym $mod+1 exec --no-startup-id $workspacer go -n 1 15 | bindsym $mod+2 exec --no-startup-id $workspacer go -n 2 16 | bindsym $mod+3 exec --no-startup-id $workspacer go -n 3 17 | bindsym $mod+4 exec --no-startup-id $workspacer go -n 4 18 | bindsym $mod+5 exec --no-startup-id $workspacer go -n 5 19 | bindsym $mod+6 exec --no-startup-id $workspacer go -n 6 20 | bindsym $mod+7 exec --no-startup-id $workspacer go -n 7 21 | bindsym $mod+8 exec --no-startup-id $workspacer go -n 8 22 | bindsym $mod+9 exec --no-startup-id $workspacer go -n 9 23 | bindsym $mod+0 exec --no-startup-id $workspacer go -n 10 24 | 25 | # move focused container to workspace 26 | bindsym $mod+Shift+grave exec --no-startup-id ~/.config/i3/i3-moveto-workspace 27 | bindsym $mod+Shift+1 exec --no-startup-id $workspacer move -n 1 28 | bindsym $mod+Shift+2 exec --no-startup-id $workspacer move -n 2 29 | bindsym $mod+Shift+3 exec --no-startup-id $workspacer move -n 3 30 | bindsym $mod+Shift+4 exec --no-startup-id $workspacer move -n 4 31 | bindsym $mod+Shift+5 exec --no-startup-id $workspacer move -n 5 32 | bindsym $mod+Shift+6 exec --no-startup-id $workspacer move -n 6 33 | bindsym $mod+Shift+7 exec --no-startup-id $workspacer move -n 7 34 | bindsym $mod+Shift+8 exec --no-startup-id $workspacer move -n 8 35 | bindsym $mod+Shift+9 exec --no-startup-id $workspacer move -n 9 36 | bindsym $mod+Shift+0 exec --no-startup-id $workspacer move -n 10 37 | 38 | # renaming workspace 39 | bindsym $mod+n exec --no-startup-id ~/.config/i3/i3-rename-workspace 40 | bindsym $mod+Shift+n exec --no-startup-id ~/.config/i3/i3-renumber-workspace -------------------------------------------------------------------------------- /i3-goto-workspace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | i3-input -f "-*-ubuntu-bold-r-*-*-26-*-*-*-*-*-*-*" -F "exec --no-startup-id ~/.config/i3/i3-workspacer.py go --exact --number \"%s\"" -P 'Go To Workspace Number: ' -------------------------------------------------------------------------------- /i3-moveto-workspace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | i3-input -f "-*-ubuntu-bold-r-*-*-26-*-*-*-*-*-*-*" -F "exec --no-startup-id ~/.config/i3/i3-workspacer.py move --exact --number \"%s\"" -P 'Move To Workspace Number: ' -------------------------------------------------------------------------------- /i3-rename-workspace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | i3-input -f "-*-ubuntu-bold-r-*-*-26-*-*-*-*-*-*-*" -F "exec --no-startup-id ~/.config/i3/i3-workspacer.py rename --name \"%s\"" -P 'Workspace Name: ' -------------------------------------------------------------------------------- /i3-renumber-workspace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | i3-input -f "-*-ubuntu-bold-r-*-*-26-*-*-*-*-*-*-*" -F "exec --no-startup-id ~/.config/i3/i3-workspacer.py rename --number \"%s\"" -P 'Workspace Number: ' -------------------------------------------------------------------------------- /i3-workspacer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import i3ipc # Required for interactions with i3 and its IPC interface 4 | import sys # Required for exiting 5 | import math # Required for rounding 6 | import argparse # Required for easy arguments to command 7 | 8 | VERSION = (1, 0) 9 | 10 | i3 = i3ipc.Connection() 11 | 12 | 13 | def go_to_workspace(workspace_number): 14 | if workspace_number <= 0: 15 | workspace_number = 1 16 | i3_command = 'workspace number %s' % (workspace_number) 17 | print('i3 Command: %s' % i3_command) 18 | i3.command(i3_command) 19 | 20 | 21 | def move_to_workspace(workspace_number): 22 | if workspace_number <= 0: 23 | print('Not doing anything to a negative workspace') 24 | return 1 25 | i3_command = 'move container to workspace number %s' % (workspace_number) 26 | print('i3 Command: %s' % i3_command) 27 | i3.command(i3_command) 28 | 29 | 30 | def rename_workspace(workspace_name): 31 | i3_command = 'rename workspace to "%s"' % (workspace_name) 32 | print('i3 Command: %s' % i3_command) 33 | i3.command(i3_command) 34 | 35 | 36 | def get_workspace_number_and_name(workspace_name): 37 | try: 38 | if ': ' in workspace_name: 39 | workspace_number, workspace_name = workspace_name.split(': ') 40 | else: 41 | workspace_number = workspace_name 42 | workspace_number = int(workspace_number) 43 | except ValueError: 44 | print("Unable to parse Workspace's number and name") 45 | return (1, workspace_name) 46 | return (workspace_number, workspace_name) 47 | 48 | 49 | def make_workspace_name(workspace_number, workspace_name): 50 | return ': '.join([str(workspace_number), workspace_name]) 51 | 52 | 53 | def get_workspace_block(workspace_number): 54 | return int(math.floor((workspace_number-1)/10))*10 55 | 56 | 57 | def shift_workspace_number(workspace_number, direction, number, exact): 58 | if direction is not None: 59 | if direction == 'up': 60 | workspace_number += 10 61 | elif direction == 'down': 62 | workspace_number -= 10 63 | elif direction == 'next': 64 | workspace_number += 1 65 | elif direction == 'prev': 66 | workspace_number -= 1 67 | elif number is not None: 68 | if exact is True: 69 | workspace_number = number 70 | else: 71 | workspace_block = get_workspace_block(workspace_number) 72 | workspace_number_in_block = number 73 | workspace_number = workspace_block + workspace_number_in_block 74 | return workspace_number 75 | 76 | 77 | def action_go(workspace_number, workspace_name, args): 78 | workspace_number = shift_workspace_number(workspace_number, args.direction, args.number, args.exact) 79 | go_to_workspace(workspace_number) 80 | 81 | 82 | def action_move(workspace_number, workspace_name, args): 83 | workspace_number = shift_workspace_number(workspace_number, args.direction, args.number, args.exact) 84 | return move_to_workspace(workspace_number) 85 | 86 | 87 | def action_rename(workspace_number, workspace_name, args): 88 | if args.number is not None: 89 | workspace_number = args.number 90 | if args.name is not None: 91 | workspace_name = args.name 92 | rename_workspace(make_workspace_name(workspace_number, workspace_name)) 93 | 94 | 95 | def parse_args(): 96 | parser = argparse.ArgumentParser(description='simplifying large amounts of i3 workspaces') 97 | parser.set_defaults(action=None) 98 | parser.add_argument('--version', action='version', version="%(prog)s {}.{}".format(*VERSION)) 99 | 100 | subparsers = parser.add_subparsers(help='check help for each command for required arguments') 101 | 102 | parser_go = subparsers.add_parser('go', help="like the 'workspace number' command") 103 | parser_go.set_defaults(action=action_go) 104 | parser_go.add_argument('--exact', action='store_true', 105 | help="number arguments will be the actual workspace number instead of the index in the current block") 106 | group_go = parser_go.add_mutually_exclusive_group(required=True) 107 | group_go.add_argument('-d', '--direction', choices=['up', 'down', 'next', 'prev'], 108 | help='operate relative to the current workspace; up/down = +/- 10') 109 | group_go.add_argument('-n', '--number', type=int, 110 | help='specify workspace number') 111 | 112 | parser_move = subparsers.add_parser('move', help="like the 'move container to workspace number' command") 113 | parser_move.set_defaults(action=action_move) 114 | parser_move.add_argument('--exact', action='store_true', 115 | help="number arguments will be the actual workspace number instead of the index in the current block") 116 | group_move = parser_move.add_mutually_exclusive_group(required=True) 117 | group_move.add_argument('-d', '--direction', choices=['up', 'down', 'next', 'prev'], 118 | help='operate relative to the current workspace; up/down = +/- 10') 119 | group_move.add_argument('-n', '--number', type=int, 120 | help='specify workspace number') 121 | 122 | parser_rename = subparsers.add_parser('rename', help="like the 'rename workspace to' command") 123 | parser_rename.set_defaults(action=action_rename) 124 | parser_rename.add_argument('-na', '--name', 125 | help='change the name of the current workspace') 126 | parser_rename.add_argument('-nu', '--number', type=int, 127 | help='change the number of the current workspace') 128 | 129 | args = parser.parse_args() 130 | 131 | if args.action is None: 132 | parser.print_help() 133 | return 134 | 135 | return args 136 | 137 | 138 | def main(): 139 | args = parse_args() 140 | 141 | workspace_name = i3.get_tree().find_focused().workspace().name 142 | workspace_number, workspace_name = get_workspace_number_and_name(workspace_name) 143 | 144 | sys.exit(args.action(workspace_number, workspace_name, args)) 145 | 146 | 147 | if __name__ == '__main__': 148 | main() 149 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Look at the config-example to see how I've used this script. I use i3-input for the renaming and renumbering of the workspace and the specific workspace number commands. This is not inside of the script; it's used within the config to send to the script. I had issues with escaping when using the i3-input program inside of the config, so I made some script files to wrap those commands. 2 | 3 | # Features 4 | * Standardized workspace names: '#: Name' 5 | * Easily Rename and Renumber current workspace 6 | * Go and Move within 1-10 workspace 'blocks' 7 | * Go and Move to next/previous workspace 'block' 8 | * Go and Move directly to specific workspace number 9 | 10 | # Usage 11 | You should add keybindings to your i3 config to fit the workflow you want, as I haven't covered all of the usage of this program with the example keybindings. 12 | 13 | I swapped the standard 1-10 workspace go and move keybindings to use this script instead. Without the '--exact' argument, this will go and move to the 1-10 workspaces of the current block of 10, e.g. 3 will be 33 if you're in the 31-40 block. 14 | 15 | I added two new keybindings to use the directional up and down versions to go and move between the blocks of 10. There's also two for going and moving to a specifically numbered workspace. 16 | 17 | Two more rename and renumber the current workspace, and I usually name every workspace and sometimes push them to other blocks. 18 | 19 | ``` 20 | usage: i3-workspacer.py [-h] [--version] {go,move,rename} ... 21 | 22 | simplifying large amounts of i3 workspaces 23 | 24 | positional arguments: 25 | {go,move,rename} check help for each command for required arguments 26 | go like the 'workspace number' command 27 | move like the 'move container to workspace number' command 28 | rename like the 'rename workspace to' command 29 | 30 | optional arguments: 31 | -h, --help show this help message and exit 32 | --version show program's version number and exit 33 | ``` 34 | ## Go command 35 | ``` 36 | usage: i3-workspacer.py go [-h] [--exact] (-d {up,down,next,prev} | -n NUMBER) 37 | 38 | optional arguments: 39 | -h, --help show this help message and exit 40 | --exact number arguments will be the actual workspace number 41 | instead of the index in the current block 42 | -d {up,down,next,prev}, --direction {up,down,next,prev} 43 | operate relative to the current workspace; up/down = 44 | +/- 10 45 | -n NUMBER, --number NUMBER 46 | specify workspace number 47 | ``` 48 | ## Move command 49 | ``` 50 | usage: i3-workspacer.py move [-h] [--exact] 51 | (-d {up,down,next,prev} | -n NUMBER) 52 | 53 | optional arguments: 54 | -h, --help show this help message and exit 55 | --exact number arguments will be the actual workspace number 56 | instead of the index in the current block 57 | -d {up,down,next,prev}, --direction {up,down,next,prev} 58 | operate relative to the current workspace; up/down = 59 | +/- 10 60 | -n NUMBER, --number NUMBER 61 | specify workspace number 62 | ``` 63 | ## Rename command 64 | ``` 65 | usage: i3-workspacer.py rename [-h] [-na NAME] [-nu NUMBER] 66 | 67 | optional arguments: 68 | -h, --help show this help message and exit 69 | -na NAME, --name NAME 70 | change the name of the current workspace 71 | -nu NUMBER, --number NUMBER 72 | change the number of the current workspace 73 | ``` --------------------------------------------------------------------------------