From ef4d268225ed9b8de115e3d283d4b6367db37e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sun, 27 Mar 2022 23:34:01 +0200 Subject: [PATCH 1/6] Make default size larger, and closer to PinePhone form factor. --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index cd6974d..8e72b80 100644 --- a/main.py +++ b/main.py @@ -140,7 +140,7 @@ class Application(Gtk.Application): application=self, task_manager=self.task_manager, ) - win.set_default_size(600, 400) + win.set_default_size(720, 1024) # PinePhone screen is 720x1440 win.present() From d9772908195d1b8f00cb4f61c451cbef8ee19562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sun, 27 Mar 2022 23:34:01 +0200 Subject: [PATCH 2/6] Make default size closer to PinePhone form factor. --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index cd6974d..422f055 100644 --- a/main.py +++ b/main.py @@ -140,7 +140,9 @@ class Application(Gtk.Application): application=self, task_manager=self.task_manager, ) - win.set_default_size(600, 400) + + # PinePhone screen is 720x1440 (portrait) but, has 2x pixel density + win.set_default_size(360, 720) win.present() From 5846868f9f72b76b2f7323e337a94aac4df4e03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sun, 27 Mar 2022 23:44:58 +0200 Subject: [PATCH 3/6] Fix iterative load to not clear and re-draw items. --- main.py | 24 ++++++++++------------- task_manager.py | 51 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/main.py b/main.py index 422f055..213f683 100644 --- a/main.py +++ b/main.py @@ -58,7 +58,10 @@ class MainWindow(Gtk.Window): self.set_child(self.scrollview) self.loading += 1 - self.task_manager.get_task_list(self.on_task_list_ready) + self.task_manager.get_task_list( + self.on_task_list_update, + self.on_task_list_ready, + ) ## Rendering def build_agenda_task_row(self, task): @@ -83,7 +86,7 @@ class MainWindow(Gtk.Window): row.set_child(hbox) - return row + return row def on_ready(self): self.loading -= 1 @@ -94,21 +97,14 @@ class MainWindow(Gtk.Window): self.progress_spinner.stop() ## Callbacks - def on_task_list_ready(self, agenda): - # TODO: Avoid reconstructing the whole list every time - i = 0 - child = self.task_list.get_first_child() - while child is not None: - was = child - child = child.get_next_sibling() - i += 1 - self.task_list.remove(was) - - for item in agenda.with_hour: + 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)) - for item in agenda.no_hour: + for item in new_rows.no_hour: self.task_list.append(self.build_agenda_task_row(item)) + + def on_task_list_ready(self, success): self.on_ready() ## Reactions diff --git a/task_manager.py b/task_manager.py index 4671408..2584fd1 100644 --- a/task_manager.py +++ b/task_manager.py @@ -91,22 +91,19 @@ class TaskManager: self.docs = docs - def get_task_list(self, callback): + def get_task_list(self, progress_callback, complete_callback): def aux(): if self.docs is None: last_result = None + # No docs read yet, load them iteratively for doc in self.load(): - result = self.get_agenda() - if ((last_result is None) - or (len(result.with_hour) != len(last_result.with_hour)) - or (len(result.no_hour) != len(last_result.no_hour))): - print("Loaded:", doc._path) - GObject.idle_add(callback, result) - print("Load completed") + result = self.get_agenda_from_doc(doc) + GObject.idle_add(progress_callback, result) else: result = self.get_agenda() print("Result", result) - GObject.idle_add(callback, result) + GObject.idle_add(progress_callback, result) + GObject.idle_add(complete_callback, True) thread = threading.Thread(target=aux) thread.start() @@ -153,3 +150,39 @@ class TaskManager: with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time), no_hour=other_items, ) + + def get_agenda_from_doc(self, doc: OrgDoc) -> Agenda: + headline_count = 0 + items_in_agenda = [] + now = datetime.now() + + for hl in doc.getAllHeadlines(): + headline_count += 1 + + if ( + hl.scheduled + and isinstance(hl.scheduled, OrgTime) + and hl.scheduled.time.active + ): + if is_today(hl.scheduled): + items_in_agenda.append(hl) + elif (hl.scheduled.time.to_datetime() < now) and hl.is_todo: + items_in_agenda.append(hl) + + items_with_hour = [ + item + for item in items_in_agenda + if item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour + ] + other_items = [ + item + for item in items_in_agenda + if not ( + item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour + ) + ] + + return Agenda( + with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time), + no_hour=other_items, + ) \ No newline at end of file From 247cf7cf8a368fe55ab5f14b8fddf09fccc1aabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sun, 27 Mar 2022 23:46:17 +0200 Subject: [PATCH 4/6] Rename to org-convergence. --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 213f683..6a9596f 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ import threading import task_manager -APP_TITLE = "Org-mob" +APP_TITLE = "Org-Convergence" DOCS_PATH = os.environ["ORG_PATH"] STYLE_FILE_PATH = os.path.join( os.path.dirname(os.path.abspath(__file__)), @@ -119,7 +119,7 @@ class Application(Gtk.Application): """ Main Aplication class """ def __init__(self): - super().__init__(application_id='com.codigoparallevar.gtk4-organizer', + super().__init__(application_id='com.codigoparallevar.org-convergence', flags=Gio.ApplicationFlags.FLAGS_NONE) self.task_manager = task_manager.TaskManager(DOCS_PATH) From 6c22d19c6c13e7f4d37158d10fd9a46cbecaf05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Sun, 27 Mar 2022 23:57:01 +0200 Subject: [PATCH 5/6] Experiment with a cleaner stylesheet. --- style.css | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/style.css b/style.css index e54b961..b2bb664 100644 --- a/style.css +++ b/style.css @@ -1,7 +1,4 @@ #task-list { - border: 1px solid #cdc7c2; - padding: 1ex; - margin: 1ex; } #task-list .state-button, @@ -10,11 +7,7 @@ } window { - background-color: #d6d5d4; } #task-list .task-name { - border: none; - border-bottom: 1px solid #ccc; - border-radius: 0; -} \ No newline at end of file +} From c83b424248dcc27d89dfb02bf4eb323f33c8fc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Mon, 28 Mar 2022 00:44:33 +0200 Subject: [PATCH 6/6] Add e to open-in-emacs using emacsclient. --- emacs_client.py | 8 ++++++++ main.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 emacs_client.py diff --git a/emacs_client.py b/emacs_client.py new file mode 100644 index 0000000..d7c685a --- /dev/null +++ b/emacs_client.py @@ -0,0 +1,8 @@ +import subprocess + +def navigate_emacs_to_id(item_id): + item_id = item_id.replace('"', '\\"') + return subprocess.check_call([ + 'emacsclient', '-e', '(org-id-goto "{}")'.format(item_id)], + stdout=subprocess.DEVNULL, + ) diff --git a/main.py b/main.py index 6a9596f..a3ba921 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ import logging import threading import task_manager +import emacs_client APP_TITLE = "Org-Convergence" DOCS_PATH = os.environ["ORG_PATH"] @@ -24,6 +25,9 @@ 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): @@ -50,6 +54,7 @@ class MainWindow(Gtk.Window): 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 @@ -63,6 +68,19 @@ class MainWindow(Gtk.Window): 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() @@ -100,9 +118,11 @@ class MainWindow(Gtk.Window): 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() @@ -140,6 +160,15 @@ class Application(Gtk.Application): # 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()