Calendar: independent week scrolling and weekend colors
- calendar_scroll replaced by per-week calendar_scrolls[4] + active_week - Tab cycles through weeks within Calendar, Left/Right switch week - Up/Down scroll only the active week independently - Sat/Sun rendered in Magenta, weekdays in Cyan, today in Yellow
This commit is contained in:
+39
-9
@@ -39,7 +39,8 @@ pub struct App {
|
|||||||
pub task_list_scroll: u16,
|
pub task_list_scroll: u16,
|
||||||
pub detail_scroll: u16,
|
pub detail_scroll: u16,
|
||||||
pub notes_scroll: u16,
|
pub notes_scroll: u16,
|
||||||
pub calendar_scroll: u16,
|
pub calendar_scrolls: [u16; 4],
|
||||||
|
pub calendar_active_week: usize,
|
||||||
pub db: Arc<Db>,
|
pub db: Arc<Db>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub api_client: Arc<ApiClient>,
|
pub api_client: Arc<ApiClient>,
|
||||||
@@ -110,7 +111,8 @@ impl App {
|
|||||||
task_list_scroll: 0,
|
task_list_scroll: 0,
|
||||||
detail_scroll: 0,
|
detail_scroll: 0,
|
||||||
notes_scroll: 0,
|
notes_scroll: 0,
|
||||||
calendar_scroll: 0,
|
calendar_scrolls: [0; 4],
|
||||||
|
calendar_active_week: 0,
|
||||||
db,
|
db,
|
||||||
api_client,
|
api_client,
|
||||||
needs_auth: !has_token,
|
needs_auth: !has_token,
|
||||||
@@ -312,13 +314,25 @@ impl App {
|
|||||||
|
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Tab => {
|
KeyCode::Tab => {
|
||||||
|
match self.focus {
|
||||||
|
Focus::Calendar => {
|
||||||
|
if self.calendar_active_week < 3 {
|
||||||
|
self.calendar_active_week += 1;
|
||||||
|
} else {
|
||||||
|
self.calendar_active_week = 0;
|
||||||
|
self.focus = Focus::Tabs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
self.focus = match self.focus {
|
self.focus = match self.focus {
|
||||||
Focus::Tabs => Focus::TaskList,
|
Focus::Tabs => Focus::TaskList,
|
||||||
Focus::TaskList => Focus::Detail,
|
Focus::TaskList => Focus::Detail,
|
||||||
Focus::Detail => Focus::Calendar,
|
Focus::Detail => Focus::Calendar,
|
||||||
Focus::Calendar => Focus::Tabs,
|
_ => Focus::Tabs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
KeyCode::Up if key.modifiers.contains(KeyModifiers::ALT) => {
|
KeyCode::Up if key.modifiers.contains(KeyModifiers::ALT) => {
|
||||||
if self.focus == Focus::TaskList && !self.tasks.is_empty() {
|
if self.focus == Focus::TaskList && !self.tasks.is_empty() {
|
||||||
self.reorder_task(-1);
|
self.reorder_task(-1);
|
||||||
@@ -340,7 +354,7 @@ impl App {
|
|||||||
self.detail_scroll = self.detail_scroll.saturating_sub(1);
|
self.detail_scroll = self.detail_scroll.saturating_sub(1);
|
||||||
}
|
}
|
||||||
Focus::Calendar => {
|
Focus::Calendar => {
|
||||||
self.calendar_scroll = self.calendar_scroll.saturating_sub(1);
|
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_sub(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
@@ -355,25 +369,41 @@ impl App {
|
|||||||
self.detail_scroll += 1;
|
self.detail_scroll += 1;
|
||||||
}
|
}
|
||||||
Focus::Calendar => {
|
Focus::Calendar => {
|
||||||
self.calendar_scroll += 1;
|
self.calendar_scrolls[self.calendar_active_week] = self.calendar_scrolls[self.calendar_active_week].saturating_add(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
if self.focus == Focus::Tabs && !self.lists.is_empty() {
|
match self.focus {
|
||||||
if self.selected_list + 1 < self.lists.len() {
|
Focus::Tabs => {
|
||||||
|
if !self.lists.is_empty() && self.selected_list + 1 < self.lists.len() {
|
||||||
self.selected_list += 1;
|
self.selected_list += 1;
|
||||||
self.load_tasks();
|
self.load_tasks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Focus::Calendar => {
|
||||||
|
if self.calendar_active_week < 3 {
|
||||||
|
self.calendar_active_week += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
if self.focus == Focus::Tabs && !self.lists.is_empty() {
|
match self.focus {
|
||||||
if self.selected_list > 0 {
|
Focus::Tabs => {
|
||||||
|
if !self.lists.is_empty() && self.selected_list > 0 {
|
||||||
self.selected_list -= 1;
|
self.selected_list -= 1;
|
||||||
self.load_tasks();
|
self.load_tasks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Focus::Calendar => {
|
||||||
|
if self.calendar_active_week > 0 {
|
||||||
|
self.calendar_active_week -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('n') | KeyCode::Char('N') => {
|
KeyCode::Char('n') | KeyCode::Char('N') => {
|
||||||
if !self.needs_auth {
|
if !self.needs_auth {
|
||||||
|
|||||||
+2
-1
@@ -150,7 +150,8 @@ fn main() -> io::Result<()> {
|
|||||||
task_list_scroll: app.task_list_scroll,
|
task_list_scroll: app.task_list_scroll,
|
||||||
detail_scroll: app.detail_scroll,
|
detail_scroll: app.detail_scroll,
|
||||||
notes_scroll: app.notes_scroll,
|
notes_scroll: app.notes_scroll,
|
||||||
calendar_scroll: app.calendar_scroll,
|
calendar_scrolls: &app.calendar_scrolls,
|
||||||
|
calendar_active_week: app.calendar_active_week,
|
||||||
auth_error: app.auth_error.as_deref(),
|
auth_error: app.auth_error.as_deref(),
|
||||||
sync_stats: &app.sync_stats,
|
sync_stats: &app.sync_stats,
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-14
@@ -602,7 +602,8 @@ pub fn render_calendar_panel(
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
events: &[CalendarEvent],
|
events: &[CalendarEvent],
|
||||||
focused: bool,
|
focused: bool,
|
||||||
_scroll: u16,
|
scrolls: &[u16; 4],
|
||||||
|
active_week: usize,
|
||||||
) {
|
) {
|
||||||
if area.width < 20 || area.height < 3 {
|
if area.width < 20 || area.height < 3 {
|
||||||
return;
|
return;
|
||||||
@@ -629,7 +630,7 @@ pub fn render_calendar_panel(
|
|||||||
let week_title = format!(" W/C {} ", week_start.format("%d/%m"));
|
let week_title = format!(" W/C {} ", week_start.format("%d/%m"));
|
||||||
let col_area = cols[week_idx];
|
let col_area = cols[week_idx];
|
||||||
|
|
||||||
let border = if focused { FOCUS_COLOR } else { Color::DarkGray };
|
let border = if focused && week_idx == active_week { FOCUS_COLOR } else { Color::DarkGray };
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(border))
|
.border_style(Style::default().fg(border))
|
||||||
@@ -651,9 +652,6 @@ pub fn render_calendar_panel(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner = block.inner(col_area);
|
|
||||||
let inner_h = inner.height as usize;
|
|
||||||
|
|
||||||
let mut lines: Vec<Line> = Vec::new();
|
let mut lines: Vec<Line> = Vec::new();
|
||||||
|
|
||||||
for day_offset in 0..7 {
|
for day_offset in 0..7 {
|
||||||
@@ -661,6 +659,8 @@ pub fn render_calendar_panel(
|
|||||||
|
|
||||||
let day_style = if day == today {
|
let day_style = if day == today {
|
||||||
Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
|
Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
|
||||||
|
} else if matches!(day.weekday(), chrono::Weekday::Sat | chrono::Weekday::Sun) {
|
||||||
|
Style::default().fg(Color::Magenta).add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)
|
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)
|
||||||
};
|
};
|
||||||
@@ -679,10 +679,6 @@ pub fn render_calendar_panel(
|
|||||||
);
|
);
|
||||||
lines.push(Line::from(Span::styled(day_label, day_style)));
|
lines.push(Line::from(Span::styled(day_label, day_style)));
|
||||||
|
|
||||||
if lines.len() >= inner_h {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for event in events.iter().filter(|e| e.start.map_or(false, |s| s.date() == day)) {
|
for event in events.iter().filter(|e| e.start.map_or(false, |s| s.date() == day)) {
|
||||||
let time_str = event.start.map(|s| s.format("%H:%M").to_string()).unwrap_or_default();
|
let time_str = event.start.map(|s| s.format("%H:%M").to_string()).unwrap_or_default();
|
||||||
let line_text = format!(" {} {}", time_str, event.summary);
|
let line_text = format!(" {} {}", time_str, event.summary);
|
||||||
@@ -690,14 +686,12 @@ pub fn render_calendar_panel(
|
|||||||
line_text,
|
line_text,
|
||||||
Style::default().fg(DETAIL_COLOR),
|
Style::default().fg(DETAIL_COLOR),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
if lines.len() >= inner_h {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let paragraph = Paragraph::new(Text::from(lines)).block(block);
|
let paragraph = Paragraph::new(Text::from(lines))
|
||||||
|
.block(block)
|
||||||
|
.scroll((scrolls[week_idx], 0));
|
||||||
frame.render_widget(paragraph, col_area);
|
frame.render_widget(paragraph, col_area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -48,7 +48,8 @@ pub struct AppView<'a> {
|
|||||||
pub detail_scroll: u16,
|
pub detail_scroll: u16,
|
||||||
pub notes_scroll: u16,
|
pub notes_scroll: u16,
|
||||||
pub calendar_events: &'a [CalendarEvent],
|
pub calendar_events: &'a [CalendarEvent],
|
||||||
pub calendar_scroll: u16,
|
pub calendar_scrolls: &'a [u16; 4],
|
||||||
|
pub calendar_active_week: usize,
|
||||||
pub auth_error: Option<&'a str>,
|
pub auth_error: Option<&'a str>,
|
||||||
pub sync_stats: &'a SyncStats,
|
pub sync_stats: &'a SyncStats,
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,8 @@ pub fn draw(frame: &mut Frame, view: AppView) {
|
|||||||
calendar_area,
|
calendar_area,
|
||||||
view.calendar_events,
|
view.calendar_events,
|
||||||
is_calendar_focused,
|
is_calendar_focused,
|
||||||
view.calendar_scroll,
|
view.calendar_scrolls,
|
||||||
|
view.calendar_active_week,
|
||||||
);
|
);
|
||||||
|
|
||||||
render_status_bar(frame, status_area, view.network_status, view.sync_stats);
|
render_status_bar(frame, status_area, view.network_status, view.sync_stats);
|
||||||
|
|||||||
Reference in New Issue
Block a user