#!/usr/bin/env python3 import sys import os import logging import threading import task_manager import emacs_client APP_TITLE = "Org-Convergence" DOCS_PATH = os.environ["ORG_PATH"] STYLE_FILE_PATH = os.path.join( os.path.dirname(os.path.abspath(__file__)), "style.css") MIN_TITLE_WIDTH_CHARS = 10 import gi gi.require_version("Gtk", "4.0") gi.require_version('Polkit', '1.0') gi.require_version(namespace='Adw', version='1') from gi.repository import Gtk, Polkit, GObject, Gio, Adw, Gdk class MainWindow(Gtk.Window): __gsignals__ = { "open-in-emacs": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )), } ## Setup def __init__(self, *, title, application, task_manager, with_titlebar=True): super().__init__(title=title, application=application) self.application = application self.task_manager = task_manager self.loading = 0 if with_titlebar: self.header_bar = Gtk.HeaderBar() # self.header_bar.set_show_close_button(True) # self.header_bar.props.title = APP_TITLE self.set_titlebar(self.header_bar) self.progress_spinner = Gtk.Spinner() self.progress_spinner.start() self.header_bar.pack_end(self.progress_spinner) else: self.header_bar = None self.progress_spinner = None # self.main_box = Gtk.Box(name='main-box', vexpand=True, hexpand=True) self.scrollview = Gtk.ScrolledWindow(vexpand=True, hexpand=True) self.task_list = Gtk.ListBox(name='task-list') self.scrollview.set_child(self.task_list) self.item_rows = [] # self.main_box.props.valign = Gtk.Align.CENTER # self.main_box.props.halign = Gtk.Align.CENTER # self.main_box.append(self.scrollview) # self.set_child(self.main_box) self.set_child(self.scrollview) self.loading += 1 self.task_manager.get_task_list( self.on_task_list_update, self.on_task_list_ready, ) ## Keyboard shortcuts def open_in_emacs(self, *args): row = self.task_list.get_selected_row() if row is None: return item = self.item_rows[row.get_index()] item_id = item.id if item_id is None: logging.warning("No ID found for item: {}".format(item)) return emacs_client.navigate_emacs_to_id(item_id) ## Rendering def build_agenda_task_row(self, task): row = Gtk.ListBoxRow() hbox = Gtk.Box() state_button = Gtk.Button.new_with_label(task.state or '') state_button.props.css_classes = ('state-button',) state_button.connect("clicked", self.on_status_button_clicked) hbox.append(state_button) clock_button = Gtk.Button.new_with_label('C') clock_button.props.css_classes = ('clock-button',) clock_button.connect("clicked", self.on_clock_button_clicked) hbox.append(clock_button) # task_name_label = Gtk.Entry(text=task.title, width_chars=max(MIN_TITLE_WIDTH_CHARS, len(task.title))) task_name_label = Gtk.Label() task_name_label.set_text(task.title) task_name_label.props.css_classes = ('task-name',) hbox.append(task_name_label) row.set_child(hbox) return row def on_ready(self): self.loading -= 1 if self.loading < 0: self.loading = 0 elif self.loading == 0: if self.progress_spinner is not None: self.progress_spinner.stop() ## Callbacks def on_task_list_update(self, new_rows): for item in new_rows.with_hour: self.task_list.append(self.build_agenda_task_row(item)) self.item_rows.append(item) for item in new_rows.no_hour: self.task_list.append(self.build_agenda_task_row(item)) self.item_rows.append(item) def on_task_list_ready(self, success): self.on_ready() ## Reactions def on_status_button_clicked(self, button): print('Status button clicked: {}'.format(button)) def on_clock_button_clicked(self, button): print('Clock button clicked: {}'.format(button)) class Application(Gtk.Application): """ Main Aplication class """ def __init__(self): super().__init__(application_id='com.codigoparallevar.org-convergence', flags=Gio.ApplicationFlags.FLAGS_NONE) self.task_manager = task_manager.TaskManager(DOCS_PATH) def do_activate(self): win = self.props.active_window if not win: if os.path.exists(STYLE_FILE_PATH): style_provider = Gtk.CssProvider() Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) style_provider.load_from_path(STYLE_FILE_PATH) win = MainWindow( title=APP_TITLE, application=self, task_manager=self.task_manager, ) # PinePhone screen is 720x1440 (portrait) but, has 2x pixel density win.set_default_size(360, 720) ## Load shortcuts # Open in emacs action = Gio.SimpleAction.new("open-in-emacs", None) action.connect("activate", win.open_in_emacs) self.add_action(action) self.set_accels_for_action('app.open-in-emacs', ["e"]) win.present() def main(): """ Run the main application""" # GObject.threads_init() logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") app = Application() return app.run(sys.argv) if __name__ == '__main__': main()