refactor(core): reorganize module structure
This commit is contained in:
parent
59ab96be98
commit
a6c1863f57
22 changed files with 231 additions and 164 deletions
|
|
@ -4,9 +4,9 @@
|
||||||
// CLI test harness for the noctua_core document foundation.
|
// CLI test harness for the noctua_core document foundation.
|
||||||
|
|
||||||
use clap::{ArgGroup, Parser};
|
use clap::{ArgGroup, Parser};
|
||||||
use noctua_core::document::command::SessionCommand;
|
|
||||||
use noctua_core::document::manager::{DocumentManager, DocumentState};
|
use noctua_core::document::manager::{DocumentManager, DocumentState};
|
||||||
use noctua_core::document::session::CollectionKind;
|
use noctua_core::document::session::command::SessionCommand;
|
||||||
|
use noctua_core::document::session::data::CollectionKind;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// ── ANSI helpers ──
|
// ── ANSI helpers ──
|
||||||
|
|
@ -139,7 +139,7 @@ fn main() {
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
&format!("New session '{session_name}'"),
|
&format!("New browser session '{session_name}'"),
|
||||||
SessionCommand::New {
|
SessionCommand::New {
|
||||||
name: session_name.clone(),
|
name: session_name.clone(),
|
||||||
kind: CollectionKind::DirectoryBrowser,
|
kind: CollectionKind::DirectoryBrowser,
|
||||||
|
|
@ -154,7 +154,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
&format!("Scan directory {}", dir.display()),
|
&format!("Add directory {}", dir.display()),
|
||||||
SessionCommand::AddDirectory { dir },
|
SessionCommand::AddDirectory { dir },
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
|
|
@ -186,7 +186,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
&format!("Scan directory {}", path.display()),
|
&format!("Add directory {}", path.display()),
|
||||||
SessionCommand::AddDirectory { dir: path },
|
SessionCommand::AddDirectory { dir: path },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,13 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/command.rs
|
// src/document/command.rs
|
||||||
//
|
//
|
||||||
// Command enums for session management and document operations.
|
// Commands that operate on the active document itself.
|
||||||
|
|
||||||
use crate::document::session::CollectionKind;
|
/// Commands operating on the active document (zoom, export, annotate, …).
|
||||||
use std::path::PathBuf;
|
///
|
||||||
|
/// The `DocumentManager` dispatches these after resolving which document is
|
||||||
/// Commands operating on sessions and their document collections.
|
/// active – the UI never needs to know the underlying format.
|
||||||
/// All collection-mutating commands operate on the active session
|
|
||||||
/// unless they explicitly name a session.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum SessionCommand {
|
|
||||||
// -- Lifecycle --
|
|
||||||
/// Create a new empty session of the given kind.
|
|
||||||
New { name: String, kind: CollectionKind },
|
|
||||||
/// Open sessions from an existing .ron file.
|
|
||||||
Open { path: PathBuf },
|
|
||||||
/// Switch the active session by name.
|
|
||||||
Select { name: String },
|
|
||||||
|
|
||||||
// -- Collection management --
|
|
||||||
/// Scan a directory and replace the active session's items.
|
|
||||||
AddDirectory { dir: PathBuf },
|
|
||||||
/// Add a single document to the active session.
|
|
||||||
AddDocument { path: PathBuf },
|
|
||||||
/// Copy a document from one session to another.
|
|
||||||
CopyDocument {
|
|
||||||
source: String,
|
|
||||||
target: String,
|
|
||||||
source_index: usize,
|
|
||||||
target_index: usize,
|
|
||||||
},
|
|
||||||
/// Remove a document by index from the active session.
|
|
||||||
RemoveDocument { index: usize },
|
|
||||||
|
|
||||||
// -- Navigation --
|
|
||||||
/// Navigate to the next document in the active session.
|
|
||||||
NextDocument,
|
|
||||||
/// Navigate to the previous document in the active session.
|
|
||||||
PreviousDocument,
|
|
||||||
/// Select a document by index in the active session.
|
|
||||||
SelectDocument { index: usize },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Commands operating on the active document itself.
|
|
||||||
/// Reserved for future use (rotate, flip, zoom, export, …).
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DocumentCommand {
|
pub enum DocumentCommand {
|
||||||
// Future: Rotate, Flip, Zoom, Export, ...
|
// Reserved for future use: Rotate, Flip, Zoom, Export, Annotate, …
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// src/document/format.rs
|
|
||||||
//
|
|
||||||
// Single source of truth for document format detection.
|
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
/// The three document formats Noctua understands.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum DocumentFormat {
|
|
||||||
/// Raster images – anything libcosmic's image crate can decode.
|
|
||||||
Raster,
|
|
||||||
/// Vector graphics (SVG).
|
|
||||||
Vector,
|
|
||||||
/// Portable documents (PDF).
|
|
||||||
Portable,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Detects the document format from the file extension.
|
|
||||||
///
|
|
||||||
/// Returns `None` for unknown extensions so directory scans can skip
|
|
||||||
/// unsupported files early. At load time the underlying library
|
|
||||||
/// (ImageReader, usvg, pdfium) is the final authority.
|
|
||||||
pub fn detect_format(path: &Path) -> Option<DocumentFormat> {
|
|
||||||
match path
|
|
||||||
.extension()
|
|
||||||
.and_then(|e| e.to_str())
|
|
||||||
.map(|e| e.to_lowercase())
|
|
||||||
.as_deref()
|
|
||||||
{
|
|
||||||
Some("pdf") => Some(DocumentFormat::Portable),
|
|
||||||
Some("svg") => Some(DocumentFormat::Vector),
|
|
||||||
Some("png" | "jpg" | "jpeg" | "webp" | "gif" | "bmp" | "tiff" | "tif") => {
|
|
||||||
Some(DocumentFormat::Raster)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +1,56 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/loader.rs
|
// src/document/loader.rs
|
||||||
//
|
//
|
||||||
// Document loading: format dispatch and directory scanning.
|
// Document loading: format detection, format dispatch, and directory scanning.
|
||||||
|
|
||||||
use crate::document::format::{detect_format, DocumentFormat};
|
use crate::document::model::{PortableModel, RasterModel, VectorModel};
|
||||||
use crate::document::portable::PortableModel;
|
use crate::document::session::data::{SessionData, SessionItem};
|
||||||
use crate::document::raster::RasterModel;
|
use crate::document::types::PageLayout;
|
||||||
use crate::document::session::{SessionData, SessionItem};
|
use crate::document::DocumentContent;
|
||||||
use crate::document::vector::VectorModel;
|
|
||||||
use crate::document::{core::PageLayout, DocumentContent};
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// The three document formats Noctua understands.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum DocumentFormat {
|
||||||
|
/// Raster images – anything the image crate can decode.
|
||||||
|
Raster,
|
||||||
|
/// Vector graphics (SVG).
|
||||||
|
Vector,
|
||||||
|
/// Portable documents (PDF).
|
||||||
|
Portable,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detects the document format from the file extension.
|
||||||
|
///
|
||||||
|
/// Returns `None` for unknown extensions so directory scans can skip
|
||||||
|
/// unsupported files early. At load time the underlying library
|
||||||
|
/// (`ImageReader`, `usvg`, `pdfium`) is the final authority.
|
||||||
|
fn detect_format(path: &Path) -> Option<DocumentFormat> {
|
||||||
|
match path
|
||||||
|
.extension()
|
||||||
|
.and_then(|e| e.to_str())
|
||||||
|
.map(str::to_lowercase)
|
||||||
|
.as_deref()
|
||||||
|
{
|
||||||
|
Some("pdf") => Some(DocumentFormat::Portable),
|
||||||
|
Some("svg") => Some(DocumentFormat::Vector),
|
||||||
|
Some("png" | "jpg" | "jpeg" | "webp" | "gif" | "bmp" | "tiff" | "tif") => {
|
||||||
|
Some(DocumentFormat::Raster)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads a document from a path and returns its content.
|
/// Loads a document from a path and returns its content.
|
||||||
pub fn load_document(path: &Path) -> Result<DocumentContent> {
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`Error::NotFound`] if the path does not exist.
|
||||||
|
/// Returns [`Error::Unsupported`] if the file extension is not recognised.
|
||||||
|
pub(crate) fn load_document(path: &Path) -> Result<DocumentContent> {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Err(Error::NotFound(format!("{:?}", path)));
|
return Err(Error::NotFound(format!("{}", path.display())));
|
||||||
}
|
}
|
||||||
|
|
||||||
match detect_format(path) {
|
match detect_format(path) {
|
||||||
|
|
@ -51,10 +86,10 @@ pub fn load_document(path: &Path) -> Result<DocumentContent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scans a directory for supported files and populates the session.
|
/// Scans a directory for supported files and populates the session.
|
||||||
pub fn scan_directory_into(session: &mut SessionData, dir: &Path) {
|
pub(crate) fn scan_directory_into(session: &mut SessionData, dir: &Path) {
|
||||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
if let Ok(entries) = std::fs::read_dir(dir) {
|
||||||
let mut paths: Vec<_> = entries
|
let mut paths: Vec<_> = entries
|
||||||
.filter_map(|e| e.ok())
|
.filter_map(std::result::Result::ok)
|
||||||
.map(|e| e.path())
|
.map(|e| e.path())
|
||||||
.filter(|p| p.is_file() && detect_format(p).is_some())
|
.filter(|p| p.is_file() && detect_format(p).is_some())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,22 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/mod.rs
|
// src/document/mod.rs
|
||||||
//
|
//
|
||||||
// Document abstraction and format definitions.
|
// Document abstraction – the public surface of the document subsystem.
|
||||||
|
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod core;
|
|
||||||
pub mod format;
|
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod portable;
|
pub mod model;
|
||||||
pub mod raster;
|
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod store;
|
pub mod types;
|
||||||
pub mod vector;
|
|
||||||
|
|
||||||
use self::portable::PortableModel;
|
use self::model::{PortableModel, RasterModel, VectorModel};
|
||||||
use self::raster::RasterModel;
|
|
||||||
use self::vector::VectorModel;
|
|
||||||
|
|
||||||
/// Represents the physical content of a loaded document.
|
/// The decoded content of a loaded document.
|
||||||
#[derive(Debug, Clone)]
|
///
|
||||||
|
/// This enum is internal to `noctua_core`. The UI only ever sees a `RawImage`
|
||||||
|
/// produced by the render subsystem – it never matches on this type.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum DocumentContent {
|
pub enum DocumentContent {
|
||||||
Raster(RasterModel),
|
Raster(RasterModel),
|
||||||
Vector(VectorModel),
|
Vector(VectorModel),
|
||||||
|
|
|
||||||
12
core/src/document/model/mod.rs
Normal file
12
core/src/document/model/mod.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/model/mod.rs
|
||||||
|
//
|
||||||
|
// The three concrete document models Noctua understands.
|
||||||
|
|
||||||
|
pub mod portable;
|
||||||
|
pub mod raster;
|
||||||
|
pub mod vector;
|
||||||
|
|
||||||
|
pub use portable::PortableModel;
|
||||||
|
pub use raster::RasterModel;
|
||||||
|
pub use vector::VectorModel;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/portable.rs
|
// src/document/model/portable.rs
|
||||||
//
|
//
|
||||||
// Portable document format model (PDF).
|
// Portable document format model (PDF).
|
||||||
|
|
||||||
use crate::document::core::PageLayout;
|
use crate::document::types::PageLayout;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/raster.rs
|
// src/document/model/raster.rs
|
||||||
//
|
//
|
||||||
// Raster image document model (JPEG, PNG, etc.).
|
// Raster image document model (JPEG, PNG, etc.).
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/vector.rs
|
// src/document/model/vector.rs
|
||||||
//
|
//
|
||||||
// Vector image document model (SVG).
|
// Vector image document model (SVG).
|
||||||
|
|
||||||
use crate::document::core::ViewBox;
|
use crate::document::types::ViewBox;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
45
core/src/document/session/command.rs
Normal file
45
core/src/document/session/command.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/session/command.rs
|
||||||
|
//
|
||||||
|
// Commands that operate on sessions and their document collections.
|
||||||
|
|
||||||
|
use crate::document::session::data::CollectionKind;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Commands operating on sessions and their document collections.
|
||||||
|
///
|
||||||
|
/// All collection-mutating commands operate on the active session
|
||||||
|
/// unless they explicitly name a session.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SessionCommand {
|
||||||
|
// Lifecycle
|
||||||
|
/// Create a new empty session of the given kind.
|
||||||
|
New { name: String, kind: CollectionKind },
|
||||||
|
/// Open sessions from an existing .ron file.
|
||||||
|
Open { path: PathBuf },
|
||||||
|
/// Switch the active session by name.
|
||||||
|
Select { name: String },
|
||||||
|
|
||||||
|
// Collection management
|
||||||
|
/// Scan a directory and replace the active session's items.
|
||||||
|
AddDirectory { dir: PathBuf },
|
||||||
|
/// Add a single document to the active session.
|
||||||
|
AddDocument { path: PathBuf },
|
||||||
|
/// Copy a document from one session to another.
|
||||||
|
CopyDocument {
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
source_index: usize,
|
||||||
|
target_index: usize,
|
||||||
|
},
|
||||||
|
/// Remove a document by index from the active session.
|
||||||
|
RemoveDocument { index: usize },
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
/// Navigate to the next document in the active session.
|
||||||
|
NextDocument,
|
||||||
|
/// Navigate to the previous document in the active session.
|
||||||
|
PreviousDocument,
|
||||||
|
/// Select a document by index in the active session.
|
||||||
|
SelectDocument { index: usize },
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/session.rs
|
// src/document/session/data.rs
|
||||||
//
|
//
|
||||||
// A session is the unit of work in Noctua. It groups a set of document items
|
// A session is the unit of work in Noctua. It groups a set of document items
|
||||||
// under a name and tracks which item is currently active.
|
// under a name and tracks which item is currently active.
|
||||||
|
|
@ -42,6 +42,7 @@ pub struct SessionItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionItem {
|
impl SessionItem {
|
||||||
|
#[must_use]
|
||||||
pub fn new(path: PathBuf, page_index: usize) -> Self {
|
pub fn new(path: PathBuf, page_index: usize) -> Self {
|
||||||
Self { path, page_index }
|
Self { path, page_index }
|
||||||
}
|
}
|
||||||
|
|
@ -52,8 +53,8 @@ impl SessionItem {
|
||||||
pub struct SessionData {
|
pub struct SessionData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub kind: CollectionKind,
|
pub kind: CollectionKind,
|
||||||
/// For DirectoryBrowser: the source directory to scan.
|
/// For `DirectoryBrowser`: the source directory to scan.
|
||||||
/// For DocumentCollection: None (items are managed individually).
|
/// For `DocumentCollection`: None (items are managed individually).
|
||||||
/// The .ron session file path is tracked separately.
|
/// The .ron session file path is tracked separately.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
|
|
@ -63,6 +64,7 @@ pub struct SessionData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionData {
|
impl SessionData {
|
||||||
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: CollectionKind,
|
kind: CollectionKind,
|
||||||
11
core/src/document/session/mod.rs
Normal file
11
core/src/document/session/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/session/mod.rs
|
||||||
|
//
|
||||||
|
// Session subsystem: data model, commands, and persistence.
|
||||||
|
|
||||||
|
pub mod command;
|
||||||
|
pub mod data;
|
||||||
|
pub mod store;
|
||||||
|
|
||||||
|
pub use command::SessionCommand;
|
||||||
|
pub use data::{CollectionKind, SessionData, SessionItem};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/store.rs
|
// src/document/session/store.rs
|
||||||
//
|
//
|
||||||
// RON-based persistence for session data: load and save session lists.
|
// RON-based persistence for session data: load and save session lists.
|
||||||
//
|
//
|
||||||
|
|
@ -27,23 +27,37 @@
|
||||||
// ),
|
// ),
|
||||||
// ]
|
// ]
|
||||||
|
|
||||||
use crate::document::session::SessionData;
|
use crate::document::session::data::SessionData;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Loads all sessions from a RON file.
|
/// Loads all sessions from a RON file.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`Error::Io`] if the file cannot be read.
|
||||||
|
/// Returns [`Error::Session`] if the file cannot be parsed.
|
||||||
pub fn load_sessions(path: &Path) -> Result<Vec<SessionData>> {
|
pub fn load_sessions(path: &Path) -> Result<Vec<SessionData>> {
|
||||||
let content = std::fs::read_to_string(path).map_err(Error::Io)?;
|
let content = std::fs::read_to_string(path).map_err(Error::Io)?;
|
||||||
|
|
||||||
ron::from_str(&content)
|
ron::from_str(&content).map_err(|e| {
|
||||||
.map_err(|e| Error::Session(format!("Failed to parse session file {:?}: {}", path, e)))
|
Error::Session(format!(
|
||||||
|
"Failed to parse session file {}: {e}",
|
||||||
|
path.display()
|
||||||
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves all sessions to a RON file.
|
/// Saves all sessions to a RON file.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`Error::Session`] if the sessions cannot be serialized.
|
||||||
|
/// Returns [`Error::Io`] if the file cannot be written.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn save_sessions(path: &Path, sessions: &[SessionData]) -> Result<()> {
|
pub fn save_sessions(path: &Path, sessions: &[SessionData]) -> Result<()> {
|
||||||
let content = ron::ser::to_string_pretty(sessions, ron::ser::PrettyConfig::default())
|
let content = ron::ser::to_string_pretty(sessions, ron::ser::PrettyConfig::default())
|
||||||
.map_err(|e| Error::Session(format!("Failed to serialize sessions: {}", e)))?;
|
.map_err(|e| Error::Session(format!("Failed to serialize sessions: {e}")))?;
|
||||||
|
|
||||||
std::fs::write(path, content).map_err(Error::Io)
|
std::fs::write(path, content).map_err(Error::Io)
|
||||||
}
|
}
|
||||||
17
core/src/document/types/image.rs
Normal file
17
core/src/document/types/image.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/types/image.rs
|
||||||
|
//
|
||||||
|
// Raw pixel buffer passed from the render subsystem to the UI.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A decoded, format-independent pixel buffer ready for display.
|
||||||
|
///
|
||||||
|
/// The UI receives this type from the `DocumentManager` and never needs to
|
||||||
|
/// know which document format (PDF, SVG, raster) produced it.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RawImage {
|
||||||
|
pub data: Arc<Vec<u8>>,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/document/core.rs
|
// src/document/types/layout.rs
|
||||||
//
|
//
|
||||||
// Core domain models for document representation.
|
// PageLayout – describes the vertical arrangement of pages in a document.
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct RawImage {
|
|
||||||
pub data: Arc<Vec<u8>>,
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// The computed layout of all pages in a multi-page document.
|
||||||
|
///
|
||||||
|
/// Stores absolute y-offsets for each page so the render subsystem can
|
||||||
|
/// determine which pages are visible at a given scroll position without
|
||||||
|
/// iterating the full list on every frame.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PageLayout {
|
pub struct PageLayout {
|
||||||
pub page_starts: Vec<f64>,
|
pub page_starts: Vec<f64>,
|
||||||
|
|
@ -21,15 +17,8 @@ pub struct PageLayout {
|
||||||
pub max_width: f64,
|
pub max_width: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ViewBox {
|
|
||||||
pub x: f64,
|
|
||||||
pub y: f64,
|
|
||||||
pub width: f64,
|
|
||||||
pub height: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageLayout {
|
impl PageLayout {
|
||||||
|
#[must_use]
|
||||||
pub fn new(page_sizes: Vec<(f64, f64)>, gap: f64) -> Self {
|
pub fn new(page_sizes: Vec<(f64, f64)>, gap: f64) -> Self {
|
||||||
let mut page_starts = Vec::with_capacity(page_sizes.len());
|
let mut page_starts = Vec::with_capacity(page_sizes.len());
|
||||||
let mut y = 0.0_f64;
|
let mut y = 0.0_f64;
|
||||||
|
|
@ -43,7 +32,7 @@ impl PageLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_height = if !page_sizes.is_empty() { y - gap } else { 0.0 };
|
let total_height = if page_sizes.is_empty() { 0.0 } else { y - gap };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
page_starts,
|
page_starts,
|
||||||
12
core/src/document/types/mod.rs
Normal file
12
core/src/document/types/mod.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/types/mod.rs
|
||||||
|
//
|
||||||
|
// Shared domain primitives used across the document and render subsystems.
|
||||||
|
|
||||||
|
pub mod image;
|
||||||
|
pub mod layout;
|
||||||
|
pub mod viewbox;
|
||||||
|
|
||||||
|
pub use image::RawImage;
|
||||||
|
pub use layout::PageLayout;
|
||||||
|
pub use viewbox::ViewBox;
|
||||||
16
core/src/document/types/viewbox.rs
Normal file
16
core/src/document/types/viewbox.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// src/document/types/viewbox.rs
|
||||||
|
//
|
||||||
|
// ViewBox – describes the visible coordinate space of a vector document.
|
||||||
|
|
||||||
|
/// The coordinate space declared by an SVG `viewBox` attribute.
|
||||||
|
///
|
||||||
|
/// Used by the render subsystem to map document coordinates to screen pixels
|
||||||
|
/// without exposing SVG internals to the UI.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ViewBox {
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
pub width: f64,
|
||||||
|
pub height: f64,
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/error/kinds.rs
|
// src/error.rs
|
||||||
//
|
//
|
||||||
// Central error types for Noctua.
|
// Central error and result types for noctua_core.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -24,11 +24,11 @@ pub enum Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::NotFound(msg) => write!(f, "Not found: {}", msg),
|
Error::NotFound(msg) => write!(f, "Not found: {msg}"),
|
||||||
Error::Unsupported(msg) => write!(f, "Unsupported: {}", msg),
|
Error::Unsupported(msg) => write!(f, "Unsupported: {msg}"),
|
||||||
Error::Render(msg) => write!(f, "Render error: {}", msg),
|
Error::Render(msg) => write!(f, "Render error: {msg}"),
|
||||||
Error::Session(msg) => write!(f, "Session error: {}", msg),
|
Error::Session(msg) => write!(f, "Session error: {msg}"),
|
||||||
Error::Io(err) => write!(f, "I/O error: {}", err),
|
Error::Io(err) => write!(f, "I/O error: {err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// src/error/mod.rs
|
|
||||||
//
|
|
||||||
// Central error module – re-exports the shared Error and Result types.
|
|
||||||
|
|
||||||
mod kinds;
|
|
||||||
|
|
||||||
pub use kinds::{Error, Result};
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
//
|
//
|
||||||
// Public API of the noctua_core library.
|
// Public API of the noctua_core library.
|
||||||
|
|
||||||
pub mod document;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
pub mod document;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
|
||||||
|
pub use document::command::DocumentCommand;
|
||||||
|
pub use document::manager::DocumentManager;
|
||||||
|
pub use document::session::SessionCommand;
|
||||||
|
pub use document::types::RawImage;
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,4 @@
|
||||||
// Rendering subsystem: converts document content into displayable output.
|
// Rendering subsystem: converts document content into displayable output.
|
||||||
|
|
||||||
pub mod converter;
|
pub mod converter;
|
||||||
pub mod page;
|
|
||||||
pub mod thumbnail;
|
pub mod thumbnail;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// src/render/page.rs
|
|
||||||
//
|
|
||||||
// Page layout and rendering primitives (scaffold – not yet implemented).
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue