Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,830 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
* simple Picture Viewer V. 0.9
|
||||
*
|
||||
* Copyright (c) 2021 Dario Deledda. All rights reserved.
|
||||
* Use of this source code is governed by an MIT license
|
||||
* that can be found in the LICENSE file.
|
||||
*
|
||||
* TODO:
|
||||
* - add an example with shaders
|
||||
**********************************************************************/
|
||||
import os
|
||||
import gg
|
||||
import sokol.gfx
|
||||
import sokol.sgl
|
||||
import sokol.sapp
|
||||
import stbi
|
||||
import compress.szip
|
||||
import strings
|
||||
|
||||
// Help text
|
||||
const help_text_rows = [
|
||||
'Image Viewer 0.9 help.',
|
||||
'',
|
||||
'ESC/q - Quit',
|
||||
'cur. right - Next image',
|
||||
'cur. left - Previous image',
|
||||
'cur. up - Next folder',
|
||||
'cur. down - Previous folder',
|
||||
'F - Toggle full screen',
|
||||
'R - Rotate image of 90 degree',
|
||||
'I - Toggle the info text',
|
||||
'',
|
||||
'mouse wheel - next/previous images',
|
||||
'keep pressed left Mouse button - Pan on the image',
|
||||
'keep pressed right Mouse button - Zoom on the image',
|
||||
]
|
||||
|
||||
const win_width = 800
|
||||
const win_height = 800
|
||||
const bg_color = gg.black
|
||||
const pi_2 = 3.14159265359 / 2.0
|
||||
// const uv = [f32(0), 0, 1, 0, 1, 1, 0, 1]! // used for zoom icon during rotations
|
||||
|
||||
const text_drop_files = 'Drop here some images/folder/zip to navigate in the pics'
|
||||
const text_scanning = 'Scanning...'
|
||||
const text_loading = 'Loading...'
|
||||
|
||||
enum Viewer_state {
|
||||
loading
|
||||
scanning
|
||||
show
|
||||
error
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
pip_viewer sgl.Pipeline
|
||||
texture gfx.Image
|
||||
sampler gfx.Sampler
|
||||
init_flag bool
|
||||
mouse_x f32 = -1.0
|
||||
mouse_y f32 = -1.0
|
||||
scroll_y int
|
||||
|
||||
state Viewer_state = .scanning
|
||||
// translation
|
||||
tr_flag bool
|
||||
tr_x f32 = 0.0
|
||||
tr_y f32 = 0.0
|
||||
last_tr_x f32 = 0.0
|
||||
last_tr_y f32 = 0.0
|
||||
// scaling
|
||||
sc_flag bool
|
||||
scale f32 = 1.0
|
||||
sc_x f32 = 0.0
|
||||
sc_y f32 = 0.0
|
||||
last_sc_x f32 = 0.0
|
||||
last_sc_y f32 = 0.0
|
||||
// loaded image
|
||||
img_w int
|
||||
img_h int
|
||||
img_ratio f32 = 1.0
|
||||
// item list
|
||||
item_list &Item_list = unsafe { nil }
|
||||
// Text info and help
|
||||
show_info_flag bool = true
|
||||
show_help_flag bool
|
||||
// zip container
|
||||
zip &szip.Zip = unsafe { nil } // pointer to the szip structure
|
||||
zip_index int = -1 // index of the zip container item
|
||||
// memory buffer
|
||||
mem_buf voidptr // buffer used to load items from files/containers
|
||||
mem_buf_size int // size of the buffer
|
||||
// font
|
||||
font_path string // path to the temp font file
|
||||
// logo
|
||||
logo_path string // path of the temp font logo
|
||||
logo_texture gfx.Image
|
||||
logo_sampler gfx.Sampler
|
||||
logo_w int
|
||||
logo_h int
|
||||
logo_ratio f32 = 1.0
|
||||
// string builder
|
||||
bl strings.Builder = strings.new_builder(512)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Texture functions
|
||||
*
|
||||
******************************************************************************/
|
||||
fn create_texture(w int, h int, buf &u8) (gfx.Image, gfx.Sampler) {
|
||||
sz := w * h * 4
|
||||
mut img_desc := gfx.ImageDesc{
|
||||
width: w
|
||||
height: h
|
||||
num_mipmaps: 0
|
||||
// usage: .dynamic
|
||||
label: &u8(unsafe { nil })
|
||||
d3d11_texture: 0
|
||||
}
|
||||
// comment if .dynamic is enabled
|
||||
img_desc.data.subimage[0][0] = gfx.Range{
|
||||
ptr: buf
|
||||
size: usize(sz)
|
||||
}
|
||||
|
||||
sg_img := gfx.make_image(&img_desc)
|
||||
|
||||
mut smp_desc := gfx.SamplerDesc{
|
||||
min_filter: .linear
|
||||
mag_filter: .linear
|
||||
wrap_u: .clamp_to_edge
|
||||
wrap_v: .clamp_to_edge
|
||||
}
|
||||
|
||||
sg_smp := gfx.make_sampler(&smp_desc)
|
||||
return sg_img, sg_smp
|
||||
}
|
||||
|
||||
fn destroy_texture(sg_img gfx.Image) {
|
||||
gfx.destroy_image(sg_img)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Memory buffer
|
||||
*
|
||||
******************************************************************************/
|
||||
@[inline]
|
||||
fn (mut app App) resize_buf_if_needed(in_size int) {
|
||||
// manage the memory buffer
|
||||
if app.mem_buf_size < in_size {
|
||||
println('Managing FILE memory buffer, allocated [${in_size}]Bytes')
|
||||
// free previous buffer if any exist
|
||||
if app.mem_buf_size > 0 {
|
||||
unsafe {
|
||||
free(app.mem_buf)
|
||||
}
|
||||
}
|
||||
// allocate the memory
|
||||
unsafe {
|
||||
app.mem_buf = malloc(int(in_size))
|
||||
app.mem_buf_size = int(in_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Loading functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// read_bytes from file in `path` in the memory buffer of app.
|
||||
@[manualfree]
|
||||
fn (mut app App) read_bytes(path string) bool {
|
||||
mut fp := os.vfopen(path, 'rb') or {
|
||||
eprintln('ERROR: Can not open the file [${path}].')
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
C.fclose(fp)
|
||||
}
|
||||
cseek := C.fseek(fp, 0, C.SEEK_END)
|
||||
if cseek != 0 {
|
||||
eprintln('ERROR: Can not seek in the file [${path}].')
|
||||
return false
|
||||
}
|
||||
fsize := C.ftell(fp)
|
||||
if fsize < 0 {
|
||||
eprintln('ERROR: File [${path}] has size is 0.')
|
||||
return false
|
||||
}
|
||||
C.rewind(fp)
|
||||
|
||||
app.resize_buf_if_needed(int(fsize))
|
||||
|
||||
nr_read_elements := int(C.fread(app.mem_buf, fsize, 1, fp))
|
||||
if nr_read_elements == 0 && fsize > 0 {
|
||||
eprintln('ERROR: Can not read the file [${path}] in the memory buffer.')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// read a file as []u8
|
||||
pub fn read_bytes_from_file(file_path string) []u8 {
|
||||
mut buffer := []u8{}
|
||||
buffer = os.read_bytes(file_path) or {
|
||||
eprintln('ERROR: Texture file: [${file_path}] NOT FOUND.')
|
||||
exit(0)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
fn (mut app App) load_texture_from_buffer(buf voidptr, buf_len int) (gfx.Image, gfx.Sampler, int, int) {
|
||||
// load image
|
||||
stbi.set_flip_vertically_on_load(true)
|
||||
img := stbi.load_from_memory(buf, buf_len) or {
|
||||
eprintln('ERROR: Can not load image from buffer, file: [${app.item_list.lst[app.item_list.item_index]}].')
|
||||
return app.logo_texture, app.sampler, app.logo_w, app.logo_h
|
||||
// exit(1)
|
||||
}
|
||||
sg_img, sg_smp := create_texture(int(img.width), int(img.height), img.data)
|
||||
unsafe {
|
||||
img.free()
|
||||
}
|
||||
return sg_img, sg_smp, int(img.width), int(img.height)
|
||||
}
|
||||
|
||||
pub fn (mut app App) load_texture_from_file(file_name string) (gfx.Image, gfx.Sampler, int, int) {
|
||||
app.read_bytes(file_name)
|
||||
return app.load_texture_from_buffer(app.mem_buf, app.mem_buf_size)
|
||||
}
|
||||
|
||||
pub fn show_logo(mut app App) {
|
||||
clear_modifier_params(mut app)
|
||||
if app.texture != app.logo_texture {
|
||||
destroy_texture(app.texture)
|
||||
gfx.destroy_sampler(app.sampler)
|
||||
}
|
||||
app.texture = app.logo_texture
|
||||
app.sampler = app.logo_sampler
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
// app.gg.refresh_ui()
|
||||
}
|
||||
|
||||
pub fn load_image(mut app App) {
|
||||
if app.item_list.loaded == false || app.init_flag == false {
|
||||
// show_logo(mut app)
|
||||
// app.state = .show
|
||||
return
|
||||
}
|
||||
app.state = .loading
|
||||
clear_modifier_params(mut app)
|
||||
// destroy the texture, avoid to destroy the logo
|
||||
if app.texture != app.logo_texture {
|
||||
destroy_texture(app.texture)
|
||||
gfx.destroy_sampler(app.sampler)
|
||||
}
|
||||
|
||||
// load from .ZIP file
|
||||
if app.item_list.is_inside_a_container() == true {
|
||||
app.texture, app.sampler, app.img_w, app.img_h = app.load_texture_from_zip() or {
|
||||
eprintln('ERROR: Can not load image from .ZIP file [${app.item_list.lst[app.item_list.item_index]}].')
|
||||
show_logo(mut app)
|
||||
app.state = .show
|
||||
return
|
||||
}
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
app.state = .show
|
||||
// app.gg.refresh_ui()
|
||||
return
|
||||
}
|
||||
|
||||
// if we are out of the zip, close it
|
||||
if app.zip_index >= 0 {
|
||||
app.zip_index = -1
|
||||
app.zip.close()
|
||||
}
|
||||
|
||||
file_path := app.item_list.get_file_path()
|
||||
if file_path != '' {
|
||||
// println("${app.item_list.lst[app.item_list.item_index]} ${file_path} ${app.item_list.lst.len}")
|
||||
app.texture, app.sampler, app.img_w, app.img_h = app.load_texture_from_file(file_path)
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
// println("texture: [${app.img_w},${app.img_h}] ratio: ${app.img_ratio}")
|
||||
} else {
|
||||
app.texture = app.logo_texture
|
||||
app.sampler = app.logo_sampler
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
println('texture NOT FOUND: use logo!')
|
||||
}
|
||||
app.state = .show
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Init / Cleanup
|
||||
*
|
||||
******************************************************************************/
|
||||
fn app_init(mut app App) {
|
||||
app.init_flag = true
|
||||
|
||||
// 3d pipeline
|
||||
mut pipdesc := gfx.PipelineDesc{}
|
||||
unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
|
||||
|
||||
color_state := gfx.ColorTargetState{
|
||||
blend: gfx.BlendState{
|
||||
enabled: true
|
||||
src_factor_rgb: .src_alpha
|
||||
dst_factor_rgb: .one_minus_src_alpha
|
||||
}
|
||||
}
|
||||
pipdesc.colors[0] = color_state
|
||||
|
||||
pipdesc.depth = gfx.DepthState{
|
||||
write_enabled: true
|
||||
compare: .less_equal
|
||||
}
|
||||
pipdesc.cull_mode = .back
|
||||
app.pip_viewer = sgl.make_pipeline(&pipdesc)
|
||||
|
||||
// load logo
|
||||
app.logo_texture, app.logo_sampler, app.logo_w, app.logo_h =
|
||||
app.load_texture_from_file(app.logo_path)
|
||||
app.logo_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = app.logo_ratio
|
||||
app.texture = app.logo_texture
|
||||
app.sampler = app.logo_sampler
|
||||
|
||||
println('INIT DONE!')
|
||||
|
||||
// init done, load the first image if any
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
fn cleanup(mut app App) {
|
||||
gfx.shutdown()
|
||||
|
||||
// delete temp files
|
||||
os.rm(app.font_path) or { eprintln('ERROR: Can not delete temp font file.') }
|
||||
os.rm(app.logo_path) or { eprintln('ERROR: Can not delete temp logo file.') }
|
||||
println('Cleaning done.')
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Draw functions
|
||||
*
|
||||
******************************************************************************/
|
||||
@[manualfree]
|
||||
fn frame(mut app App) {
|
||||
ws := gg.window_size_real_pixels()
|
||||
if ws.width <= 0 || ws.height <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mut ratio := f32(ws.width) / ws.height
|
||||
dw := ws.width
|
||||
dh := ws.height
|
||||
|
||||
app.gg.begin()
|
||||
sgl.defaults()
|
||||
|
||||
// set viewport
|
||||
sgl.viewport(0, 0, dw, dh, true)
|
||||
|
||||
// enable our pipeline
|
||||
sgl.load_pipeline(app.pip_viewer)
|
||||
sgl.enable_texture()
|
||||
sgl.texture(app.texture, app.sampler)
|
||||
|
||||
// translation
|
||||
tr_x := app.tr_x / app.img_w
|
||||
tr_y := -app.tr_y / app.img_h
|
||||
sgl.push_matrix()
|
||||
sgl.translate(tr_x, tr_y, 0.0)
|
||||
// scaling/zoom
|
||||
sgl.scale(2.0 * app.scale, 2.0 * app.scale, 0.0)
|
||||
// rotation
|
||||
mut rotation := 0
|
||||
if app.state == .show && app.item_list.n_item > 0 {
|
||||
rotation = app.item_list.lst[app.item_list.item_index].rotation
|
||||
sgl.rotate(pi_2 * f32(rotation), 0.0, 0.0, -1.0)
|
||||
}
|
||||
|
||||
// draw the image
|
||||
mut w := f32(0.5)
|
||||
mut h := f32(0.5)
|
||||
|
||||
// for 90 and 270 degree invert w and h
|
||||
// rotation change image ratio, manage it
|
||||
if rotation & 1 == 1 {
|
||||
tmp := w
|
||||
w = h
|
||||
h = tmp
|
||||
h /= app.img_ratio * ratio
|
||||
} else {
|
||||
h /= app.img_ratio / ratio
|
||||
}
|
||||
|
||||
// manage image overflow in case of strange scales
|
||||
if h > 0.5 {
|
||||
reduction_factor := 0.5 / h
|
||||
h = h * reduction_factor
|
||||
w = w * reduction_factor
|
||||
}
|
||||
if w > 0.5 {
|
||||
reduction_factor := 0.5 / w
|
||||
h = h * reduction_factor
|
||||
w = w * reduction_factor
|
||||
}
|
||||
|
||||
// println("${w},${h}")
|
||||
// white multiplicator for now
|
||||
mut c := [u8(255), 255, 255]!
|
||||
sgl.begin_quads()
|
||||
sgl.v2f_t2f_c3b(-w, -h, 0, 0, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(w, -h, 1, 0, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(w, h, 1, 1, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(-w, h, 0, 1, c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
|
||||
// restore all the transformations
|
||||
sgl.pop_matrix()
|
||||
|
||||
// Zoom icon
|
||||
/*
|
||||
if app.show_info_flag == true && app.scale > 1 {
|
||||
mut bw := f32(0.25)
|
||||
mut bh := f32(0.25 / app.img_ratio)
|
||||
|
||||
// manage the rotations
|
||||
if rotation & 1 == 1 {
|
||||
bw,bh = bh,bw
|
||||
}
|
||||
mut bx := f32(1 - bw)
|
||||
mut by := f32(1 - bh)
|
||||
if rotation & 1 == 1 {
|
||||
bx,by = by,bx
|
||||
}
|
||||
|
||||
bh_old1 := bh
|
||||
bh *= ratio
|
||||
by += (bh_old1 - bh)
|
||||
|
||||
// draw the zoom icon
|
||||
sgl.begin_quads()
|
||||
r := int(u32(rotation) << 1)
|
||||
sgl.v2f_t2f_c3b(bx , by , uv[(0 + r) & 7] , uv[(1 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx + bw, by , uv[(2 + r) & 7] , uv[(3 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx + bw, by + bh, uv[(4 + r) & 7] , uv[(5 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx , by + bh, uv[(6 + r) & 7] , uv[(7 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
|
||||
// draw the zoom rectangle
|
||||
sgl.disable_texture()
|
||||
|
||||
bw_old := bw
|
||||
bh_old := bh
|
||||
bw /= app.scale
|
||||
bh /= app.scale
|
||||
bx += (bw_old - bw) / 2 - (tr_x / 8) / app.scale
|
||||
by += (bh_old - bh) / 2 - ((tr_y / 8) / app.scale) * ratio
|
||||
|
||||
c = [u8(255),255,0]! // yellow
|
||||
sgl.begin_line_strip()
|
||||
sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx + bw, by , c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx + bw, by + bh, c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx , by + bh, c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
}
|
||||
*/
|
||||
sgl.disable_texture()
|
||||
|
||||
//
|
||||
// Draw info text
|
||||
//
|
||||
x := 10
|
||||
y := 10
|
||||
|
||||
app.gg.begin()
|
||||
|
||||
if app.state in [.scanning, .loading] {
|
||||
if app.state == .scanning {
|
||||
draw_text(mut app, text_scanning, x, y, 20)
|
||||
} else {
|
||||
draw_text(mut app, text_loading, x, y, 20)
|
||||
}
|
||||
} else if app.state == .show {
|
||||
// print the info text if needed
|
||||
if app.item_list.n_item > 0 && app.show_info_flag == true {
|
||||
/*
|
||||
// waiting for better autofree
|
||||
num := app.item_list.lst[app.item_list.item_index].n_item
|
||||
of_num := app.item_list.n_item
|
||||
x_screen := int(w*2*app.scale*dw)
|
||||
y_screen := int(h*2*app.scale*dw)
|
||||
rotation_angle := 90 * rotation
|
||||
scale_str := "${app.scale:.2}"
|
||||
text := "${num}/${of_num} [${app.img_w},${app.img_h}]=>[${x_screen},${y_screen}] ${app.item_list.lst[app.item_list.item_index].name} scale: ${scale_str} rotation: ${rotation_angle}"
|
||||
//text := "${num}/${of_num}"
|
||||
draw_text(mut app, text, 10, 10, 20)
|
||||
unsafe{
|
||||
text.free()
|
||||
}
|
||||
*/
|
||||
|
||||
// Using string builder to avoid memory leak
|
||||
num := app.item_list.lst[app.item_list.item_index].n_item
|
||||
of_num := app.item_list.n_item
|
||||
x_screen := int(w * 2 * app.scale * dw)
|
||||
y_screen := int(h * 2 * app.scale * dw)
|
||||
rotation_angle := 90 * rotation
|
||||
scale_str := '${app.scale:.2}'
|
||||
app.bl.clear()
|
||||
app.bl.write_string('${num}/${of_num}')
|
||||
app.bl.write_string(' [${app.img_w}x${app.img_h}]=>[${x_screen}x${y_screen}]')
|
||||
app.bl.write_string(' ${app.item_list.lst[app.item_list.item_index].name}')
|
||||
app.bl.write_string(' scale: ${scale_str} rotation: ${rotation_angle}')
|
||||
draw_text(mut app, app.bl.str(), 10, 10, 20)
|
||||
} else {
|
||||
if app.item_list.n_item <= 0 {
|
||||
draw_text(mut app, text_drop_files, 10, 10, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Draw Help text
|
||||
//
|
||||
if app.show_help_flag == true {
|
||||
mut txt_y := 30
|
||||
for r in help_text_rows {
|
||||
draw_text(mut app, r, 10, txt_y, 20)
|
||||
txt_y += 20
|
||||
}
|
||||
}
|
||||
|
||||
app.gg.end()
|
||||
}
|
||||
|
||||
// draw readable text
|
||||
fn draw_text(mut app App, in_txt string, in_x int, in_y int, fnt_sz f32) {
|
||||
scale := app.gg.scale
|
||||
font_size := int(fnt_sz * scale)
|
||||
|
||||
mut txt_conf_c0 := gg.TextCfg{
|
||||
color: gg.white // gg.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
|
||||
align: .left
|
||||
size: font_size
|
||||
}
|
||||
mut txt_conf_c1 := gg.TextCfg{
|
||||
color: gg.black // gg.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
|
||||
align: .left
|
||||
size: font_size
|
||||
}
|
||||
|
||||
x := int(in_x * scale)
|
||||
y := int(in_y * scale)
|
||||
app.gg.draw_text(x + 2, y + 2, in_txt, txt_conf_c0)
|
||||
app.gg.draw_text(x, y, in_txt, txt_conf_c1)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* events management
|
||||
*
|
||||
******************************************************************************/
|
||||
fn clear_modifier_params(mut app App) {
|
||||
app.scale = 1.0
|
||||
|
||||
app.sc_flag = false
|
||||
app.sc_x = 0
|
||||
app.sc_y = 0
|
||||
app.last_sc_x = 0
|
||||
app.last_sc_y = 0
|
||||
|
||||
app.tr_flag = false
|
||||
app.tr_x = 0
|
||||
app.tr_y = 0
|
||||
app.last_tr_x = 0
|
||||
app.last_tr_y = 0
|
||||
}
|
||||
|
||||
fn my_event_manager(mut ev gg.Event, mut app App) {
|
||||
// Handle window closure
|
||||
if ev.typ == .quit_requested {
|
||||
cleanup(mut app)
|
||||
exit(0)
|
||||
}
|
||||
// navigation using the mouse wheel
|
||||
app.scroll_y = int(ev.scroll_y)
|
||||
if app.scroll_y != 0 {
|
||||
inc := int(-1 * app.scroll_y / 4)
|
||||
if app.item_list.n_item > 0 {
|
||||
app.item_list.get_next_item(inc)
|
||||
load_image(mut app)
|
||||
}
|
||||
}
|
||||
|
||||
if ev.typ == .mouse_move {
|
||||
app.mouse_x = ev.mouse_x
|
||||
app.mouse_y = ev.mouse_y
|
||||
}
|
||||
if ev.typ == .touches_began || ev.typ == .touches_moved {
|
||||
if ev.num_touches > 0 {
|
||||
touch_point := ev.touches[0]
|
||||
app.mouse_x = touch_point.pos_x
|
||||
app.mouse_y = touch_point.pos_y
|
||||
}
|
||||
}
|
||||
|
||||
// clear all parameters
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .middle {
|
||||
clear_modifier_params(mut app)
|
||||
}
|
||||
|
||||
// ws := gg.window_size_real_pixels()
|
||||
// ratio := f32(ws.width) / ws.height
|
||||
// dw := ws.width
|
||||
// dh := ws.height
|
||||
|
||||
// --- translate ---
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .left {
|
||||
app.tr_flag = true
|
||||
app.last_tr_x = app.mouse_x
|
||||
app.last_tr_y = app.mouse_y
|
||||
}
|
||||
if ev.typ == .mouse_up && ev.mouse_button == .left && app.tr_flag == true {
|
||||
app.tr_flag = false
|
||||
}
|
||||
if ev.typ == .mouse_move && app.tr_flag == true {
|
||||
app.tr_x += (app.mouse_x - app.last_tr_x) * 3 * app.gg.scale
|
||||
app.tr_y += (app.mouse_y - app.last_tr_y) * 3 * app.gg.scale
|
||||
app.last_tr_x = app.mouse_x
|
||||
app.last_tr_y = app.mouse_y
|
||||
// println("Translate: ${app.tr_x} ${app.tr_y}")
|
||||
}
|
||||
|
||||
// --- scaling ---
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .right && app.sc_flag == false {
|
||||
app.sc_flag = true
|
||||
app.last_sc_x = app.mouse_x
|
||||
app.last_sc_y = app.mouse_y
|
||||
}
|
||||
if ev.typ == .mouse_up && ev.mouse_button == .right && app.sc_flag == true {
|
||||
app.sc_flag = false
|
||||
}
|
||||
if ev.typ == .mouse_move && app.sc_flag == true {
|
||||
app.sc_x = app.mouse_x - app.last_sc_x
|
||||
app.sc_y = app.mouse_y - app.last_sc_y
|
||||
app.last_sc_x = app.mouse_x
|
||||
app.last_sc_y = app.mouse_y
|
||||
|
||||
app.scale += f32(app.sc_x / 100)
|
||||
if app.scale < 0.1 {
|
||||
app.scale = 0.1
|
||||
}
|
||||
if app.scale > 32 {
|
||||
app.scale = 32
|
||||
}
|
||||
}
|
||||
|
||||
if ev.typ == .key_down {
|
||||
// println(ev.key_code)
|
||||
|
||||
// Exit using the ESC key or Q key
|
||||
if ev.key_code == .escape || ev.key_code == .q {
|
||||
cleanup(mut app)
|
||||
exit(0)
|
||||
}
|
||||
// Toggle info text OSD
|
||||
if ev.key_code == .i {
|
||||
app.show_info_flag = !app.show_info_flag
|
||||
}
|
||||
// Toggle help text
|
||||
if ev.key_code == .h {
|
||||
app.show_help_flag = !app.show_help_flag
|
||||
}
|
||||
|
||||
// do actions only if there are items in the list
|
||||
if app.item_list.loaded == true && app.item_list.n_item > 0 {
|
||||
// show previous image
|
||||
if ev.key_code == .left {
|
||||
app.item_list.get_next_item(-1)
|
||||
load_image(mut app)
|
||||
}
|
||||
// show next image
|
||||
if ev.key_code == .right {
|
||||
app.item_list.get_next_item(1)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
// jump to the next container if possible
|
||||
if ev.key_code == .up {
|
||||
app.item_list.go_to_next_container(1)
|
||||
load_image(mut app)
|
||||
}
|
||||
// jump to the previous container if possible
|
||||
if ev.key_code == .down {
|
||||
app.item_list.go_to_next_container(-1)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
// rotate the image
|
||||
if ev.key_code == .r {
|
||||
app.item_list.rotate(1)
|
||||
}
|
||||
|
||||
// full screen
|
||||
if ev.key_code == .f {
|
||||
println('Full screen state: ${sapp.is_fullscreen()}')
|
||||
sapp.toggle_fullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drag&drop
|
||||
if ev.typ == .files_dropped {
|
||||
app.state = .scanning
|
||||
// set logo texture during scanning
|
||||
show_logo(mut app)
|
||||
|
||||
num := sapp.get_num_dropped_files()
|
||||
mut file_list := []string{}
|
||||
for i in 0 .. num {
|
||||
file_list << sapp.get_dropped_file_path(i)
|
||||
}
|
||||
println('Scanning: ${file_list}')
|
||||
app.item_list = &Item_list{}
|
||||
app.item_list.loaded = false
|
||||
|
||||
// load_image(mut app)
|
||||
// go app.item_list.get_items_list(file_list)
|
||||
|
||||
load_and_show(file_list, mut app)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_and_show(file_list []string, mut app App) {
|
||||
app.item_list.get_items_list(file_list)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Main
|
||||
*
|
||||
******************************************************************************/
|
||||
fn main() {
|
||||
// mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf'))
|
||||
font_name := 'RobotoMono-Regular.ttf'
|
||||
font_path := os.join_path(os.temp_dir(), font_name)
|
||||
println('Temporary path for the font file: [${font_path}]')
|
||||
|
||||
// if the font doesn't exist create it from the embedded one
|
||||
if os.exists(font_path) == false {
|
||||
println('Write font [${font_name}] in temp folder.')
|
||||
embedded_file := $embed_file('../assets/fonts/RobotoMono-Regular.ttf')
|
||||
os.write_file(font_path, embedded_file.to_string()) or {
|
||||
eprintln('ERROR: not able to write font file to [${font_path}]')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// logo image
|
||||
logo_name := 'logo.png'
|
||||
logo_path := os.join_path(os.temp_dir(), logo_name)
|
||||
println('Temporary path for the logo: [${logo_path}]')
|
||||
// if the logo doesn't exist create it from the embedded one
|
||||
if os.exists(logo_path) == false {
|
||||
println('Write logo [${logo_name}] in temp folder.')
|
||||
embedded_file := $embed_file('../assets/logo.png')
|
||||
os.write_file(logo_path, embedded_file.to_string()) or {
|
||||
eprintln('ERROR: not able to write logo file to [${logo_path}]')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// App init
|
||||
mut app := &App{
|
||||
gg: unsafe { nil }
|
||||
// zip fields
|
||||
zip: unsafe { nil }
|
||||
item_list: unsafe { nil }
|
||||
}
|
||||
|
||||
app.state = .scanning
|
||||
app.logo_path = logo_path
|
||||
app.font_path = font_path
|
||||
|
||||
// Scan all the arguments to find images
|
||||
app.item_list = &Item_list{}
|
||||
// app.item_list.get_items_list(os.args[1..])
|
||||
load_and_show(os.args[1..], mut app)
|
||||
|
||||
app.gg = gg.new_context(
|
||||
width: win_width
|
||||
height: win_height
|
||||
create_window: true
|
||||
window_title: 'V Image viewer 0.8'
|
||||
user_data: app
|
||||
bg_color: bg_color
|
||||
frame_fn: frame
|
||||
init_fn: app_init
|
||||
cleanup_fn: cleanup
|
||||
event_fn: my_event_manager
|
||||
font_path: font_path
|
||||
enable_dragndrop: true
|
||||
max_dropped_files: 64
|
||||
max_dropped_file_path_length: 2048
|
||||
// ui_mode: true
|
||||
)
|
||||
|
||||
app.gg.run()
|
||||
}
|
||||
Reference in new issue
Block a user