refactor(src/window): remove unused ApplicationSwitcher component
This commit is contained in:
4 files changed
+442
-329
No files matched your search
@@ -1,5 +1,5 @@
|
||||
from fabric.utils import logger
|
||||
from fabric.hyprland.widgets import HyprlandWorkspaces, WorkspaceButton
|
||||
from fabric.utils import logger
|
||||
from fabric.widgets.box import Box
|
||||
|
||||
from services.config import get_config, on_config_change
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from fabric.utils import Gdk, GdkPixbuf, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.eventbox import EventBox
|
||||
from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
from services.config import config, on_config_change
|
||||
from services.modus import get_clients, get_active_window, focus_window
|
||||
from utils.functions import is_special_workspace
|
||||
from utils.icon_resolver import IconResolver
|
||||
|
||||
|
||||
class ApplicationSwitcher(Window):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="application-switcher",
|
||||
title="modus-switcher",
|
||||
layer="top",
|
||||
anchor="center",
|
||||
exclusivity="auto",
|
||||
keyboard_mode="exclusive",
|
||||
visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.icon_resolver = IconResolver()
|
||||
self.windows = []
|
||||
self.current_index = 0
|
||||
self.tab_pressed = False
|
||||
self.icon_size = 96
|
||||
self._pixbuf_cache: Dict[Tuple[str, int], GdkPixbuf.Pixbuf] = {}
|
||||
|
||||
on_config_change(self._on_config_changed)
|
||||
|
||||
container = Box(
|
||||
name="application-switcher-container",
|
||||
orientation="v",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
expand=True,
|
||||
)
|
||||
self.add(container)
|
||||
|
||||
self.view = Box(
|
||||
name="application-switcher-view",
|
||||
orientation="h",
|
||||
spacing=16,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
container.add(self.view)
|
||||
|
||||
self.selection_label = Label(
|
||||
name="switcher-selection-label",
|
||||
label="",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
style_classes=["switcher-selection-label"],
|
||||
)
|
||||
container.add(self.selection_label)
|
||||
|
||||
self.workspace_label = Label(
|
||||
name="switcher-workspace-label",
|
||||
label="",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
style_classes=["switcher-workspace-label"],
|
||||
)
|
||||
container.add(self.workspace_label)
|
||||
|
||||
self.connect("key-press-event", self.on_key_press)
|
||||
self.connect("key-release-event", self.on_key_release)
|
||||
|
||||
self.show_all()
|
||||
self.hide()
|
||||
|
||||
@property
|
||||
def items_per_row(self):
|
||||
return config().get("window_switcher_items_per_row", 10)
|
||||
|
||||
def _on_config_changed(self, new_config, old_config):
|
||||
if config().has_changed(
|
||||
"hide_special_workspace", old_config
|
||||
) or config().has_changed("window_switcher_items_per_row", old_config):
|
||||
if self.get_visible():
|
||||
self.update_windows()
|
||||
|
||||
def show_switcher(self) -> None:
|
||||
self.update_windows()
|
||||
if not self.windows:
|
||||
return
|
||||
|
||||
self.show()
|
||||
self.grab_keyboard()
|
||||
self.tab_pressed = False
|
||||
|
||||
def hide_switcher(self) -> None:
|
||||
self.hide()
|
||||
self.ungrab_keyboard()
|
||||
|
||||
def create_icon_for_window(self, window):
|
||||
class_name = window.get("class", "").lower()
|
||||
key = (class_name, self.icon_size)
|
||||
if key not in self._pixbuf_cache:
|
||||
icon_img = self.icon_resolver.get_icon_pixbuf(class_name, self.icon_size)
|
||||
if not icon_img:
|
||||
icon_img = self.icon_resolver.get_icon_pixbuf(
|
||||
"application-x-executable-symbolic", self.icon_size
|
||||
)
|
||||
if icon_img:
|
||||
self._pixbuf_cache[key] = icon_img
|
||||
icon_img = self._pixbuf_cache.get(key)
|
||||
icon_image = Image()
|
||||
if icon_img:
|
||||
icon_image.set_from_pixbuf(icon_img)
|
||||
return icon_image
|
||||
|
||||
def _is_special_workspace(self, client):
|
||||
return is_special_workspace(client)
|
||||
|
||||
def update_windows(self) -> None:
|
||||
for child in self.view.get_children():
|
||||
child.destroy()
|
||||
|
||||
try:
|
||||
clients = get_clients()
|
||||
if not clients:
|
||||
return
|
||||
|
||||
hide_special = config().get("hide_special_workspace", True)
|
||||
|
||||
filtered_windows = []
|
||||
for c in clients:
|
||||
if c.get("hidden", False):
|
||||
continue
|
||||
if hide_special and self._is_special_workspace(c):
|
||||
continue
|
||||
filtered_windows.append(c)
|
||||
|
||||
self.windows = filtered_windows
|
||||
|
||||
active_window = get_active_window()
|
||||
|
||||
self.current_index = 0
|
||||
if active_window:
|
||||
for i, window in enumerate(self.windows):
|
||||
if window.get("address") == active_window.get("address"):
|
||||
self.current_index = i
|
||||
break
|
||||
|
||||
rows_box = Box(orientation="v", spacing=16)
|
||||
self.view.add(rows_box)
|
||||
|
||||
current_row = None
|
||||
items_per_row = self.items_per_row
|
||||
|
||||
for i, window in enumerate(self.windows):
|
||||
if i % items_per_row == 0:
|
||||
current_row = Box(
|
||||
name="window-row",
|
||||
orientation="h",
|
||||
spacing=16,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
rows_box.add(current_row)
|
||||
|
||||
icon_image = self.create_icon_for_window(window)
|
||||
|
||||
button_content = Box(
|
||||
name="switcher-button",
|
||||
orientation="v",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
children=[
|
||||
Box(
|
||||
name="switcher-icon-box",
|
||||
style_classes=["window-basic", "sleek-border"],
|
||||
children=[icon_image],
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
event_box = EventBox(
|
||||
name="window-button",
|
||||
style_classes=["active"] if i == self.current_index else None,
|
||||
child=button_content,
|
||||
events=["button-press-event"],
|
||||
)
|
||||
event_box.connect(
|
||||
"button-press-event", lambda w, e, idx=i: self._on_item_clicked(idx)
|
||||
)
|
||||
current_row.add(event_box)
|
||||
|
||||
self.view.show_all()
|
||||
self.update_selection()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update windows: {e}")
|
||||
|
||||
def on_key_press(self, _, event):
|
||||
keyval = event.keyval
|
||||
state = event.state
|
||||
alt_pressed = bool(state & Gdk.ModifierType.MOD1_MASK)
|
||||
|
||||
if not self.windows:
|
||||
return False
|
||||
|
||||
if keyval == Gdk.KEY_Escape:
|
||||
self.hide_switcher()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Tab:
|
||||
if not self.tab_pressed or alt_pressed:
|
||||
self.current_index = (self.current_index + 1) % len(self.windows)
|
||||
self.update_selection()
|
||||
self.tab_pressed = True
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_ISO_Left_Tab or (
|
||||
keyval == Gdk.KEY_Tab and (state & Gdk.ModifierType.SHIFT_MASK)
|
||||
):
|
||||
self.current_index = (self.current_index - 1) % len(self.windows)
|
||||
self.update_selection()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Return:
|
||||
self.activate_selected()
|
||||
self.hide_switcher()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Right or keyval == Gdk.KEY_l:
|
||||
self.current_index = (self.current_index + 1) % len(self.windows)
|
||||
self.update_selection()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Left or keyval == Gdk.KEY_h:
|
||||
self.current_index = (self.current_index - 1) % len(self.windows)
|
||||
self.update_selection()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Down:
|
||||
next_index = self.current_index + self.items_per_row
|
||||
if next_index < len(self.windows):
|
||||
self.current_index = next_index
|
||||
self.update_selection()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Up:
|
||||
next_index = self.current_index - self.items_per_row
|
||||
if next_index >= 0:
|
||||
self.current_index = next_index
|
||||
self.update_selection()
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def on_key_release(self, _, event):
|
||||
keyval = event.keyval
|
||||
|
||||
if keyval in (Gdk.KEY_Alt_L, Gdk.KEY_Alt_R):
|
||||
self.activate_selected()
|
||||
self.hide_switcher()
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Tab:
|
||||
self.tab_pressed = False
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def update_selection(self):
|
||||
all_buttons = []
|
||||
rows_box = self.view.get_children()[0] if self.view.get_children() else None
|
||||
if rows_box:
|
||||
for row in rows_box.get_children():
|
||||
all_buttons.extend(row.get_children())
|
||||
|
||||
for i, child in enumerate(all_buttons):
|
||||
if i == self.current_index:
|
||||
child.add_style_class("active")
|
||||
current_window = self.windows[self.current_index]
|
||||
app_class = current_window.get("class", "Unknown")
|
||||
if app_class.islower():
|
||||
app_class = app_class.capitalize()
|
||||
self.selection_label.set_label(app_class)
|
||||
|
||||
workspace = current_window.get("workspace", {})
|
||||
workspace_name = workspace.get("name", "Unknown")
|
||||
self.workspace_label.set_label(f"Workspace {workspace_name}")
|
||||
else:
|
||||
child.remove_style_class("active")
|
||||
|
||||
def activate_selected(self):
|
||||
if not self.windows or self.current_index >= len(self.windows):
|
||||
return
|
||||
|
||||
window = self.windows[self.current_index]
|
||||
address = window.get("address")
|
||||
if address:
|
||||
focus_window(address)
|
||||
|
||||
def _on_item_clicked(self, index):
|
||||
self.current_index = index
|
||||
self.update_selection()
|
||||
self.activate_selected()
|
||||
self.hide_switcher()
|
||||
|
||||
def grab_keyboard(self):
|
||||
try:
|
||||
display = Gdk.Display.get_default()
|
||||
seat = display.get_default_seat()
|
||||
window = self.get_window()
|
||||
seat.grab(window, Gdk.SeatCapabilities.KEYBOARD, False, None, None, None)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to grab keyboard: {e}")
|
||||
|
||||
def ungrab_keyboard(self):
|
||||
try:
|
||||
display = Gdk.Display.get_default()
|
||||
seat = display.get_default_seat()
|
||||
seat.ungrab()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to ungrab keyboard: {e}")
|
||||
@@ -0,0 +1,3 @@
|
||||
from window.switcher.main import ApplicationSwitcher
|
||||
|
||||
__all__ = ["ApplicationSwitcher"]
|
||||
@@ -0,0 +1,438 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from fabric.utils import Gdk, GdkPixbuf, GLib, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.eventbox import EventBox
|
||||
from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.overlay import Overlay
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
from services.config import config, on_config_change
|
||||
from services.modus import close_window, focus_window, get_active_window, get_clients
|
||||
from utils.functions import is_special_workspace
|
||||
from utils.icon_resolver import IconResolver
|
||||
|
||||
|
||||
class _SwitcherItem:
|
||||
__slots__ = (
|
||||
"button",
|
||||
"item_box",
|
||||
"icon_box",
|
||||
"name_label",
|
||||
"image",
|
||||
"overlay",
|
||||
"badge_box",
|
||||
"badge_label",
|
||||
"address",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
button,
|
||||
item_box,
|
||||
icon_box,
|
||||
name_label,
|
||||
image,
|
||||
overlay,
|
||||
badge_box,
|
||||
badge_label,
|
||||
address,
|
||||
):
|
||||
self.button = button
|
||||
self.item_box = item_box
|
||||
self.icon_box = icon_box
|
||||
self.name_label = name_label
|
||||
self.image = image
|
||||
self.overlay = overlay
|
||||
self.badge_box = badge_box
|
||||
self.badge_label = badge_label
|
||||
self.address = address
|
||||
|
||||
|
||||
class ApplicationSwitcher(Window):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="application-switcher",
|
||||
title="modus-switcher",
|
||||
layer="top",
|
||||
anchor="center",
|
||||
keyboard_mode="exclusive",
|
||||
visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.icon_resolver = IconResolver()
|
||||
self.windows: list[dict] = []
|
||||
self.current_index = 0
|
||||
self._prev_index = -1
|
||||
self.icon_size = 80
|
||||
self._pixbuf_cache: OrderedDict = OrderedDict()
|
||||
self._items: list[_SwitcherItem] = []
|
||||
self._pending_close = None
|
||||
|
||||
on_config_change(self._on_config_changed)
|
||||
|
||||
self.view = Box(
|
||||
name="app-switcher-view",
|
||||
orientation="h",
|
||||
spacing=4,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
|
||||
container = Box(
|
||||
name="app-switcher-container",
|
||||
orientation="v",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
children=[self.view],
|
||||
)
|
||||
self.add(container)
|
||||
|
||||
self.connect("key-press-event", self.on_key_press)
|
||||
self.connect("key-release-event", self.on_key_release)
|
||||
|
||||
self.show_all()
|
||||
self.set_visible(False)
|
||||
|
||||
def _on_config_changed(self, new_config, old_config):
|
||||
if config().has_changed("hide_special_workspace", old_config):
|
||||
if self.get_visible():
|
||||
self._rebuild()
|
||||
|
||||
def show_switcher(self) -> None:
|
||||
self._rebuild()
|
||||
if not self.windows:
|
||||
return
|
||||
|
||||
self.set_opacity(0.0)
|
||||
self.show()
|
||||
self.ungrab_keyboard()
|
||||
self.grab_keyboard()
|
||||
GLib.timeout_add(16, self._fade_in)
|
||||
|
||||
def _fade_in(self):
|
||||
current = self.get_opacity()
|
||||
if current >= 1.0:
|
||||
return False
|
||||
self.set_opacity(min(1.0, current + 0.15))
|
||||
return True
|
||||
|
||||
def hide_switcher(self) -> None:
|
||||
self.set_opacity(0.0)
|
||||
if self._pending_close is not None:
|
||||
close_window(self._pending_close)
|
||||
self._pending_close = None
|
||||
GLib.timeout_add(150, self.hide)
|
||||
self.ungrab_keyboard()
|
||||
|
||||
def _get_pixbuf(self, class_name: str) -> GdkPixbuf.Pixbuf | None:
|
||||
key = (class_name, self.icon_size)
|
||||
if key in self._pixbuf_cache:
|
||||
return self._pixbuf_cache[key]
|
||||
|
||||
if len(self._pixbuf_cache) > 128:
|
||||
self._pixbuf_cache.popitem(last=False)
|
||||
|
||||
pixbuf = self.icon_resolver.get_icon_pixbuf(class_name, self.icon_size)
|
||||
if not pixbuf:
|
||||
pixbuf = self.icon_resolver.get_icon_pixbuf(
|
||||
"application-x-executable-symbolic", self.icon_size
|
||||
)
|
||||
if pixbuf:
|
||||
self._pixbuf_cache[key] = pixbuf
|
||||
return pixbuf
|
||||
|
||||
def _create_item(self) -> _SwitcherItem:
|
||||
image = Image()
|
||||
overlay = Overlay(child=image)
|
||||
|
||||
name_label = Label(
|
||||
label="",
|
||||
name="app-switcher-item-label",
|
||||
h_align="center",
|
||||
)
|
||||
|
||||
icon_box = Box(
|
||||
name="app-switcher-icon-box",
|
||||
children=[overlay],
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
|
||||
item_box = Box(
|
||||
name="app-switcher-item",
|
||||
orientation="v",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
spacing=2,
|
||||
children=[icon_box, name_label],
|
||||
)
|
||||
|
||||
event_box = EventBox(
|
||||
name="app-switcher-button",
|
||||
child=item_box,
|
||||
events=["button-press-event"],
|
||||
can_focus=False,
|
||||
)
|
||||
event_box.connect("button-press-event", self._on_item_clicked)
|
||||
event_box.connect("enter-notify-event", self._on_item_enter)
|
||||
event_box.connect("leave-notify-event", self._on_item_leave)
|
||||
|
||||
return _SwitcherItem(
|
||||
button=event_box,
|
||||
item_box=item_box,
|
||||
icon_box=icon_box,
|
||||
name_label=name_label,
|
||||
image=image,
|
||||
overlay=overlay,
|
||||
badge_box=None,
|
||||
badge_label=None,
|
||||
address=None,
|
||||
)
|
||||
|
||||
def _clear_states(self):
|
||||
for item in self._items:
|
||||
item.button.remove_style_class("hovered")
|
||||
item.button.remove_style_class("selected")
|
||||
item.name_label.set_opacity(0.0)
|
||||
|
||||
def _rebuild(self) -> None:
|
||||
try:
|
||||
self._prev_index = -1
|
||||
self._clear_states()
|
||||
|
||||
for child in list(self.view.get_children()):
|
||||
if not any(item.button == child for item in self._items):
|
||||
self.view.remove(child)
|
||||
child.destroy()
|
||||
|
||||
clients = get_clients()
|
||||
if not clients:
|
||||
self.windows = []
|
||||
for item in self._items:
|
||||
item.button.set_visible(False)
|
||||
return
|
||||
|
||||
hide_special = config().get("hide_special_workspace", True)
|
||||
filtered = []
|
||||
for c in clients:
|
||||
if c.get("hidden", False):
|
||||
continue
|
||||
if hide_special and is_special_workspace(c):
|
||||
continue
|
||||
filtered.append(c)
|
||||
|
||||
active = get_active_window()
|
||||
active_addr = active.get("address") if active else None
|
||||
if active_addr:
|
||||
idx = next(
|
||||
(
|
||||
i
|
||||
for i, w in enumerate(filtered)
|
||||
if w.get("address") == active_addr
|
||||
),
|
||||
None,
|
||||
)
|
||||
if idx is not None and idx > 0:
|
||||
filtered.insert(0, filtered.pop(idx))
|
||||
|
||||
self.windows = [
|
||||
{
|
||||
"address": w["address"],
|
||||
"class": w.get("class") or "Unknown",
|
||||
"workspace_id": w.get("workspace", {}).get("id"),
|
||||
}
|
||||
for w in filtered
|
||||
]
|
||||
self.current_index = 0
|
||||
|
||||
while len(self._items) < len(self.windows):
|
||||
item = self._create_item()
|
||||
self._items.append(item)
|
||||
self.view.add(item.button)
|
||||
|
||||
for i, item in enumerate(self._items):
|
||||
visible = i < len(self.windows)
|
||||
item.button.set_visible(visible)
|
||||
if not visible:
|
||||
item.button.remove_style_class("hovered")
|
||||
|
||||
for i, item in enumerate(self._items[: len(self.windows)]):
|
||||
win = self.windows[i]
|
||||
item.address = win["address"]
|
||||
|
||||
pixbuf = self._get_pixbuf(win["class"].lower())
|
||||
if pixbuf:
|
||||
item.image.set_from_pixbuf(pixbuf)
|
||||
item.name_label.set_text(win["class"])
|
||||
|
||||
ws_id = win["workspace_id"]
|
||||
if ws_id is not None:
|
||||
if item.badge_box is None:
|
||||
badge_label = Label(
|
||||
label=str(ws_id),
|
||||
name="app-switcher-workspace-badge-text",
|
||||
)
|
||||
badge_box = Box(
|
||||
name="app-switcher-workspace-badge",
|
||||
children=[badge_label],
|
||||
h_align="end",
|
||||
v_align="end",
|
||||
)
|
||||
item.overlay.add_overlay(badge_box)
|
||||
item.badge_box = badge_box
|
||||
item.badge_label = badge_label
|
||||
else:
|
||||
item.badge_label.set_text(str(ws_id))
|
||||
item.badge_box.set_visible(True)
|
||||
else:
|
||||
if item.badge_box is not None:
|
||||
item.badge_box.set_visible(False)
|
||||
|
||||
self._update_selection()
|
||||
except Exception:
|
||||
logger.exception("Failed to build switcher")
|
||||
|
||||
def on_key_press(self, _, event):
|
||||
try:
|
||||
return self._on_key_press_impl(_, event)
|
||||
except Exception:
|
||||
logger.exception("on_key_press error")
|
||||
return False
|
||||
|
||||
def _on_key_press_impl(self, _, event):
|
||||
keyval = event.keyval
|
||||
state = event.state
|
||||
name = Gdk.keyval_name(keyval)
|
||||
|
||||
if not self.windows:
|
||||
return False
|
||||
|
||||
if keyval == Gdk.KEY_Escape:
|
||||
self.hide_switcher()
|
||||
return True
|
||||
|
||||
if name in ("Tab", "ISO_Left_Tab", "KP_Tab"):
|
||||
if state & Gdk.ModifierType.SHIFT_MASK or name == "ISO_Left_Tab":
|
||||
self.current_index = (self.current_index - 1) % len(self.windows)
|
||||
else:
|
||||
self.current_index = (self.current_index + 1) % len(self.windows)
|
||||
self._update_selection()
|
||||
return True
|
||||
|
||||
if keyval in (Gdk.KEY_Right, Gdk.KEY_l):
|
||||
self.current_index = (self.current_index + 1) % len(self.windows)
|
||||
self._update_selection()
|
||||
return True
|
||||
|
||||
if keyval in (Gdk.KEY_Left, Gdk.KEY_h):
|
||||
self.current_index = (self.current_index - 1) % len(self.windows)
|
||||
self._update_selection()
|
||||
return True
|
||||
|
||||
if keyval in (Gdk.KEY_Delete, Gdk.KEY_BackSpace):
|
||||
self._close_selected()
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def on_key_release(self, _, event):
|
||||
keyval = event.keyval
|
||||
if keyval in (Gdk.KEY_Alt_L, Gdk.KEY_Alt_R):
|
||||
self._activate_selected()
|
||||
self.hide_switcher()
|
||||
return True
|
||||
return False
|
||||
|
||||
def _update_selection(self):
|
||||
try:
|
||||
if self._prev_index >= 0 and self._prev_index < len(self._items):
|
||||
prev = self._items[self._prev_index]
|
||||
prev.button.remove_style_class("selected")
|
||||
prev.name_label.set_opacity(0.0)
|
||||
|
||||
if self.current_index < len(self._items):
|
||||
curr = self._items[self.current_index]
|
||||
curr.button.add_style_class("selected")
|
||||
curr.name_label.set_opacity(1.0)
|
||||
|
||||
self._prev_index = self.current_index
|
||||
except Exception:
|
||||
logger.exception("_update_selection error")
|
||||
|
||||
def _activate_selected(self):
|
||||
if not self.windows or self.current_index >= len(self.windows):
|
||||
return
|
||||
address = self.windows[self.current_index]["address"]
|
||||
if address:
|
||||
focus_window(address)
|
||||
|
||||
def _close_selected(self):
|
||||
try:
|
||||
self._close_selected_impl()
|
||||
except Exception:
|
||||
logger.exception("_close_selected error")
|
||||
|
||||
def _close_selected_impl(self):
|
||||
if not self.windows or self.current_index >= len(self.windows):
|
||||
return
|
||||
|
||||
idx = self.current_index
|
||||
self._pending_close = self.windows[idx]["address"]
|
||||
|
||||
item = self._items.pop(idx)
|
||||
item.button.set_visible(False)
|
||||
|
||||
self.windows.pop(idx)
|
||||
|
||||
if not self.windows:
|
||||
self.hide_switcher()
|
||||
return
|
||||
|
||||
if idx < self.current_index:
|
||||
self.current_index -= 1
|
||||
elif self.current_index >= len(self.windows):
|
||||
self.current_index = len(self.windows) - 1
|
||||
|
||||
self._prev_index = -1
|
||||
self._update_selection()
|
||||
|
||||
def _on_item_clicked(self, event_box, _event):
|
||||
for i, item in enumerate(self._items):
|
||||
if item.button == event_box:
|
||||
self.current_index = i
|
||||
break
|
||||
self._activate_selected()
|
||||
self.hide_switcher()
|
||||
return True
|
||||
|
||||
def _on_item_enter(self, event_box, _event):
|
||||
event_box.add_style_class("hovered")
|
||||
return False
|
||||
|
||||
def _on_item_leave(self, event_box, _event):
|
||||
event_box.remove_style_class("hovered")
|
||||
return False
|
||||
|
||||
def grab_keyboard(self):
|
||||
try:
|
||||
display = Gdk.Display.get_default()
|
||||
seat = display.get_default_seat()
|
||||
window = self.get_window()
|
||||
status = seat.grab(
|
||||
window, Gdk.SeatCapabilities.KEYBOARD, False, None, None, None
|
||||
)
|
||||
if status != Gdk.GrabStatus.SUCCESS:
|
||||
logger.warning(f"Keyboard grab failed: {status}")
|
||||
except Exception:
|
||||
logger.exception("Failed to grab keyboard")
|
||||
|
||||
def ungrab_keyboard(self):
|
||||
try:
|
||||
display = Gdk.Display.get_default()
|
||||
seat = display.get_default_seat()
|
||||
seat.ungrab()
|
||||
except Exception:
|
||||
logger.exception("Failed to ungrab keyboard")
|
||||
Reference in new issue
Block a user