tweaked project mode layout +unsaved detection

This commit is contained in:
WanderingPenwing 2024-03-01 21:37:20 +01:00
parent 024c8579f6
commit d5c5ab9dba
5 changed files with 225 additions and 217 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "calcifer" name = "calcifer"
version = "1.1.0" version = "1.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -48,5 +48,6 @@ Real Ui
Better error handling Better error handling
# releases : #1.2.0 :
latest : command can fetch multiple lines in the buffer in one frame Project mode (when opening a .project file)
Support for js

1
calcifer.project Normal file
View file

@ -0,0 +1 @@
{"categories":[{"name":"to do","content":[{"name":"clean up","description":"using the feedback from ourstory discord","id":1},{"name":"be able to delete item","description":"in project mode add a button in item edit window to delete an item","id":4},{"name":"be able to delete category","description":"in project mode, if no focus and name is empty => delete category\n\nalready did xD I am a geniius\nfor clarification, I had a loong pause in develompent, and had forgotten I had this implemented, and while looking for where to code it, i found at the exact right place, the exact code needed","id":5},{"name":"update category layout","description":"take inspo form tab layout (number of column is max( min col, n_col +1)\nor scroll ?","id":6},{"name":"keep tree in save","description":"keep track of the opened tabs and reopens them\n","id":1},{"name":"u","description":"// Hello there","id":2},{"name":"less enter trigger (project)","description":"if in a textbox, don't open the item window when enter is pressed","id":3}]},{"name":"in progress","content":[]},{"name":"done","content":[{"name":"mark tab as unsaved (project)","description":"when modifying a project, mark it as unsaved","id":2},{"name":"fix + color in project","description":"the '+' to add an item has a wrong color if last item in list is selected","id":3}]},{"name":"bugs","content":[]},{"name":"+","content":[]}]}

View file

@ -357,7 +357,12 @@ impl Calcifer {
} }
match self.project_content.save_to_code() { match self.project_content.save_to_code() {
Ok(code) => current_tab.code = code, Ok(code) => {
if current_tab.code != code {
current_tab.code = code;
current_tab.saved = false;
}
}
Err(_err) => (), Err(_err) => (),
} }
} }

View file

