Feature 1: date buttons in EditTask popup
- Add 3 buttons (Today, Tomorrow, Next Week) below Notes - Popup height increased from 10 to 12 - Tab/Up/Down cycle through 5 fields (Title, Notes, 3 buttons) - Enter on a date button saves task and sets due date - Text editing keys restricted to fields 0 and 1
This commit is contained in:
+28
-10
@@ -496,12 +496,12 @@ impl App {
|
|||||||
self.show_popup = None;
|
self.show_popup = None;
|
||||||
}
|
}
|
||||||
KeyCode::Tab | KeyCode::Down => {
|
KeyCode::Tab | KeyCode::Down => {
|
||||||
let new_field = (field + 1) % 2;
|
let new_field = (field + 1) % 5;
|
||||||
self.edit_field = new_field;
|
self.edit_field = new_field;
|
||||||
self.show_popup = Some(Popup::EditTask { field: new_field });
|
self.show_popup = Some(Popup::EditTask { field: new_field });
|
||||||
}
|
}
|
||||||
KeyCode::Up => {
|
KeyCode::Up => {
|
||||||
let new_field = (field + 1) % 2;
|
let new_field = (field + 4) % 5;
|
||||||
self.edit_field = new_field;
|
self.edit_field = new_field;
|
||||||
self.show_popup = Some(Popup::EditTask { field: new_field });
|
self.show_popup = Some(Popup::EditTask { field: new_field });
|
||||||
}
|
}
|
||||||
@@ -513,10 +513,28 @@ impl App {
|
|||||||
let notes = self.popup_secondary.trim().to_string();
|
let notes = self.popup_secondary.trim().to_string();
|
||||||
let notes_opt = if notes.is_empty() { None } else { Some(notes) };
|
let notes_opt = if notes.is_empty() { None } else { Some(notes) };
|
||||||
|
|
||||||
|
let due = match field {
|
||||||
|
2 => Some(chrono::Local::now().naive_local()),
|
||||||
|
3 => {
|
||||||
|
let tomorrow = chrono::Local::now().naive_local() + chrono::Duration::days(1);
|
||||||
|
let time = NaiveTime::from_hms_opt(9, 0, 0).unwrap();
|
||||||
|
Some(chrono::NaiveDateTime::new(tomorrow.date(), time))
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let next_week = chrono::Local::now().naive_local() + chrono::Duration::days(7);
|
||||||
|
let time = NaiveTime::from_hms_opt(9, 0, 0).unwrap();
|
||||||
|
Some(chrono::NaiveDateTime::new(next_week.date(), time))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(task_id) = self.editing_task_id.take() {
|
if let Some(task_id) = self.editing_task_id.take() {
|
||||||
if let Some(task) = self.tasks.iter_mut().find(|t| t.id == task_id) {
|
if let Some(task) = self.tasks.iter_mut().find(|t| t.id == task_id) {
|
||||||
task.title = title;
|
task.title = title;
|
||||||
task.notes = notes_opt;
|
task.notes = notes_opt;
|
||||||
|
if let Some(d) = due {
|
||||||
|
task.due = Some(d);
|
||||||
|
}
|
||||||
self.db.update_task(task).ok();
|
self.db.update_task(task).ok();
|
||||||
self.db.push_sync(
|
self.db.push_sync(
|
||||||
SyncAction::Update,
|
SyncAction::Update,
|
||||||
@@ -535,7 +553,7 @@ impl App {
|
|||||||
title,
|
title,
|
||||||
notes: notes_opt,
|
notes: notes_opt,
|
||||||
status: TaskStatus::NeedsAction,
|
status: TaskStatus::NeedsAction,
|
||||||
due: None,
|
due,
|
||||||
position: 0,
|
position: 0,
|
||||||
created_at: None,
|
created_at: None,
|
||||||
updated_at: None,
|
updated_at: None,
|
||||||
@@ -556,7 +574,7 @@ impl App {
|
|||||||
if *field == 0 {
|
if *field == 0 {
|
||||||
self.popup_input.insert(self.popup_cursor, c);
|
self.popup_input.insert(self.popup_cursor, c);
|
||||||
self.popup_cursor += c.len_utf8();
|
self.popup_cursor += c.len_utf8();
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
self.popup_secondary.insert(self.popup_secondary_cursor, c);
|
self.popup_secondary.insert(self.popup_secondary_cursor, c);
|
||||||
self.popup_secondary_cursor += c.len_utf8();
|
self.popup_secondary_cursor += c.len_utf8();
|
||||||
}
|
}
|
||||||
@@ -568,7 +586,7 @@ impl App {
|
|||||||
self.popup_input.replace_range(before..self.popup_cursor, "");
|
self.popup_input.replace_range(before..self.popup_cursor, "");
|
||||||
self.popup_cursor = before;
|
self.popup_cursor = before;
|
||||||
}
|
}
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
if self.popup_secondary_cursor > 0 {
|
if self.popup_secondary_cursor > 0 {
|
||||||
let before = self.popup_secondary.floor_char_boundary(self.popup_secondary_cursor - 1);
|
let before = self.popup_secondary.floor_char_boundary(self.popup_secondary_cursor - 1);
|
||||||
self.popup_secondary.replace_range(before..self.popup_secondary_cursor, "");
|
self.popup_secondary.replace_range(before..self.popup_secondary_cursor, "");
|
||||||
@@ -584,7 +602,7 @@ impl App {
|
|||||||
self.popup_input.replace_range(self.popup_cursor..self.popup_cursor + c.len_utf8(), "");
|
self.popup_input.replace_range(self.popup_cursor..self.popup_cursor + c.len_utf8(), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
if self.popup_secondary_cursor < self.popup_secondary.len() {
|
if self.popup_secondary_cursor < self.popup_secondary.len() {
|
||||||
let s = &self.popup_secondary[self.popup_secondary_cursor..];
|
let s = &self.popup_secondary[self.popup_secondary_cursor..];
|
||||||
if let Some(c) = s.chars().next() {
|
if let Some(c) = s.chars().next() {
|
||||||
@@ -598,7 +616,7 @@ impl App {
|
|||||||
if self.popup_cursor > 0 {
|
if self.popup_cursor > 0 {
|
||||||
self.popup_cursor = self.popup_input.floor_char_boundary(self.popup_cursor - 1);
|
self.popup_cursor = self.popup_input.floor_char_boundary(self.popup_cursor - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
if self.popup_secondary_cursor > 0 {
|
if self.popup_secondary_cursor > 0 {
|
||||||
self.popup_secondary_cursor = self.popup_secondary.floor_char_boundary(self.popup_secondary_cursor - 1);
|
self.popup_secondary_cursor = self.popup_secondary.floor_char_boundary(self.popup_secondary_cursor - 1);
|
||||||
}
|
}
|
||||||
@@ -612,7 +630,7 @@ impl App {
|
|||||||
self.popup_cursor += c.len_utf8();
|
self.popup_cursor += c.len_utf8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
if self.popup_secondary_cursor < self.popup_secondary.len() {
|
if self.popup_secondary_cursor < self.popup_secondary.len() {
|
||||||
let s = &self.popup_secondary[self.popup_secondary_cursor..];
|
let s = &self.popup_secondary[self.popup_secondary_cursor..];
|
||||||
if let Some(c) = s.chars().next() {
|
if let Some(c) = s.chars().next() {
|
||||||
@@ -624,14 +642,14 @@ impl App {
|
|||||||
KeyCode::Home => {
|
KeyCode::Home => {
|
||||||
if *field == 0 {
|
if *field == 0 {
|
||||||
self.popup_cursor = 0;
|
self.popup_cursor = 0;
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
self.popup_secondary_cursor = 0;
|
self.popup_secondary_cursor = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::End => {
|
KeyCode::End => {
|
||||||
if *field == 0 {
|
if *field == 0 {
|
||||||
self.popup_cursor = self.popup_input.len();
|
self.popup_cursor = self.popup_input.len();
|
||||||
} else {
|
} else if *field == 1 {
|
||||||
self.popup_secondary_cursor = self.popup_secondary.len();
|
self.popup_secondary_cursor = self.popup_secondary.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-8
@@ -266,9 +266,8 @@ pub fn render_edit_task_popup(
|
|||||||
notes_cursor: usize,
|
notes_cursor: usize,
|
||||||
active_field: usize,
|
active_field: usize,
|
||||||
) {
|
) {
|
||||||
let popup_area = centered_rect(75, 10, area);
|
let popup_area = centered_rect(75, 12, area);
|
||||||
|
|
||||||
// Clear the area first to prevent style/symbol bleed from previously rendered widgets
|
|
||||||
frame.render_widget(Clear, popup_area);
|
frame.render_widget(Clear, popup_area);
|
||||||
|
|
||||||
let outer_block = Block::default()
|
let outer_block = Block::default()
|
||||||
@@ -280,7 +279,6 @@ pub fn render_edit_task_popup(
|
|||||||
frame.render_widget(Clear, popup_area);
|
frame.render_widget(Clear, popup_area);
|
||||||
frame.render_widget(outer_block, popup_area);
|
frame.render_widget(outer_block, popup_area);
|
||||||
|
|
||||||
// Split inner area into rows
|
|
||||||
let rows = Layout::default()
|
let rows = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
@@ -288,6 +286,8 @@ pub fn render_edit_task_popup(
|
|||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Length(1),
|
||||||
])
|
])
|
||||||
.split(inner_area);
|
.split(inner_area);
|
||||||
|
|
||||||
@@ -321,19 +321,45 @@ pub fn render_edit_task_popup(
|
|||||||
.block(notes_block);
|
.block(notes_block);
|
||||||
frame.render_widget(notes_para, rows[2]);
|
frame.render_widget(notes_para, rows[2]);
|
||||||
|
|
||||||
|
// ── Date buttons row ──
|
||||||
|
let today_style = if active_field == 2 {
|
||||||
|
Style::default().fg(FOCUS_COLOR).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(Color::DarkGray)
|
||||||
|
};
|
||||||
|
let tomorrow_style = if active_field == 3 {
|
||||||
|
Style::default().fg(FOCUS_COLOR).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(Color::DarkGray)
|
||||||
|
};
|
||||||
|
let next_week_style = if active_field == 4 {
|
||||||
|
Style::default().fg(FOCUS_COLOR).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(Color::DarkGray)
|
||||||
|
};
|
||||||
|
let buttons = Paragraph::new(Line::from(vec![
|
||||||
|
Span::styled(" [ Today ] ", today_style),
|
||||||
|
Span::raw(" "),
|
||||||
|
Span::styled(" [ Tomorrow ] ", tomorrow_style),
|
||||||
|
Span::raw(" "),
|
||||||
|
Span::styled(" [ Next Week ] ", next_week_style),
|
||||||
|
]))
|
||||||
|
.alignment(Alignment::Center);
|
||||||
|
frame.render_widget(buttons, rows[4]);
|
||||||
|
|
||||||
// ── Hint row ──
|
// ── Hint row ──
|
||||||
let hint = Paragraph::new(Line::from(Span::styled(
|
let hint = Paragraph::new(Line::from(Span::styled(
|
||||||
" Tab:switch field Enter:save Esc:cancel ",
|
" Tab:switch field Enter:save Esc:cancel ",
|
||||||
Style::default().fg(Color::Gray),
|
Style::default().fg(Color::Gray),
|
||||||
)))
|
)))
|
||||||
.alignment(Alignment::Center);
|
.alignment(Alignment::Center);
|
||||||
frame.render_widget(hint, rows[3]);
|
frame.render_widget(hint, rows[5]);
|
||||||
|
|
||||||
// ── Cursor ──
|
// ── Cursor ──
|
||||||
let (cursor_x, cursor_y) = if active_field == 0 {
|
let (cursor_x, cursor_y) = match active_field {
|
||||||
(rows[0].x + 1 + title_cursor as u16, rows[0].y + 1)
|
0 => (rows[0].x + 1 + title_cursor as u16, rows[0].y + 1),
|
||||||
} else {
|
1 => (rows[2].x + 1 + notes_cursor as u16, rows[2].y + 1),
|
||||||
(rows[2].x + 1 + notes_cursor as u16, rows[2].y + 1)
|
_ => return,
|
||||||
};
|
};
|
||||||
frame.set_cursor_position(ratatui::layout::Position::new(cursor_x, cursor_y));
|
frame.set_cursor_position(ratatui::layout::Position::new(cursor_x, cursor_y));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user