diff --git a/src/board.py b/src/board.py index 9c7530e..3073701 100644 --- a/src/board.py +++ b/src/board.py @@ -1,18 +1,24 @@ """ This module contains classes and functions to contain the kanban board information """ class Board: - def __init__(self): + def __init__(self, file = None): """ Initialize the Board class, this class has three important class variables. These are: self.sprint | str - name of the current sprint self.columns | list(str) - columns in kanban board - self.tasks | list(list(str)) - tasks in each column + self.tasks | list(list()) - tasks in each column """ self.sprint = None self.columns = list() self.tasks = list() + self.file = '' + self.file = file + + if file: + self.parse_md(file) + def parse_md(self, file): """ Upon starting the code we need to parse the markdown file which contains our board @@ -48,11 +54,56 @@ class Board: # Now add the task to the list structures elif item_type == "-": - self.tasks[-1].append(' '.join(line.split(' ')[1:])) + # The tasks are a list of [col index, task name] + self.tasks[-1].append(" "+' '.join(line.split(' ')[1:])) + + + def write_md(self): + with open(self.file, 'w') as f: + f.write('#\n\n') + for i,col in enumerate(self.columns): + f.write('## {}\n\n'.format(col)) + for task in self.tasks[i]: + f.write('- {}\n'.format(task[1][1:])) + f.write('\n') + + + def move_task(self, col_index, task_index, direction): + """ This class method moves tasks between columns by incrementing/decrementing the column + index + + Arguments: + col_index - index of the column we are in + task_index - index of the task we are changing in the column + direction - direction to move the task + + Returns: + moved - True if a task was moved else false + """ + task = self.tasks[col_index][task_index] + if col_index+direction >= 0 and col_index+direction < len(self.columns): + self.tasks[col_index+direction].append(task) + del self.tasks[col_index][task_index] + return True + + else: + return False + def print_board_items(self): - for col, tasks in zip(self.columns, self.tasks): - print(col, tasks) + for i, col in enumerate(self.columns): + print(col) + print(self.tasks[i]) + + def get_columns(self): + """ Return columns""" + return self.columns + def get_tasks(self): + """ Return tasks""" + return self.tasks + def get_task(self, icol, itask): + """ Return a task based on column and task index""" + return self.tasks[icol][itask] diff --git a/src/layout.tcss b/src/layout.tcss new file mode 100644 index 0000000..99fb0e1 --- /dev/null +++ b/src/layout.tcss @@ -0,0 +1,24 @@ +Screen { + layout: horizontal; + overflow-x: auto; +} + +.column { + width: 1fr; + height: 100%; + padding: 2 2 2 2; + border-right: dashed #458588; +} + +.last-column{ + width: 1fr; + height: 100%; + padding: 2 2 2 2; + +} + +.header { + content-align: center top; + color: #458588; + text-style: bold underline; +} diff --git a/src/tui.py b/src/tui.py new file mode 100644 index 0000000..8868bd4 --- /dev/null +++ b/src/tui.py @@ -0,0 +1,119 @@ +from textual.app import App, ComposeResult +from textual.widgets import Static, Label, ListItem, ListView +from textual.containers import Horizontal, Vertical +from textual.binding import Binding +from board import Board + + +class TaskList(ListView): + """ + 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 KanbanForm(App): + CSS_PATH = 'layout.tcss' + BINDINGS = [ + Binding("l", "fnext", "Focus Next", show=False, priority=True), + Binding("h", "fprev", "Focus Prev", show=False, priority=True), + Binding("L", "move_up", "Focus Next", show=False, priority=True), + Binding("H", "move_down", "Focus Prev", show=False, priority=True), + Binding('q', 'exit', "Exit", priority=True, show=False) + ] + + def compose(self): + """ + Initialization function for form + """ + # Initialize our board class + self.board = Board(file = '.board.md') + 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' + else: + col_class = 'last-column' + with Vertical(classes=col_class): + yield Static(col, classes='header') + yield TaskList( + *[ListItem(Label(task)) for task in self.board.get_tasks()[i]]) + + # Now make all TaskLists except the first have no highlights + def action_fnext(self): + """ Focus next column""" + self.children[0].focus_next() + + def action_move_up(self): + icol, itask = self.get_col_task() + text = self.board.get_task(icol, itask) + moved = self.board.move_task(icol, itask, 1) + if moved: + query = self.query(selector=TaskList) + self.focused.highlighted_child.remove() + 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 """ + self.children[0].focus_previous() + + def action_move_down(self): + icol, itask = self.get_col_task() + text = self.board.get_task(icol, itask) + moved = self.board.move_task(icol, itask, -1) + if moved: + query = self.query(selector=TaskList) + self.focused.highlighted_child.remove() + query.nodes[icol-1].append(ListItem(Label(text))) + self.focused.action_cursor_down() + self.action_fprev() + self.focused.action_cursor_down() + + def action_exit(self): + """ Exit the application """ + 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. + """ + focused_col = self.focused + query = self.query(selector=TaskList) + + # First get the column index + for i, child in enumerate(query.nodes): + if focused_col == child: + col_index = i + + # Now get the indext of the item in the list + to_move = focused_col.highlighted_child + for i, child in enumerate(focused_col.children): + if to_move == child: + task_index = i + + return col_index, task_index + + +# 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() + +