diff --git a/Cargo.lock b/Cargo.lock
index 8d598be..9fe23d4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -677,6 +677,22 @@ dependencies = [
"piper",
]
+[[package]]
+name = "bounding-box"
+version = "0.1.0"
+source = "git+https://github.com/aftershootco/ndcv-bridge#16c3b2e9105d3528a6ffbc2e1fe9046cb6930a6e"
+dependencies = [
+ "color",
+ "itertools 0.14.0",
+ "nalgebra",
+ "ndarray",
+ "num",
+ "ordered-float",
+ "simba",
+ "thiserror 2.0.17",
+ "tracing",
+]
+
[[package]]
name = "bstr"
version = "1.12.0"
@@ -983,6 +999,12 @@ dependencies = [
"unicode-width",
]
+[[package]]
+name = "color"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18ef4657441fb193b65f34dc39b3781f0dfec23d3bd94d0eeb4e88cde421edb"
+
[[package]]
name = "color_quant"
version = "1.1.0"
@@ -3153,6 +3175,7 @@ name = "mm"
version = "0.1.0"
dependencies = [
"ash",
+ "bounding-box",
"clap",
"clap_complete",
"error-stack",
@@ -3255,6 +3278,21 @@ dependencies = [
"getrandom 0.2.16",
]
+[[package]]
+name = "ndarray"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "portable-atomic",
+ "portable-atomic-util",
+ "rawpointer",
+]
+
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
@@ -3600,6 +3638,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+[[package]]
+name = "ordered-float"
+version = "5.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "ordered-stream"
version = "0.2.0"
@@ -3787,6 +3834,21 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
+[[package]]
+name = "portable-atomic"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
[[package]]
name = "postage"
version = "0.5.0"
diff --git a/Cargo.toml b/Cargo.toml
index 057d116..02d0962 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,10 +14,14 @@ thiserror = "2.0"
tokio = "1.43.1"
tracing = "0.1"
tracing-subscriber = "0.3"
-gpui = { git = "https://github.com/uttarayan21/zed", default-features = false, features = ["wayland"] }
+gpui = { git = "https://github.com/uttarayan21/zed", default-features = false, features = [
+ "wayland",
+] }
nalgebra = "0.34.1"
wayland-sys = { version = "0.31.7", default-features = false }
wayland-backend = { version = "0.3.11", default-features = false }
ignore = { version = "0.4.23", features = ["simd-accel"] }
unicode-segmentation = "1.12.0"
ash = { version = "0.38.0", features = ["linked"] }
+
+bounding-box = { git = "https://github.com/aftershootco/ndcv-bridge" }
diff --git a/assets/arrow_circle.svg b/assets/arrow_circle.svg
new file mode 100644
index 0000000..90e352b
--- /dev/null
+++ b/assets/arrow_circle.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/main.rs b/src/main.rs
index 6a0b14e..07f2b48 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -18,11 +18,11 @@ pub fn main() -> Result<()> {
.change_context(Error)
.attach("Failed to canonicalize path")?;
let files = walker(&input);
- if files.is_empty() {
- return Err(Error)
- .attach("No files found in the folder")
- .attach(input.display().to_string());
- }
+ // if files.is_empty() {
+ // return Err(Error)
+ // .attach("No files found in the folder")
+ // .attach(input.display().to_string());
+ // }
viewer::run(files);
Ok(())
diff --git a/src/viewer.rs b/src/viewer.rs
index 2e38216..544ffbe 100644
--- a/src/viewer.rs
+++ b/src/viewer.rs
@@ -1,21 +1,54 @@
use gpui::{
- App, Application, Bounds, Context, FocusHandle, KeyBinding, ObjectFit, Window, WindowBounds,
- WindowOptions, actions, div, img, prelude::*, px, rgb, size,
+ Animation, AnimationExt, App, Application, Bounds, Canvas, Context, FocusHandle, KeyBinding,
+ ObjectFit, Transformation, Window, WindowBounds, WindowOptions, actions, black, bounce, canvas,
+ div, ease_in_out, img, percentage, prelude::*, pulsating_between, px, rgb, rgba, size, svg,
};
use nalgebra::Vector2;
-use std::path::PathBuf;
+use std::{path::PathBuf, time::Duration};
struct MMViewer {
files: Vec,
current: usize,
- zoom: f32,
- pan: Vector2,
+ last: Option,
+ // zoom: f32,
+ // pan: Vector2,
focus: FocusHandle,
fit: ObjectFit,
}
actions!(mm, [Quit, NextImage, PrevImage, FirstImage, LastImage, Fit]);
+#[derive(Debug, Clone)]
+pub struct BoundingBox {
+ inner: bounding_box::Aabb2,
+ pub width: gpui::Pixels,
+ pub color: gpui::Rgba,
+}
+
+impl From> for BoundingBox {
+ fn from(aabb: bounding_box::Aabb2) -> Self {
+ Self {
+ inner: aabb,
+ width: px(2.0),
+ color: gpui::green().to_rgb(),
+ }
+ }
+}
+
+// impl Render for BoundingBox {
+// fn render(&mut self, window: &mut Window, _cx: &mut Context) -> impl IntoElement {
+// let mut builder = gpui::PathBuilder::stroke(self.width);
+// let aabb = self.inner;
+// builder.move_to(gpui::Point::new(px(aabb.x1()), px(aabb.y1())));
+// builder.line_to(gpui::Point::new(px(aabb.x2()), px(aabb.y1())));
+// builder.line_to(gpui::Point::new(px(aabb.x2()), px(aabb.y2())));
+// builder.line_to(gpui::Point::new(px(aabb.x1()), px(aabb.y2())));
+// builder.line_to(gpui::Point::new(px(aabb.x1()), px(aabb.y1())));
+// let path = builder.build().expect("Failed to build path");
+// window.paint_path(path, gpui::green())
+// }
+// }
+
impl Render for MMViewer {
fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement {
div()
@@ -31,34 +64,60 @@ impl Render for MMViewer {
.size_full()
.flex_col()
.justify_center()
- .bg(rgb(0x505050))
+ .bg(rgba(0xffffffff))
.child(if let Some(file) = self.files.get(self.current) {
- div().flex().flex_row().size_full().justify_center().child(
- img(file.clone())
- .object_fit(self.fit())
- .size_full()
- .with_loading(|| {
- div()
- .flex()
- .flex_row()
- .size_full()
- .child("Loading...")
- .bg(rgb(0xffffff))
- .justify_center()
- .into_any()
- }),
- )
+ div()
+ .flex()
+ .flex_row()
+ .size_full()
+ .justify_center()
+ .child(
+ img(file.clone())
+ .id("loupe")
+ .object_fit(self.fit())
+ .size_full()
+ .with_loading(|| Self::loading().into_any_element()),
+ )
+ .relative()
+ .into_any()
} else {
- div().child(format!(
- "No image found (index: {}, total: {})",
- self.current,
- self.files.len()
- ))
+ Self::loading().into_any_element()
+ // div().child(format!(
+ // "No image found (index: {}, total: {})",
+ // self.current,
+ // self.files.len()
+ // ))
})
}
}
+const ARROW_CIRCLE_SVG: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/arrow_circle.svg");
+
+fn wheel() -> impl IntoElement {
+ svg()
+ .path(ARROW_CIRCLE_SVG)
+ .flex_none()
+ .size_full()
+ .text_color(black())
+ .with_animation(
+ "wheel",
+ Animation::new(Duration::from_secs(2))
+ .repeat()
+ .with_easing(bounce(ease_in_out)),
+ move |svg, delta| svg.with_transformation(Transformation::rotate(percentage(delta))),
+ )
+}
+
impl MMViewer {
+ fn loading() -> impl IntoElement {
+ div()
+ .size_full()
+ .flex_none()
+ .bg(rgb(0xff00ff))
+ .child(wheel())
+ .debug()
+ }
+
fn focus_handle(&self, _: &App) -> FocusHandle {
self.focus.clone()
}
@@ -143,8 +202,9 @@ pub fn run(files: Vec) {
cx.new(|cx| MMViewer {
files,
current: 0,
- zoom: 1.0,
- pan: Vector2::new(0.0, 0.0),
+ last: None,
+ // zoom: 1.0,
+ // pan: Vector2::new(0.0, 0.0),
focus: cx.focus_handle(),
fit: ObjectFit::Contain,
})