feat: use fenster ipc (sway/i3 compatible)
This commit is contained in:
30
bar/main.py
30
bar/main.py
@@ -12,11 +12,9 @@ else:
|
|||||||
logger.configure(handlers=[{"sink": sys.stderr, "level": LOG_LEVEL, "format": "{time} | {level} | {name}:{function}:{line} - {message}"}])
|
logger.configure(handlers=[{"sink": sys.stderr, "level": LOG_LEVEL, "format": "{time} | {level} | {name}:{function}:{line} - {message}"}])
|
||||||
|
|
||||||
from fabric import Application
|
from fabric import Application
|
||||||
|
from fabric.i3 import I3, I3MessageType
|
||||||
from fabric.system_tray.widgets import SystemTray
|
from fabric.system_tray.widgets import SystemTray
|
||||||
from fabric.widgets.wayland import WaylandWindow as Window
|
from fabric.widgets.wayland import WaylandWindow as Window
|
||||||
from fabric.river.widgets import (
|
|
||||||
get_river_connection,
|
|
||||||
)
|
|
||||||
from fabric.utils import (
|
from fabric.utils import (
|
||||||
get_relative_path,
|
get_relative_path,
|
||||||
)
|
)
|
||||||
@@ -24,10 +22,11 @@ from .modules.bar import StatusBar
|
|||||||
from .modules.window_fuzzy import FuzzyWindowFinder
|
from .modules.window_fuzzy import FuzzyWindowFinder
|
||||||
from .modules.stylix import get_stylix_css_path
|
from .modules.stylix import get_stylix_css_path
|
||||||
from .config import STYLIX
|
from .config import STYLIX
|
||||||
|
from .services.fenster import get_i3_connection
|
||||||
|
|
||||||
|
|
||||||
tray = SystemTray(name="system-tray", spacing=4)
|
tray = SystemTray(name="system-tray", spacing=4)
|
||||||
river = get_river_connection()
|
i3 = get_i3_connection()
|
||||||
|
|
||||||
dummy = Window(visible=False)
|
dummy = Window(visible=False)
|
||||||
finder = FuzzyWindowFinder()
|
finder = FuzzyWindowFinder()
|
||||||
@@ -56,17 +55,22 @@ else:
|
|||||||
|
|
||||||
def spawn_bars():
|
def spawn_bars():
|
||||||
global notmuch_widget
|
global notmuch_widget
|
||||||
logger.info("[Bar] Spawning bars after river ready")
|
logger.info("[Bar] Spawning bars")
|
||||||
outputs = river.outputs
|
outputs_reply = I3.send_command("", I3MessageType.GET_OUTPUTS)
|
||||||
|
|
||||||
if not outputs:
|
if not (outputs_reply.is_ok and isinstance(outputs_reply.reply, list)):
|
||||||
logger.warning("[Bar] No outputs found — skipping bar spawn")
|
logger.warning("[Bar] Failed to get outputs — skipping bar spawn")
|
||||||
return
|
return
|
||||||
|
|
||||||
output_ids = sorted(outputs.keys())
|
outputs = [o for o in outputs_reply.reply if o.get("active")]
|
||||||
|
|
||||||
for i, output_id in enumerate(output_ids):
|
if not outputs:
|
||||||
bar = StatusBar(display=output_id, tray=tray if i == 0 else None, monitor=i)
|
logger.warning("[Bar] No active outputs found — skipping bar spawn")
|
||||||
|
return
|
||||||
|
|
||||||
|
for i, output in enumerate(outputs):
|
||||||
|
output_name = output.get("name", f"Unknown-{i}")
|
||||||
|
bar = StatusBar(display=output_name, tray=tray if i == 0 else None, monitor=i)
|
||||||
bar_windows.append(bar)
|
bar_windows.append(bar)
|
||||||
if i == 0 and bar.notmuch:
|
if i == 0 and bar.notmuch:
|
||||||
notmuch_widget = bar.notmuch
|
notmuch_widget = bar.notmuch
|
||||||
@@ -75,10 +79,10 @@ def spawn_bars():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if river.ready:
|
if i3.ready:
|
||||||
spawn_bars()
|
spawn_bars()
|
||||||
else:
|
else:
|
||||||
river.connect("notify::ready", lambda sender, pspec: spawn_bars())
|
i3.connect("notify::ready", lambda *_: spawn_bars())
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,8 @@ from bar.modules.calendar import CalendarService, CalendarPopup
|
|||||||
from bar.modules.notmuch import NotmuchWidget
|
from bar.modules.notmuch import NotmuchWidget
|
||||||
from fabric.widgets.wayland import WaylandWindow as Window
|
from fabric.widgets.wayland import WaylandWindow as Window
|
||||||
from fabric.system_tray.widgets import SystemTray
|
from fabric.system_tray.widgets import SystemTray
|
||||||
from fabric.river.widgets import (
|
from bar.widgets.fenster import FensterWorkspaces, FensterWorkspaceButton, FensterActiveWindow
|
||||||
RiverWorkspaces,
|
from bar.services.fenster import get_i3_connection
|
||||||
RiverWorkspaceButton,
|
|
||||||
RiverActiveWindow,
|
|
||||||
get_river_connection,
|
|
||||||
)
|
|
||||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||||
from bar.services.system_stats import SystemStatsService
|
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):
|
class StatusBar(Window):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
display: int,
|
display: str,
|
||||||
tray: SystemTray | None = None,
|
tray: SystemTray | None = None,
|
||||||
monitor: int = 1,
|
monitor: int = 1,
|
||||||
river_service=None,
|
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name="bar",
|
name="bar",
|
||||||
@@ -43,17 +38,11 @@ class StatusBar(Window):
|
|||||||
all_visible=False,
|
all_visible=False,
|
||||||
monitor=monitor,
|
monitor=monitor,
|
||||||
)
|
)
|
||||||
if river_service:
|
|
||||||
self.river = river_service
|
|
||||||
else:
|
|
||||||
self.river = get_river_connection()
|
|
||||||
|
|
||||||
self.workspaces = RiverWorkspaces(
|
self.workspaces = FensterWorkspaces(
|
||||||
display,
|
output=display,
|
||||||
name="workspaces",
|
name="workspaces",
|
||||||
spacing=4,
|
spacing=4,
|
||||||
buttons_factory=lambda ws_id: RiverWorkspaceButton(id=ws_id, label=None),
|
|
||||||
river_service=self.river,
|
|
||||||
)
|
)
|
||||||
# Create calendar components (refresh every 2 minutes)
|
# Create calendar components (refresh every 2 minutes)
|
||||||
self.calendar_service = CalendarService(update_interval=120000)
|
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.calendar_service.connect("events-changed", self.update_calendar_display)
|
||||||
self.system_tray = tray
|
self.system_tray = tray
|
||||||
|
|
||||||
self.active_window = RiverActiveWindow(
|
self.active_window = FensterActiveWindow(
|
||||||
name="active-window",
|
name="active-window",
|
||||||
max_length=50,
|
max_length=50,
|
||||||
style="color: #ffffff; font-size: 14px; font-weight: bold;",
|
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.wayland import WaylandWindow as Window
|
||||||
from fabric.widgets.box import Box
|
from fabric.widgets.box import Box
|
||||||
from fabric.widgets.label import Label
|
from fabric.widgets.label import Label
|
||||||
from fabric.widgets.entry import Entry
|
from fabric.widgets.entry import Entry
|
||||||
from fabric.utils import idle_add
|
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
from bar.services.fenster import get_i3_connection
|
||||||
|
|
||||||
|
|
||||||
class FuzzyWindowFinder(Window):
|
class FuzzyWindowFinder(Window):
|
||||||
@@ -21,7 +21,9 @@ class FuzzyWindowFinder(Window):
|
|||||||
visible=False,
|
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")
|
self.viewport = Box(name="viewport", spacing=4, orientation="v")
|
||||||
|
|
||||||
@@ -46,30 +48,81 @@ class FuzzyWindowFinder(Window):
|
|||||||
self.add(self.picker_box)
|
self.add(self.picker_box)
|
||||||
self.arrange_viewport("")
|
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, *_):
|
def notify_text(self, entry, *_):
|
||||||
text = entry.get_text()
|
text = entry.get_text()
|
||||||
self.arrange_viewport(text) # Update list on typing
|
self.arrange_viewport(text)
|
||||||
print(text)
|
|
||||||
|
|
||||||
def on_search_entry_key_press(self, widget, event):
|
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]:
|
if event.keyval in [Gdk.KEY_Escape, 103]:
|
||||||
self.hide()
|
self.hide()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def on_search_entry_activate(self, text):
|
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 = ""):
|
def arrange_viewport(self, query: str = ""):
|
||||||
self.viewport.children = [] # Clear previous entries
|
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:
|
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(
|
self.viewport.add(
|
||||||
Box(name="slot-box", orientation="h", children=[Label(label=window)])
|
Box(name="slot-box", orientation="h", children=[Label(label=display_text)])
|
||||||
)
|
)
|
||||||
|
|||||||
29
bar/services/fenster.py
Normal file
29
bar/services/fenster.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""
|
||||||
|
Fenster/Sway IPC connection helper.
|
||||||
|
|
||||||
|
Provides a singleton I3 connection configured for Fenster's SWAYSOCK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from fabric.i3 import I3
|
||||||
|
|
||||||
|
|
||||||
|
_connection: I3 | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_i3_connection() -> I3:
|
||||||
|
"""Get the singleton I3 connection, configured for Fenster."""
|
||||||
|
global _connection
|
||||||
|
if _connection is None:
|
||||||
|
swaysock = os.environ.get("SWAYSOCK")
|
||||||
|
if swaysock:
|
||||||
|
I3.SOCKET_PATH = swaysock
|
||||||
|
elif not I3.SOCKET_PATH:
|
||||||
|
runtime_dir = os.environ.get(
|
||||||
|
"XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"
|
||||||
|
)
|
||||||
|
fallback = os.path.join(runtime_dir, "fenster.sock")
|
||||||
|
if os.path.exists(fallback):
|
||||||
|
I3.SOCKET_PATH = fallback
|
||||||
|
_connection = I3()
|
||||||
|
return _connection
|
||||||
221
bar/widgets/fenster.py
Normal file
221
bar/widgets/fenster.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
"""
|
||||||
|
Fenster widgets for workspace and window management via sway IPC.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fabric.i3 import I3, I3Event, I3MessageType
|
||||||
|
from fabric.utils.helpers import bulk_connect
|
||||||
|
from fabric.widgets.box import Box
|
||||||
|
from fabric.widgets.button import Button
|
||||||
|
from fabric.widgets.label import Label
|
||||||
|
from bar.services.fenster import get_i3_connection
|
||||||
|
|
||||||
|
|
||||||
|
class FensterWorkspaceButton(Button):
|
||||||
|
"""Button representing a single workspace"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
workspace_num: int,
|
||||||
|
i3: I3 | None = None,
|
||||||
|
label: str | None = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
self._workspace_num = workspace_num
|
||||||
|
self._i3 = i3 or get_i3_connection()
|
||||||
|
|
||||||
|
display_label = label if label is not None else str(workspace_num)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
name=f"workspace-button-{workspace_num}",
|
||||||
|
child=Label(label=display_label),
|
||||||
|
on_clicked=self._on_clicked,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_style_class("workspace-button")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def workspace_num(self) -> int:
|
||||||
|
return self._workspace_num
|
||||||
|
|
||||||
|
def _on_clicked(self, *args):
|
||||||
|
self._i3.send_command(f"workspace {self._workspace_num}")
|
||||||
|
|
||||||
|
def set_focused(self, focused: bool):
|
||||||
|
if focused:
|
||||||
|
self.add_style_class("focused")
|
||||||
|
else:
|
||||||
|
self.remove_style_class("focused")
|
||||||
|
|
||||||
|
def set_visible_on_output(self, visible: bool):
|
||||||
|
if visible:
|
||||||
|
self.add_style_class("visible")
|
||||||
|
else:
|
||||||
|
self.remove_style_class("visible")
|
||||||
|
|
||||||
|
def set_has_windows(self, has_windows: bool):
|
||||||
|
if has_windows:
|
||||||
|
self.add_style_class("has-windows")
|
||||||
|
else:
|
||||||
|
self.remove_style_class("has-windows")
|
||||||
|
|
||||||
|
|
||||||
|
class FensterWorkspaces(Box):
|
||||||
|
"""Container widget showing all workspaces"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
output: str | None = None,
|
||||||
|
i3: I3 | None = None,
|
||||||
|
buttons_factory=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name=kwargs.pop("name", "workspaces"),
|
||||||
|
spacing=kwargs.pop("spacing", 4),
|
||||||
|
orientation="h",
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._output = output
|
||||||
|
self._i3 = i3 or get_i3_connection()
|
||||||
|
self._buttons_factory = buttons_factory or self._default_button_factory
|
||||||
|
self._buttons = {}
|
||||||
|
|
||||||
|
bulk_connect(
|
||||||
|
self._i3,
|
||||||
|
{
|
||||||
|
"event::workspace::focus": self._on_workspace_event,
|
||||||
|
"event::workspace::init": self._on_workspace_event,
|
||||||
|
"event::workspace::empty": self._on_workspace_event,
|
||||||
|
"event::workspace::urgent": self._on_workspace_event,
|
||||||
|
"event::window::new": self._on_window_event,
|
||||||
|
"event::window::close": self._on_window_event,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._i3.ready:
|
||||||
|
self._refresh_workspaces()
|
||||||
|
else:
|
||||||
|
self._i3.connect("notify::ready", lambda *_: self._refresh_workspaces())
|
||||||
|
|
||||||
|
def _default_button_factory(self, workspace_num: int) -> FensterWorkspaceButton:
|
||||||
|
return FensterWorkspaceButton(workspace_num=workspace_num, i3=self._i3)
|
||||||
|
|
||||||
|
def _on_workspace_event(self, _, event: I3Event):
|
||||||
|
self._refresh_workspaces()
|
||||||
|
|
||||||
|
def _on_window_event(self, _, event: I3Event):
|
||||||
|
self._refresh_workspaces()
|
||||||
|
|
||||||
|
def _refresh_workspaces(self):
|
||||||
|
reply = I3.send_command("", I3MessageType.GET_WORKSPACES)
|
||||||
|
if reply.is_ok and isinstance(reply.reply, list):
|
||||||
|
self._update_workspaces(reply.reply)
|
||||||
|
|
||||||
|
def _update_workspaces(self, workspaces: list):
|
||||||
|
focused_ws = None
|
||||||
|
workspace_nums = set()
|
||||||
|
for ws in workspaces:
|
||||||
|
ws_num = ws.get("num")
|
||||||
|
if ws_num is not None:
|
||||||
|
workspace_nums.add(ws_num)
|
||||||
|
if ws.get("focused"):
|
||||||
|
focused_ws = ws_num
|
||||||
|
|
||||||
|
# Remove buttons for workspaces that no longer exist
|
||||||
|
for ws_num in list(self._buttons.keys()):
|
||||||
|
if ws_num not in workspace_nums:
|
||||||
|
button = self._buttons.pop(ws_num)
|
||||||
|
self.remove(button)
|
||||||
|
|
||||||
|
# Add/update buttons for current workspaces
|
||||||
|
for ws in sorted(workspaces, key=lambda w: w.get("num", 0)):
|
||||||
|
ws_num = ws.get("num")
|
||||||
|
if ws_num is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ws_num not in self._buttons:
|
||||||
|
button = self._buttons_factory(ws_num)
|
||||||
|
self._buttons[ws_num] = button
|
||||||
|
self.add(button)
|
||||||
|
|
||||||
|
button = self._buttons[ws_num]
|
||||||
|
button.set_focused(ws_num == focused_ws)
|
||||||
|
|
||||||
|
ws_output = ws.get("output")
|
||||||
|
is_visible = ws_output == self._output if self._output is not None else False
|
||||||
|
button.set_visible_on_output(is_visible)
|
||||||
|
|
||||||
|
window_count = ws.get("window_count", 0)
|
||||||
|
button.set_has_windows(window_count > 0)
|
||||||
|
|
||||||
|
# Sort buttons by workspace number
|
||||||
|
sorted_buttons = sorted(self._buttons.values(), key=lambda b: b.workspace_num)
|
||||||
|
for i, button in enumerate(sorted_buttons):
|
||||||
|
self.reorder_child(button, i)
|
||||||
|
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
class FensterActiveWindow(Label):
|
||||||
|
"""Label showing the title of the focused window"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
i3: I3 | None = None,
|
||||||
|
max_length: int = 50,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name=kwargs.pop("name", "active-window"),
|
||||||
|
label="",
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._i3 = i3 or get_i3_connection()
|
||||||
|
self._max_length = max_length
|
||||||
|
|
||||||
|
bulk_connect(
|
||||||
|
self._i3,
|
||||||
|
{
|
||||||
|
"event::window::focus": self._on_window_event,
|
||||||
|
"event::window::title": self._on_window_event,
|
||||||
|
"event::window::close": self._on_window_close,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._i3.ready:
|
||||||
|
self._initialize()
|
||||||
|
else:
|
||||||
|
self._i3.connect("notify::ready", lambda *_: self._initialize())
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
tree_reply = I3.send_command("", I3MessageType.GET_TREE)
|
||||||
|
if tree_reply.is_ok and isinstance(tree_reply.reply, dict):
|
||||||
|
focused = self._find_focused(tree_reply.reply)
|
||||||
|
if focused:
|
||||||
|
self._set_title(focused.get("name", ""))
|
||||||
|
return
|
||||||
|
self.set_label("")
|
||||||
|
|
||||||
|
def _find_focused(self, node: dict) -> dict | None:
|
||||||
|
if node.get("focused") and node.get("type") == "con":
|
||||||
|
return node
|
||||||
|
for child in node.get("nodes", []) + node.get("floating_nodes", []):
|
||||||
|
result = self._find_focused(child)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _on_window_event(self, _, event: I3Event):
|
||||||
|
container = event.data.get("container", {})
|
||||||
|
self._set_title(container.get("name", ""))
|
||||||
|
|
||||||
|
def _on_window_close(self, _, event: I3Event):
|
||||||
|
self._initialize()
|
||||||
|
|
||||||
|
def _set_title(self, title: str):
|
||||||
|
if len(title) > self._max_length:
|
||||||
|
title = title[: self._max_length - 3] + "..."
|
||||||
|
self.set_label(title)
|
||||||
10
flake.lock
generated
10
flake.lock
generated
@@ -6,15 +6,15 @@
|
|||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747045720,
|
"lastModified": 1770146720,
|
||||||
"narHash": "sha256-2Z0F4hnluJZunwRfx80EQXpjGLhunV2wrseT42nzh7M=",
|
"narHash": "sha256-YVlwsUz4SLj8qYAb21ernT3lDB/piU1V6hTW/UjikWA=",
|
||||||
"owner": "Makesesama",
|
"owner": "Fabric-Development",
|
||||||
"repo": "fabric",
|
"repo": "fabric",
|
||||||
"rev": "dae50c763e8bf2b4e5807b49b9e62425e0725cfa",
|
"rev": "fd2aabbd7e1859aa7c11c626a6c36a937aca736a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Makesesama",
|
"owner": "Fabric-Development",
|
||||||
"repo": "fabric",
|
"repo": "fabric",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
nixpkgs.url = "github:NixOS/nixpkgs/24.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/24.11";
|
||||||
unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
fabric.url = "github:Makesesama/fabric";
|
fabric.url = "github:Fabric-Development/fabric";
|
||||||
home-manager.url = "github:nix-community/home-manager";
|
home-manager.url = "github:nix-community/home-manager";
|
||||||
home-manager.inputs.nixpkgs.follows = "nixpkgs";
|
home-manager.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user