All views be rendering big stuff
This commit is contained in:
parent
570d495faa
commit
dcd1bd6d3f
@ -7,7 +7,7 @@ use crate::pdf::object::Resolve;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
|
||||
use pdf::object::{Object, ObjectWrite, PlainRef, Stream};
|
||||
use pdf::object::{Object, ObjectWrite, PlainRef, Stream, Trace};
|
||||
use pdf::primitive::Primitive;
|
||||
use pdf::xref::XRef;
|
||||
use regex::Regex;
|
||||
@ -62,6 +62,19 @@ pub struct PrimitiveModel {
|
||||
pub expanded: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct PrimitiveTreeView {
|
||||
pub depth: usize,
|
||||
pub key: String,
|
||||
pub ptype: String,
|
||||
pub sub_type: String,
|
||||
pub value: String,
|
||||
pub container: bool,
|
||||
pub expanded: bool,
|
||||
pub path: Vec<PathTrace>,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct PathTrace {
|
||||
pub key: String,
|
||||
@ -83,6 +96,7 @@ pub struct PageModel {
|
||||
pub struct TreeViewRequest {
|
||||
key: String,
|
||||
children: Vec<TreeViewRequest>,
|
||||
expand: bool,
|
||||
}
|
||||
|
||||
impl TreeViewRequest {
|
||||
@ -304,32 +318,45 @@ fn resolve_parent(step: Step, file: &CosFile) -> Result<(Primitive, PathTrace),
|
||||
#[tauri::command]
|
||||
fn get_prim_tree_by_path(
|
||||
id: &str,
|
||||
path: TreeViewRequest,
|
||||
paths: Vec<TreeViewRequest>,
|
||||
session: State<Mutex<Session>>,
|
||||
) -> Result<PrimitiveModel, String> {
|
||||
) -> Result<Vec<PrimitiveTreeView>, String> {
|
||||
let session_guard = session
|
||||
.lock()
|
||||
.map_err(|_| "Failed to lock the session mutex.".to_string())?;
|
||||
let file = get_file_from_state(id, &session_guard)?;
|
||||
|
||||
get_prim_tree_by_path_with_file(path, &file.cos_file)
|
||||
let results = paths
|
||||
.into_iter()
|
||||
.map(|path| get_prim_tree_by_path_with_file(path, &file.cos_file))
|
||||
.collect::<Result<Vec<_>, String>>()?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn get_prim_tree_by_path_with_file(
|
||||
node: TreeViewRequest,
|
||||
file: &CosFile,
|
||||
) -> Result<PrimitiveModel, String> {
|
||||
) -> Result<Vec<PrimitiveTreeView>, String> {
|
||||
let step = node.step()?;
|
||||
let (parent, trace) = resolve_parent(step.clone(), file)?;
|
||||
|
||||
let path = vec![trace];
|
||||
let mut parent_model = PrimitiveModel::from_primitive_with_children(&parent, path);
|
||||
let trace = vec![trace];
|
||||
let mut parent_model: PrimitiveModel;
|
||||
if node.expand {
|
||||
parent_model = PrimitiveModel::from_primitive_with_children(&parent, trace);
|
||||
|
||||
for child in node.children.iter() {
|
||||
expand(child, &mut parent_model, &parent, file)?;
|
||||
for child in node.children.iter() {
|
||||
expand(child, &mut parent_model, &parent, file)?;
|
||||
}
|
||||
} else {
|
||||
parent_model = PrimitiveModel::from_primitive(step.get_key(), &parent, trace);
|
||||
}
|
||||
|
||||
Ok(parent_model)
|
||||
Ok(PrimitiveTreeView::flatten(0, parent_model))
|
||||
}
|
||||
|
||||
fn expand(
|
||||
@ -338,20 +365,21 @@ fn expand(
|
||||
parent: &Primitive,
|
||||
file: &CosFile,
|
||||
) -> Result<(), String> {
|
||||
if !node.expand {
|
||||
return Ok(());
|
||||
}
|
||||
let step = node.step()?;
|
||||
let prim = resolve_step(parent, &step)?;
|
||||
if let Primitive::Reference(x_ref) = prim {
|
||||
let jump = resolve_xref(x_ref.id, file)?;
|
||||
// parent_model.ptype = format!("{}-Reference", jump.get_debug_name());
|
||||
let mut jump_trace = parent_model.trace.clone();
|
||||
jump_trace.push(PathTrace::new(step.get_key(), x_ref.id.to_string()));
|
||||
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
||||
to_expand.add_children(
|
||||
&jump,
|
||||
append_path_with_jump(step.get_key(), x_ref.id.to_string(), &to_expand.trace),
|
||||
);
|
||||
to_expand.add_children(&jump, &jump_trace);
|
||||
expand_children(node, file, &jump, &mut to_expand)?;
|
||||
} else {
|
||||
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
||||
to_expand.add_children(prim, append_path(step.get_key(), &to_expand.trace));
|
||||
to_expand.add_children(prim, &to_expand.trace.clone());
|
||||
expand_children(node, file, prim, &mut to_expand)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -426,6 +454,9 @@ fn retrieve_trailer(file: &CosFile) -> Primitive {
|
||||
}
|
||||
|
||||
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
|
||||
if page_num <= 0 {
|
||||
return Err("Page 0 does not exist, use 1-based index!".to_string());
|
||||
}
|
||||
let page_rc = t!(file.get_page(page_num - 1));
|
||||
let p_ref = page_rc.get_ref().get_inner();
|
||||
Ok((
|
||||
@ -516,12 +547,6 @@ fn get_file_from_state<'a>(
|
||||
.ok_or_else(|| format!("File with id {} does not exist!", id))
|
||||
}
|
||||
|
||||
fn append_path_with_jump(key: String, last_jump: String, path: &Vec<PathTrace>) -> Vec<PathTrace> {
|
||||
let mut new_path = path.clone();
|
||||
new_path.push(PathTrace::new(key, last_jump));
|
||||
new_path
|
||||
}
|
||||
|
||||
fn append_path(key: String, path: &Vec<PathTrace>) -> Vec<PathTrace> {
|
||||
let mut new_path = path.clone();
|
||||
let last_jump = new_path.last().unwrap().last_jump.clone();
|
||||
@ -541,7 +566,7 @@ impl PrimitiveModel {
|
||||
Primitive::Dictionary(_) => "-".to_string(),
|
||||
Primitive::Array(arr) => PrimitiveModel::format_arr_content(arr),
|
||||
Primitive::Reference(pref) => {
|
||||
format!("Obj Number: {} Gen Number: {}", pref.id, pref.gen)
|
||||
format!("Obj Nr: {} Gen Nr: {}", pref.id, pref.gen)
|
||||
}
|
||||
|
||||
Primitive::Name(name) => name.clone().as_str().to_string(),
|
||||
@ -595,17 +620,20 @@ impl PrimitiveModel {
|
||||
result
|
||||
}
|
||||
|
||||
fn from_primitive_with_children(primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
|
||||
fn from_primitive_with_children(
|
||||
primitive: &Primitive,
|
||||
trace: Vec<PathTrace>,
|
||||
) -> PrimitiveModel {
|
||||
let mut model = PrimitiveModel::from_primitive(
|
||||
path.last().unwrap().key.clone(),
|
||||
trace.last().unwrap().key.clone(),
|
||||
primitive,
|
||||
path.clone(),
|
||||
trace.clone(),
|
||||
);
|
||||
model.add_children(primitive, path);
|
||||
model.add_children(primitive, &trace);
|
||||
model
|
||||
}
|
||||
|
||||
fn add_children(&mut self, primitive: &Primitive, path: Vec<PathTrace>) {
|
||||
fn add_children(&mut self, primitive: &Primitive, path: &Vec<PathTrace>) {
|
||||
self.expanded = true;
|
||||
match primitive {
|
||||
Primitive::Dictionary(dict) => dict.iter().for_each(|(name, value)| {
|
||||
@ -619,6 +647,13 @@ impl PrimitiveModel {
|
||||
self.add_child(i.to_string(), obj, append_path(i.to_string(), &path));
|
||||
}),
|
||||
Primitive::Stream(stream) => {
|
||||
stream.info.iter().for_each(|(name, value)| {
|
||||
self.add_child(
|
||||
name.clone().as_str().to_string(),
|
||||
value,
|
||||
append_path(name.clone().as_str().to_string(), &path),
|
||||
);
|
||||
});
|
||||
self.children.push(PrimitiveModel {
|
||||
key: "Data".to_string(),
|
||||
ptype: "Stream Data".to_string(),
|
||||
@ -628,13 +663,6 @@ impl PrimitiveModel {
|
||||
trace: append_path("Data".to_string(), &path),
|
||||
expanded: false,
|
||||
});
|
||||
stream.info.iter().for_each(|(name, value)| {
|
||||
self.add_child(
|
||||
name.clone().as_str().to_string(),
|
||||
value,
|
||||
append_path(name.clone().as_str().to_string(), &path),
|
||||
);
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
@ -654,6 +682,42 @@ impl PrimitiveModel {
|
||||
fn get_child(&mut self, key: String) -> Option<&mut PrimitiveModel> {
|
||||
self.children.iter_mut().find(|child| child.key == key)
|
||||
}
|
||||
|
||||
fn is_container(&self) -> bool {
|
||||
self.ptype == "Dictionary"
|
||||
|| self.ptype == "Array"
|
||||
|| self.ptype == "Stream"
|
||||
|| self.ptype == "Reference"
|
||||
}
|
||||
fn drain_children(&mut self) -> Vec<PrimitiveModel> {
|
||||
self.children.drain(..).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveTreeView {
|
||||
fn from_primitive(depth: usize, primitive: PrimitiveModel) -> PrimitiveTreeView {
|
||||
let is_container = primitive.is_container();
|
||||
PrimitiveTreeView {
|
||||
depth: depth,
|
||||
key: primitive.key,
|
||||
ptype: primitive.ptype,
|
||||
sub_type: primitive.sub_type,
|
||||
value: primitive.value,
|
||||
container: is_container,
|
||||
expanded: primitive.expanded,
|
||||
path: primitive.trace,
|
||||
active: true,
|
||||
}
|
||||
}
|
||||
fn flatten(depth: usize, mut primitive: PrimitiveModel) -> Vec<PrimitiveTreeView> {
|
||||
let mut views: Vec<PrimitiveTreeView> = Vec::new();
|
||||
let children = primitive.drain_children();
|
||||
views.push(PrimitiveTreeView::from_primitive(depth, primitive));
|
||||
children.into_iter().for_each(|child| {
|
||||
views.extend(PrimitiveTreeView::flatten(depth + 1, child.clone()));
|
||||
});
|
||||
views
|
||||
}
|
||||
}
|
||||
#[tauri::command]
|
||||
fn get_xref_table(id: &str, session: State<Mutex<Session>>) -> Result<XRefTableModel, String> {
|
||||
@ -686,7 +750,7 @@ fn get_xref_table_model_with_file(file: &CosFile) -> Result<XRefTableModel, Stri
|
||||
}
|
||||
XRef::Stream { stream_id, index } => XRefEntryModel {
|
||||
obj_num: i as u64,
|
||||
gen_num: *stream_id as u64,
|
||||
gen_num: 0,
|
||||
obj_type: "Stream".into(),
|
||||
offset: *index as u64,
|
||||
},
|
||||
|
||||
@ -80,13 +80,26 @@ mod tests {
|
||||
}],
|
||||
});
|
||||
let root = TreeViewRequest {
|
||||
key: "/".to_string(),
|
||||
key: "Trailer".to_string(),
|
||||
children: path,
|
||||
};
|
||||
|
||||
let message = format!("Retrieval of {:?}", root);
|
||||
let prim = timed!(get_prim_tree_by_path_with_file(root, &file), message);
|
||||
print_node(prim.unwrap(), 0);
|
||||
let prim = timed!(get_prim_tree_by_path_with_file(root, &file), message).unwrap();
|
||||
for ele in prim {
|
||||
println!(
|
||||
"{}{} | {} | {} | {:?}",
|
||||
" ".repeat(ele.depth),
|
||||
ele.key,
|
||||
ele.ptype,
|
||||
ele.value,
|
||||
ele.path
|
||||
.iter()
|
||||
.map(|p| p.key.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("/")
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_read_by_path() {
|
||||
@ -94,7 +107,7 @@ mod tests {
|
||||
FileOptions::cached().open(FILE_PATH).unwrap(),
|
||||
"Loading file"
|
||||
);
|
||||
let path = "/Root/Pages";
|
||||
let path = "/Trailer/Root/Pages";
|
||||
|
||||
let message = format!("Retrieval of {:?}", path);
|
||||
let prim = timed!(get_prim_model_by_path_with_file(path, &file), message);
|
||||
@ -122,9 +135,8 @@ mod tests {
|
||||
"writing trailer"
|
||||
);
|
||||
let trail_model = PrimitiveModel::from_primitive_with_children(
|
||||
"Trailer".to_string(),
|
||||
&trail,
|
||||
vec![PathTrace::new("/".to_string(), "/".to_string())],
|
||||
vec![PathTrace::new("Trailer".to_string(), "Trailer".to_string())],
|
||||
);
|
||||
print_node(trail_model, 5);
|
||||
println!("{:?}", file.trailer.info_dict);
|
||||
|
||||
@ -34,21 +34,31 @@
|
||||
loadContents(path, id);
|
||||
});
|
||||
|
||||
$inspect(contents);
|
||||
function loadContents(path: string | undefined, id: string) {
|
||||
console.log("Loading contents for", path, id);
|
||||
if (!path || !id) return;
|
||||
|
||||
invoke<ContentModel>("get_contents", { id, path })
|
||||
.then((result) => {
|
||||
console.log("Contents loaded", result);
|
||||
contents = result;
|
||||
if (contents && editor) {
|
||||
const text = contents.parts
|
||||
.map((part) => part.join("\n"))
|
||||
.join(
|
||||
"\n\n%-------------------% EOF %-------------------%\n\n",
|
||||
);
|
||||
let text = "";
|
||||
if (contents.parts.length > 1) {
|
||||
let i = 0;
|
||||
for (let part of contents.parts) {
|
||||
text +=
|
||||
"%----------------% Contents[" +
|
||||
i +
|
||||
"] %--------------%\n\n";
|
||||
for (let line of part) {
|
||||
text += " " + line + "\n";
|
||||
}
|
||||
text +=
|
||||
"\n%-------------------% EOF %-------------------%\n\n";
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
text = contents.parts[0].join("\n");
|
||||
}
|
||||
editor.setValue(text);
|
||||
}
|
||||
})
|
||||
|
||||
@ -4,13 +4,9 @@
|
||||
import PrimitiveView from "./PrimitiveView.svelte";
|
||||
import TreeView from "./TreeView.svelte";
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import PageList from "./PageList.svelte";
|
||||
import ContentsView from "./ContentsView.svelte";
|
||||
import TreeViewRequest from "../models/TreeViewRequest.svelte";
|
||||
import TreeViewState from "../models/TreeViewState.svelte";
|
||||
import type { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
TreeViewRequest;
|
||||
import { onMount } from "svelte";
|
||||
let {
|
||||
treeShowing,
|
||||
xrefTableShowing,
|
||||
@ -68,19 +64,7 @@
|
||||
minSize={treeShowing || pagesShowing ? 2 : 0}
|
||||
maxSize={treeShowing || pagesShowing ? 100 : 0}
|
||||
>
|
||||
{#if pagesShowing}
|
||||
<PageList {fState} h={height}></PageList>
|
||||
{:else if treeShowing}
|
||||
<div class="overflow-auto" style="height: {height}px">
|
||||
<TreeView
|
||||
{pathSelectedHandler}
|
||||
file_id={fState.file.id}
|
||||
root={TreeViewRequest.TRAILER}
|
||||
active={true}
|
||||
details={false}
|
||||
></TreeView>
|
||||
</div>
|
||||
{/if}
|
||||
<TreeView {fState} {height}></TreeView>
|
||||
</Pane>
|
||||
|
||||
<Pane minSize={1}>
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
import TreeView from "./TreeView.svelte";
|
||||
import TreeViewRequest from "../models/TreeViewRequest.svelte";
|
||||
import type { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
|
||||
let { fState, h }: { fState: FileViewState; h: number } = $props();
|
||||
let selected_page_num: number | undefined = $state(undefined);
|
||||
let pages = $derived(fState.file.pages);
|
||||
|
||||
function pathSelectedHandler(event: PathSelectedEvent) {
|
||||
let path = event.detail.path;
|
||||
if (
|
||||
path.length > 0 &&
|
||||
path[0].startsWith("Page") &&
|
||||
!isNaN(+path[0].replace("Page", ""))
|
||||
) {
|
||||
selected_page_num = +path[0].replace("Page", "");
|
||||
}
|
||||
fState.selectPath(path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-y-auto" style="height: {h}px">
|
||||
{#each pages as page}
|
||||
<TreeView
|
||||
file_id={fState.file.id}
|
||||
root={new TreeViewRequest("Page" + page.page_num, [])}
|
||||
{pathSelectedHandler}
|
||||
active={selected_page_num === page.page_num}
|
||||
details={true}
|
||||
></TreeView>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.key-field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
@apply bg-forge-acc;
|
||||
}
|
||||
|
||||
.t-header {
|
||||
@apply uppercase text-xs text-forge-text p-5 bg-forge-sec border-r;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.t-data {
|
||||
@apply border border-forge-sec text-xs text-forge-text text-left;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ref-cell {
|
||||
@apply min-w-[100px] w-[100px] min-h-[20px] h-[20px] p-1 m-0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.page-cell {
|
||||
@apply min-w-[150px] w-[150px] min-h-[20px] h-[20px] p-1 m-0;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
@ -1,35 +1,35 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
FileOutline,
|
||||
FolderArrowRightOutline,
|
||||
FolderOutline,
|
||||
CodeOutline,
|
||||
} from "flowbite-svelte-icons";
|
||||
|
||||
import {FileOutline, FolderArrowRightOutline, FolderOutline, CodeOutline} from "flowbite-svelte-icons";
|
||||
|
||||
let {ptype}: {ptype: string} = $props()
|
||||
|
||||
let { ptype }: { ptype: string } = $props();
|
||||
</script>
|
||||
{#if ptype === "Dictionary"}
|
||||
<FolderOutline class="stroke-blue-300 text-blue-300 primitive-icon"/>
|
||||
{:else if ptype === "Array"}
|
||||
<FolderOutline class=" text-orange-300 primitive-icon "/>
|
||||
{:else if ptype === "Reference"}
|
||||
<FolderArrowRightOutline class=" text-purple-300 primitive-icon"/>
|
||||
{:else if ptype === "Integer"}
|
||||
<FileOutline class="text-pink-300 primitive-icon"/>
|
||||
{:else if ptype === "Number"}
|
||||
<FileOutline class="text-lime-300 primitive-icon"/>
|
||||
{:else if ptype === "Boolean"}
|
||||
<FileOutline class="text-fuchsia-300 primitive-icon"/>
|
||||
{:else if ptype === "String"}
|
||||
<FileOutline class="text-green-300 primitive-icon"/>
|
||||
{:else if ptype === "Name"}
|
||||
<FileOutline class="text-red-400 primitive-icon"/>
|
||||
{:else if ptype === "Stream Data"}
|
||||
<CodeOutline class="text-purple-400 primitive_icon"/>
|
||||
{:else}
|
||||
<FileOutline/>
|
||||
{/if}
|
||||
|
||||
{#if ptype === "Dictionary"}
|
||||
<FolderOutline class="stroke-blue-300 text-blue-300 primitive-icon" />
|
||||
{:else if ptype === "Array"}
|
||||
<FolderOutline class=" text-orange-300 primitive-icon " />
|
||||
{:else if ptype === "Reference"}
|
||||
<FolderArrowRightOutline class=" text-purple-300 primitive-icon" />
|
||||
{:else if ptype === "Integer"}
|
||||
<FileOutline class="text-pink-300 primitive-icon" />
|
||||
{:else if ptype === "Number"}
|
||||
<FileOutline class="text-lime-300 primitive-icon" />
|
||||
{:else if ptype === "Boolean"}
|
||||
<FileOutline class="text-fuchsia-300 primitive-icon" />
|
||||
{:else if ptype === "String"}
|
||||
<FileOutline class="text-green-300 primitive-icon" />
|
||||
{:else if ptype === "Name"}
|
||||
<FileOutline class="text-red-400 primitive-icon" />
|
||||
{:else if ptype === "Stream Data"}
|
||||
<CodeOutline class="text-purple-400 primitive_icon" />
|
||||
{:else}
|
||||
<FileOutline />
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
.primitive_icon {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -5,12 +5,22 @@
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import StreamEditor from "./StreamEditor.svelte";
|
||||
|
||||
const cellH = 29;
|
||||
const headerOffset = 24;
|
||||
let { fState, height }: { fState: FileViewState; height: number } =
|
||||
$props();
|
||||
let bodyHeight = $derived(height - 24);
|
||||
let fillerHeight: number = $state(0);
|
||||
|
||||
let firstEntry = $state(0);
|
||||
let lastEntry = $state(100);
|
||||
let scrollY = $state(0);
|
||||
let prim = $derived(fState.prim);
|
||||
let showContents = $state(false);
|
||||
let tableHeight = $state(0);
|
||||
let entriesToDisplay: Primitive[] = $derived(
|
||||
prim ? prim.children.slice(firstEntry, lastEntry) : [],
|
||||
);
|
||||
let tableHeight = $derived(prim ? prim.children.length * cellH : 0);
|
||||
|
||||
let bodyHeight = $derived(height - headerOffset);
|
||||
let editorHeight = $derived(Math.max(800, bodyHeight - tableHeight));
|
||||
$inspect(fState.highlightedPrim);
|
||||
function handlePrimSelect(prim: Primitive) {
|
||||
@ -25,6 +35,14 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function handleScroll(event: Event & { currentTarget: HTMLElement }) {
|
||||
scrollY = event.currentTarget.scrollTop;
|
||||
firstEntry = Math.floor(scrollY / cellH);
|
||||
lastEntry = Math.ceil((scrollY + bodyHeight) / cellH);
|
||||
|
||||
fillerHeight = firstEntry * cellH;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if prim && prim.children && prim.children.length > 0}
|
||||
@ -41,31 +59,44 @@
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="overflow-y-auto" style="height: {bodyHeight}px">
|
||||
<table bind:clientHeight={tableHeight}>
|
||||
<tbody>
|
||||
{#each prim.children as entry}
|
||||
<tr
|
||||
class:selected={entry.key ===
|
||||
fState.highlightedPrim?.key}
|
||||
class="hover:bg-forge-sec"
|
||||
onclick={() => (fState.highlightedPrim = entry)}
|
||||
ondblclick={() => handlePrimSelect(entry)}
|
||||
>
|
||||
<td class="page-cell t-data">
|
||||
<div class="key-field">
|
||||
<PrimitiveIcon ptype={entry.ptype} />
|
||||
<p class="text-left">
|
||||
{entry.key}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ref-cell t-data">{entry.ptype}</td>
|
||||
<td class="cell t-data">{entry.value}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
onscroll={handleScroll}
|
||||
class="overflow-y-auto"
|
||||
style="height: {bodyHeight}px"
|
||||
>
|
||||
<div class="container" style="height: {tableHeight}px">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="filler" style="height: {fillerHeight}px"
|
||||
></tr>
|
||||
{#each entriesToDisplay as entry}
|
||||
<tr
|
||||
class:selected={entry.key ===
|
||||
fState.highlightedPrim?.key}
|
||||
class="row"
|
||||
onclick={() =>
|
||||
(fState.highlightedPrim = entry)}
|
||||
ondblclick={() => handlePrimSelect(entry)}
|
||||
>
|
||||
<td class="page-cell t-data">
|
||||
<div class="key-field">
|
||||
<PrimitiveIcon
|
||||
ptype={entry.ptype}
|
||||
/>
|
||||
<p class="text-left">
|
||||
{entry.key}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ref-cell t-data"
|
||||
>{entry.ptype}</td
|
||||
>
|
||||
<td class="cell t-data">{entry.value}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{#if fState.prim?.ptype === "Stream"}
|
||||
<StreamEditor
|
||||
fileId={fState.file.id}
|
||||
@ -86,20 +117,11 @@
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.path-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.path-view {
|
||||
@apply bg-forge-dark border border-forge-bound text-sm font-extralight mb-2 mt-2 rounded-sm;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: auto;
|
||||
.row {
|
||||
@apply hover:bg-forge-sec;
|
||||
height: 29px;
|
||||
min-height: 29px;
|
||||
max-height: 29px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
@ -118,17 +140,17 @@
|
||||
}
|
||||
|
||||
.cell {
|
||||
@apply min-w-[600px] w-[600px] min-h-[15px] h-[15px] p-1 m-0;
|
||||
@apply min-w-[600px] w-[600px] p-1 m-0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ref-cell {
|
||||
@apply min-w-[100px] w-[100px] min-h-[15px] h-[15px] p-1 m-0;
|
||||
@apply min-w-[100px] w-[100px] p-1 m-0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.page-cell {
|
||||
@apply min-w-[150px] w-[150px] min-h-[15px] h-[15px] p-1 m-0;
|
||||
@apply min-w-[150px] w-[150px] p-1 m-0;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -33,14 +33,11 @@
|
||||
loadContents(path, fileId);
|
||||
});
|
||||
|
||||
$inspect(contents);
|
||||
function loadContents(path: string | undefined, id: string) {
|
||||
console.log("Loading contents for", path, id);
|
||||
if (!path || !id) return;
|
||||
path = path + "/Data";
|
||||
invoke<string>("get_stream_data", { id, path })
|
||||
.then((result) => {
|
||||
console.log("Contents loaded", result);
|
||||
contents = result;
|
||||
if (contents && editor) {
|
||||
editor.setValue(contents);
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type Primitive from "../models/Primitive.svelte";
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import { CaretRightOutline, CaretDownOutline } from "flowbite-svelte-icons";
|
||||
import type TreeViewState from "../models/TreeViewState.svelte";
|
||||
import { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
let {
|
||||
file_id,
|
||||
parent_view,
|
||||
parent_path,
|
||||
treeState,
|
||||
pathSelectedHandler = $bindable(),
|
||||
details,
|
||||
}: {
|
||||
file_id: string;
|
||||
parent_view: Primitive | undefined;
|
||||
parent_path: string[];
|
||||
treeState: TreeViewState | undefined;
|
||||
pathSelectedHandler: any;
|
||||
details: boolean;
|
||||
} = $props();
|
||||
|
||||
function copyPathAndAppend(key: string): string[] {
|
||||
const _path = copyPath();
|
||||
_path.push(key);
|
||||
return _path;
|
||||
}
|
||||
|
||||
function copyPath(): string[] {
|
||||
const _path: string[] = [];
|
||||
|
||||
for (let item of parent_path) {
|
||||
_path.push(item);
|
||||
}
|
||||
return _path;
|
||||
}
|
||||
|
||||
function selectItem(child: Primitive) {
|
||||
let _path = copyPathAndAppend(child.key);
|
||||
if (child.expanded) {
|
||||
treeState?.collapseTree(_path);
|
||||
} else {
|
||||
treeState?.expandTree(_path);
|
||||
}
|
||||
const event = new PathSelectedEvent(file_id, _path);
|
||||
pathSelectedHandler(event);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if parent_view}
|
||||
{#each parent_view.children as child}
|
||||
<button
|
||||
class="row hover:bg-forge-sec w-full"
|
||||
onclick={() => selectItem(child)}
|
||||
>
|
||||
<div class="item" style="margin-left: {parent_path.length * 1}em">
|
||||
{#if child.isContainer()}
|
||||
<div>
|
||||
<span class="caret"
|
||||
>{#if child.expanded}<CaretDownOutline
|
||||
/>{:else}<CaretRightOutline />{/if}</span
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="no-caret"></span>
|
||||
{/if}
|
||||
<div class="item">
|
||||
<PrimitiveIcon ptype={child.ptype} />
|
||||
<div class="row">
|
||||
<p>
|
||||
{child.key}
|
||||
</p>
|
||||
{#if details}
|
||||
<p class="hint">
|
||||
{" | " +
|
||||
child.ptype +
|
||||
" | " +
|
||||
child.sub_type +
|
||||
" | " +
|
||||
child.value}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="ml-1 text-forge-sec small">
|
||||
{child.sub_type}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{#if child.children.length > 0}
|
||||
<svelte:self
|
||||
{file_id}
|
||||
parent_view={child}
|
||||
parent_path={copyPathAndAppend(child.key)}
|
||||
{treeState}
|
||||
bind:pathSelectedHandler
|
||||
{details}
|
||||
></svelte:self>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.hint {
|
||||
@apply ml-1 text-forge-text_hint text-xs whitespace-nowrap font-extralight;
|
||||
}
|
||||
|
||||
.select-button {
|
||||
@apply hover:bg-forge-sec pr-3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply text-sm rounded;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.small {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row {
|
||||
@apply ml-1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Remove default bullets */
|
||||
ul,
|
||||
#myUL {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply pl-5;
|
||||
}
|
||||
|
||||
.no-caret {
|
||||
@apply pl-5;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.caret {
|
||||
@apply text-forge-sec;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.caret-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
|
||||
.active {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@ -1,80 +1,148 @@
|
||||
<script lang="ts">
|
||||
import TreeNode from "./TreeNode.svelte";
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import TreeViewState from "../models/TreeViewState.svelte";
|
||||
import type TreeViewRequest from "../models/TreeViewRequest.svelte";
|
||||
import TreeViewRequest from "../models/TreeViewRequest.svelte";
|
||||
import { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
import { CaretDownOutline, CaretRightOutline } from "flowbite-svelte-icons";
|
||||
import type { PrimitiveView } from "../models/PrimitiveView";
|
||||
import { onMount } from "svelte";
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
|
||||
const rowHeight = 24;
|
||||
let {
|
||||
file_id,
|
||||
root,
|
||||
pathSelectedHandler,
|
||||
active,
|
||||
details,
|
||||
fState,
|
||||
height,
|
||||
}: {
|
||||
file_id: string;
|
||||
root: TreeViewRequest;
|
||||
pathSelectedHandler: any;
|
||||
active: boolean;
|
||||
details: boolean;
|
||||
fState: FileViewState;
|
||||
height: number;
|
||||
} = $props();
|
||||
|
||||
let h = $state(100);
|
||||
let states: TreeViewState[] = $state([]);
|
||||
let treeState: TreeViewState | undefined = $derived(
|
||||
states.find((state) => state.file_id === file_id),
|
||||
);
|
||||
const file_id = $derived(fState.file.id);
|
||||
|
||||
if (active) {
|
||||
loadTreeView();
|
||||
}
|
||||
let fillerHeight: number = $state(0);
|
||||
let states: TreeViewState[] = [];
|
||||
let firstEntry = $state(0);
|
||||
let lastEntry = $state(100);
|
||||
let scrollY = $state(0);
|
||||
let entryCount = $state(0);
|
||||
|
||||
function loadTreeView() {
|
||||
if (!treeState) {
|
||||
let newState = new TreeViewState(file_id, root);
|
||||
newState.loadTreeView();
|
||||
let treeState: TreeViewState | undefined = $state(undefined);
|
||||
|
||||
let totalHeight: number = $state(100);
|
||||
let entries: PrimitiveView[] | undefined = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
treeState = states.find((state) => state.file_id === file_id);
|
||||
updateTreeView();
|
||||
});
|
||||
|
||||
async function updateTreeView() {
|
||||
if (!treeState || treeState.file_id !== file_id) {
|
||||
let newState = new TreeViewState(file_id, fState);
|
||||
await newState.loadTreeView();
|
||||
states.push(newState);
|
||||
treeState = newState;
|
||||
totalHeight = treeState.getEntryCount() * rowHeight;
|
||||
}
|
||||
firstEntry = Math.floor(scrollY / rowHeight);
|
||||
lastEntry = Math.ceil((scrollY + height) / rowHeight);
|
||||
entryCount = treeState?.getEntryCount();
|
||||
totalHeight = Math.max(entryCount * rowHeight, 0);
|
||||
fillerHeight = firstEntry * rowHeight;
|
||||
entries = treeState.getEntries(firstEntry, lastEntry);
|
||||
}
|
||||
|
||||
function select() {
|
||||
loadTreeView();
|
||||
pathSelectedHandler(new PathSelectedEvent(file_id, [root.key]));
|
||||
function handleSelect(prim: PrimitiveView) {
|
||||
if (prim.expanded && prim.container) {
|
||||
treeState
|
||||
?.collapseTree(prim.path.map((path) => path.key))
|
||||
.then(() => {
|
||||
updateTreeView();
|
||||
});
|
||||
return;
|
||||
} else if (prim.container) {
|
||||
treeState
|
||||
?.expandTree(prim.path.map((path) => path.key))
|
||||
.then(() => {
|
||||
updateTreeView();
|
||||
});
|
||||
}
|
||||
fState.selectPathHandler(
|
||||
new PathSelectedEvent(
|
||||
file_id,
|
||||
prim.path.map((path) => path.key),
|
||||
),
|
||||
);
|
||||
updateTreeView();
|
||||
}
|
||||
|
||||
function formatDisplayKey(key: string) {
|
||||
if (key.startsWith("Page") && !key.startsWith("Pages")) {
|
||||
return key.replace("Page", "Page ");
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function handleScroll(event: Event & { currentTarget: HTMLElement }) {
|
||||
scrollY = event.currentTarget.scrollTop;
|
||||
updateTreeView();
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul id="myUL">
|
||||
{#if root}
|
||||
<li>
|
||||
<button
|
||||
class="item hover:bg-forge-sec w-full"
|
||||
onmouseenter={() => loadTreeView()}
|
||||
onclick={() => select()}
|
||||
>
|
||||
{#if active && treeState?.view}
|
||||
<span class="caret"><CaretDownOutline /></span>
|
||||
{:else}
|
||||
<span class="caret"><CaretRightOutline /></span>
|
||||
<div onscroll={handleScroll} class="overflow-auto" style="height: {height}px">
|
||||
{#if entries}
|
||||
<div style="height: {totalHeight}px; width: 100%">
|
||||
<div
|
||||
class="filler"
|
||||
style="height: {fillerHeight}px; width: 100%"
|
||||
></div>
|
||||
{#each entries as entry}
|
||||
{#if entry.active}
|
||||
{#if entry.depth == 0}
|
||||
<div
|
||||
style="height: 1px; width: 100%;"
|
||||
class="bg-forge-bound"
|
||||
></div>
|
||||
{/if}
|
||||
<button
|
||||
class="row text-sm hover:bg-forge-sec w-full group whitespace-nowrap"
|
||||
style="height: {rowHeight}px;"
|
||||
onclick={() => handleSelect(entry)}
|
||||
>
|
||||
<div style="margin-left: {entry.depth * 1.25}em">
|
||||
{#if entry.container}
|
||||
<div>
|
||||
<span
|
||||
class="caret group-hover:text-forge-text_hint"
|
||||
>{#if entry.expanded}<CaretDownOutline
|
||||
/>{:else}<CaretRightOutline
|
||||
/>{/if}</span
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="no-caret"></span>
|
||||
{/if}
|
||||
</div>
|
||||
<PrimitiveIcon ptype={entry.ptype} />
|
||||
<p class="pl-1">
|
||||
{formatDisplayKey(entry.key)}
|
||||
</p>
|
||||
<p
|
||||
class="details group-hover:text-forge-text_hint whitespace-nowrap"
|
||||
>
|
||||
{" | " +
|
||||
entry.value +
|
||||
" | " +
|
||||
entry.sub_type +
|
||||
" | " +
|
||||
entry.ptype}
|
||||
</p>
|
||||
</button>
|
||||
{/if}
|
||||
<div class="item">
|
||||
<PrimitiveIcon ptype={"Dictionary"} />
|
||||
{root.key}
|
||||
</div>
|
||||
</button>
|
||||
{#if active && treeState?.view}
|
||||
<TreeNode
|
||||
{file_id}
|
||||
bind:pathSelectedHandler
|
||||
parent_view={treeState.view}
|
||||
parent_path={treeState.view.getPath()}
|
||||
{treeState}
|
||||
{details}
|
||||
></TreeNode>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.item {
|
||||
@ -83,14 +151,41 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
ul,
|
||||
#myUL {
|
||||
list-style-type: none;
|
||||
}
|
||||
/* Style the caret/arrow */
|
||||
|
||||
.caret {
|
||||
@apply text-forge-sec;
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.details {
|
||||
@apply ml-1 text-forge-prim text-xs whitespace-nowrap font-extralight;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply text-sm rounded;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.small {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.no-caret {
|
||||
@apply pl-5;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -84,10 +84,10 @@
|
||||
<td class="cell t-header border-forge-sec">Offset</td>
|
||||
</tr>
|
||||
<tr
|
||||
class={selectedPath === "/"
|
||||
class={selectedPath === "Trailer"
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(undefined)}
|
||||
onclick={() => fState.selectXref(undefined)}
|
||||
>
|
||||
<td class="cell t-data">Trailer</td>
|
||||
<td class="cell t-data">65535</td>
|
||||
@ -112,7 +112,7 @@
|
||||
class={selectedPath === entry.obj_num
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(entry)}
|
||||
onclick={() => fState.selectXref(entry)}
|
||||
>
|
||||
<td class="cell t-data">{entry.obj_num}</td>
|
||||
<td class="cell t-data">{entry.gen_num}</td>
|
||||
@ -149,15 +149,4 @@
|
||||
.scrollContainer {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.xref-modal {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 281px;
|
||||
background: var(--background-color);
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -55,7 +55,7 @@ export default class FileViewState {
|
||||
}
|
||||
|
||||
public selectPathHandler(event: PathSelectedEvent) {
|
||||
console.log("Selecting path", event.detail);
|
||||
|
||||
if (event.detail.file_id !== this.file.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
15
src/models/PrimitiveView.ts
Normal file
15
src/models/PrimitiveView.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { Trace } from "./Primitive.svelte";
|
||||
|
||||
export class PrimitiveView {
|
||||
constructor(
|
||||
public depth: number,
|
||||
public key: string,
|
||||
public ptype: string,
|
||||
public sub_type: string,
|
||||
public value: string,
|
||||
public container: boolean,
|
||||
public expanded: boolean,
|
||||
public path: Trace[],
|
||||
public active: boolean,
|
||||
) { }
|
||||
}
|
||||
@ -3,11 +3,12 @@ export default class TreeViewRequest {
|
||||
public key: string;
|
||||
public children: TreeViewRequest[];
|
||||
public displayName: string;
|
||||
public active: boolean;
|
||||
expand: boolean;
|
||||
|
||||
constructor(
|
||||
key: string,
|
||||
children: TreeViewRequest[]
|
||||
children: TreeViewRequest[],
|
||||
expand: boolean = false,
|
||||
) {
|
||||
if (key.startsWith("Page")) {
|
||||
this.displayName = "Page " + key.slice(4);
|
||||
@ -16,13 +17,21 @@ export default class TreeViewRequest {
|
||||
}
|
||||
this.key = key;
|
||||
this.children = children;
|
||||
this.active = true;
|
||||
this.expand = expand;
|
||||
}
|
||||
|
||||
static TRAILER = new TreeViewRequest("Trailer", [new TreeViewRequest("Root", [])]);
|
||||
static fromPageCount(pageCount: number) {
|
||||
let roots = [TreeViewRequest.TRAILER];
|
||||
for (let i = 0; i < pageCount; i++) {
|
||||
roots.push(new TreeViewRequest("Page" + (i + 1), []));
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
static TRAILER = new TreeViewRequest("Trailer", [new TreeViewRequest("Root", [], true)], true);
|
||||
|
||||
public clone(): TreeViewRequest {
|
||||
return new TreeViewRequest(this.key, this.children.map(child => child.clone()));
|
||||
return new TreeViewRequest(this.key, this.children.map(child => child.clone()), this.expand);
|
||||
}
|
||||
|
||||
public getChild(key: string) {
|
||||
@ -30,16 +39,17 @@ export default class TreeViewRequest {
|
||||
}
|
||||
|
||||
public addChild(key: string) {
|
||||
let child = new TreeViewRequest(key, [])
|
||||
this.expand = true;
|
||||
let child = new TreeViewRequest(key, [], true)
|
||||
this.children.push(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
public clearChildren() {
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
public removeChild(key: string) {
|
||||
this.children = this.children.filter(child => child.key !== key);
|
||||
}
|
||||
}
|
||||
|
||||
public setExpand(expand: boolean) {
|
||||
this.expand = expand;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,79 +1,164 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import Primitive from "./Primitive.svelte";
|
||||
import TreeViewRequest from "./TreeViewRequest.svelte";
|
||||
import type { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
import { PrimitiveView } from "./PrimitiveView";
|
||||
import type { Trace } from "./Primitive.svelte";
|
||||
import type FileViewState from "./FileViewState.svelte";
|
||||
|
||||
export default class TreeViewState {
|
||||
|
||||
private root: TreeViewRequest = $state(new TreeViewRequest("Trailer", [new TreeViewRequest("Root", [])]));
|
||||
public view: Primitive | undefined = $state();
|
||||
active: boolean = $state(false);
|
||||
|
||||
private request: TreeViewRequest[];
|
||||
private activeRequest: Map<string, TreeViewRequest> = $state(new Map());
|
||||
private activeEntries: Map<string, PrimitiveView[]> = $state(new Map());
|
||||
file_id: string;
|
||||
|
||||
constructor(file_id: string, root: TreeViewRequest) {
|
||||
console.log("Creating tree view state", file_id, root);
|
||||
constructor(file_id: string, fState: FileViewState) {
|
||||
|
||||
this.request = TreeViewRequest.fromPageCount(+fState.file.page_count);
|
||||
this.activeRequest.set(this.request[0].key, this.request[0]);
|
||||
this.file_id = file_id;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public loadTreeView() {
|
||||
this.setTreeViewRequest(this.getRoot());
|
||||
public getEntryCount() {
|
||||
let count = 0;
|
||||
this.activeEntries.forEach((value, key) => {
|
||||
count += value.length - 1;
|
||||
});
|
||||
return this.request.length + count;
|
||||
}
|
||||
|
||||
public getRoot(): TreeViewRequest {
|
||||
return this.root.clone();
|
||||
public getEntries(start: number, end: number): PrimitiveView[] {
|
||||
let i = 0;
|
||||
let result: PrimitiveView[] = [];
|
||||
for (let request of this.request) {
|
||||
if (this.activeEntries.has(request.key)) {
|
||||
let entries = this.activeEntries.get(request.key);
|
||||
for (let entry of entries) {
|
||||
if (i >= start) {
|
||||
result.push(entry);
|
||||
}
|
||||
i += 1;
|
||||
if (i >= end) {
|
||||
console.log("end", i, end);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i >= start) {
|
||||
result.push(
|
||||
new PrimitiveView(
|
||||
0,
|
||||
request.key,
|
||||
"Dictionary",
|
||||
"-",
|
||||
"-",
|
||||
true,
|
||||
false,
|
||||
[{ key: request.key, last_jump: request.key } as Trace],
|
||||
true,
|
||||
))
|
||||
}
|
||||
i += 1;
|
||||
if (i >= end) {
|
||||
console.log("end", i, end);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public setTreeViewRequest(treeViewRequest: TreeViewRequest) {
|
||||
console.log("Loading tree view", treeViewRequest);
|
||||
invoke<Primitive>("get_prim_tree_by_path", {
|
||||
public toView(request: TreeViewRequest): PrimitiveView {
|
||||
return new PrimitiveView(
|
||||
0,
|
||||
request.key,
|
||||
"Dictionary",
|
||||
"-",
|
||||
"-",
|
||||
true,
|
||||
false,
|
||||
[{ key: request.key, last_jump: request.key } as Trace],
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
public async loadTreeView() {
|
||||
const activeRequests = Array.from(this.activeRequest.values());
|
||||
await this.updateTreeViewRequest(activeRequests);
|
||||
}
|
||||
|
||||
public getRoot(): TreeViewRequest[] {
|
||||
let _roots: TreeViewRequest[] = [];
|
||||
this.request.forEach((root) => {
|
||||
_roots.push(root.clone());
|
||||
});
|
||||
return _roots;
|
||||
}
|
||||
|
||||
public async updateTreeViewRequest(treeViewRequests: TreeViewRequest[]) {
|
||||
|
||||
let result = await invoke<PrimitiveView[]>("get_prim_tree_by_path", {
|
||||
id: this.file_id,
|
||||
path: treeViewRequest,
|
||||
})
|
||||
.then((result) => {
|
||||
this.view = new Primitive(result);
|
||||
this.root = treeViewRequest;
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
paths: treeViewRequests,
|
||||
});
|
||||
for (let i = 0; i < treeViewRequests.length; i++) {
|
||||
let request = treeViewRequests[i];
|
||||
if (request.expand) {
|
||||
this.activeRequest.set(request.key, request);
|
||||
} else {
|
||||
this.activeRequest.delete(request.key);
|
||||
}
|
||||
let prim = result.filter(r => r.path[0].key == request.key);
|
||||
if (prim) {
|
||||
this.activeEntries.set(request.key, prim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public expandTree(path: string[]) {
|
||||
public async expandTree(path: string[]) {
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path");
|
||||
return;
|
||||
}
|
||||
console.log("Expanding tree", this.getRoot(), path);
|
||||
let root = this.getRoot();
|
||||
let root = this.activeRequest.get(path[0]);
|
||||
if (!root) {
|
||||
root = this.getRoot().find((root) => root.key === path[0]);
|
||||
if (!root) {
|
||||
console.error("Root not found for path: " + path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
root.setExpand(true);
|
||||
let node = root;
|
||||
for (let key of path.slice(1, path.length)) {
|
||||
let _node: TreeViewRequest | undefined = node.getChild(key);
|
||||
if (_node) {
|
||||
_node.setExpand(true);
|
||||
node = _node;
|
||||
} else {
|
||||
node = node.addChild(key);
|
||||
}
|
||||
}
|
||||
this.setTreeViewRequest(root);
|
||||
await this.updateTreeViewRequest([root]);
|
||||
}
|
||||
|
||||
public collapseTree(path: string[]) {
|
||||
public async collapseTree(path: string[]) {
|
||||
console.log("collapseTree", path);
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path");
|
||||
return;
|
||||
}
|
||||
if (path.length == 1) {
|
||||
this.view = this.view?.withoutChildren();
|
||||
let root: TreeViewRequest | undefined = this.activeRequest.get(path[0]);
|
||||
if (!root) {
|
||||
console.error("Root not found for path: " + path);
|
||||
return;
|
||||
}
|
||||
let root: TreeViewRequest = this.root;
|
||||
let node: TreeViewRequest | undefined = root;
|
||||
for (let key of path.slice(1, path.length - 1)) {
|
||||
for (let key of path.slice(1, path.length)) {
|
||||
console.log("collapseTree", key);
|
||||
node = node?.getChild(key);
|
||||
}
|
||||
if (node) {
|
||||
node.removeChild(path[path.length - 1]);
|
||||
this.setTreeViewRequest(root);
|
||||
node.setExpand(false);
|
||||
await this.updateTreeViewRequest([root]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export default {
|
||||
bound: 'rgba(0, 0, 0, 0.29)',
|
||||
text: '#dadada',
|
||||
text_sec: '#838686',
|
||||
text_hint: '#5adada',
|
||||
text_hint: '#5cc4c4c2',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user