feat(viewer): add object fit functionality for image display
Some checks failed
build / checks-matrix (push) Successful in 19m20s
build / checks-build (push) Has been cancelled
docs / docs (push) Has been cancelled
build / codecov (push) Has been cancelled

This commit is contained in:
uttarayan21
2025-10-08 02:35:58 +05:30
parent 935ac583f7
commit 63e05c994b

View File

@@ -1,20 +1,20 @@
use gpui::{ use gpui::{
App, Application, Bounds, Context, FocusHandle, KeyBinding, Window, WindowBounds, App, Application, Bounds, Context, FocusHandle, KeyBinding, ObjectFit, Window, WindowBounds,
WindowOptions, actions, div, img, prelude::*, px, rgb, size, WindowOptions, actions, div, img, prelude::*, px, rgb, size,
}; };
use nalgebra::Vector2; use nalgebra::Vector2;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug, Clone)]
struct MMViewer { struct MMViewer {
files: Vec<PathBuf>, files: Vec<PathBuf>,
current: usize, current: usize,
zoom: f32, zoom: f32,
pan: Vector2<f32>, pan: Vector2<f32>,
focus: FocusHandle, focus: FocusHandle,
fit: ObjectFit,
} }
actions!(mm, [Quit, NextImage, PrevImage, FirstImage, LastImage]); actions!(mm, [Quit, NextImage, PrevImage, FirstImage, LastImage, Fit]);
impl Render for MMViewer { impl Render for MMViewer {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
@@ -25,6 +25,7 @@ impl Render for MMViewer {
.on_action(cx.listener(Self::prev_image)) .on_action(cx.listener(Self::prev_image))
.on_action(cx.listener(Self::first_image)) .on_action(cx.listener(Self::first_image))
.on_action(cx.listener(Self::last_image)) .on_action(cx.listener(Self::last_image))
.on_action(cx.listener(Self::object_fit))
.on_action(cx.listener(Self::quit)) .on_action(cx.listener(Self::quit))
.flex() .flex()
.size_full() .size_full()
@@ -32,12 +33,22 @@ impl Render for MMViewer {
.justify_center() .justify_center()
.bg(rgb(0x505050)) .bg(rgb(0x505050))
.child(if let Some(file) = self.files.get(self.current) { .child(if let Some(file) = self.files.get(self.current) {
div() div().flex().flex_row().size_full().justify_center().child(
.flex() img(file.clone())
.flex_row() .object_fit(self.fit())
.size_full() .size_full()
.justify_center() .with_loading(|| {
.child(img(file.clone()).size_full()) div()
.flex()
.flex_row()
.size_full()
.child("Loading...")
.bg(rgb(0xffffff))
.justify_center()
.into_any()
// .align_center()
}),
)
} else { } else {
div().child(format!( div().child(format!(
"No image found (index: {}, total: {})", "No image found (index: {}, total: {})",
@@ -53,6 +64,16 @@ impl MMViewer {
self.focus.clone() self.focus.clone()
} }
fn fit(&self) -> ObjectFit {
match self.fit {
ObjectFit::Fill => ObjectFit::Fill,
ObjectFit::Contain => ObjectFit::Contain,
ObjectFit::Cover => ObjectFit::Cover,
ObjectFit::ScaleDown => ObjectFit::ScaleDown,
ObjectFit::None => ObjectFit::None,
}
}
fn next_image(&mut self, _: &NextImage, _: &mut Window, cx: &mut Context<Self>) { fn next_image(&mut self, _: &NextImage, _: &mut Window, cx: &mut Context<Self>) {
if self.current + 1 < self.files.len() { if self.current + 1 < self.files.len() {
self.current += 1; self.current += 1;
@@ -81,6 +102,17 @@ impl MMViewer {
} }
} }
fn object_fit(&mut self, _: &Fit, _: &mut Window, cx: &mut Context<Self>) {
match self.fit {
ObjectFit::Fill => self.fit = ObjectFit::Contain,
ObjectFit::Contain => self.fit = ObjectFit::Cover,
ObjectFit::Cover => self.fit = ObjectFit::ScaleDown,
ObjectFit::ScaleDown => self.fit = ObjectFit::None,
ObjectFit::None => self.fit = ObjectFit::Fill,
}
cx.notify();
}
fn quit(&mut self, _: &Quit, _: &mut Window, cx: &mut Context<Self>) { fn quit(&mut self, _: &Quit, _: &mut Window, cx: &mut Context<Self>) {
cx.quit(); cx.quit();
} }
@@ -88,7 +120,7 @@ impl MMViewer {
pub fn run(files: Vec<PathBuf>) { pub fn run(files: Vec<PathBuf>) {
Application::new().run(|cx: &mut App| { Application::new().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800f32), px(600f32)), cx); let bounds = Bounds::maximized(None, cx);
cx.bind_keys([ cx.bind_keys([
KeyBinding::new("q", Quit, None), KeyBinding::new("q", Quit, None),
KeyBinding::new("Escape", Quit, None), KeyBinding::new("Escape", Quit, None),
@@ -98,6 +130,7 @@ pub fn run(files: Vec<PathBuf>) {
KeyBinding::new("left", PrevImage, None), KeyBinding::new("left", PrevImage, None),
KeyBinding::new("shift-g", LastImage, None), KeyBinding::new("shift-g", LastImage, None),
KeyBinding::new("g", FirstImage, None), KeyBinding::new("g", FirstImage, None),
KeyBinding::new("c", Fit, None),
]); ]);
let window = cx let window = cx
.open_window( .open_window(
@@ -113,6 +146,7 @@ pub fn run(files: Vec<PathBuf>) {
zoom: 1.0, zoom: 1.0,
pan: Vector2::new(0.0, 0.0), pan: Vector2::new(0.0, 0.0),
focus: cx.focus_handle(), focus: cx.focus_handle(),
fit: ObjectFit::Contain,
}) })
}, },
) )