roundabout,
created on Monday, 9 December 2024, 17:56:31 (1733766991),
received on Thursday, 12 December 2024, 14:59:13 (1734015553)
Author identity: vlad <vlad.muntoiu@gmail.com>
76e49229b6f7b8eaab55049e74074291e3140490
izvor.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.38.2 --><!-- Generated with glade 3.40.0 --><interface> <requires lib="gtk+" version="3.24"/> <object class="GtkMenu" id="main-menu">
@@ -23,15 +23,6 @@
<signal name="activate" handler="menu-preferences" swapped="no"/> </object> </child> <child><object class="GtkMenuItem"><property name="visible">True</property><property name="can-focus">False</property><property name="label" translatable="yes">Manage _history</property><property name="use-underline">True</property><signal name="activate" handler="menu-history" swapped="no"/></object></child><child> <object class="GtkSeparatorMenuItem"> <property name="visible">True</property>
@@ -68,6 +59,7 @@
</object> </child> </object> <object class="GtkEntryBuffer" id="search-query-buffer"/><object class="GtkApplicationWindow" id="root"> <property name="can-focus">False</property> <property name="default-width">576</property>
@@ -171,5 +163,4 @@
</object> </child> </object> <object class="GtkEntryBuffer" id="search-query-buffer"/></interface>
main.py
@@ -24,14 +24,17 @@ import gbulb
from pathlib import Path from typing import AsyncIterable, AsyncIterator, Collection, TypeVar, Iterable, Callable, Any from xdg.Config import icon_size gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gdk, Pango, GLib import gettext import json_ = gettext.gettext _T = TypeVar("_T") ICON_SIZE = 32async def merge_generators_with_params(generator_funcs: list[Callable[..., AsyncIterable]], *args, **kwargs):
@@ -52,7 +55,7 @@ async def merge_generators_with_params(generator_funcs: list[Callable[..., Async
class ResultInfoWidget(Gtk.ListBoxRow): def __init__(self, name: str, description: str, image: tuple[str, Any], execute: Callable[[], None]):def __init__(self, name: str, description: str, image: tuple[str, Any], execute: Callable[[], None], icon_size=32, show_description=True):super().__init__() self.name = name self.description = description
@@ -62,10 +65,12 @@ class ResultInfoWidget(Gtk.ListBoxRow):
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) self.add(self.box) if image[0] == "logo":self.image = Gtk.Image.new_from_icon_name(self.image[1], Gtk.IconSize.LARGE_TOOLBAR)self.image.set_pixel_size(ICON_SIZE)self.box.pack_start(self.image, False, False, 0)if icon_size: # If the icon size is 0, have no icons if image[0] == "logo": self.image = Gtk.Image.new_from_icon_name(self.image[1], Gtk.IconSize.LARGE_TOOLBAR) self.image.set_pixel_size(icon_size) self.box.pack_start(self.image, False, False, 0)self.label_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.box.pack_start(self.label_box, True, True, 0)
@@ -79,12 +84,12 @@ class ResultInfoWidget(Gtk.ListBoxRow):
self.name_label.set_attributes(attributes) self.label_box.pack_start(self.name_label, True, True, 0) self.description_label = Gtk.Label(label=self.description)self.description_label.set_line_wrap(True)self.description_label.set_justify(Gtk.Justification.LEFT)self.description_label.set_halign(Gtk.Align.START)self.label_box.pack_start(self.description_label, True, True, 0)if show_description: self.description_label = Gtk.Label(label=self.description) self.description_label.set_line_wrap(True) self.description_label.set_justify(Gtk.Justification.LEFT) self.description_label.set_halign(Gtk.Align.START) self.label_box.pack_start(self.description_label, True, True, 0)class Izvor(Gtk.Application): USER_CONFIGS = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / "izvor"
@@ -96,6 +101,7 @@ class Izvor(Gtk.Application):
SYSTEM_PROVIDERS = SYSTEM_DATA / "providers" SYSTEM_PROVIDER_CONFIGS = SYSTEM_CONFIGS / "providers" ENABLED_PROVIDERS_FILE = USER_CONFIGS / "enabled_providers" APPEARANCE_CONFIG_FILE = USER_CONFIGS / "appearance.json"def __init__(self): super().__init__(application_id="com.roundabout-host.roundabout.izvor")
@@ -106,7 +112,7 @@ class Izvor(Gtk.Application):
self.window = self.builder.get_object("root") self.window.connect("destroy", self.kill) self.window.connect("key-press-event", self.check_escape) self.window.set_title("Izvor")self.window.set_title(_("Launcher"))if not os.getenv("XDG_DATA_HOME"): print("XDG_DATA_HOME is not set. Using default path.")
@@ -141,6 +147,16 @@ class Izvor(Gtk.Application):
for provider in self.available_providers: f.write(provider + "\n") if not self.APPEARANCE_CONFIG_FILE.exists(): self.APPEARANCE_CONFIG_FILE.touch() with open(self.APPEARANCE_CONFIG_FILE, "w") as f: json.dump({"icon_size": 32, "show_result_descriptions": True}, f) with open(self.APPEARANCE_CONFIG_FILE, "r") as f: appearance_config = json.load(f) self.icon_size = appearance_config.get("icon_size", 32) self.show_result_descriptions = appearance_config.get("show_result_descriptions", True) self.permanently_enabled_providers = {} with open(self.ENABLED_PROVIDERS_FILE, "r") as f:
@@ -170,8 +186,12 @@ class Izvor(Gtk.Application):
self.provider_checkboxes[provider.stem].show() self.provider_checkboxes[provider.stem].connect("toggled", self.update_enabled_providers) self.update_task = None def call_update_results(widget): asyncio.create_task(self.update_results(widget))if self.update_task is not None: self.update_task.cancel() self.update_task = asyncio.create_task(self.update_results(widget))def execute_result(widget, row): row.execute()
@@ -184,6 +204,7 @@ class Izvor(Gtk.Application):
"providers-menu-all-toggled": self.update_enabled_providers_all, "menu-about": self.about, "menu-providers": self.provider_menu, "menu-preferences": self.preferences,} )
@@ -196,7 +217,9 @@ class Izvor(Gtk.Application):
else: self.enabled_providers.pop(provider, None) asyncio.create_task(self.update_results(None))if self.update_task is not None: self.update_task.cancel() self.update_task = asyncio.create_task(self.update_results(widget))def update_enabled_providers_all(self, widget): for checkbox in self.provider_checkboxes.values():
@@ -243,10 +266,15 @@ class Izvor(Gtk.Application):
self.scroll_to_row(rows[prev_index], results_list.get_adjustment()) return True if event.keyval == Gdk.KEY_Return and current_row:# Execute the selected rowcurrent_row.activate()return Trueif event.keyval == Gdk.KEY_Return: if current_row: # Execute the selected row current_row.activate() return True elif len(rows) > 0: # Execute the first row rows[0].activate() return Truereturn False
@@ -301,7 +329,7 @@ class Izvor(Gtk.Application):
provider_description.set_line_wrap(True) icon = Gtk.Image.new_from_icon_name(loaded_provider.icon, Gtk.IconSize.LARGE_TOOLBAR) icon.set_pixel_size(ICON_SIZE)icon.set_pixel_size(self.icon_size)switch = Gtk.Switch() switch.set_valign(Gtk.Align.CENTER)
@@ -320,6 +348,31 @@ class Izvor(Gtk.Application):
providers_window.show_all() def preferences(self, widget): preferences_builder = Gtk.Builder() preferences_builder.add_from_file("preferences.ui") preferences_window = preferences_builder.get_object("preferences-window") preferences_window.connect("destroy", lambda _: preferences_window.destroy()) def update_appearance_preferences(widget, *args): self.icon_size = preferences_builder.get_object("icon-size").get_value() self.show_result_descriptions = preferences_builder.get_object("show-result-descriptions").get_active() with open(self.APPEARANCE_CONFIG_FILE, "w") as f: json.dump({"icon_size": self.icon_size, "show_result_descriptions": self.show_result_descriptions}, f) # Regenerate the results to reflect the changes if self.update_task is not None: self.update_task.cancel() self.update_task = asyncio.create_task(self.update_results(widget)) preferences_builder.connect_signals( { "update-appearance": update_appearance_preferences, } ) preferences_window.show_all() def update_permanently_enabled_providers(self, widget, param, provider): if widget.get_active():
@@ -333,6 +386,8 @@ class Izvor(Gtk.Application):
async def update_results(self, widget): print("Updating results...") spinner = self.builder.get_object("spinner") spinner.start()generators = [] query = self.builder.get_object("search-query-buffer").get_text() # print(f"Query: {query}")
@@ -352,11 +407,12 @@ class Izvor(Gtk.Application):
async for result in merge_generators_with_params(generators, query): # print(result) result_box = ResultInfoWidget(result["name"], result["description"], result["image"], result["execute"])result_box = ResultInfoWidget(result["name"], result["description"], result["image"], result["execute"], self.icon_size, self.show_result_descriptions)results_list.add(result_box) result_box.show_all() results_list.show_all() spinner.stop()if __name__ == "__main__":
preferences.ui
@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.40.0 --> <interface> <requires lib="gtk+" version="3.24"/> <object class="GtkAdjustment" id="icon-size"> <property name="upper">128</property> <property name="value">32</property> <property name="step-increment">1</property> <property name="page-increment">16</property> <signal name="value-changed" handler="update-appearance" swapped="no"/> </object> <object class="GtkWindow" id="preferences-window"> <property name="can-focus">False</property> <property name="title" translatable="yes">Preferences</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can-focus">False</property> <child> <object class="GtkStackSidebar"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="stack">stack</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkStack" id="stack"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="transition-type">slide-up-down</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="margin-start">4</property> <property name="margin-end">4</property> <property name="margin-top">4</property> <property name="margin-bottom">4</property> <property name="orientation">vertical</property> <property name="spacing">4</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="spacing">4</property> <child> <object class="GtkLabel"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="label" translatable="yes">Show result descriptions</property> <property name="xalign">0</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSwitch" id="show-result-descriptions"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="active">True</property> <signal name="state-set" handler="update-appearance" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="spacing">4</property> <child> <object class="GtkLabel"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="label" translatable="yes">Icon size</property> <property name="xalign">0</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSpinButton" id="icon-size-button"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="adjustment">icon-size</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="name">appearance</property> <property name="title" translatable="yes">Appearance</property> </packing> </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> </object> </interface>