import os import subprocess from libqtile import bar, layout, qtile, widget, hook from libqtile.config import Click, Drag, Group, Key, Match, Screen from libqtile.lazy import lazy from libqtile.utils import guess_terminal # --- AUTOSTART --- @hook.subscribe.startup_once def autostart(): script = os.path.expanduser("~/.config/qtile/autostart.sh") if os.path.isfile(script): subprocess.Popen([script]) mod = "mod4" terminal = guess_terminal() keys = [ # Switch between windows Key([mod], "h", lazy.layout.left(), desc="Move focus to left"), Key([mod], "l", lazy.layout.right(), desc="Move focus to right"), Key([mod], "j", lazy.layout.down(), desc="Move focus down"), Key([mod], "k", lazy.layout.up(), desc="Move focus up"), Key([mod], "space", lazy.layout.next(), desc="Move window focus to other window"), # Move windows Key([mod, "shift"], "h", lazy.layout.shuffle_left()), Key([mod, "shift"], "l", lazy.layout.shuffle_right()), Key([mod, "shift"], "j", lazy.layout.shuffle_down()), Key([mod, "shift"], "k", lazy.layout.shuffle_up()), # Resize windows Key([mod, "control"], "h", lazy.layout.grow_left()), Key([mod, "control"], "l", lazy.layout.grow_right()), Key([mod, "control"], "j", lazy.layout.grow_down()), Key([mod, "control"], "k", lazy.layout.grow_up()), Key([mod], "n", lazy.layout.normalize()), Key([mod], "t", lazy.spawn("xfce4-terminal --hide-menubar"), desc="Launch terminal"), Key([mod], "Return", lazy.spawn("rofi -show drun"), desc="Launch rofi"), Key([mod], "Tab", lazy.next_layout(), desc="Toggle between layouts"), Key([mod, "control"], "w", lazy.window.kill(), desc="Kill focused window"), Key([mod], "f", lazy.window.toggle_fullscreen()), Key([mod, "control"], "r", lazy.reload_config(), desc="Reload the config"), Key([mod, "control"], "q", lazy.shutdown(), desc="Shutdown Qtile"), Key([mod, "shift"], "End", lazy.spawn("waller-rofi"), desc="Wallpaper selector"), ] # --- REFINED WORKSPACE PAIRING (XFCE-STYLE) --- workspace_names = ["1", "2", "3"] groups = [] for name in workspace_names: # Screen 0 (Right) groups.append(Group(name=f"{name}R", screen_affinity=0)) # Screen 1 (Left/Vertical) groups.append(Group(name=f"{name}L", screen_affinity=1)) def switch_to_workspace(name): """Switch both monitors to the same workspace number while keeping focus on the current screen""" def _inner(qtile): current_screen_index = qtile.current_screen.index right_group = f"{name}R" left_group = f"{name}L" # We switch the current screen LAST to ensure focus doesn't jump to the other monitor if current_screen_index == 0: qtile.groups_map[left_group].toscreen(screen=1, toggle=False) qtile.groups_map[right_group].toscreen(screen=0, toggle=False) else: qtile.groups_map[right_group].toscreen(screen=0, toggle=False) qtile.groups_map[left_group].toscreen(screen=1, toggle=False) return _inner def move_window_to_workspace(name): """Move current window to the paired group on the same screen and follow it""" def _inner(qtile): if not qtile.current_window: return # Determine which screen the window is currently on current_screen_index = qtile.current_screen.index target_group = f"{name}R" if current_screen_index == 0 else f"{name}L" # Move to group and then trigger the dual-screen switch qtile.current_window.togroup(target_group, switch_group=False) switch_to_workspace(name)(qtile) return _inner # Bind the keys for workspaces for name in workspace_names: keys.extend([ Key([mod], name, lazy.function(switch_to_workspace(name)), desc=f"Switch to workspace {name}"), Key([mod, "shift"], name, lazy.function(move_window_to_workspace(name)), desc=f"Move window to workspace {name}"), ]) # Workspace navigation (Ctrl + Alt + Arrow keys) def next_workspace(): def _inner(qtile): current_group = qtile.screens[0].group.name # Check screen 0 to see what number we are on current_num = current_group[0] # Get the number from "1R", "2R", etc. idx = workspace_names.index(current_num) next_name = workspace_names[(idx + 1) % len(workspace_names)] switch_to_workspace(next_name)(qtile) return _inner def prev_workspace(): def _inner(qtile): current_group = qtile.screens[0].group.name current_num = current_group[0] idx = workspace_names.index(current_num) prev_name = workspace_names[(idx - 1) % len(workspace_names)] switch_to_workspace(prev_name)(qtile) return _inner keys.extend([ Key(["control", "mod1"], "Right", lazy.function(next_workspace())), Key(["control", "mod1"], "Left", lazy.function(prev_workspace())), ]) # --- LAYOUTS --- layouts = [ layout.Columns(border_focus_stack=["#E5B680", "#E54865"], border_width=4), layout.Max(), layout.MonadTall(), layout.MonadWide(), ] widget_defaults = dict( font="sans", fontsize=12, padding=3, ) extension_defaults = widget_defaults.copy() # --- SCREENS --- # We use this_screen_border to ensure that the workspace indicator # stays highlighted on BOTH monitors even if one isn't focused. screens = [ # Monitor 1 (Primary - Right) Screen( top=bar.Bar([ widget.CurrentLayout(), widget.GroupBox( visible_groups=[f"{n}R" for n in workspace_names], parse_text=lambda x: x[0], disable_drag=True, highlight_method='block', this_current_screen_border='#E5B680', # Highlight when focused this_screen_border='#5c5c5c', # Highlight when NOT focused (but active) active='#ffffff', inactive='#848484', ), widget.Prompt(), widget.WindowName(), widget.Systray(), widget.Clock(format="%Y-%m-%d %a %I:%M %p"), widget.QuickExit(), ], 24), ), # Monitor 2 (Vertical - Left) Screen( top=bar.Bar([ widget.CurrentLayout(), widget.GroupBox( visible_groups=[f"{n}L" for n in workspace_names], parse_text=lambda x: x[0], disable_drag=True, highlight_method='block', this_current_screen_border='#E5B680', this_screen_border='#5c5c5c', active='#ffffff', inactive='#848484', ), widget.WindowName(), widget.Clock(format="%Y-%m-%d %a %I:%M %p"), ], 24), ), ] # --- MOUSE & SETTINGS --- mouse = [ Drag([mod], "Button1", lazy.window.set_position_floating(), start=lazy.window.get_position()), Drag([mod], "Button3", lazy.window.set_size_floating(), start=lazy.window.get_size()), Click([mod], "Button2", lazy.window.bring_to_front()), ] dgroups_key_binder = None dgroups_app_rules = [] follow_mouse_focus = True bring_front_click = False floats_kept_above = True cursor_warp = False floating_layout = layout.Floating( float_rules=[ *layout.Floating.default_float_rules, Match(wm_class="confirmreset"), Match(wm_class="makebranch"), Match(wm_class="maketag"), Match(wm_class="ssh-askpass"), Match(title="branchdialog"), Match(title="pinentry"), ] ) auto_fullscreen = True focus_on_window_activation = "smart" reconfigure_screens = True auto_minimize = True wmname = "LG3D"