diff --git a/pyproject.toml b/pyproject.toml index 4aba2fe..3c15a10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,9 @@ classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console" ] - +dependencies = [ + "textual" +] [project.urls] Homepage = "https://alexselimov.com/git/aselimov/pykanban" Issues = "https://github.com/aselimov/PyKanban/issues" diff --git a/resources/.board.yaml b/resources/.board.yaml new file mode 100644 index 0000000..f64db91 --- /dev/null +++ b/resources/.board.yaml @@ -0,0 +1,19 @@ +columns: +- To Do +- In Progress +- Review +- Done +tasks: +- column: To Do + description: "We want to be able to retire some done tickets \nwithout having to\ + \ delete them." + score: '5' + summary: Work out some way to handle sprints +- column: To Do + description: 'I want to add footers which describe the key shortcuts, + + Additionally this should be disabledable via a command line argument + + ' + score: '5' + summary: 'Add some footers for the key shortcuts ' diff --git a/src/pykban/tui.py b/src/pykban/tui.py index f0b9bf8..d1ec013 100644 --- a/src/pykban/tui.py +++ b/src/pykban/tui.py @@ -3,24 +3,32 @@ from textual.widgets import Static, Label, ListItem, ListView, TextArea, Input from textual.containers import Horizontal, Vertical from textual.screen import Screen from textual.binding import Binding -from board import Board, Task +from .board import Board, Task + + +def run_tui(): + kb = KanbanForm() + kb.run() class TaskList(ListView): """ - Inherited widget from Listview to use as the kanban board columns + Inherited widget from Listview to use as the kanban board columns """ + # Keybinds BINDINGS = [ Binding("k", "cursor_up", "Cursor Up", show=False, priority=True), Binding("j", "cursor_down", "Cursor Down", show=False, priority=True), ] + class EditTaskScreen(Screen): """ - This is a screen used to edit the name of a task + This is a screen used to edit the name of a task """ - CSS=""" + + CSS = """ Label{ width:50%; background: #282828; @@ -47,10 +55,11 @@ class EditTaskScreen(Screen): } """ BINDINGS = [ - Binding('ctrl+s', 'save', 'Save Changes', priority=True), - Binding('escape', 'exit', 'Exit Without Changes', priority=True), + Binding("ctrl+s", "save", "Save Changes", priority=True), + Binding("escape", "exit", "Exit Without Changes", priority=True), ] - def __init__(self,text): + + def __init__(self, text): """ Initialize the screen """ @@ -61,19 +70,18 @@ class EditTaskScreen(Screen): """ Compose the widgets on the screen, this screen doesn't need dynamic layout changes """ - yield Label('Task Name:') + yield Label("Task Name:") yield Input(value=self.text.summary) - yield Label('Score:') + yield Label("Score:") if self.text.score: yield Input(value=self.text.score) else: yield Input(value="") - yield Label('Description:') + yield Label("Description:") if self.text.description: - yield TextArea(self.text.description, language='markdown') + yield TextArea(self.text.description, language="markdown") else: - yield TextArea(language='markdown') - + yield TextArea(language="markdown") def action_save(self): query = self.query(selector=Input) @@ -86,11 +94,13 @@ class EditTaskScreen(Screen): def action_exit(self): self.dismiss(None) + class EditColScreen(Screen): """ - This is a screen used to edit the name of a task + This is a screen used to edit the name of a task """ - CSS=""" + + CSS = """ Label{ width:50%; background: #282828; @@ -104,10 +114,11 @@ class EditColScreen(Screen): } """ BINDINGS = [ - Binding('ctrl+s', 'save', 'Save Changes', priority=True), - Binding('enter', 'save', 'Save Changes', priority=True), + Binding("ctrl+s", "save", "Save Changes", priority=True), + Binding("enter", "save", "Save Changes", priority=True), ] - def __init__(self,text): + + def __init__(self, text): """ Initialize the screen """ @@ -118,67 +129,94 @@ class EditColScreen(Screen): """ Compose the widgets on the screen, this screen doesn't need dynamic layout changes """ - yield Label('Column Name:') + yield Label("Column Name:") yield Input(value=self.text) - def action_save(self): query = self.query(selector=Input) self.dismiss(query.nodes[0].value) class KanbanForm(App): - CSS_PATH = 'layout.tcss' + CSS_PATH = "layout.tcss" BINDINGS = [ - Binding("a", "new_task", "Add New Task", show=False, ), - Binding("l", "fnext", "Focus Next", show=False, ), - Binding("h", "fprev", "Focus Prev", show=False, ), + Binding( + "a", + "new_task", + "Add New Task", + show=False, + ), + Binding( + "l", + "fnext", + "Focus Next", + show=False, + ), + Binding( + "h", + "fprev", + "Focus Prev", + show=False, + ), Binding("L", "move_up", "Focus Next", show=False), Binding("H", "move_down", "Focus Prev", show=False), - Binding("e", "edit_task", "Edit Task", show=False,), - Binding("r", "edit_column", "Edit Column Name", show=False,), - Binding("d", "delete_task", "Delete Task", show=False,), - Binding('q', 'exit', "Exit") - ] + Binding( + "e", + "edit_task", + "Edit Task", + show=False, + ), + Binding( + "r", + "edit_column", + "Edit Column Name", + show=False, + ), + Binding( + "x", + "delete_task", + "Delete Task", + show=False, + ), + Binding("q", "exit", "Exit"), + ] def compose(self): """ Initialization function for form """ # Initialize our board class - self.board = Board(file = '.board.yaml') + self.board = Board(file=".board.yaml") self.cols = list() self.col_widgets = list() - - + with Horizontal(): - for i,col in enumerate(self.board.get_columns()): - if i < len(self.board.get_columns())-1: - col_class = 'column' + for i, col in enumerate(self.board.get_columns()): + if i < len(self.board.get_columns()) - 1: + col_class = "column" else: - col_class = 'last-column' + col_class = "last-column" with Vertical(classes=col_class): if i == 0: - yield Static(col, classes='header-focused') + yield Static(col, classes="header-focused") else: - yield Static(col, classes='header') + yield Static(col, classes="header") yield TaskList( - *[ListItem(Label(task.summary)) for task in self.board.get_tasks()[i]]) + *[ListItem(Label(task.summary)) for task in self.board.get_tasks()[i]] + ) def action_fnext(self): - """ Focus next column""" + """Focus next column""" query = self.query(selector=Static) - query = [node for node in query.nodes if str(node) == 'Static()'] + query = [node for node in query.nodes if str(node) == "Static()"] icol, _ = self.get_col_task() - query[icol].classes="header" + query[icol].classes = "header" self.children[0].focus_next() try: - query[icol+1].classes="header-focused" + query[icol + 1].classes = "header-focused" except IndexError: - query[0].classes="header-focused" - - + query[0].classes = "header-focused" def action_move_up(self): icol, itask = self.get_col_task() @@ -187,23 +225,22 @@ class KanbanForm(App): if moved: query = self.query(selector=TaskList) self.focused.highlighted_child.remove() - query.nodes[icol+1].append(ListItem(Label(text))) + query.nodes[icol + 1].append(ListItem(Label(text))) self.focused.action_cursor_down() self.action_fnext() self.focused.action_cursor_down() - def action_fprev(self): - """ Focus previous column """ + """Focus previous column""" query = self.query(selector=Static) - query = [node for node in query.nodes if str(node) == 'Static()'] + query = [node for node in query.nodes if str(node) == "Static()"] icol, _ = self.get_col_task() - query[icol].classes="header" + query[icol].classes = "header" self.children[0].focus_previous() try: - query[icol-1].classes="header-focused" + query[icol - 1].classes = "header-focused" except IndexError: - query[-1].classes="header-focused" + query[-1].classes = "header-focused" def action_move_down(self): icol, itask = self.get_col_task() @@ -212,7 +249,7 @@ class KanbanForm(App): if moved: query = self.query(selector=TaskList) self.focused.highlighted_child.remove() - query.nodes[icol-1].append(ListItem(Label(text))) + query.nodes[icol - 1].append(ListItem(Label(text))) self.focused.action_cursor_down() self.action_fprev() self.focused.action_cursor_down() @@ -221,9 +258,9 @@ class KanbanForm(App): icol, itask = self.get_col_task() task = self.board.get_task(icol, itask) self.push_screen(EditTaskScreen(task), self.update_task) - + def action_new_task(self): - self.push_screen(EditTaskScreen(Task(None,None,None)), self.new_task) + self.push_screen(EditTaskScreen(Task(None, None, None)), self.new_task) def action_edit_column(self): icol, itask = self.get_col_task() @@ -233,25 +270,23 @@ class KanbanForm(App): def action_delete_task(self): icol, itask = self.get_col_task() self.focused.highlighted_child.remove() - self.board.del_task(icol,itask) + self.board.del_task(icol, itask) def update_col(self, text): - """ Update the column - """ + """Update the column""" icol, itask = self.get_col_task() query = self.query(selector=Static) - query = [node for node in query.nodes if str(node) == 'Static()'] + query = [node for node in query.nodes if str(node) == "Static()"] query[icol].update(text) self.board.get_columns()[icol] = text - def action_exit(self): - """ Exit the application """ - self.board.write_yaml(file='.board.yaml') + """Exit the application""" + self.board.write_yaml(file=".board.yaml") self.exit() def get_col_task(self): - """ + """ This function gets the relevant column and task from the Board object for the current selected item in the tui. """ @@ -267,15 +302,15 @@ class KanbanForm(App): to_move = focused_col.highlighted_child task_index = None for i, child in enumerate(focused_col.children): - if to_move == child: + if to_move == child: task_index = i return col_index, task_index - + def update_task(self, task): - """ This function gets the text inputted in the edit screen and updates the underlying - task and the board class - + """This function gets the text inputted in the edit screen and updates the underlying + task and the board class + """ if task: icol, itask = self.get_col_task() @@ -283,20 +318,9 @@ class KanbanForm(App): self.board.update_task(icol, itask, task) def new_task(self, task): - """ This function adds a new task to our board - """ + """This function adds a new task to our board""" if task: - icol,_ = self.get_col_task() + icol, _ = self.get_col_task() self.focused.mount(ListItem(Label(task.summary))) self.board.add_task(icol, task) self.focused.action_cursor_down() - -# def on_key(self): -# with open('log','a') as f: -# f.write("{}".format(self.children[0].focus_next)) - -if __name__ == "__main__": - kb = KanbanForm() - kb.run() - -