feat: integrar painel de listas no painel de tarefas
- Remove Focus::Tabs, tabs das listas passam para dentro do painel esquerdo - Layout simplificado: top area eliminada, body/calendar/status apenas - Navegação entre listas com ←/→ no foco TaskList - Tabs dinâmicas: separador '|', alinhamento à esquerda, número variável conforme largura do terminal - Indicadores ' < ' e '>>' para overflow horizontal - Scroll horizontal (list_tabs_scroll) com clamping automático
This commit is contained in:
+44
-73
@@ -26,6 +26,7 @@ pub struct App {
|
|||||||
pub tasks: Vec<Task>,
|
pub tasks: Vec<Task>,
|
||||||
pub calendar_events: Vec<CalendarEvent>,
|
pub calendar_events: Vec<CalendarEvent>,
|
||||||
pub selected_list: usize,
|
pub selected_list: usize,
|
||||||
|
pub list_tabs_scroll: usize,
|
||||||
pub selected_task: usize,
|
pub selected_task: usize,
|
||||||
pub focus: Focus,
|
pub focus: Focus,
|
||||||
pub show_popup: Option<Popup>,
|
pub show_popup: Option<Popup>,
|
||||||
@@ -103,8 +104,9 @@ impl App {
|
|||||||
tasks,
|
tasks,
|
||||||
calendar_events: Vec::new(),
|
calendar_events: Vec::new(),
|
||||||
selected_list: 0,
|
selected_list: 0,
|
||||||
|
list_tabs_scroll: 0,
|
||||||
selected_task: 0,
|
selected_task: 0,
|
||||||
focus: Focus::Tabs,
|
focus: Focus::TaskList,
|
||||||
show_popup,
|
show_popup,
|
||||||
network_status: NetworkStatus::Online,
|
network_status: NetworkStatus::Online,
|
||||||
popup_input: String::new(),
|
popup_input: String::new(),
|
||||||
@@ -335,15 +337,14 @@ impl App {
|
|||||||
self.calendar_active_week += 1;
|
self.calendar_active_week += 1;
|
||||||
} else {
|
} else {
|
||||||
self.calendar_active_week = 0;
|
self.calendar_active_week = 0;
|
||||||
self.focus = Focus::Tabs;
|
self.focus = Focus::TaskList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.focus = match self.focus {
|
self.focus = match self.focus {
|
||||||
Focus::Tabs => Focus::TaskList,
|
|
||||||
Focus::TaskList => Focus::Detail,
|
Focus::TaskList => Focus::Detail,
|
||||||
Focus::Detail => Focus::Calendar,
|
Focus::Detail => Focus::Calendar,
|
||||||
_ => Focus::Tabs,
|
Focus::Calendar => Focus::TaskList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,7 +389,6 @@ impl App {
|
|||||||
Focus::Calendar => {
|
Focus::Calendar => {
|
||||||
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_sub(1);
|
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_sub(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
},
|
},
|
||||||
KeyCode::Down => match self.focus {
|
KeyCode::Down => match self.focus {
|
||||||
Focus::TaskList => {
|
Focus::TaskList => {
|
||||||
@@ -404,13 +404,15 @@ impl App {
|
|||||||
Focus::Calendar => {
|
Focus::Calendar => {
|
||||||
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_add(1);
|
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_add(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
},
|
},
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
match self.focus {
|
match self.focus {
|
||||||
Focus::Tabs => {
|
Focus::TaskList => {
|
||||||
if !self.lists.is_empty() && self.selected_list + 1 < self.lists.len() {
|
if !self.lists.is_empty() && self.selected_list + 1 < self.lists.len() {
|
||||||
self.selected_list += 1;
|
self.selected_list += 1;
|
||||||
|
if self.selected_list >= self.list_tabs_scroll + 4 {
|
||||||
|
self.list_tabs_scroll += 1;
|
||||||
|
}
|
||||||
self.load_tasks();
|
self.load_tasks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,9 +426,12 @@ impl App {
|
|||||||
}
|
}
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
match self.focus {
|
match self.focus {
|
||||||
Focus::Tabs => {
|
Focus::TaskList => {
|
||||||
if !self.lists.is_empty() && self.selected_list > 0 {
|
if !self.lists.is_empty() && self.selected_list > 0 {
|
||||||
self.selected_list -= 1;
|
self.selected_list -= 1;
|
||||||
|
if self.selected_list < self.list_tabs_scroll {
|
||||||
|
self.list_tabs_scroll = self.list_tabs_scroll.saturating_sub(1);
|
||||||
|
}
|
||||||
self.load_tasks();
|
self.load_tasks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,23 +450,12 @@ impl App {
|
|||||||
}
|
}
|
||||||
KeyCode::Char('d') | KeyCode::Char('D') => {
|
KeyCode::Char('d') | KeyCode::Char('D') => {
|
||||||
if !self.needs_auth {
|
if !self.needs_auth {
|
||||||
let context = match self.focus {
|
let context = if !self.tasks.is_empty() && self.selected_task < self.tasks.len() {
|
||||||
Focus::Tabs => {
|
let title = &self.tasks[self.selected_task].title;
|
||||||
if self.selected_list < self.lists.len() {
|
let preview: String = title.chars().take(40).collect();
|
||||||
format!("Delete list: \"{}\"?", self.lists[self.selected_list].title)
|
format!("Delete task: \"{}\"?", preview)
|
||||||
} else {
|
} else {
|
||||||
"Delete this list?".to_string()
|
"Delete this task?".to_string()
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if !self.tasks.is_empty() && self.selected_task < self.tasks.len() {
|
|
||||||
let title = &self.tasks[self.selected_task].title;
|
|
||||||
let preview: String = title.chars().take(40).collect();
|
|
||||||
format!("Delete task: \"{}\"?", preview)
|
|
||||||
} else {
|
|
||||||
"Delete this task?".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
self.show_popup = Some(Popup::ConfirmDelete { context });
|
self.show_popup = Some(Popup::ConfirmDelete { context });
|
||||||
}
|
}
|
||||||
@@ -851,55 +845,28 @@ impl App {
|
|||||||
self.show_popup = None;
|
self.show_popup = None;
|
||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
match self.focus {
|
if !self.tasks.is_empty() && self.selected_task < self.tasks.len() {
|
||||||
Focus::Tabs => {
|
let task = &self.tasks[self.selected_task];
|
||||||
if self.selected_list < self.lists.len() {
|
let task_id = task.id.clone();
|
||||||
let list_id = self.lists[self.selected_list].id.clone();
|
let list_id = task.list_id.clone();
|
||||||
let title = self.lists[self.selected_list].title.clone();
|
let title = task.title.clone();
|
||||||
self.db.delete_list(&list_id).ok();
|
self.db.delete_task(&task_id).ok();
|
||||||
self.db.push_sync(
|
if !list_id.contains('-') {
|
||||||
SyncAction::DeleteList,
|
self.db.push_sync(
|
||||||
&list_id,
|
SyncAction::Delete,
|
||||||
&list_id,
|
&task_id,
|
||||||
"",
|
&list_id,
|
||||||
).ok();
|
"",
|
||||||
crate::log_msg(&format!(
|
).ok();
|
||||||
"[task_app] LIST DELETE: title=\"{}\" id={}",
|
|
||||||
title, list_id
|
|
||||||
));
|
|
||||||
self.trigger_sync();
|
|
||||||
self.load_lists();
|
|
||||||
if self.selected_list >= self.lists.len() {
|
|
||||||
self.selected_list = self.lists.len().saturating_sub(1);
|
|
||||||
}
|
|
||||||
self.load_tasks();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Focus::TaskList | Focus::Detail | Focus::Calendar => {
|
crate::log_msg(&format!(
|
||||||
if !self.tasks.is_empty() && self.selected_task < self.tasks.len() {
|
"[task_app] TASK DELETE: title=\"{}\" id={} list={}",
|
||||||
let task = &self.tasks[self.selected_task];
|
title, task_id, list_id
|
||||||
let task_id = task.id.clone();
|
));
|
||||||
let list_id = task.list_id.clone();
|
self.trigger_sync();
|
||||||
let title = task.title.clone();
|
self.load_tasks();
|
||||||
self.db.delete_task(&task_id).ok();
|
if self.selected_task >= self.tasks.len() {
|
||||||
if !list_id.contains('-') {
|
self.selected_task = self.tasks.len().saturating_sub(1);
|
||||||
self.db.push_sync(
|
|
||||||
SyncAction::Delete,
|
|
||||||
&task_id,
|
|
||||||
&list_id,
|
|
||||||
"",
|
|
||||||
).ok();
|
|
||||||
}
|
|
||||||
crate::log_msg(&format!(
|
|
||||||
"[task_app] TASK DELETE: title=\"{}\" id={} list={}",
|
|
||||||
title, task_id, list_id
|
|
||||||
));
|
|
||||||
self.trigger_sync();
|
|
||||||
self.load_tasks();
|
|
||||||
if self.selected_task >= self.tasks.len() {
|
|
||||||
self.selected_task = self.tasks.len().saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.show_popup = None;
|
self.show_popup = None;
|
||||||
@@ -1258,6 +1225,10 @@ impl App {
|
|||||||
|
|
||||||
fn load_lists(&mut self) {
|
fn load_lists(&mut self) {
|
||||||
self.lists = self.db.get_lists();
|
self.lists = self.db.get_lists();
|
||||||
|
let max_scroll = self.lists.len().saturating_sub(4);
|
||||||
|
if self.list_tabs_scroll > max_scroll {
|
||||||
|
self.list_tabs_scroll = max_scroll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_tasks(&mut self) {
|
fn load_tasks(&mut self) {
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ fn main() -> io::Result<()> {
|
|||||||
tasks: &app.tasks,
|
tasks: &app.tasks,
|
||||||
calendar_events: &app.calendar_events,
|
calendar_events: &app.calendar_events,
|
||||||
selected_list: app.selected_list,
|
selected_list: app.selected_list,
|
||||||
|
list_tabs_scroll: app.list_tabs_scroll,
|
||||||
selected_task: app.selected_task,
|
selected_task: app.selected_task,
|
||||||
focus: app.focus.clone(),
|
focus: app.focus.clone(),
|
||||||
show_popup: app.show_popup.as_ref(),
|
show_popup: app.show_popup.as_ref(),
|
||||||
|
|||||||
+92
-42
@@ -4,7 +4,7 @@ use chrono::Datelike;
|
|||||||
|
|
||||||
use ratatui::style::{Color, Modifier, Style};
|
use ratatui::style::{Color, Modifier, Style};
|
||||||
use ratatui::text::{Line, Span, Text};
|
use ratatui::text::{Line, Span, Text};
|
||||||
use ratatui::widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Tabs, Wrap};
|
use ratatui::widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Wrap};
|
||||||
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
@@ -12,7 +12,6 @@ use crate::domain::models::*;
|
|||||||
use crate::app::SyncStats;
|
use crate::app::SyncStats;
|
||||||
use super::NetworkStatus;
|
use super::NetworkStatus;
|
||||||
|
|
||||||
const TAB_COLOR: Color = Color::Cyan;
|
|
||||||
const FOCUS_COLOR: Color = Color::Yellow;
|
const FOCUS_COLOR: Color = Color::Yellow;
|
||||||
const SELECTED_COLOR: Color = Color::Green;
|
const SELECTED_COLOR: Color = Color::Green;
|
||||||
const DETAIL_COLOR: Color = Color::White;
|
const DETAIL_COLOR: Color = Color::White;
|
||||||
@@ -22,37 +21,6 @@ const STATUS_SYNC: Color = Color::Yellow;
|
|||||||
const POPUP_BG: Color = Color::Black;
|
const POPUP_BG: Color = Color::Black;
|
||||||
const POPUP_BORDER: Color = Color::Cyan;
|
const POPUP_BORDER: Color = Color::Cyan;
|
||||||
|
|
||||||
pub fn render_tabs_bar(
|
|
||||||
frame: &mut Frame,
|
|
||||||
area: Rect,
|
|
||||||
lists: &[TaskList],
|
|
||||||
selected: usize,
|
|
||||||
focused: bool,
|
|
||||||
) {
|
|
||||||
let tab_titles: Vec<&str> = lists.iter().map(|l| l.title.as_str()).collect();
|
|
||||||
|
|
||||||
let block = Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(Style::default().fg(if focused { FOCUS_COLOR } else { TAB_COLOR }))
|
|
||||||
.title(" Lists ")
|
|
||||||
.title_alignment(Alignment::Left);
|
|
||||||
|
|
||||||
let tabs = if tab_titles.is_empty() {
|
|
||||||
Tabs::new(vec![Line::from(" No lists ")])
|
|
||||||
.block(block)
|
|
||||||
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
|
|
||||||
.select(selected)
|
|
||||||
} else {
|
|
||||||
Tabs::new(tab_titles.into_iter().map(|t| Line::from(Span::raw(t))).collect::<Vec<_>>())
|
|
||||||
.block(block)
|
|
||||||
.divider(Span::raw(" | "))
|
|
||||||
.highlight_style(Style::default().fg(FOCUS_COLOR).add_modifier(Modifier::BOLD))
|
|
||||||
.select(selected)
|
|
||||||
};
|
|
||||||
|
|
||||||
frame.render_widget(tabs, area);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn relative_due_str(due: chrono::NaiveDateTime) -> (String, Color) {
|
fn relative_due_str(due: chrono::NaiveDateTime) -> (String, Color) {
|
||||||
let now = chrono::Local::now().naive_local();
|
let now = chrono::Local::now().naive_local();
|
||||||
let diff = due - now;
|
let diff = due - now;
|
||||||
@@ -74,6 +42,9 @@ fn relative_due_str(due: chrono::NaiveDateTime) -> (String, Color) {
|
|||||||
pub fn render_task_list(
|
pub fn render_task_list(
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
|
lists: &[TaskList],
|
||||||
|
selected_list: usize,
|
||||||
|
list_tabs_scroll: usize,
|
||||||
tasks: &[Task],
|
tasks: &[Task],
|
||||||
selected: usize,
|
selected: usize,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
@@ -84,7 +55,26 @@ pub fn render_task_list(
|
|||||||
let done = tasks.iter().filter(|t| t.status == TaskStatus::Completed).count();
|
let done = tasks.iter().filter(|t| t.status == TaskStatus::Completed).count();
|
||||||
let todo = total - done;
|
let todo = total - done;
|
||||||
|
|
||||||
let content_width = (area.width as usize).saturating_sub(5);
|
let block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(if focused { FOCUS_COLOR } else { Color::DarkGray }))
|
||||||
|
.title(format!(" Tasks ({} todo / {} done) ", todo, done))
|
||||||
|
.title_alignment(Alignment::Left);
|
||||||
|
|
||||||
|
let inner = block.inner(area);
|
||||||
|
frame.render_widget(&block, area);
|
||||||
|
|
||||||
|
let inner_split = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(0)])
|
||||||
|
.split(inner);
|
||||||
|
|
||||||
|
let tabs_area = inner_split[0];
|
||||||
|
let tasks_area = inner_split[1];
|
||||||
|
|
||||||
|
render_list_tabs(frame, tabs_area, lists, selected_list, list_tabs_scroll, focused);
|
||||||
|
|
||||||
|
let content_width = (tasks_area.width as usize).saturating_sub(1);
|
||||||
|
|
||||||
let items: Vec<ListItem> = tasks
|
let items: Vec<ListItem> = tasks
|
||||||
.iter()
|
.iter()
|
||||||
@@ -160,14 +150,8 @@ pub fn render_task_list(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let block = Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(Style::default().fg(if focused { FOCUS_COLOR } else { Color::DarkGray }))
|
|
||||||
.title(format!(" Tasks ({} todo / {} done) ", todo, done))
|
|
||||||
.title_alignment(Alignment::Left);
|
|
||||||
|
|
||||||
let list = List::new(items)
|
let list = List::new(items)
|
||||||
.block(block)
|
.block(Block::default())
|
||||||
.highlight_style(
|
.highlight_style(
|
||||||
Style::default()
|
Style::default()
|
||||||
.bg(if focused { Color::DarkGray } else { Color::Black })
|
.bg(if focused { Color::DarkGray } else { Color::Black })
|
||||||
@@ -176,7 +160,73 @@ pub fn render_task_list(
|
|||||||
)
|
)
|
||||||
.highlight_symbol(">> ");
|
.highlight_symbol(">> ");
|
||||||
|
|
||||||
frame.render_stateful_widget(list, area, &mut ratatui::widgets::ListState::default().with_selected(Some(selected)));
|
frame.render_stateful_widget(list, tasks_area, &mut ratatui::widgets::ListState::default().with_selected(Some(selected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_list_tabs(
|
||||||
|
frame: &mut Frame,
|
||||||
|
area: Rect,
|
||||||
|
lists: &[TaskList],
|
||||||
|
selected_list: usize,
|
||||||
|
scroll: usize,
|
||||||
|
focused: bool,
|
||||||
|
) {
|
||||||
|
if lists.is_empty() || area.width < 4 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_width = area.width as usize;
|
||||||
|
let mut spans: Vec<Span> = Vec::new();
|
||||||
|
|
||||||
|
let mut used = if scroll > 0 {
|
||||||
|
spans.push(Span::styled(" <", Style::default().fg(Color::DarkGray)));
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
spans.push(Span::raw(" "));
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut last_idx = scroll.wrapping_sub(1);
|
||||||
|
for i in scroll..lists.len() {
|
||||||
|
let title = &lists[i].title;
|
||||||
|
let title_len = title.chars().count();
|
||||||
|
let need_sep = if i > scroll { 3 } else { 0 };
|
||||||
|
let tab_space = title_len + 2;
|
||||||
|
|
||||||
|
if used + need_sep + tab_space > max_width {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if need_sep > 0 {
|
||||||
|
spans.push(Span::raw(" | "));
|
||||||
|
used += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_selected = i == selected_list;
|
||||||
|
let style = if is_selected && focused {
|
||||||
|
Style::default().fg(FOCUS_COLOR).add_modifier(Modifier::BOLD)
|
||||||
|
} else if is_selected {
|
||||||
|
Style::default().fg(FOCUS_COLOR)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(Color::Cyan)
|
||||||
|
};
|
||||||
|
|
||||||
|
spans.push(Span::styled(format!(" {} ", title), style));
|
||||||
|
used += title_len + 2;
|
||||||
|
last_idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_idx + 1 < lists.len() && used + 5 <= max_width {
|
||||||
|
spans.push(Span::raw(" | "));
|
||||||
|
spans.push(Span::styled(">>", Style::default().fg(Color::DarkGray)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if used < max_width {
|
||||||
|
spans.push(Span::raw(" ".repeat(max_width - used)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let paragraph = Paragraph::new(Line::from(spans));
|
||||||
|
frame.render_widget(paragraph, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_detail(
|
pub fn render_detail(
|
||||||
|
|||||||
+7
-9
@@ -11,7 +11,6 @@ use components::*;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Focus {
|
pub enum Focus {
|
||||||
Tabs,
|
|
||||||
TaskList,
|
TaskList,
|
||||||
Detail,
|
Detail,
|
||||||
Calendar,
|
Calendar,
|
||||||
@@ -39,6 +38,7 @@ pub struct AppView<'a> {
|
|||||||
pub lists: &'a [TaskList],
|
pub lists: &'a [TaskList],
|
||||||
pub tasks: &'a [Task],
|
pub tasks: &'a [Task],
|
||||||
pub selected_list: usize,
|
pub selected_list: usize,
|
||||||
|
pub list_tabs_scroll: usize,
|
||||||
pub selected_task: usize,
|
pub selected_task: usize,
|
||||||
pub focus: Focus,
|
pub focus: Focus,
|
||||||
pub show_popup: Option<&'a Popup>,
|
pub show_popup: Option<&'a Popup>,
|
||||||
@@ -68,20 +68,15 @@ pub fn draw(frame: &mut Frame, view: AppView) {
|
|||||||
let main_layout = Layout::default()
|
let main_layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Min(0),
|
Constraint::Min(0),
|
||||||
Constraint::Length(12),
|
Constraint::Length(12),
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
])
|
])
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
let tabs_area = main_layout[0];
|
let body_area = main_layout[0];
|
||||||
let body_area = main_layout[1];
|
let calendar_area = main_layout[1];
|
||||||
let calendar_area = main_layout[2];
|
let status_area = main_layout[2];
|
||||||
let status_area = main_layout[3];
|
|
||||||
|
|
||||||
let is_tabs_focused = view.focus == Focus::Tabs;
|
|
||||||
render_tabs_bar(frame, tabs_area, view.lists, view.selected_list, is_tabs_focused);
|
|
||||||
|
|
||||||
let body_layout = Layout::default()
|
let body_layout = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
@@ -92,6 +87,9 @@ pub fn draw(frame: &mut Frame, view: AppView) {
|
|||||||
render_task_list(
|
render_task_list(
|
||||||
frame,
|
frame,
|
||||||
body_layout[0],
|
body_layout[0],
|
||||||
|
view.lists,
|
||||||
|
view.selected_list,
|
||||||
|
view.list_tabs_scroll,
|
||||||
view.tasks,
|
view.tasks,
|
||||||
view.selected_task,
|
view.selected_task,
|
||||||
is_task_list_focused,
|
is_task_list_focused,
|
||||||
|
|||||||
Reference in New Issue
Block a user