171 lines
5.0 KiB
Python
171 lines
5.0 KiB
Python
import json
|
|
import os
|
|
|
|
from fabric.widgets.box import Box
|
|
from fabric.widgets.button import Button
|
|
from fabric.widgets.entry import Entry
|
|
from fabric.widgets.label import Label
|
|
from fabric.widgets.scrolledwindow import ScrolledWindow
|
|
import utils.icons as icons
|
|
|
|
|
|
class TodoManager(Box):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(
|
|
name="todo-launcher",
|
|
all_visible=False,
|
|
visible=False,
|
|
**kwargs,
|
|
)
|
|
|
|
self._arranger_handler = 0
|
|
self.todo_manager = TodoListManager(self)
|
|
self.viewport = None
|
|
|
|
self.todo_entry = Entry(
|
|
name="todo-entry",
|
|
h_expand=True,
|
|
on_activate=lambda entry, *_: self.handle_add_todo(entry.get_text()),
|
|
)
|
|
|
|
self.header_box = Box(
|
|
name="header-box",
|
|
spacing=10,
|
|
orientation="h",
|
|
children=[
|
|
self.todo_entry,
|
|
Button(
|
|
name="add-button",
|
|
child=Label(name="todo-add-button", markup=icons.add),
|
|
tooltip_text="Add Todo",
|
|
on_clicked=lambda *_: self.handle_add_todo(
|
|
self.todo_entry.get_text()
|
|
),
|
|
),
|
|
],
|
|
)
|
|
|
|
self.launcher_box = Box(
|
|
name="todo-launcher-box",
|
|
spacing=10,
|
|
orientation="v",
|
|
h_expand=True,
|
|
children=[self.header_box],
|
|
)
|
|
|
|
self.add(self.launcher_box)
|
|
|
|
def open_launcher(self):
|
|
if not self.viewport:
|
|
self.viewport = Box(name="viewport", spacing=4, orientation="v")
|
|
self.scrolled_window = ScrolledWindow(
|
|
name="scrolled-window",
|
|
spacing=10,
|
|
min_content_size=(-1, -1),
|
|
h_scrollbar_policy="never",
|
|
child=self.viewport,
|
|
)
|
|
self.launcher_box.add(self.scrolled_window)
|
|
|
|
self.viewport.children = []
|
|
self.todo_manager.arrange_viewport()
|
|
|
|
self.viewport.show()
|
|
self.todo_entry.grab_focus()
|
|
|
|
def handle_add_todo(self, text: str):
|
|
if text.strip():
|
|
self.todo_manager.add_todo(text)
|
|
self.todo_entry.set_text("")
|
|
|
|
|
|
class TodoListManager:
|
|
def __init__(self, launcher):
|
|
self.launcher = launcher
|
|
self.todos = []
|
|
self.todo_file = os.path.expanduser("~/.local/state/modus/todos.json")
|
|
self.load_todos()
|
|
|
|
def load_todos(self):
|
|
os.makedirs(os.path.dirname(self.todo_file), exist_ok=True)
|
|
try:
|
|
if os.path.exists(self.todo_file):
|
|
with open(self.todo_file, "r") as f:
|
|
self.todos = json.load(f)
|
|
except Exception as e:
|
|
print(f"Error loading todos: {e}")
|
|
self.todos = []
|
|
|
|
def save_todos(self):
|
|
try:
|
|
with open(self.todo_file, "w") as f:
|
|
json.dump(self.todos, f)
|
|
except Exception as e:
|
|
print(f"Error saving todos: {e}")
|
|
|
|
def add_todo(self, content: str):
|
|
todo = {"content": content, "completed": False, "id": len(self.todos)}
|
|
self.todos.append(todo)
|
|
self.save_todos()
|
|
self.arrange_viewport()
|
|
|
|
def toggle_todo(self, todo_id: int):
|
|
for todo in self.todos:
|
|
if todo["id"] == todo_id:
|
|
todo["completed"] = not todo["completed"]
|
|
break
|
|
self.save_todos()
|
|
self.arrange_viewport()
|
|
|
|
def delete_todo(self, todo_id: int):
|
|
self.todos = [todo for todo in self.todos if todo["id"] != todo_id]
|
|
self.save_todos()
|
|
self.arrange_viewport()
|
|
|
|
def bake_todo_slot(self, todo: dict) -> Box:
|
|
todo_box = Box(
|
|
name="todo-item",
|
|
spacing=10,
|
|
orientation="h",
|
|
h_expand=True,
|
|
)
|
|
|
|
checkbox = Button(
|
|
name="checkbox",
|
|
child=Label(
|
|
name="todo-checkbox",
|
|
markup=icons.check_box if todo["completed"] else icons.box,
|
|
),
|
|
on_clicked=lambda _, todo_id=todo["id"]: self.toggle_todo(todo_id),
|
|
)
|
|
|
|
label = Label(
|
|
label=todo["content"],
|
|
h_expand=True,
|
|
v_align="center",
|
|
h_align="start",
|
|
name="todo-label-completed" if todo["completed"] else "todo-label",
|
|
)
|
|
|
|
delete_button = Button(
|
|
name="delete-button",
|
|
child=Label(name="todo-delete-button", markup=icons.trash),
|
|
on_clicked=lambda _, todo_id=todo["id"]: self.delete_todo(todo_id),
|
|
)
|
|
|
|
todo_box.add(checkbox)
|
|
todo_box.add(label)
|
|
todo_box.add(delete_button)
|
|
|
|
return todo_box
|
|
|
|
def arrange_viewport(self):
|
|
if not self.launcher.viewport:
|
|
return
|
|
|
|
self.launcher.viewport.children = []
|
|
|
|
for todo in self.todos:
|
|
todo_slot = self.bake_todo_slot(todo)
|
|
self.launcher.viewport.add(todo_slot)
|