tweaked project mode layout +unsaved detection
This commit is contained in:
parent
024c8579f6
commit
d5c5ab9dba
|
@ -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
|
||||||
|
|
|
@ -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
1
calcifer.project
Normal 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":[]}]}
|
|
@ -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) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue