v / examples / viewer / view.v
830 lines · 741 sloc · 21.13 KB · f052d322b7e536e239136bc001398034ed58e56b
Raw
1/**********************************************************************
2*
3* simple Picture Viewer V. 0.9
4*
5* Copyright (c) 2021 Dario Deledda. All rights reserved.
6* Use of this source code is governed by an MIT license
7* that can be found in the LICENSE file.
8*
9* TODO:
10* - add an example with shaders
11**********************************************************************/
12import os
13import gg
14import sokol.gfx
15import sokol.sgl
16import sokol.sapp
17import stbi
18import compress.szip
19import strings
20
21// Help text
22const help_text_rows = [
23 'Image Viewer 0.9 help.',
24 '',
25 'ESC/q - Quit',
26 'cur. right - Next image',
27 'cur. left - Previous image',
28 'cur. up - Next folder',
29 'cur. down - Previous folder',
30 'F - Toggle full screen',
31 'R - Rotate image of 90 degree',
32 'I - Toggle the info text',
33 '',
34 'mouse wheel - next/previous images',
35 'keep pressed left Mouse button - Pan on the image',
36 'keep pressed right Mouse button - Zoom on the image',
37]
38
39const win_width = 800
40const win_height = 800
41const bg_color = gg.black
42const pi_2 = 3.14159265359 / 2.0
43// const uv = [f32(0), 0, 1, 0, 1, 1, 0, 1]! // used for zoom icon during rotations
44
45const text_drop_files = 'Drop here some images/folder/zip to navigate in the pics'
46const text_scanning = 'Scanning...'
47const text_loading = 'Loading...'
48
49enum Viewer_state {
50 loading
51 scanning
52 show
53 error
54}
55
56struct App {
57mut:
58 gg &gg.Context = unsafe { nil }
59 pip_viewer sgl.Pipeline
60 texture gfx.Image
61 sampler gfx.Sampler
62 init_flag bool
63 mouse_x f32 = -1.0
64 mouse_y f32 = -1.0
65 scroll_y int
66
67 state Viewer_state = .scanning
68 // translation
69 tr_flag bool
70 tr_x f32 = 0.0
71 tr_y f32 = 0.0
72 last_tr_x f32 = 0.0
73 last_tr_y f32 = 0.0
74 // scaling
75 sc_flag bool
76 scale f32 = 1.0
77 sc_x f32 = 0.0
78 sc_y f32 = 0.0
79 last_sc_x f32 = 0.0
80 last_sc_y f32 = 0.0
81 // loaded image
82 img_w int
83 img_h int
84 img_ratio f32 = 1.0
85 // item list
86 item_list &Item_list = unsafe { nil }
87 // Text info and help
88 show_info_flag bool = true
89 show_help_flag bool
90 // zip container
91 zip &szip.Zip = unsafe { nil } // pointer to the szip structure
92 zip_index int = -1 // index of the zip container item
93 // memory buffer
94 mem_buf voidptr // buffer used to load items from files/containers
95 mem_buf_size int // size of the buffer
96 // font
97 font_path string // path to the temp font file
98 // logo
99 logo_path string // path of the temp font logo
100 logo_texture gfx.Image
101 logo_sampler gfx.Sampler
102 logo_w int
103 logo_h int
104 logo_ratio f32 = 1.0
105 // string builder
106 bl strings.Builder = strings.new_builder(512)
107}
108
109/******************************************************************************
110*
111* Texture functions
112*
113******************************************************************************/
114fn create_texture(w int, h int, buf &u8) (gfx.Image, gfx.Sampler) {
115 sz := w * h * 4
116 mut img_desc := gfx.ImageDesc{
117 width: w
118 height: h
119 num_mipmaps: 0
120 // usage: .dynamic
121 label: &u8(unsafe { nil })
122 d3d11_texture: 0
123 }
124 // comment if .dynamic is enabled
125 img_desc.data.subimage[0][0] = gfx.Range{
126 ptr: buf
127 size: usize(sz)
128 }
129
130 sg_img := gfx.make_image(&img_desc)
131
132 mut smp_desc := gfx.SamplerDesc{
133 min_filter: .linear
134 mag_filter: .linear
135 wrap_u: .clamp_to_edge
136 wrap_v: .clamp_to_edge
137 }
138
139 sg_smp := gfx.make_sampler(&smp_desc)
140 return sg_img, sg_smp
141}
142
143fn destroy_texture(sg_img gfx.Image) {
144 gfx.destroy_image(sg_img)
145}
146
147/******************************************************************************
148*
149* Memory buffer
150*
151******************************************************************************/
152@[inline]
153fn (mut app App) resize_buf_if_needed(in_size int) {
154 // manage the memory buffer
155 if app.mem_buf_size < in_size {
156 println('Managing FILE memory buffer, allocated [${in_size}]Bytes')
157 // free previous buffer if any exist
158 if app.mem_buf_size > 0 {
159 unsafe {
160 free(app.mem_buf)
161 }
162 }
163 // allocate the memory
164 unsafe {
165 app.mem_buf = malloc(int(in_size))
166 app.mem_buf_size = int(in_size)
167 }
168 }
169}
170
171/******************************************************************************
172*
173* Loading functions
174*
175******************************************************************************/
176// read_bytes from file in `path` in the memory buffer of app.
177@[manualfree]
178fn (mut app App) read_bytes(path string) bool {
179 mut fp := os.vfopen(path, 'rb') or {
180 eprintln('ERROR: Can not open the file [${path}].')
181 return false
182 }
183 defer {
184 C.fclose(fp)
185 }
186 cseek := C.fseek(fp, 0, C.SEEK_END)
187 if cseek != 0 {
188 eprintln('ERROR: Can not seek in the file [${path}].')
189 return false
190 }
191 fsize := C.ftell(fp)
192 if fsize < 0 {
193 eprintln('ERROR: File [${path}] has size is 0.')
194 return false
195 }
196 C.rewind(fp)
197
198 app.resize_buf_if_needed(int(fsize))
199
200 nr_read_elements := int(C.fread(app.mem_buf, fsize, 1, fp))
201 if nr_read_elements == 0 && fsize > 0 {
202 eprintln('ERROR: Can not read the file [${path}] in the memory buffer.')
203 return false
204 }
205 return true
206}
207
208// read a file as []u8
209pub fn read_bytes_from_file(file_path string) []u8 {
210 mut buffer := []u8{}
211 buffer = os.read_bytes(file_path) or {
212 eprintln('ERROR: Texture file: [${file_path}] NOT FOUND.')
213 exit(0)
214 }
215 return buffer
216}
217
218fn (mut app App) load_texture_from_buffer(buf voidptr, buf_len int) (gfx.Image, gfx.Sampler, int, int) {
219 // load image
220 stbi.set_flip_vertically_on_load(true)
221 img := stbi.load_from_memory(buf, buf_len) or {
222 eprintln('ERROR: Can not load image from buffer, file: [${app.item_list.lst[app.item_list.item_index]}].')
223 return app.logo_texture, app.sampler, app.logo_w, app.logo_h
224 // exit(1)
225 }
226 sg_img, sg_smp := create_texture(int(img.width), int(img.height), img.data)
227 unsafe {
228 img.free()
229 }
230 return sg_img, sg_smp, int(img.width), int(img.height)
231}
232
233pub fn (mut app App) load_texture_from_file(file_name string) (gfx.Image, gfx.Sampler, int, int) {
234 app.read_bytes(file_name)
235 return app.load_texture_from_buffer(app.mem_buf, app.mem_buf_size)
236}
237
238pub fn show_logo(mut app App) {
239 clear_modifier_params(mut app)
240 if app.texture != app.logo_texture {
241 destroy_texture(app.texture)
242 gfx.destroy_sampler(app.sampler)
243 }
244 app.texture = app.logo_texture
245 app.sampler = app.logo_sampler
246 app.img_w = app.logo_w
247 app.img_h = app.logo_h
248 app.img_ratio = f32(app.img_w) / f32(app.img_h)
249 // app.gg.refresh_ui()
250}
251
252pub fn load_image(mut app App) {
253 if app.item_list.loaded == false || app.init_flag == false {
254 // show_logo(mut app)
255 // app.state = .show
256 return
257 }
258 app.state = .loading
259 clear_modifier_params(mut app)
260 // destroy the texture, avoid to destroy the logo
261 if app.texture != app.logo_texture {
262 destroy_texture(app.texture)
263 gfx.destroy_sampler(app.sampler)
264 }
265
266 // load from .ZIP file
267 if app.item_list.is_inside_a_container() == true {
268 app.texture, app.sampler, app.img_w, app.img_h = app.load_texture_from_zip() or {
269 eprintln('ERROR: Can not load image from .ZIP file [${app.item_list.lst[app.item_list.item_index]}].')
270 show_logo(mut app)
271 app.state = .show
272 return
273 }
274 app.img_ratio = f32(app.img_w) / f32(app.img_h)
275 app.state = .show
276 // app.gg.refresh_ui()
277 return
278 }
279
280 // if we are out of the zip, close it
281 if app.zip_index >= 0 {
282 app.zip_index = -1
283 app.zip.close()
284 }
285
286 file_path := app.item_list.get_file_path()
287 if file_path != '' {
288 // println("${app.item_list.lst[app.item_list.item_index]} ${file_path} ${app.item_list.lst.len}")
289 app.texture, app.sampler, app.img_w, app.img_h = app.load_texture_from_file(file_path)
290 app.img_ratio = f32(app.img_w) / f32(app.img_h)
291 // println("texture: [${app.img_w},${app.img_h}] ratio: ${app.img_ratio}")
292 } else {
293 app.texture = app.logo_texture
294 app.sampler = app.logo_sampler
295 app.img_w = app.logo_w
296 app.img_h = app.logo_h
297 app.img_ratio = f32(app.img_w) / f32(app.img_h)
298 println('texture NOT FOUND: use logo!')
299 }
300 app.state = .show
301}
302
303/******************************************************************************
304*
305* Init / Cleanup
306*
307******************************************************************************/
308fn app_init(mut app App) {
309 app.init_flag = true
310
311 // 3d pipeline
312 mut pipdesc := gfx.PipelineDesc{}
313 unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
314
315 color_state := gfx.ColorTargetState{
316 blend: gfx.BlendState{
317 enabled: true
318 src_factor_rgb: .src_alpha
319 dst_factor_rgb: .one_minus_src_alpha
320 }
321 }
322 pipdesc.colors[0] = color_state
323
324 pipdesc.depth = gfx.DepthState{
325 write_enabled: true
326 compare: .less_equal
327 }
328 pipdesc.cull_mode = .back
329 app.pip_viewer = sgl.make_pipeline(&pipdesc)
330
331 // load logo
332 app.logo_texture, app.logo_sampler, app.logo_w, app.logo_h =
333 app.load_texture_from_file(app.logo_path)
334 app.logo_ratio = f32(app.img_w) / f32(app.img_h)
335
336 app.img_w = app.logo_w
337 app.img_h = app.logo_h
338 app.img_ratio = app.logo_ratio
339 app.texture = app.logo_texture
340 app.sampler = app.logo_sampler
341
342 println('INIT DONE!')
343
344 // init done, load the first image if any
345 load_image(mut app)
346}
347
348fn cleanup(mut app App) {
349 gfx.shutdown()
350
351 // delete temp files
352 os.rm(app.font_path) or { eprintln('ERROR: Can not delete temp font file.') }
353 os.rm(app.logo_path) or { eprintln('ERROR: Can not delete temp logo file.') }
354 println('Cleaning done.')
355}
356
357/******************************************************************************
358*
359* Draw functions
360*
361******************************************************************************/
362@[manualfree]
363fn frame(mut app App) {
364 ws := gg.window_size_real_pixels()
365 if ws.width <= 0 || ws.height <= 0 {
366 return
367 }
368
369 mut ratio := f32(ws.width) / ws.height
370 dw := ws.width
371 dh := ws.height
372
373 app.gg.begin()
374 sgl.defaults()
375
376 // set viewport
377 sgl.viewport(0, 0, dw, dh, true)
378
379 // enable our pipeline
380 sgl.load_pipeline(app.pip_viewer)
381 sgl.enable_texture()
382 sgl.texture(app.texture, app.sampler)
383
384 // translation
385 tr_x := app.tr_x / app.img_w
386 tr_y := -app.tr_y / app.img_h
387 sgl.push_matrix()
388 sgl.translate(tr_x, tr_y, 0.0)
389 // scaling/zoom
390 sgl.scale(2.0 * app.scale, 2.0 * app.scale, 0.0)
391 // rotation
392 mut rotation := 0
393 if app.state == .show && app.item_list.n_item > 0 {
394 rotation = app.item_list.lst[app.item_list.item_index].rotation
395 sgl.rotate(pi_2 * f32(rotation), 0.0, 0.0, -1.0)
396 }
397
398 // draw the image
399 mut w := f32(0.5)
400 mut h := f32(0.5)
401
402 // for 90 and 270 degree invert w and h
403 // rotation change image ratio, manage it
404 if rotation & 1 == 1 {
405 tmp := w
406 w = h
407 h = tmp
408 h /= app.img_ratio * ratio
409 } else {
410 h /= app.img_ratio / ratio
411 }
412
413 // manage image overflow in case of strange scales
414 if h > 0.5 {
415 reduction_factor := 0.5 / h
416 h = h * reduction_factor
417 w = w * reduction_factor
418 }
419 if w > 0.5 {
420 reduction_factor := 0.5 / w
421 h = h * reduction_factor
422 w = w * reduction_factor
423 }
424
425 // println("${w},${h}")
426 // white multiplicator for now
427 mut c := [u8(255), 255, 255]!
428 sgl.begin_quads()
429 sgl.v2f_t2f_c3b(-w, -h, 0, 0, c[0], c[1], c[2])
430 sgl.v2f_t2f_c3b(w, -h, 1, 0, c[0], c[1], c[2])
431 sgl.v2f_t2f_c3b(w, h, 1, 1, c[0], c[1], c[2])
432 sgl.v2f_t2f_c3b(-w, h, 0, 1, c[0], c[1], c[2])
433 sgl.end()
434
435 // restore all the transformations
436 sgl.pop_matrix()
437
438 // Zoom icon
439 /*
440 if app.show_info_flag == true && app.scale > 1 {
441 mut bw := f32(0.25)
442 mut bh := f32(0.25 / app.img_ratio)
443
444 // manage the rotations
445 if rotation & 1 == 1 {
446 bw,bh = bh,bw
447 }
448 mut bx := f32(1 - bw)
449 mut by := f32(1 - bh)
450 if rotation & 1 == 1 {
451 bx,by = by,bx
452 }
453
454 bh_old1 := bh
455 bh *= ratio
456 by += (bh_old1 - bh)
457
458 // draw the zoom icon
459 sgl.begin_quads()
460 r := int(u32(rotation) << 1)
461 sgl.v2f_t2f_c3b(bx , by , uv[(0 + r) & 7] , uv[(1 + r) & 7], c[0], c[1], c[2])
462 sgl.v2f_t2f_c3b(bx + bw, by , uv[(2 + r) & 7] , uv[(3 + r) & 7], c[0], c[1], c[2])
463 sgl.v2f_t2f_c3b(bx + bw, by + bh, uv[(4 + r) & 7] , uv[(5 + r) & 7], c[0], c[1], c[2])
464 sgl.v2f_t2f_c3b(bx , by + bh, uv[(6 + r) & 7] , uv[(7 + r) & 7], c[0], c[1], c[2])
465 sgl.end()
466
467 // draw the zoom rectangle
468 sgl.disable_texture()
469
470 bw_old := bw
471 bh_old := bh
472 bw /= app.scale
473 bh /= app.scale
474 bx += (bw_old - bw) / 2 - (tr_x / 8) / app.scale
475 by += (bh_old - bh) / 2 - ((tr_y / 8) / app.scale) * ratio
476
477 c = [u8(255),255,0]! // yellow
478 sgl.begin_line_strip()
479 sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
480 sgl.v2f_c3b(bx + bw, by , c[0], c[1], c[2])
481 sgl.v2f_c3b(bx + bw, by + bh, c[0], c[1], c[2])
482 sgl.v2f_c3b(bx , by + bh, c[0], c[1], c[2])
483 sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
484 sgl.end()
485 }
486 */
487 sgl.disable_texture()
488
489 //
490 // Draw info text
491 //
492 x := 10
493 y := 10
494
495 app.gg.begin()
496
497 if app.state in [.scanning, .loading] {
498 if app.state == .scanning {
499 draw_text(mut app, text_scanning, x, y, 20)
500 } else {
501 draw_text(mut app, text_loading, x, y, 20)
502 }
503 } else if app.state == .show {
504 // print the info text if needed
505 if app.item_list.n_item > 0 && app.show_info_flag == true {
506 /*
507 // waiting for better autofree
508 num := app.item_list.lst[app.item_list.item_index].n_item
509 of_num := app.item_list.n_item
510 x_screen := int(w*2*app.scale*dw)
511 y_screen := int(h*2*app.scale*dw)
512 rotation_angle := 90 * rotation
513 scale_str := "${app.scale:.2}"
514 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}"
515 //text := "${num}/${of_num}"
516 draw_text(mut app, text, 10, 10, 20)
517 unsafe{
518 text.free()
519 }
520 */
521
522 // Using string builder to avoid memory leak
523 num := app.item_list.lst[app.item_list.item_index].n_item
524 of_num := app.item_list.n_item
525 x_screen := int(w * 2 * app.scale * dw)
526 y_screen := int(h * 2 * app.scale * dw)
527 rotation_angle := 90 * rotation
528 scale_str := '${app.scale:.2}'
529 app.bl.clear()
530 app.bl.write_string('${num}/${of_num}')
531 app.bl.write_string(' [${app.img_w}x${app.img_h}]=>[${x_screen}x${y_screen}]')
532 app.bl.write_string(' ${app.item_list.lst[app.item_list.item_index].name}')
533 app.bl.write_string(' scale: ${scale_str} rotation: ${rotation_angle}')
534 draw_text(mut app, app.bl.str(), 10, 10, 20)
535 } else {
536 if app.item_list.n_item <= 0 {
537 draw_text(mut app, text_drop_files, 10, 10, 20)
538 }
539 }
540 }
541
542 //
543 // Draw Help text
544 //
545 if app.show_help_flag == true {
546 mut txt_y := 30
547 for r in help_text_rows {
548 draw_text(mut app, r, 10, txt_y, 20)
549 txt_y += 20
550 }
551 }
552
553 app.gg.end()
554}
555
556// draw readable text
557fn draw_text(mut app App, in_txt string, in_x int, in_y int, fnt_sz f32) {
558 scale := app.gg.scale
559 font_size := int(fnt_sz * scale)
560
561 mut txt_conf_c0 := gg.TextCfg{
562 color: gg.white // gg.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
563 align: .left
564 size: font_size
565 }
566 mut txt_conf_c1 := gg.TextCfg{
567 color: gg.black // gg.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
568 align: .left
569 size: font_size
570 }
571
572 x := int(in_x * scale)
573 y := int(in_y * scale)
574 app.gg.draw_text(x + 2, y + 2, in_txt, txt_conf_c0)
575 app.gg.draw_text(x, y, in_txt, txt_conf_c1)
576}
577
578/******************************************************************************
579*
580* events management
581*
582******************************************************************************/
583fn clear_modifier_params(mut app App) {
584 app.scale = 1.0
585
586 app.sc_flag = false
587 app.sc_x = 0
588 app.sc_y = 0
589 app.last_sc_x = 0
590 app.last_sc_y = 0
591
592 app.tr_flag = false
593 app.tr_x = 0
594 app.tr_y = 0
595 app.last_tr_x = 0
596 app.last_tr_y = 0
597}
598
599fn my_event_manager(mut ev gg.Event, mut app App) {
600 // Handle window closure
601 if ev.typ == .quit_requested {
602 cleanup(mut app)
603 exit(0)
604 }
605 // navigation using the mouse wheel
606 app.scroll_y = int(ev.scroll_y)
607 if app.scroll_y != 0 {
608 inc := int(-1 * app.scroll_y / 4)
609 if app.item_list.n_item > 0 {
610 app.item_list.get_next_item(inc)
611 load_image(mut app)
612 }
613 }
614
615 if ev.typ == .mouse_move {
616 app.mouse_x = ev.mouse_x
617 app.mouse_y = ev.mouse_y
618 }
619 if ev.typ == .touches_began || ev.typ == .touches_moved {
620 if ev.num_touches > 0 {
621 touch_point := ev.touches[0]
622 app.mouse_x = touch_point.pos_x
623 app.mouse_y = touch_point.pos_y
624 }
625 }
626
627 // clear all parameters
628 if ev.typ == .mouse_down && ev.mouse_button == .middle {
629 clear_modifier_params(mut app)
630 }
631
632 // ws := gg.window_size_real_pixels()
633 // ratio := f32(ws.width) / ws.height
634 // dw := ws.width
635 // dh := ws.height
636
637 // --- translate ---
638 if ev.typ == .mouse_down && ev.mouse_button == .left {
639 app.tr_flag = true
640 app.last_tr_x = app.mouse_x
641 app.last_tr_y = app.mouse_y
642 }
643 if ev.typ == .mouse_up && ev.mouse_button == .left && app.tr_flag == true {
644 app.tr_flag = false
645 }
646 if ev.typ == .mouse_move && app.tr_flag == true {
647 app.tr_x += (app.mouse_x - app.last_tr_x) * 3 * app.gg.scale
648 app.tr_y += (app.mouse_y - app.last_tr_y) * 3 * app.gg.scale
649 app.last_tr_x = app.mouse_x
650 app.last_tr_y = app.mouse_y
651 // println("Translate: ${app.tr_x} ${app.tr_y}")
652 }
653
654 // --- scaling ---
655 if ev.typ == .mouse_down && ev.mouse_button == .right && app.sc_flag == false {
656 app.sc_flag = true
657 app.last_sc_x = app.mouse_x
658 app.last_sc_y = app.mouse_y
659 }
660 if ev.typ == .mouse_up && ev.mouse_button == .right && app.sc_flag == true {
661 app.sc_flag = false
662 }
663 if ev.typ == .mouse_move && app.sc_flag == true {
664 app.sc_x = app.mouse_x - app.last_sc_x
665 app.sc_y = app.mouse_y - app.last_sc_y
666 app.last_sc_x = app.mouse_x
667 app.last_sc_y = app.mouse_y
668
669 app.scale += f32(app.sc_x / 100)
670 if app.scale < 0.1 {
671 app.scale = 0.1
672 }
673 if app.scale > 32 {
674 app.scale = 32
675 }
676 }
677
678 if ev.typ == .key_down {
679 // println(ev.key_code)
680
681 // Exit using the ESC key or Q key
682 if ev.key_code == .escape || ev.key_code == .q {
683 cleanup(mut app)
684 exit(0)
685 }
686 // Toggle info text OSD
687 if ev.key_code == .i {
688 app.show_info_flag = !app.show_info_flag
689 }
690 // Toggle help text
691 if ev.key_code == .h {
692 app.show_help_flag = !app.show_help_flag
693 }
694
695 // do actions only if there are items in the list
696 if app.item_list.loaded == true && app.item_list.n_item > 0 {
697 // show previous image
698 if ev.key_code == .left {
699 app.item_list.get_next_item(-1)
700 load_image(mut app)
701 }
702 // show next image
703 if ev.key_code == .right {
704 app.item_list.get_next_item(1)
705 load_image(mut app)
706 }
707
708 // jump to the next container if possible
709 if ev.key_code == .up {
710 app.item_list.go_to_next_container(1)
711 load_image(mut app)
712 }
713 // jump to the previous container if possible
714 if ev.key_code == .down {
715 app.item_list.go_to_next_container(-1)
716 load_image(mut app)
717 }
718
719 // rotate the image
720 if ev.key_code == .r {
721 app.item_list.rotate(1)
722 }
723
724 // full screen
725 if ev.key_code == .f {
726 println('Full screen state: ${sapp.is_fullscreen()}')
727 sapp.toggle_fullscreen()
728 }
729 }
730 }
731
732 // drag&drop
733 if ev.typ == .files_dropped {
734 app.state = .scanning
735 // set logo texture during scanning
736 show_logo(mut app)
737
738 num := sapp.get_num_dropped_files()
739 mut file_list := []string{}
740 for i in 0 .. num {
741 file_list << sapp.get_dropped_file_path(i)
742 }
743 println('Scanning: ${file_list}')
744 app.item_list = &Item_list{}
745 app.item_list.loaded = false
746
747 // load_image(mut app)
748 // go app.item_list.get_items_list(file_list)
749
750 load_and_show(file_list, mut app)
751 }
752}
753
754fn load_and_show(file_list []string, mut app App) {
755 app.item_list.get_items_list(file_list)
756 load_image(mut app)
757}
758
759/******************************************************************************
760*
761* Main
762*
763******************************************************************************/
764fn main() {
765 // mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf'))
766 font_name := 'RobotoMono-Regular.ttf'
767 font_path := os.join_path(os.temp_dir(), font_name)
768 println('Temporary path for the font file: [${font_path}]')
769
770 // if the font doesn't exist create it from the embedded one
771 if os.exists(font_path) == false {
772 println('Write font [${font_name}] in temp folder.')
773 embedded_file := $embed_file('../assets/fonts/RobotoMono-Regular.ttf')
774 os.write_file(font_path, embedded_file.to_string()) or {
775 eprintln('ERROR: not able to write font file to [${font_path}]')
776 exit(1)
777 }
778 }
779
780 // logo image
781 logo_name := 'logo.png'
782 logo_path := os.join_path(os.temp_dir(), logo_name)
783 println('Temporary path for the logo: [${logo_path}]')
784 // if the logo doesn't exist create it from the embedded one
785 if os.exists(logo_path) == false {
786 println('Write logo [${logo_name}] in temp folder.')
787 embedded_file := $embed_file('../assets/logo.png')
788 os.write_file(logo_path, embedded_file.to_string()) or {
789 eprintln('ERROR: not able to write logo file to [${logo_path}]')
790 exit(1)
791 }
792 }
793
794 // App init
795 mut app := &App{
796 gg: unsafe { nil }
797 // zip fields
798 zip: unsafe { nil }
799 item_list: unsafe { nil }
800 }
801
802 app.state = .scanning
803 app.logo_path = logo_path
804 app.font_path = font_path
805
806 // Scan all the arguments to find images
807 app.item_list = &Item_list{}
808 // app.item_list.get_items_list(os.args[1..])
809 load_and_show(os.args[1..], mut app)
810
811 app.gg = gg.new_context(
812 width: win_width
813 height: win_height
814 create_window: true
815 window_title: 'V Image viewer 0.8'
816 user_data: app
817 bg_color: bg_color
818 frame_fn: frame
819 init_fn: app_init
820 cleanup_fn: cleanup
821 event_fn: my_event_manager
822 font_path: font_path
823 enable_dragndrop: true
824 max_dropped_files: 64
825 max_dropped_file_path_length: 2048
826 // ui_mode: true
827 )
828
829 app.gg.run()
830}
831