feat: use fenster ipc (sway/i3 compatible)
This commit is contained in:
@@ -13,12 +13,8 @@ from bar.modules.calendar import CalendarService, CalendarPopup
|
||||
from bar.modules.notmuch import NotmuchWidget
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
from fabric.system_tray.widgets import SystemTray
|
||||
from fabric.river.widgets import (
|
||||
RiverWorkspaces,
|
||||
RiverWorkspaceButton,
|
||||
RiverActiveWindow,
|
||||
get_river_connection,
|
||||
)
|
||||
from bar.widgets.fenster import FensterWorkspaces, FensterWorkspaceButton, FensterActiveWindow
|
||||
from bar.services.fenster import get_i3_connection
|
||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||
from bar.services.system_stats import SystemStatsService
|
||||
|
||||
@@ -28,10 +24,9 @@ from bar.config import VINYL, BATTERY, BAR_HEIGHT, WINDOW_TITLE, NOTMUCH
|
||||
class StatusBar(Window):
|
||||
def __init__(
|
||||
self,
|
||||
display: int,
|
||||
display: str,
|
||||
tray: SystemTray | None = None,
|
||||
monitor: int = 1,
|
||||
river_service=None,
|
||||
):
|
||||
super().__init__(
|
||||
name="bar",
|
||||
@@ -43,17 +38,11 @@ class StatusBar(Window):
|
||||
all_visible=False,
|
||||
monitor=monitor,
|
||||
)
|
||||
if river_service:
|
||||
self.river = river_service
|
||||
else:
|
||||
self.river = get_river_connection()
|
||||
|
||||
self.workspaces = RiverWorkspaces(
|
||||
display,
|
||||
self.workspaces = FensterWorkspaces(
|
||||
output=display,
|
||||
name="workspaces",
|
||||
spacing=4,
|
||||
buttons_factory=lambda ws_id: RiverWorkspaceButton(id=ws_id, label=None),
|
||||
river_service=self.river,
|
||||
)
|
||||
# Create calendar components (refresh every 2 minutes)
|
||||
self.calendar_service = CalendarService(update_interval=120000)
|
||||
@@ -74,7 +63,7 @@ class StatusBar(Window):
|
||||
self.calendar_service.connect("events-changed", self.update_calendar_display)
|
||||
self.system_tray = tray
|
||||
|
||||
self.active_window = RiverActiveWindow(
|
||||
self.active_window = FensterActiveWindow(
|
||||
name="active-window",
|
||||
max_length=50,
|
||||
style="color: #ffffff; font-size: 14px; font-weight: bold;",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import operator
|
||||
from fabric.i3 import I3, I3MessageType
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.utils import idle_add
|
||||
from gi.repository import Gdk
|
||||
from bar.services.fenster import get_i3_connection
|
||||
|
||||
|
||||
class FuzzyWindowFinder(Window):
|
||||
@@ -21,7 +21,9 @@ class FuzzyWindowFinder(Window):
|
||||
visible=False,
|
||||
)
|
||||
|
||||
self._all_windows = ["Test", "Uwu", "Tidal"]
|
||||
self._i3 = get_i3_connection()
|
||||
self._all_windows = []
|
||||
self._refresh_windows()
|
||||
|
||||
self.viewport = Box(name="viewport", spacing=4, orientation="v")
|
||||
|
||||
@@ -46,30 +48,81 @@ class FuzzyWindowFinder(Window):
|
||||
self.add(self.picker_box)
|
||||
self.arrange_viewport("")
|
||||
|
||||
def _refresh_windows(self):
|
||||
"""Refresh the window list via GET_TREE"""
|
||||
self._all_windows = []
|
||||
tree_reply = I3.send_command("", I3MessageType.GET_TREE)
|
||||
if not (tree_reply.is_ok and isinstance(tree_reply.reply, dict)):
|
||||
return
|
||||
|
||||
tree = tree_reply.reply
|
||||
# Traverse: root → outputs → workspaces → containers
|
||||
for output_node in tree.get("nodes", []):
|
||||
for ws_node in output_node.get("nodes", []):
|
||||
ws_num = ws_node.get("num", 0)
|
||||
for con in ws_node.get("nodes", []):
|
||||
if con.get("type") == "con":
|
||||
self._all_windows.append({
|
||||
"id": con.get("id"),
|
||||
"app_id": con.get("app_id", ""),
|
||||
"title": con.get("name", ""),
|
||||
"workspace": ws_num,
|
||||
})
|
||||
for con in ws_node.get("floating_nodes", []):
|
||||
if con.get("type") == "con":
|
||||
self._all_windows.append({
|
||||
"id": con.get("id"),
|
||||
"app_id": con.get("app_id", ""),
|
||||
"title": con.get("name", ""),
|
||||
"workspace": ws_num,
|
||||
})
|
||||
|
||||
def show(self):
|
||||
"""Override show to refresh windows before displaying"""
|
||||
self._refresh_windows()
|
||||
self.arrange_viewport(self.search_entry.get_text())
|
||||
super().show()
|
||||
|
||||
def notify_text(self, entry, *_):
|
||||
text = entry.get_text()
|
||||
self.arrange_viewport(text) # Update list on typing
|
||||
print(text)
|
||||
self.arrange_viewport(text)
|
||||
|
||||
def on_search_entry_key_press(self, widget, event):
|
||||
# if event.keyval in (Gdk.KEY_Up, Gdk.KEY_Down, Gdk.KEY_Left, Gdk.KEY_Right):
|
||||
# self.move_selection_2d(event.keyval)
|
||||
# return True
|
||||
print(event.keyval)
|
||||
if event.keyval in [Gdk.KEY_Escape, 103]:
|
||||
self.hide()
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_search_entry_activate(self, text):
|
||||
print(f"activate {text}")
|
||||
"""Focus the first matching window"""
|
||||
filtered = self._filter_windows(text)
|
||||
if filtered:
|
||||
window_id = filtered[0].get("id")
|
||||
if window_id is not None:
|
||||
I3.send_command(f"[con_id={window_id}] focus")
|
||||
self.hide()
|
||||
|
||||
def _filter_windows(self, query: str) -> list:
|
||||
"""Filter windows based on query matching title or app_id"""
|
||||
if not query:
|
||||
return self._all_windows
|
||||
query_lower = query.lower()
|
||||
return [
|
||||
w for w in self._all_windows
|
||||
if query_lower in w.get("title", "").lower()
|
||||
or query_lower in w.get("app_id", "").lower()
|
||||
]
|
||||
|
||||
def arrange_viewport(self, query: str = ""):
|
||||
self.viewport.children = [] # Clear previous entries
|
||||
|
||||
filtered = [w for w in self._all_windows if query.lower() in w.lower()]
|
||||
filtered = self._filter_windows(query)
|
||||
|
||||
for window in filtered:
|
||||
title = window.get("title", "")
|
||||
app_id = window.get("app_id", "")
|
||||
ws_num = window.get("workspace", 0)
|
||||
display_text = f"[{ws_num}] {app_id}: {title}" if app_id else f"[{ws_num}] {title}"
|
||||
self.viewport.add(
|
||||
Box(name="slot-box", orientation="h", children=[Label(label=window)])
|
||||
Box(name="slot-box", orientation="h", children=[Label(label=display_text)])
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user