Skip to content

Commit

Permalink
Add boolean and func options to .when() (#4544)
Browse files Browse the repository at this point in the history
  • Loading branch information
elParaguayo committed Nov 1, 2023
1 parent 1050e1a commit f634e3a
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 5 deletions.
10 changes: 9 additions & 1 deletion libqtile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@
# SOFTWARE.


qtile = None
class _UndefinedCore:
name = None


class _UndefinedQtile:
core = _UndefinedCore()


qtile = _UndefinedQtile()


def init(q):
Expand Down
33 changes: 30 additions & 3 deletions libqtile/lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
from libqtile.command.client import InteractiveCommandClient
from libqtile.command.graph import CommandGraphCall, CommandGraphNode
from libqtile.command.interface import CommandInterface
from libqtile.log_utils import logger

if TYPE_CHECKING:
from typing import Iterable
from typing import Callable, Iterable

from libqtile.command.graph import SelectorType
from libqtile.config import Match
Expand All @@ -53,6 +54,8 @@ def __init__(self, call: CommandGraphCall, args: tuple, kwargs: dict) -> None:
self._if_no_focused: bool = False
self._layouts: set[str] = set()
self._when_floating = True
self._condition: bool | None = None
self._func: Callable[[], bool] = lambda: True

def __call__(self, *args, **kwargs):
"""Convenience method to allow users to pass arguments to
Expand Down Expand Up @@ -97,10 +100,12 @@ def when(
if_no_focused: bool = False,
layout: Iterable[str] | str | None = None,
when_floating: bool = True,
func: Callable | None = None,
condition: bool | None = None,
) -> "LazyCall":
"""Enable call only for given layout(s) and floating state
"""Enable call only for matching criteria.
Parameters
Keyword parameters
----------
focused: Match or None
Match criteria to enable call for the current window.
Expand All @@ -117,11 +122,20 @@ def when(
If None, enable the call for all layouts.
when_floating: bool
Enable call when the current window is floating.
func: callable
Enable call when the result of the callable evaluates to True
condition: a boolean value to determine whether the lazy object should
be run. Unlike 'func', the condition is evaluated once when the config
file is first loaded.
"""
self._focused = focused

self._if_no_focused = if_no_focused

self._condition = condition
self._func = func

if layout is not None:
self._layouts = {layout} if isinstance(layout, str) else set(layout)

Expand All @@ -131,6 +145,9 @@ def when(
def check(self, q) -> bool:
cur_win_floating = q.current_window and q.current_window.floating

if self._condition is False:
return False

if self._focused:
if q.current_window and not self._focused.compare(q.current_window):
return False
Expand All @@ -144,6 +161,16 @@ def check(self, q) -> bool:
if self._layouts and q.current_layout.name not in self._layouts:
return False

if self._func:
try:
result = self._func()
except Exception:
logger.exception("Error when running function in lazy call. Ignoring.")
result = True

if not result:
return False

return True


Expand Down
3 changes: 2 additions & 1 deletion libqtile/scripts/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from typing import TYPE_CHECKING

import libqtile.backend
from libqtile import confreader
from libqtile import confreader, qtile
from libqtile.utils import get_config_file
from libqtile.log_utils import logger

Expand All @@ -55,6 +55,7 @@ def rename_process():


def make_qtile(options) -> Qtile | None:
qtile.core.name = options.backend
if missing_deps := libqtile.backend.has_deps(options.backend):
print(f"Backend '{options.backend}' missing required Python dependencies:")
for dep in missing_deps:
Expand Down
38 changes: 38 additions & 0 deletions test/test_when.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ class WhenConfig(Config):
focused=config.Match(wm_class="TestWindow"), if_no_focused=True
),
),
config.Key(
["control"],
"t",
lazy.next_layout().when(condition=1 + 1 == 2)
),
config.Key(
["control"],
"f",
lazy.next_layout().when(condition=1 + 1 == 3)
),
config.Key(
["control", "shift"],
"t",
lazy.next_layout().when(func=lambda: True)
),
config.Key(
["control", "shift"],
"f",
lazy.next_layout().when(func=lambda: False)
),
]
layouts = [layout.MonadWide(), layout.MonadTall()]

Expand Down Expand Up @@ -90,3 +110,21 @@ def test_when(manager):
# This does go to the next layout as empty is matched
manager.c.simulate_keypress(["control"], "m")
assert manager.c.layout.info() != prev_layout_info

# Test boolean argument
prev_layout_info = manager.c.layout.info()

manager.c.simulate_keypress(["control"], "f")
assert manager.c.layout.info() == prev_layout_info

manager.c.simulate_keypress(["control"], "t")
assert manager.c.layout.info() != prev_layout_info

# Test function argument
prev_layout_info = manager.c.layout.info()

manager.c.simulate_keypress(["control", "shift"], "f")
assert manager.c.layout.info() == prev_layout_info

manager.c.simulate_keypress(["control", "shift"], "t")
assert manager.c.layout.info() != prev_layout_info

0 comments on commit f634e3a

Please sign in to comment.