♻️ refactor(launcher): improve code structure and plugin functionality

This commit is contained in:
sankalp committed 2025-07-30 13:04:22 +05:45
1 parent 19a2b925d5
commit 0f07104205
31 files changed
+536 -618

No files matched your search

+12 -4
View File
@@ -32,8 +32,12 @@ class CircleImage(Gtk.DrawingArea, Widget):
style: str | None = None,
tooltip_text: str | None = None,
tooltip_markup: str | None = None,
h_align: Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None = None,
v_align: Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None = None,
h_align: (
Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None
) = None,
v_align: (
Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None
) = None,
h_expand: bool = False,
v_expand: bool = False,
size: int | None = None,
@@ -57,7 +61,9 @@ class CircleImage(Gtk.DrawingArea, Widget):
)
self.size = size if size is not None else 100 # Default size if not provided
self._angle = 0
self._orig_image: GdkPixbuf.Pixbuf | None = None # Original image for reprocessing
self._orig_image: GdkPixbuf.Pixbuf | None = (
None # Original image for reprocessing
)
self._image: GdkPixbuf.Pixbuf | None = None
if image_file:
pix = GdkPixbuf.Pixbuf.new_from_file(image_file)
@@ -79,7 +85,9 @@ class CircleImage(Gtk.DrawingArea, Widget):
else:
square_size = width
if square_size != self.size:
pixbuf = pixbuf.scale_simple(self.size, self.size, GdkPixbuf.InterpType.BILINEAR)
pixbuf = pixbuf.scale_simple(
self.size, self.size, GdkPixbuf.InterpType.BILINEAR
)
return pixbuf
def on_draw(self, widget: "CircleImage", ctx: cairo.Context):
+4 -3
View File
@@ -556,9 +556,10 @@ class Conversion:
to_type = self.clean_type(to_type)
if from_type1 == from_type2:
return self.convert(
value1 + value2, from_type1, to_type
), to_type + addition
return (
self.convert(value1 + value2, from_type1, to_type),
to_type + addition,
)
else:
res = 0
res += self.convert(value1, from_type1, to_type)
+6 -2
View File
@@ -16,7 +16,9 @@ if not os.path.exists(data.CACHE_DIR):
class IconResolver:
def __init__(self, default_applicaiton_icon: str = "application-x-executable-symbolic"):
def __init__(
self, default_applicaiton_icon: str = "application-x-executable-symbolic"
):
if os.path.exists(ICON_CACHE_FILE):
with open(ICON_CACHE_FILE) as f:
try:
@@ -79,7 +81,9 @@ class IconResolver:
data_dir = os.path.join(data_dir, "applications")
if os.path.exists(data_dir):
files = os.listdir(data_dir)
matching = [s for s in files if "".join(app_id.lower().split()) in s.lower()]
matching = [
s for s in files if "".join(app_id.lower().split()) in s.lower()
]
if matching:
return os.path.join(data_dir, matching[0])
for word in list(filter(None, re.split(r"-|\.|_|\s", app_id))):
-143
View File
@@ -1,143 +0,0 @@
from fabric.widgets.box import Box
from utils.wayland import WaylandWindow
from config.c import c
import gi # type: ignore
gi.require_version("GtkLayerShell", "0.1")
from gi.repository import Gtk, GtkLayerShell # type: ignore
class PopupWindow(WaylandWindow):
def __init__(
self,
parent: WaylandWindow,
pointing_to: Gtk.Widget | None = None,
margin: tuple[int, ...] | str = "0px 0px 0px 0px",
**kwargs,
):
super().__init__(**kwargs)
self.exclusivity = "none"
self._parent = parent
self._pointing_widget = pointing_to
self._base_margin = self.extract_margin(margin)
self.margin = self._base_margin.values()
self.connect("notify::visible", self.do_update_handlers)
def get_coords_for_widget(self, widget: Gtk.Widget) -> tuple[int, int]:
if not ((toplevel := widget.get_toplevel()) and toplevel.is_toplevel()): # type: ignore
return 0, 0
allocation = widget.get_allocation()
x, y = widget.translate_coordinates(toplevel, allocation.x, allocation.y) or (
0,
0,
)
return round(x / 2), round(y / 2)
def set_pointing_to(self, widget: Gtk.Widget | None):
if self._pointing_widget:
try:
self._pointing_widget.disconnect_by_func(self.do_handle_size_allocate)
except Exception:
pass
self._pointing_widget = widget
return self.do_update_handlers()
def do_update_handlers(self, *_):
if not self._pointing_widget:
return
if not self.get_visible():
try:
self._pointing_widget.disconnect_by_func(self.do_handle_size_allocate)
self.disconnect_by_func(self.do_handle_size_allocate)
except Exception:
pass
return
self._pointing_widget.connect("size-allocate", self.do_handle_size_allocate)
self.connect("size-allocate", self.do_handle_size_allocate)
return self.do_handle_size_allocate()
def do_handle_size_allocate(self, *_):
return self.do_reposition(self.do_calculate_edges())
def do_calculate_edges(self):
move_axe = "x"
parent_anchor = self._parent.anchor
if len(parent_anchor) != 3:
return move_axe
if (
GtkLayerShell.Edge.LEFT in parent_anchor
and GtkLayerShell.Edge.RIGHT in parent_anchor
):
# horizontal -> move on x-axies
move_axe = "x"
if GtkLayerShell.Edge.TOP in parent_anchor:
self.anchor = "left top"
else:
self.anchor = "left bottom"
elif (
GtkLayerShell.Edge.TOP in parent_anchor
and GtkLayerShell.Edge.BOTTOM in parent_anchor
):
# vertical -> move on y-axies
move_axe = "y"
if GtkLayerShell.Edge.RIGHT in parent_anchor:
self.anchor = "top right"
else:
self.anchor = "top left"
return move_axe
def do_reposition(self, move_axe: str):
parent_margin = self._parent.margin
parent_x_margin, parent_y_margin = parent_margin[0], parent_margin[3]
height = self.get_allocated_height()
width = self.get_allocated_width()
if self._pointing_widget:
coords = self.get_coords_for_widget(self._pointing_widget)
if coords[0] + width > int(c.get_rule("Display.resolution").split("x")[0]):
coords_centered = (
round(coords[0] - (width - self._pointing_widget.get_allocated_width())),
round(coords[1] + self._pointing_widget.get_allocated_height() / 2),
)
else:
coords_centered = (
round(coords[0]),
round(coords[1] + self._pointing_widget.get_allocated_height() / 2),
)
else:
coords_centered = (
round(self._parent.get_allocated_width() / 2),
round(self._parent.get_allocated_height() / 2),
)
self.margin = tuple(
a + b
for a, b in zip(
(
(
0,
0,
0,
round((parent_x_margin + coords_centered[0])),
)
if move_axe == "x"
else (
round((parent_y_margin + coords_centered[1]) - (height / 2)),
0,
0,
0,
)
),
self._base_margin.values(),
)
)
+31 -21
View File
@@ -132,13 +132,15 @@ class WaylandWindow(Window):
@keyboard_mode.setter
def keyboard_mode(
self,
value: Literal[
"none",
"exclusive",
"on-demand",
"entry-number",
]
| KeyboardMode,
value: (
Literal[
"none",
"exclusive",
"on-demand",
"entry-number",
]
| KeyboardMode
),
):
self._keyboard_mode = get_enum_member(
KeyboardMode, value, default=KeyboardMode.NONE
@@ -226,10 +228,12 @@ class WaylandWindow(Window):
layer: Literal["background", "bottom", "top", "overlay"] | Layer = Layer.TOP,
anchor: str = "",
margin: str | Iterable[int] = "0px 0px 0px 0px",
exclusivity: Literal["auto", "normal", "none"]
| WaylandWindowExclusivity = WaylandWindowExclusivity.NONE,
keyboard_mode: Literal["none", "exclusive", "on-demand"]
| KeyboardMode = KeyboardMode.NONE,
exclusivity: (
Literal["auto", "normal", "none"] | WaylandWindowExclusivity
) = WaylandWindowExclusivity.NONE,
keyboard_mode: (
Literal["none", "exclusive", "on-demand"] | KeyboardMode
) = KeyboardMode.NONE,
pass_through: bool = False,
monitor: int | Gdk.Monitor | None = None,
title: str = "fabric",
@@ -242,12 +246,12 @@ class WaylandWindow(Window):
style_classes: Iterable[str] | str | None = None,
tooltip_text: str | None = None,
tooltip_markup: str | None = None,
h_align: Literal["fill", "start", "end", "center", "baseline"]
| Gtk.Align
| None = None,
v_align: Literal["fill", "start", "end", "center", "baseline"]
| Gtk.Align
| None = None,
h_align: (
Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None
) = None,
v_align: (
Literal["fill", "start", "end", "center", "baseline"] | Gtk.Align | None
) = None,
h_expand: bool = False,
v_expand: bool = False,
size: Iterable[int] | int | None = None,
@@ -292,7 +296,11 @@ class WaylandWindow(Window):
self.keyboard_mode = keyboard_mode
self.exclusivity = exclusivity
self.pass_through = pass_through
self.show_all() if all_visible is True else self.show() if visible is True else None
(
self.show_all()
if all_visible is True
else self.show() if visible is True else None
)
def steal_input(self) -> None:
return GtkLayerShell.set_keyboard_interactivity(self, True)
@@ -347,9 +355,11 @@ class WaylandWindow(Window):
margins = (
extract_css_values(input.lower())
if isinstance(input, str)
else input
if isinstance(input, (tuple, list)) and len(input) == 4
else (0, 0, 0, 0)
else (
input
if isinstance(input, (tuple, list)) and len(input) == 4
else (0, 0, 0, 0)
)
)
return {
Edge.TOP: margins[0],
-174
View File
@@ -1,174 +0,0 @@
import math
from fabric.core.service import Service, Signal
from gi.repository import Gtk, Gdk, GLib
import gi
gi.require_version("Gtk", "3.0")
class WigglyWidget(Gtk.DrawingArea, Service):
@Signal
def on_seek(self, ratio: float) -> None: ...
def __init__(self):
super().__init__(name="wiggle-bar")
self.phase = 0
self.set_size_request(-1, 20)
self.connect("draw", self.on_draw)
# mouse events
self.add_events(
Gdk.EventMask.BUTTON_PRESS_MASK
| Gdk.EventMask.BUTTON_RELEASE_MASK
| Gdk.EventMask.POINTER_MOTION_MASK
)
self.connect("button-press-event", self.on_button_press)
self.connect("motion-notify-event", self.on_motion)
self.connect("button-release-event", self.on_button_release)
GLib.timeout_add(16, self.update) # 60 FPS
self.value = 0.0
self.amplitude = 2
self.dragging = False
self.pause = False
self.show_all()
def animate_amplitude_to(self):
if abs(self.amplitude - self.amplitude_target) < 0.01:
self.amplitude = self.amplitude_target
self.queue_draw()
return False
else:
self.amplitude += self.amplitude_step
self.queue_draw()
return True
def update_amplitude(self, decrease):
if decrease:
self.amplitude_target = 0
self.amplitude_step = (0 - self.amplitude) / 10.0
else:
self.amplitude_target = 2
self.amplitude_step = (2 - self.amplitude) / 10.0
# animation stops when False is returned btw
GLib.timeout_add(16, self.animate_amplitude_to)
def update_value_from_x(self, x):
width = self.get_allocated_width()
self.value = max(0.0, min(1.0, x / width))
self.on_seek(self.value)
self.queue_draw()
def on_button_press(self, widget, event):
self.dragging = True
self.update_amplitude(True)
self.update_value_from_x(event.x)
return True
# def animate_progress_to(self):
# print("frame")
# if self.value < self.value_target:
# self.value = self.value_target
# print(self.value)
# self.queue_draw()
# return False
# else:
# self.value += self.value_step
# self.queue_draw()
# return True
# def update_value_from_signal(self, new_value):
# self.value_target = min(1.0, new_value)
# steps = 60 # 1s / 16ms ≈ 60 frames
# self.value_step = (self.value_target - self.value) / steps
# GLib.timeout_add(16, self.animate_progress_to)
def update_value_from_signal(self, new_value):
self.value = min(1.0, new_value)
def on_motion(self, widget, event):
if self.dragging and not self.pause:
self.update_value_from_x(event.x)
cursor = Gdk.Cursor.new(Gdk.CursorType.HAND1)
widget.get_window().set_cursor(cursor)
return True
def on_button_release(self, widget, event):
if self.pause == False:
self.dragging = False
self.update_amplitude(False)
return True
def update(self):
if self.dragging == False:
self.phase += 0.1
# if (self.value<1):
# self.value += 0.001
self.queue_draw()
return True
def on_draw(self, widget, cr):
alloc_width = self.get_allocated_width()
height = self.get_allocated_height()
center_y = height / 2
# self.amplitude = 2
frequency = 0.3
slider_diameter = 6
width = int(alloc_width * self.value) - slider_diameter + 1
cr.set_source_rgb(1, 1, 1) # color here btw
cr.set_line_width(2)
last_x = 0 # fallback values in case the slider gets dragged out of range
last_y = 0
cr.move_to(0, center_y)
for x in range(width):
y = center_y + self.amplitude * math.sin((x * frequency) + self.phase)
cr.line_to(x, y)
last_x, last_y = x, y
cr.stroke()
# Set the color using CSS - the widget should have CSS styling applied
# that sets the color to var(--primary). For now, use a fallback approach.
style_context = self.get_style_context()
try:
# Get the foreground color which should be styled with CSS
color = style_context.get_color(Gtk.StateFlags.NORMAL)
cr.set_source_rgba(color.red, color.green, color.blue, color.alpha)
except:
# Fallback to a nice blue color that matches the primary theme
cr.set_source_rgb(0.2, 0.6, 1.0)
rect_width = 6
arc_radius = slider_diameter / 2
self.draw_rounded_rect(cr, last_x, height, rect_width, rect_width, arc_radius)
cr.fill()
cr.stroke()
cr.set_source_rgb(1, 1, 1)
cr.set_line_width(1)
# This is for anti aliasing. When line width becomes 1px and the position is not an integer, two lines of opacity 50% is drawn on two pixel rows.
# This moves the line to make it pixel perfect hence not needing anti aliasing
cr.translate(0, 0.5)
cr.move_to(last_x + 2 * arc_radius, height / 2)
cr.line_to(alloc_width, height / 2)
cr.stroke()
def draw_rounded_rect(self, cr, x, y, width, _height, radius):
cr.new_sub_path()
cr.arc(x + width - radius, radius, radius, -math.pi, 0)
cr.arc(x + width - radius, y - radius, radius, 0, -math.pi)
cr.close_path()