mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-23 20:16:41 +02:00
WIP
This commit is contained in:
parent
ff6c0e60b9
commit
f6c2033b60
2 changed files with 74 additions and 29 deletions
|
@ -10,6 +10,7 @@ use task_hookrs::{import::import, task::Task, uda::UDAValue};
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tui_input::backend::crossterm::EventHandler;
|
||||
use unicode_truncate::UnicodeTruncateStr;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{Component, Frame};
|
||||
|
@ -120,8 +121,6 @@ impl TaskReport {
|
|||
|
||||
pub fn generate_rows(&mut self) -> Result<()> {
|
||||
self.rows = vec![];
|
||||
|
||||
// get all tasks as their string representation
|
||||
for task in self.tasks.iter() {
|
||||
if self.columns.is_empty() {
|
||||
break;
|
||||
|
@ -467,6 +466,44 @@ impl TaskReport {
|
|||
self.state.select(Some(self.current_selection));
|
||||
log::info!("{:?}", self.state);
|
||||
}
|
||||
|
||||
pub fn calculate_widths(&self, maximum_available_width: u16) -> Vec<usize> {
|
||||
// naive implementation of calculate widths
|
||||
let mut widths = self.labels.iter().map(String::len).collect::<Vec<usize>>();
|
||||
for i in 0..self.labels.len() {
|
||||
let max_width = self.rows.iter().map(|row| row[i].len()).max().unwrap_or(0);
|
||||
if max_width == 0 {
|
||||
widths[i] = 0
|
||||
} else {
|
||||
widths[i] = widths[i].max(max_width);
|
||||
}
|
||||
}
|
||||
for (i, header) in self.labels.iter().enumerate() {
|
||||
if header == "Description" || header == "Definition" {
|
||||
// always give description or definition the most room to breath
|
||||
widths[i] = maximum_available_width as usize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i, header) in self.labels.iter().enumerate() {
|
||||
if i == 0 {
|
||||
// always give ID a couple of extra for indicator
|
||||
widths[i] += self.config.task_report.selection_indicator.as_str().width();
|
||||
// if let TableMode::MultipleSelection = self.task_table_state.mode() {
|
||||
// widths[i] += 2
|
||||
// };
|
||||
}
|
||||
}
|
||||
// now start trimming
|
||||
while (widths.iter().sum::<usize>() as u16) >= maximum_available_width - (self.labels.len()) as u16 {
|
||||
let index = widths.iter().position(|i| i == widths.iter().max().unwrap_or(&0)).unwrap_or_default();
|
||||
if widths[index] == 1 {
|
||||
break;
|
||||
}
|
||||
widths[index] -= 1;
|
||||
}
|
||||
widths
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for TaskReport {
|
||||
|
@ -495,34 +532,20 @@ impl Component for TaskReport {
|
|||
}
|
||||
|
||||
fn draw(&mut self, f: &mut Frame<'_>, rect: Rect) -> Result<()> {
|
||||
let column_spacing = 1;
|
||||
if self.rows.len() == 0 {
|
||||
f.render_widget(Paragraph::new("No data found").block(Block::new().borders(Borders::all())), rect);
|
||||
return Ok(());
|
||||
}
|
||||
let mut total_fixed_widths = 0;
|
||||
let mut constraints = Vec::with_capacity(self.rows[0].len());
|
||||
|
||||
for i in 0..self.rows[0].len() {
|
||||
if self.columns[i] == "description" {
|
||||
constraints.push(Constraint::Min(0)); // temporary, will update later
|
||||
} else {
|
||||
let max_width = self.rows.iter().map(|row| row[i].len() as u16).max().unwrap_or(0);
|
||||
total_fixed_widths += max_width + 2; // adding 2 for padding
|
||||
constraints.push(Constraint::Length(max_width + 2));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = self.columns.iter().position(|x| x == "description") {
|
||||
let description_width = rect.width.saturating_sub(total_fixed_widths).saturating_sub(4);
|
||||
constraints[pos] = Constraint::Length(description_width);
|
||||
}
|
||||
let widths = self.calculate_widths(rect.width);
|
||||
let constraints: Vec<Constraint> = widths.iter().map(|i| Constraint::Min(*i as u16)).collect();
|
||||
let rows = self.rows.iter().map(|row| Row::new(row.clone()));
|
||||
let table = Table::new(rows)
|
||||
.header(Row::new(self.columns.clone()))
|
||||
.header(Row::new(self.labels.clone()))
|
||||
.widths(&constraints)
|
||||
.block(Block::new().borders(Borders::ALL))
|
||||
.highlight_symbol(&self.config.task_report.selection_indicator)
|
||||
.highlight_spacing(HighlightSpacing::Always);
|
||||
.highlight_spacing(HighlightSpacing::Always)
|
||||
.column_spacing(column_spacing);
|
||||
f.render_stateful_widget(table, rect, &mut self.state);
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -6,13 +6,14 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
|||
use derive_deref::{Deref, DerefMut};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
|
||||
use serde_derive::Deserialize;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use crate::{action::Action, app::Mode};
|
||||
|
||||
const CONFIG: &str = include_str!("../.config/config.json5");
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub struct TaskReportConfig {
|
||||
#[serde(default)]
|
||||
pub looping: bool,
|
||||
|
@ -22,11 +23,33 @@ pub struct TaskReportConfig {
|
|||
|
||||
impl Into<Value> for TaskReportConfig {
|
||||
fn into(self) -> Value {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("looping".to_string(), Value::from(self.looping));
|
||||
map.insert("selection_indicator".to_string(), Value::from(self.selection_indicator));
|
||||
let json_value = serde_json::to_value(self).unwrap();
|
||||
_convert_json_to_config(json_value)
|
||||
}
|
||||
}
|
||||
|
||||
Value::from(map)
|
||||
fn _convert_json_to_config(json_value: serde_json::Value) -> config::Value {
|
||||
match json_value {
|
||||
JsonValue::Null => config::Value::new(None, config::ValueKind::Nil),
|
||||
JsonValue::Bool(b) => config::Value::from(b),
|
||||
JsonValue::Number(n) => {
|
||||
if let Some(i) = n.as_i64() {
|
||||
config::Value::from(i)
|
||||
} else if let Some(f) = n.as_f64() {
|
||||
config::Value::from(f)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
JsonValue::String(s) => config::Value::from(s),
|
||||
JsonValue::Array(arr) => {
|
||||
let cv_arr: Vec<_> = arr.into_iter().map(_convert_json_to_config).collect();
|
||||
config::Value::new(None, config::ValueKind::Array(cv_arr))
|
||||
},
|
||||
JsonValue::Object(map) => {
|
||||
let cv_map: HashMap<_, _> = map.into_iter().map(|(k, v)| (k, _convert_json_to_config(v))).collect();
|
||||
config::Value::new(None, config::ValueKind::Table(cv_map))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +83,6 @@ impl Config {
|
|||
.set_default("_data_dir", data_dir.to_str().unwrap())?
|
||||
.set_default("_config_dir", config_dir.to_str().unwrap())?;
|
||||
|
||||
// List of potential configuration files provided by the user
|
||||
let config_files = [
|
||||
("config.json5", config::FileFormat::Json5),
|
||||
("config.json", config::FileFormat::Json),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue