Skip to content

Commit

Permalink
v0.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
ksk001100 committed Mar 23, 2022
1 parent 7da23a2 commit ea7ca4b
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 76 deletions.
25 changes: 24 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "picterm"
version = "0.0.3"
version = "0.0.4"
authors = ["Keisuke Toyota <[email protected]>"]
edition = "2018"
repository = "https://github.com/ksk001100/picterm"
Expand All @@ -15,3 +15,5 @@ tui = { version = "0.17", default-features = false, features = ['crossterm'] }
image = "0.23"
tokio = { version = "1", features = ["full"] }
eyre = "0.6"
seahorse = "2.0.0"
byte-unit = "4.0"
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TUI image viewer

![](screenshot.png)

## install
## Install
```bash
$ cargo install picterm
```
Expand All @@ -17,9 +17,16 @@ $ cd picterm
$ cargo install --path .
```

## usage
## Usage
```bash
$ picterm # => Current directory
$ picterm ./
$ picterm $HOME/Downloads/
```

## Support file format
- PNG
- JPG
- WebP
- BMP
- GIF
4 changes: 2 additions & 2 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ impl<'a> App<'a> {
match action {
Action::Quit => AppReturn::Exit,
Action::Increment => {
self.dispatch(IoEvent::Increment).await;
self.state.increment_index();
AppReturn::Continue
}
Action::Decrement => {
self.dispatch(IoEvent::Decrement).await;
self.state.decrement_index();
AppReturn::Continue
}
Action::Show => {
Expand Down
52 changes: 41 additions & 11 deletions src/app/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use tui::text::Spans;
pub enum AppState<'a> {
Init,
Initialized {
images: Vec<PathBuf>,
paths: Vec<PathBuf>,
selected_index: usize,
term_size: Option<TermSize>,
current_image: Option<Vec<Spans<'a>>>,
current_image_info: Option<ImageInfo>,
},
}

Expand All @@ -19,38 +20,47 @@ pub struct TermSize {
pub height: u32,
}

#[derive(Debug, Clone)]
pub struct ImageInfo {
pub name: String,
pub size: u64,
pub dimensions: (u32, u32),
}

impl<'a> AppState<'a> {
pub fn initialized(path: &str) -> Self {
let images = utils::get_image_paths(path);
let paths = utils::get_image_paths(path);
let selected_index = 0;
let current_image = None;
let term_size = None;
let current_image_info = None;
Self::Initialized {
images,
paths,
selected_index,
term_size,
current_image,
current_image_info,
}
}

pub fn is_initialized(&self) -> bool {
matches!(self, &Self::Initialized { .. })
}

pub fn get_images(&self) -> Vec<PathBuf> {
if let Self::Initialized { images, .. } = self {
images.clone()
pub fn get_paths(&self) -> Vec<PathBuf> {
if let Self::Initialized { paths, .. } = self {
paths.clone()
} else {
vec![]
}
}

pub fn get_image(&self, index: usize) -> Option<PathBuf> {
if let Self::Initialized { images, .. } = self {
if images.is_empty() {
pub fn get_path(&self, index: usize) -> Option<PathBuf> {
if let Self::Initialized { paths, .. } = self {
if paths.is_empty() {
None
} else {
Some(images[index].clone())
Some(paths[index].clone())
}
} else {
None
Expand All @@ -60,7 +70,7 @@ impl<'a> AppState<'a> {
pub fn increment_index(&mut self) {
if let Self::Initialized {
selected_index,
images,
paths: images,
..
} = self
{
Expand Down Expand Up @@ -113,6 +123,26 @@ impl<'a> AppState<'a> {
None
}
}

pub fn set_current_image_info(&mut self, image_info: ImageInfo) {
if let Self::Initialized {
current_image_info, ..
} = self
{
*current_image_info = Some(image_info);
}
}

pub fn get_current_image_info(&self) -> Option<ImageInfo> {
if let Self::Initialized {
current_image_info, ..
} = self
{
current_image_info.clone()
} else {
None
}
}
}

impl<'a> Default for AppState<'a> {
Expand Down
53 changes: 51 additions & 2 deletions src/app/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::app::state::AppState;
use crate::app::Actions;
use crate::app::App;

use byte_unit::Byte;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Style};
Expand Down Expand Up @@ -31,8 +32,16 @@ where
let title = draw_title();
rect.render_widget(title, header_chunks[0]);

let info_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)].as_ref())
.split(header_chunks[1]);

let help = draw_help(app.actions());
rect.render_widget(help, header_chunks[1]);
rect.render_widget(help, info_chunks[0]);

let info = draw_info(app.state());
rect.render_widget(info, info_chunks[1]);

let body_chunks = Layout::default()
.direction(Direction::Horizontal)
Expand All @@ -58,6 +67,46 @@ fn draw_title<'a>() -> Paragraph<'a> {
.block(Block::default().style(Style::default().fg(Color::White)))
}

fn draw_info<'a>(state: &AppState) -> Table<'a> {
let key_style = Style::default().fg(Color::LightCyan);
let value_style = Style::default().fg(Color::Gray);

let rows = if let Some(image_info) = state.get_current_image_info() {
let size = Byte::from(image_info.size)
.get_appropriate_unit(false)
.to_string();

vec![
Row::new(vec![
Cell::from(Span::styled("Name", key_style)),
Cell::from(Span::styled(image_info.name, value_style)),
]),
Row::new(vec![
Cell::from(Span::styled("Dimensions", key_style)),
Cell::from(Span::styled(
format!("{}x{}", image_info.dimensions.0, image_info.dimensions.1),
value_style,
)),
]),
Row::new(vec![
Cell::from(Span::styled("Size", key_style)),
Cell::from(Span::styled(size, value_style)),
]),
]
} else {
vec![]
};

Table::new(rows)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Plain),
)
.widths(&[Constraint::Length(20), Constraint::Percentage(80)])
.column_spacing(1)
}

fn draw_help(actions: &Actions) -> Table {
let key_style = Style::default().fg(Color::LightCyan);
let help_style = Style::default().fg(Color::Gray);
Expand Down Expand Up @@ -85,7 +134,7 @@ fn draw_help(actions: &Actions) -> Table {

fn draw_image_list<'a>(state: &AppState) -> List<'a> {
let list_items: Vec<ListItem> = state
.get_images()
.get_paths()
.iter()
.map(|img| {
ListItem::new(
Expand Down
41 changes: 21 additions & 20 deletions src/io/handler.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::IoEvent;
use crate::app::state::ImageInfo;
use crate::app::App;
use crate::image::image_fit_size;
use eyre::Result;
use image::Rgba;
use image::{GenericImageView, Rgba};
use std::sync::Arc;
use tui::style::{Color, Style};
use tui::text::{Span, Spans};
Expand All @@ -19,8 +20,6 @@ impl<'a> IoAsyncHandler<'a> {
pub async fn handle_io_event(&mut self, io_event: IoEvent) {
let _ = match io_event {
IoEvent::Initialize => self.do_initialize().await,
IoEvent::Increment => self.do_increment().await,
IoEvent::Decrement => self.do_decrement().await,
IoEvent::LoadImage => self.do_load_image().await,
};

Expand All @@ -35,29 +34,31 @@ impl<'a> IoAsyncHandler<'a> {
Ok(())
}

async fn do_increment(&mut self) -> Result<()> {
let mut app = self.app.lock().await;
app.state.increment_index();

Ok(())
}

async fn do_decrement(&mut self) -> Result<()> {
let mut app = self.app.lock().await;
app.state.decrement_index();

Ok(())
}

async fn do_load_image(&mut self) -> Result<()> {
let mut app = self.app.lock().await;

let mut result = vec![];

if let Some(index) = app.state.get_index() {
if let Some(path) = app.state.get_image(index) {
if let Some(path) = app.state.get_path(index) {
if let Some(term_size) = app.state.get_term_size() {
let img = tokio::task::block_in_place(move || image::open(path))?;
let p = path.clone();
let img = tokio::task::block_in_place(move || image::open(p))?;
let name = path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap()
.to_string();
let size = match path.metadata() {
Ok(metadata) => metadata.len(),
Err(_) => 0,
};
let info = ImageInfo {
name,
size,
dimensions: img.dimensions(),
};
app.state.set_current_image_info(info);

let (w, h) = image_fit_size(&img, term_size.width, term_size.height);
let imgbuf = img
Expand Down
2 changes: 0 additions & 2 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,5 @@ pub mod handler;
#[derive(Debug, Clone)]
pub enum IoEvent {
Initialize,
Increment,
Decrement,
LoadImage,
}
Loading

0 comments on commit ea7ca4b

Please sign in to comment.