id-based-docs (#376)

* Added some notes on what ds to use for the documents.

* Added a separate list of ids to associate ID to doc. Looks up all
documents by id to retrieve an index via linear search.

* Fixed function name.

* Removed id recycling, replaced document vector with hashmap.

* Uses the same logic for PrevDocument in closing
This commit is contained in:
Porrith Suong 2021-09-20 01:25:27 -04:00 committed by Keavon Chambers
parent 52f597234c
commit 5641f5d822
1 changed files with 72 additions and 67 deletions

View File

@ -3,9 +3,8 @@ use crate::message_prelude::*;
use graphene::layers::Layer; use graphene::layers::Layer;
use graphene::{LayerId, Operation as DocumentOperation}; use graphene::{LayerId, Operation as DocumentOperation};
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::VecDeque; use std::collections::{HashMap, VecDeque};
use super::DocumentMessageHandler; use super::DocumentMessageHandler;
use crate::consts::DEFAULT_DOCUMENT_NAME; use crate::consts::DEFAULT_DOCUMENT_NAME;
@ -36,28 +35,37 @@ pub enum DocumentsMessage {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DocumentsMessageHandler { pub struct DocumentsMessageHandler {
documents: Vec<DocumentMessageHandler>, documents: HashMap<u64, DocumentMessageHandler>,
document_ids: Vec<u64>,
document_id_counter: u64,
active_document_index: usize, active_document_index: usize,
copy_buffer: Vec<Layer>, copy_buffer: Vec<Layer>,
} }
impl DocumentsMessageHandler { impl DocumentsMessageHandler {
pub fn active_document(&self) -> &DocumentMessageHandler { pub fn active_document(&self) -> &DocumentMessageHandler {
&self.documents[self.active_document_index] let id = self.document_ids[self.active_document_index];
self.documents.get(&id).unwrap()
} }
pub fn active_document_mut(&mut self) -> &mut DocumentMessageHandler { pub fn active_document_mut(&mut self) -> &mut DocumentMessageHandler {
&mut self.documents[self.active_document_index] let id = self.document_ids[self.active_document_index];
self.documents.get_mut(&id).unwrap()
} }
fn generate_new_document_name(&self) -> String { fn generate_new_document_name(&self) -> String {
let mut doc_title_numbers = self let mut doc_title_numbers = self
.documents .document_ids
.iter() .iter()
.filter_map(|d| { .filter_map(|id| self.documents.get(&id))
d.name .map(|doc| {
doc.name
.rsplit_once(DEFAULT_DOCUMENT_NAME) .rsplit_once(DEFAULT_DOCUMENT_NAME)
.map(|(prefix, number)| (prefix.is_empty()).then(|| number.trim().parse::<isize>().ok()).flatten().unwrap_or(1)) .map(|(prefix, number)| (prefix.is_empty()).then(|| number.trim().parse::<isize>().ok()).flatten().unwrap_or(1))
.unwrap()
}) })
.collect::<Vec<isize>>(); .collect::<Vec<isize>>();
doc_title_numbers.sort_unstable(); doc_title_numbers.sort_unstable();
doc_title_numbers.iter_mut().enumerate().for_each(|(i, number)| *number = *number - i as isize - 2); doc_title_numbers.iter_mut().enumerate().for_each(|(i, number)| *number = *number - i as isize - 2);
// Uses binary search to find the index of the element where number is bigger than i // Uses binary search to find the index of the element where number is bigger than i
@ -71,11 +79,14 @@ impl DocumentsMessageHandler {
} }
fn load_document(&mut self, new_document: DocumentMessageHandler, responses: &mut VecDeque<Message>) { fn load_document(&mut self, new_document: DocumentMessageHandler, responses: &mut VecDeque<Message>) {
self.active_document_index = self.documents.len(); self.document_id_counter += 1;
self.documents.push(new_document); self.active_document_index = self.document_ids.len();
self.document_ids.push(self.document_id_counter);
self.documents.insert(self.document_id_counter, new_document);
// Send the new list of document tab names // Send the new list of document tab names
let open_documents = self.documents.iter().map(|doc| doc.name.clone()).collect(); let open_documents = self.document_ids.iter().filter_map(|id| self.documents.get(&id).map(|doc| doc.name.clone())).collect::<Vec<String>>();
responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into()); responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into());
responses.push_back(DocumentsMessage::SelectDocument(self.active_document_index).into()); responses.push_back(DocumentsMessage::SelectDocument(self.active_document_index).into());
@ -89,10 +100,14 @@ impl DocumentsMessageHandler {
impl Default for DocumentsMessageHandler { impl Default for DocumentsMessageHandler {
fn default() -> Self { fn default() -> Self {
let mut documents_map: HashMap<u64, DocumentMessageHandler> = HashMap::with_capacity(1);
documents_map.insert(0, DocumentMessageHandler::default());
Self { Self {
documents: vec![DocumentMessageHandler::default()], documents: documents_map,
active_document_index: 0, document_ids: vec![0],
copy_buffer: vec![], copy_buffer: vec![],
active_document_index: 0,
document_id_counter: 0,
} }
} }
} }
@ -103,15 +118,11 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
use DocumentsMessage::*; use DocumentsMessage::*;
match message { match message {
Document(message) => self.active_document_mut().process_action(message, ipp, responses), Document(message) => self.active_document_mut().process_action(message, ipp, responses),
SelectDocument(id) => { SelectDocument(index) => {
assert!(id < self.documents.len(), "Tried to select a document that was not initialized"); // NOTE: Potentially this will break if we ever exceed 56 bit values due to how the message parsing system works.
self.active_document_index = id; assert!(index < self.documents.len(), "Tried to select a document that was not initialized");
responses.push_back( self.active_document_index = index;
FrontendMessage::SetActiveDocument { responses.push_back(FrontendMessage::SetActiveDocument { document_index: index }.into());
document_index: self.active_document_index,
}
.into(),
);
responses.push_back(RenderDocument.into()); responses.push_back(RenderDocument.into());
responses.push_back(DocumentMessage::DocumentStructureChanged.into()); responses.push_back(DocumentMessage::DocumentStructureChanged.into());
for layer in self.active_document().layer_data.keys() { for layer in self.active_document().layer_data.keys() {
@ -132,54 +143,47 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
CloseAllDocuments => { CloseAllDocuments => {
// Empty the list of internal document data // Empty the list of internal document data
self.documents.clear(); self.documents.clear();
self.document_ids.clear();
// Create a new blank document // Create a new blank document
responses.push_back(NewDocument.into()); responses.push_back(NewDocument.into());
} }
CloseDocument(id) => { CloseDocument(index) => {
assert!(id < self.documents.len(), "Tried to select a document that was not initialized"); assert!(index < self.documents.len(), "Tried to close a document that was not initialized");
// Remove doc from the backend store; use `id` as client tabs and backend documents will be in sync // Get the ID based on the current collection of the documents.
self.documents.remove(id); let id = self.document_ids[index];
// Map the ID to an index and remove the document
// Send the new list of document tab names self.documents.remove(&id);
let open_documents = self.documents.iter().map(|doc| doc.name.clone()).collect(); self.document_ids.remove(index);
responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into());
// Last tab was closed, so create a new blank tab // Last tab was closed, so create a new blank tab
if self.documents.is_empty() { if self.document_ids.is_empty() {
self.active_document_index = 0; self.document_id_counter += 1;
responses.push_back(NewDocument.into()); self.document_ids.push(self.document_id_counter);
self.documents.insert(self.document_id_counter, DocumentMessageHandler::default());
} }
// The currently selected doc is being closed
else if id == self.active_document_index {
// The currently selected tab was the rightmost tab
if id == self.documents.len() {
self.active_document_index -= 1;
}
responses.push_back(DocumentMessage::DocumentStructureChanged.into()); self.active_document_index = if self.active_document_index >= self.document_ids.len() {
responses.push_back( self.document_ids.len() - 1
FrontendMessage::SetActiveDocument { } else {
document_index: self.active_document_index, index
} };
.into(),
); // Send the new list of document tab names
responses.push_back( let open_documents = self.document_ids.iter().filter_map(|id| self.documents.get(&id).map(|doc| doc.name.clone())).collect();
FrontendMessage::UpdateCanvas {
document: self.active_document_mut().graphene_document.render_root(), // Update the list of new documents on the front end, active tab, and ensure that document renders
} responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into());
.into(), responses.push_back(
); FrontendMessage::SetActiveDocument {
} document_index: self.active_document_index,
// Active doc will move one space to the left }
else if id < self.active_document_index { .into(),
self.active_document_index -= 1; );
responses.push_back( responses.push_back(RenderDocument.into());
FrontendMessage::SetActiveDocument { responses.push_back(DocumentMessage::DocumentStructureChanged.into());
document_index: self.active_document_index, for layer in self.active_document().layer_data.keys() {
} responses.push_back(DocumentMessage::LayerChanged(layer.clone()).into());
.into(),
);
} }
} }
NewDocument => { NewDocument => {
@ -207,16 +211,17 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
} }
GetOpenDocumentsList => { GetOpenDocumentsList => {
// Send the list of document tab names // Send the list of document tab names
let open_documents = self.documents.iter().map(|doc| doc.name.clone()).collect(); let open_documents = self.documents.values().map(|doc| doc.name.clone()).collect();
responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into()); responses.push_back(FrontendMessage::UpdateOpenDocumentsList { open_documents }.into());
} }
NextDocument => { NextDocument => {
let id = (self.active_document_index + 1) % self.documents.len(); let next = (self.active_document_index + 1) % self.document_ids.len();
responses.push_back(SelectDocument(id).into()); responses.push_back(SelectDocument(next).into());
} }
PrevDocument => { PrevDocument => {
let id = (self.active_document_index + self.documents.len() - 1) % self.documents.len(); let len = self.document_ids.len();
responses.push_back(SelectDocument(id).into()); let prev = (self.active_document_index + len - 1) % len;
responses.push_back(SelectDocument(prev).into());
} }
Copy => { Copy => {
let paths = self.active_document().selected_layers_sorted(); let paths = self.active_document().selected_layers_sorted();