@ -1,8 +1,9 @@
use eframe::egui; use eframe::egui;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
cmp::min, cmp::min,
sync::atomic::{AtomicUsize, Ordering}, cmp::max,
sync::atomic::{AtomicUsize, Ordering},
}; };
use crate::core::hex_str_to_color; use crate::core::hex_str_to_color;
@ -12,262 +13,262 @@ use crate::MAX_PROJECT_COLUMNS;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ProjectSave { pub struct ProjectSave {
pub categories: Vec<Category>, pub categories: Vec<Category>,
} }
impl ProjectSave { impl ProjectSave {
pub fn from_project(project: &Project) -> Self { pub fn from_project(project: &Project) -> Self {
Self { Self {
categories: project.categories.clone(), categories: project.categories.clone(),
} }
} }
} }
pub struct Project { pub struct Project {
pub categories: Vec<Category>, pub categories: Vec<Category>,
pub selected_item: Location, pub selected_item: Location,
pub item_window: sub_windows::ProjectItemWindow, pub item_window: sub_windows::ProjectItemWindow,
was_moving: bool, was_moving: bool,
} }
impl Project { impl Project {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
categories: vec![Category::create()], categories: vec![Category::create()],
selected_item: Location::zero(), selected_item: Location::zero(),
was_moving: false, was_moving: false,
item_window: sub_windows::ProjectItemWindow::new(), item_window: sub_windows::ProjectItemWindow::new(),
} }
} }
pub fn update_from_code(&mut self, json: String) { pub fn update_from_code(&mut self, json: String) {
match serde_json::from_str::<ProjectSave>(&json) { match serde_json::from_str::<ProjectSave>(&json) {
Ok(project_save) => self.categories = project_save.categories, Ok(project_save) => self.categories = project_save.categories,
Err(_err) => self.categories = vec![Category::create()], Err(_err) => self.categories = vec![Category::create()],
} }
} }
pub fn save_to_code(&self) -> Result<String, std::io::Error> { pub fn save_to_code(&self) -> Result<String, std::io::Error> {
Ok(serde_json::to_string(&ProjectSave::from_project(self))?) Ok(serde_json::to_string(&ProjectSave::from_project(self))?)
} }
fn add_category(&mut self) { fn add_category(&mut self) {
let last = self.categories.len() - 1; let last = self.categories.len() - 1;
self.categories[last].initialize(); self.categories[last].initialize();
if self.categories.len() < MAX_PROJECT_COLUMNS { self.categories.push(Category::create());
self.categories.push(Category::create()); }
}
}
fn delete_category(&mut self, index: usize) { fn delete_category(&mut self, index: usize) {
self.categories.remove(index); self.categories.remove(index);
let last = self.categories.len() - 1; let last = self.categories.len() - 1;
if self.categories[last].name != "+" { if self.categories[last].name != "+" {
self.categories.push(Category::create()); self.categories.push(Category::create());
} }
} }
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Category { pub struct Category {
name: String, name: String,
pub content: Vec<Item>, pub content: Vec<Item>,
} }
impl Category { impl Category {
fn create() -> Self { fn create() -> Self {
Self { Self {
name: "+".into(), name: "+".into(),
content: vec![], content: vec![],
} }
} }
fn initialize(&mut self) { fn initialize(&mut self) {
self.name = "untitled".into(); self.name = "untitled".into();
} }
} }
#[derive(Clone, Hash, Serialize, Deserialize)] #[derive(Clone, Hash, Serialize, Deserialize)]
pub struct Item { pub struct Item {
pub name: String, pub name: String,
pub description: String, pub description: String,
id: usize, id: usize,
} }
impl Item { impl Item {
fn new(name: &str) -> Self { fn new(name: &str) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
description: "// Hello there".to_string(), description: "// Hello there".to_string(),
id: get_id(), id: get_id(),
} }
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Location { pub struct Location {
pub category: usize, pub category: usize,
pub row: usize, pub row: usize,
} }
impl Location { impl Location {
fn zero() -> Self { fn zero() -> Self {
Self { Self {
category: 0, category: 0,
row: 0, row: 0,
} }
} }
} }
fn get_id() -> usize { fn get_id() -> usize {
static COUNTER: AtomicUsize = AtomicUsize::new(1); static COUNTER: AtomicUsize = AtomicUsize::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed) COUNTER.fetch_add(1, Ordering::Relaxed)
} }
pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project) { pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project) {
ui.columns(MAX_PROJECT_COLUMNS, |uis| { ui.columns(max(MAX_PROJECT_COLUMNS, project.categories.len() + 1) , |uis| {
for (category_index, category) in project.categories.clone().into_iter().enumerate() { for (category_index, category) in project.categories.clone().into_iter().enumerate() {
let ui = &mut uis[category_index]; let ui = &mut uis[category_index];
if category.name == "+" { if category.name == "+" {
if ui.add(egui::Button::new("+")).clicked() { if ui.add(egui::Button::new("+")).clicked() {
project.add_category(); project.add_category();
} }
continue; continue;
} else { } else {
let response = ui.add( let response = ui.add(
egui::TextEdit::singleline(&mut project.categories[category_index].name) egui::TextEdit::singleline(&mut project.categories[category_index].name)
.desired_width(f32::INFINITY), .desired_width(f32::INFINITY),
); );
if response.lost_focus() && project.categories[category_index].name.is_empty() { if response.lost_focus() && project.categories[category_index].name.is_empty() {
project.delete_category(category_index); project.delete_category(category_index);
} }
} }
ui.separator(); ui.separator();
for (item_index, item) in category.content.iter().enumerate() { for (item_index, item) in category.content.iter().enumerate() {
if project.selected_item if project.selected_item
== (Location { == (Location {
category: category_index, category: category_index,
row: item_index, row: item_index,
}) })
{ {
ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.bg)); ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.bg));
ui.add( ui.add(
egui::Button::new(item.name.clone()) egui::Button::new(item.name.clone())
.fill(hex_str_to_color(theme.functions)), .fill(hex_str_to_color(theme.functions)),
); );
} else { } else {
ui.style_mut().visuals.override_text_color = ui.style_mut().visuals.override_text_color =
Some(hex_str_to_color(theme.literals)); Some(hex_str_to_color(theme.literals));
if ui if ui
.add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg))) .add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg)))
.clicked() .clicked()
{ {
project.selected_item = Location { project.selected_item = Location {
category: category_index, category: category_index,
row: item_index, row: item_index,
}; };
} }
} }
} }
if category.name != "+" && ui.add(egui::Button::new("+")).clicked() { ui.style_mut().visuals.override_text_color =
project.categories[category_index] Some(hex_str_to_color(theme.literals));
.content if category.name != "+" && ui.add(egui::Button::new("+")).clicked() {
.push(Item::new("item")); project.categories[category_index]
} .content
// if category.name != "+" { .push(Item::new("item"));
// if ui.add(egui::Button::new("+")).clicked() { }
// project.categories[category_index] // if category.name != "+" {
// .content // if ui.add(egui::Button::new("+")).clicked() {
// .push(Item::new("item")); // project.categories[category_index]
// } // .content
// } // .push(Item::new("item"));
} // }
}); // }
}
});
let mut moved = false; let mut moved = false;
let category = project.selected_item.category; let category = project.selected_item.category;
let row = project.selected_item.row; let row = project.selected_item.row;
if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.shift) if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.shift)
&& project.selected_item.category > 0 && project.selected_item.category > 0
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
let temp = project.categories[category].content[row].clone(); let temp = project.categories[category].content[row].clone();
project.categories[category - 1].content.push(temp); project.categories[category - 1].content.push(temp);
project.categories[category].content.remove(row); project.categories[category].content.remove(row);
project.selected_item.category -= 1; project.selected_item.category -= 1;
project.selected_item.row = project.categories[category - 1].content.len() - 1; project.selected_item.row = project.categories[category - 1].content.len() - 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.shift) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.shift)
&& project.selected_item.category < project.categories.len() - 2 && project.selected_item.category < project.categories.len() - 2
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
let temp = project.categories[category].content[row].clone(); let temp = project.categories[category].content[row].clone();
project.categories[category + 1].content.push(temp); project.categories[category + 1].content.push(temp);
project.categories[category].content.remove(row); project.categories[category].content.remove(row);
project.selected_item.category += 1; project.selected_item.category += 1;
project.selected_item.row = project.categories[category + 1].content.len() - 1; project.selected_item.row = project.categories[category + 1].content.len() - 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp) && i.modifiers.shift) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp) && i.modifiers.shift)
&& project.selected_item.row > 0 && project.selected_item.row > 0
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
let temp = project.categories[category].content[row].clone(); let temp = project.categories[category].content[row].clone();
project.categories[category].content[row] = project.categories[category].content[row] =
project.categories[category].content[row - 1].clone(); project.categories[category].content[row - 1].clone();
project.categories[category].content[row - 1] = temp.clone(); project.categories[category].content[row - 1] = temp.clone();
project.selected_item.row -= 1; project.selected_item.row -= 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown) && i.modifiers.shift) { } else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown) && i.modifiers.shift) {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
let temp = project.categories[category].content[row].clone(); let temp = project.categories[category].content[row].clone();
project.categories[category].content[row] = project.categories[category].content[row] =
project.categories[category].content[row + 1].clone(); project.categories[category].content[row + 1].clone();
project.categories[category].content[row + 1] = temp.clone(); project.categories[category].content[row + 1] = temp.clone();
project.selected_item.row += 1; project.selected_item.row += 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft)) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft))
&& project.selected_item.category > 0 && project.selected_item.category > 0
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.category -= 1; project.selected_item.category -= 1;
project.selected_item.row = min( project.selected_item.row = min(
project.categories[category].content.len() - 1, project.categories[category].content.len() - 1,
project.selected_item.row, project.selected_item.row,
); );
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight)) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight))
&& project.selected_item.category < project.categories.len() - 2 && project.selected_item.category < project.categories.len() - 2
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.category += 1; project.selected_item.category += 1;
project.selected_item.row = min( project.selected_item.row = min(
project.categories[category].content.len() - 1, project.categories[category].content.len() - 1,
project.selected_item.row, project.selected_item.row,
); );
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) && project.selected_item.row > 0 { } else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) && project.selected_item.row > 0 {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.row -= 1; project.selected_item.row -= 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown))
&& project.selected_item.row < project.categories[category].content.len() - 1 && project.selected_item.row < project.categories[category].content.len() - 1
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.row += 1; project.selected_item.row += 1;
} }
} }
project.was_moving = moved; project.was_moving = moved;
} }