v / examples / viewer
Raw file | 830 loc (742 sloc) | 20.59 KB | Latest commit hash 017ace6ea
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 gx
15import sokol.gfx
16import sokol.sgl
17import sokol.sapp
18import stbi
19import szip
20import strings
21
22// Help text
23const (
24 help_text_rows = [
25 'Image Viewer 0.9 help.',
26 '',
27 'ESC/q - Quit',
28 'cur. right - Next image',
29 'cur. left - Previous image',
30 'cur. up - Next folder',
31 'cur. down - Previous folder',
32 'F - Toggle full screen',
33 'R - Rotate image of 90 degree',
34 'I - Toggle the info text',
35 '',
36 'mouse wheel - next/previous images',
37 'keep pressed left Mouse button - Pan on the image',
38 'keep pressed right Mouse button - Zoom on the image',
39 ]
40)
41
42const (
43 win_width = 800
44 win_height = 800
45 bg_color = gx.black
46 pi_2 = 3.14159265359 / 2.0
47 uv = [f32(0), 0, 1, 0, 1, 1, 0, 1]! // used for zoom icon during rotations
48
49 text_drop_files = 'Drop here some images/folder/zip to navigate in the pics'
50 text_scanning = 'Scanning...'
51 text_loading = 'Loading...'
52)
53
54enum Viewer_state {
55 loading
56 scanning
57 show
58 error
59}
60
61struct App {
62mut:
63 gg &gg.Context = unsafe { nil }
64 pip_viewer sgl.Pipeline
65 texture gfx.Image
66 init_flag bool
67 frame_count int
68 mouse_x int = -1
69 mouse_y int = -1
70 scroll_y int
71
72 state Viewer_state = .scanning
73 // translation
74 tr_flag bool
75 tr_x f32 = 0.0
76 tr_y f32 = 0.0
77 last_tr_x f32 = 0.0
78 last_tr_y f32 = 0.0
79 // scaling
80 sc_flag bool
81 scale f32 = 1.0
82 sc_x f32 = 0.0
83 sc_y f32 = 0.0
84 last_sc_x f32 = 0.0
85 last_sc_y f32 = 0.0
86 // loaded image
87 img_w int
88 img_h int
89 img_ratio f32 = 1.0
90 // item list
91 item_list &Item_list = unsafe { nil }
92 // Text info and help
93 show_info_flag bool = true
94 show_help_flag bool
95 // zip container
96 zip &szip.Zip = unsafe { nil } // pointer to the szip structure
97 zip_index int = -1 // index of the zip container item
98 // memory buffer
99 mem_buf voidptr // buffer used to load items from files/containers
100 mem_buf_size int // size of the buffer
101 // font
102 font_path string // path to the temp font file
103 // logo
104 logo_path string // path of the temp font logo
105 logo_texture gfx.Image
106 logo_w int
107 logo_h int
108 logo_ratio f32 = 1.0
109 // string builder
110 bl strings.Builder = strings.new_builder(512)
111}
112
113/******************************************************************************
114*
115* Texture functions
116*
117******************************************************************************/
118fn create_texture(w int, h int, buf &u8) gfx.Image {
119 sz := w * h * 4
120 mut img_desc := gfx.ImageDesc{
121 width: w
122 height: h
123 num_mipmaps: 0
124 min_filter: .linear
125 mag_filter: .linear
126 // usage: .dynamic
127 wrap_u: .clamp_to_edge
128 wrap_v: .clamp_to_edge
129 label: &u8(0)
130 d3d11_texture: 0
131 }
132 // comment if .dynamic is enabled
133 img_desc.data.subimage[0][0] = gfx.Range{
134 ptr: buf
135 size: usize(sz)
136 }
137
138 sg_img := gfx.make_image(&img_desc)
139 return sg_img
140}
141
142fn destroy_texture(sg_img gfx.Image) {
143 gfx.destroy_image(sg_img)
144}
145
146// Use only if: .dynamic is enabled
147fn update_text_texture(sg_img gfx.Image, w int, h int, buf &u8) {
148 sz := w * h * 4
149 mut tmp_sbc := gfx.ImageData{}
150 tmp_sbc.subimage[0][0] = gfx.Range{
151 ptr: buf
152 size: usize(sz)
153 }
154 gfx.update_image(sg_img, &tmp_sbc)
155}
156
157/******************************************************************************
158*
159* Memory buffer
160*
161******************************************************************************/
162[inline]
163fn (mut app App) resize_buf_if_needed(in_size int) {
164 // manage the memory buffer
165 if app.mem_buf_size < in_size {
166 println('Managing FILE memory buffer, allocated [${in_size}]Bytes')
167 // free previous buffer if any exist
168 if app.mem_buf_size > 0 {
169 unsafe {
170 free(app.mem_buf)
171 }
172 }
173 // allocate the memory
174 unsafe {
175 app.mem_buf = malloc(int(in_size))
176 app.mem_buf_size = int(in_size)
177 }
178 }
179}
180
181/******************************************************************************
182*
183* Loading functions
184*
185******************************************************************************/
186// read_bytes from file in `path` in the memory buffer of app.
187[manualfree]
188fn (mut app App) read_bytes(path string) bool {
189 mut fp := os.vfopen(path, 'rb') or {
190 eprintln('ERROR: Can not open the file [${path}].')
191 return false
192 }
193 defer {
194 C.fclose(fp)
195 }
196 cseek := C.fseek(fp, 0, C.SEEK_END)
197 if cseek != 0 {
198 eprintln('ERROR: Can not seek in the file [${path}].')
199 return false
200 }
201 fsize := C.ftell(fp)
202 if fsize < 0 {
203 eprintln('ERROR: File [${path}] has size is 0.')
204 return false
205 }
206 C.rewind(fp)
207
208 app.resize_buf_if_needed(int(fsize))
209
210 nr_read_elements := int(C.fread(app.mem_buf, fsize, 1, fp))
211 if nr_read_elements == 0 && fsize > 0 {
212 eprintln('ERROR: Can not read the file [${path}] in the memory buffer.')
213 return false
214 }
215 return true
216}
217
218// read a file as []u8
219pub fn read_bytes_from_file(file_path string) []u8 {
220 mut buffer := []u8{}
221 buffer = os.read_bytes(file_path) or {
222 eprintln('ERROR: Texure file: [${file_path}] NOT FOUND.')
223 exit(0)
224 }
225 return buffer
226}
227
228fn (mut app App) load_texture_from_buffer(buf voidptr, buf_len int) (gfx.Image, int, int) {
229 // load image
230 stbi.set_flip_vertically_on_load(true)
231 img := stbi.load_from_memory(buf, buf_len) or {
232 eprintln('ERROR: Can not load image from buffer, file: [${app.item_list.lst[app.item_list.item_index]}].')
233 return app.logo_texture, app.logo_w, app.logo_h
234 // exit(1)
235 }
236 res := create_texture(int(img.width), int(img.height), img.data)
237 unsafe {
238 img.free()
239 }
240 return res, int(img.width), int(img.height)
241}
242
243pub fn (mut app App) load_texture_from_file(file_name string) (gfx.Image, int, int) {
244 app.read_bytes(file_name)
245 return app.load_texture_from_buffer(app.mem_buf, app.mem_buf_size)
246}
247
248pub fn show_logo(mut app App) {
249 clear_modifier_params(mut app)
250 if app.texture != app.logo_texture {
251 destroy_texture(app.texture)
252 }
253 app.texture = app.logo_texture
254 app.img_w = app.logo_w
255 app.img_h = app.logo_h
256 app.img_ratio = f32(app.img_w) / f32(app.img_h)
257 // app.gg.refresh_ui()
258}
259
260pub fn load_image(mut app App) {
261 if app.item_list.loaded == false || app.init_flag == false {
262 // show_logo(mut app)
263 // app.state = .show
264 return
265 }
266 app.state = .loading
267 clear_modifier_params(mut app)
268 // destroy the texture, avoid to destroy the logo
269 if app.texture != app.logo_texture {
270 destroy_texture(app.texture)
271 }
272
273 // load from .ZIP file
274 if app.item_list.is_inside_a_container() == true {
275 app.texture, app.img_w, app.img_h = app.load_texture_from_zip() or {
276 eprintln('ERROR: Can not load image from .ZIP file [${app.item_list.lst[app.item_list.item_index]}].')
277 show_logo(mut app)
278 app.state = .show
279 return
280 }
281 app.img_ratio = f32(app.img_w) / f32(app.img_h)
282 app.state = .show
283 // app.gg.refresh_ui()
284 return
285 }
286
287 // if we are out of the zip, close it
288 if app.zip_index >= 0 {
289 app.zip_index = -1
290 app.zip.close()
291 }
292
293 file_path := app.item_list.get_file_path()
294 if file_path.len > 0 {
295 // println("${app.item_list.lst[app.item_list.item_index]} $file_path ${app.item_list.lst.len}")
296 app.texture, app.img_w, app.img_h = app.load_texture_from_file(file_path)
297 app.img_ratio = f32(app.img_w) / f32(app.img_h)
298 // println("texture: [${app.img_w},${app.img_h}] ratio: ${app.img_ratio}")
299 } else {
300 app.texture = app.logo_texture
301 app.img_w = app.logo_w
302 app.img_h = app.logo_h
303 app.img_ratio = f32(app.img_w) / f32(app.img_h)
304 println('texture NOT FOUND: use logo!')
305 }
306 app.state = .show
307}
308
309/******************************************************************************
310*
311* Init / Cleanup
312*
313******************************************************************************/
314fn app_init(mut app App) {
315 app.init_flag = true
316
317 // 3d pipeline
318 mut pipdesc := gfx.PipelineDesc{}
319 unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
320
321 color_state := gfx.ColorState{
322 blend: gfx.BlendState{
323 enabled: true
324 src_factor_rgb: .src_alpha
325 dst_factor_rgb: .one_minus_src_alpha
326 }
327 }
328 pipdesc.colors[0] = color_state
329
330 pipdesc.depth = gfx.DepthState{
331 write_enabled: true
332 compare: .less_equal
333 }
334 pipdesc.cull_mode = .back
335 app.pip_viewer = sgl.make_pipeline(&pipdesc)
336
337 // load logo
338 app.logo_texture, app.logo_w, app.logo_h = app.load_texture_from_file(app.logo_path)
339 app.logo_ratio = f32(app.img_w) / f32(app.img_h)
340
341 app.img_w = app.logo_w
342 app.img_h = app.logo_h
343 app.img_ratio = app.logo_ratio
344 app.texture = app.logo_texture
345
346 println('INIT DONE!')
347
348 // init done, load the first image if any
349 load_image(mut app)
350}
351
352fn cleanup(mut app App) {
353 gfx.shutdown()
354
355 // delete temp files
356 os.rm(app.font_path) or { eprintln('ERROR: Can not delete temp font file.') }
357 os.rm(app.logo_path) or { eprintln('ERROR: Can not delete temp logo file.') }
358 println('Cleaning done.')
359}
360
361/******************************************************************************
362*
363* Draw functions
364*
365******************************************************************************/
366[manualfree]
367fn frame(mut app App) {
368 ws := gg.window_size_real_pixels()
369 if ws.width <= 0 || ws.height <= 0 {
370 return
371 }
372
373 mut ratio := f32(ws.width) / ws.height
374 dw := ws.width
375 dh := ws.height
376
377 app.gg.begin()
378 sgl.defaults()
379
380 // set viewport
381 sgl.viewport(0, 0, dw, dh, true)
382
383 // enable our pipeline
384 sgl.load_pipeline(app.pip_viewer)
385 sgl.enable_texture()
386 sgl.texture(app.texture)
387
388 // translation
389 tr_x := app.tr_x / app.img_w
390 tr_y := -app.tr_y / app.img_h
391 sgl.push_matrix()
392 sgl.translate(tr_x, tr_y, 0.0)
393 // scaling/zoom
394 sgl.scale(2.0 * app.scale, 2.0 * app.scale, 0.0)
395 // rotation
396 mut rotation := 0
397 if app.state == .show && app.item_list.n_item > 0 {
398 rotation = app.item_list.lst[app.item_list.item_index].rotation
399 sgl.rotate(pi_2 * f32(rotation), 0.0, 0.0, -1.0)
400 }
401
402 // draw the image
403 mut w := f32(0.5)
404 mut h := f32(0.5)
405
406 // for 90 and 270 degree invert w and h
407 // rotation change image ratio, manage it
408 if rotation & 1 == 1 {
409 tmp := w
410 w = h
411 h = tmp
412 h /= app.img_ratio * ratio
413 } else {
414 h /= app.img_ratio / ratio
415 }
416
417 // manage image overflow in case of strange scales
418 if h > 0.5 {
419 reduction_factor := 0.5 / h
420 h = h * reduction_factor
421 w = w * reduction_factor
422 }
423 if w > 0.5 {
424 reduction_factor := 0.5 / w
425 h = h * reduction_factor
426 w = w * reduction_factor
427 }
428
429 // println("$w,$h")
430 // white multiplicator for now
431 mut c := [u8(255), 255, 255]!
432 sgl.begin_quads()
433 sgl.v2f_t2f_c3b(-w, -h, 0, 0, c[0], c[1], c[2])
434 sgl.v2f_t2f_c3b(w, -h, 1, 0, c[0], c[1], c[2])
435 sgl.v2f_t2f_c3b(w, h, 1, 1, c[0], c[1], c[2])
436 sgl.v2f_t2f_c3b(-w, h, 0, 1, c[0], c[1], c[2])
437 sgl.end()
438
439 // restore all the transformations
440 sgl.pop_matrix()
441
442 // Zoom icon
443 /*
444 if app.show_info_flag == true && app.scale > 1 {
445 mut bw := f32(0.25)
446 mut bh := f32(0.25 / app.img_ratio)
447
448 // manage the rotations
449 if rotation & 1 == 1 {
450 bw,bh = bh,bw
451 }
452 mut bx := f32(1 - bw)
453 mut by := f32(1 - bh)
454 if rotation & 1 == 1 {
455 bx,by = by,bx
456 }
457
458 bh_old1 := bh
459 bh *= ratio
460 by += (bh_old1 - bh)
461
462 // draw the zoom icon
463 sgl.begin_quads()
464 r := int(u32(rotation) << 1)
465 sgl.v2f_t2f_c3b(bx , by , uv[(0 + r) & 7] , uv[(1 + r) & 7], c[0], c[1], c[2])
466 sgl.v2f_t2f_c3b(bx + bw, by , uv[(2 + r) & 7] , uv[(3 + r) & 7], c[0], c[1], c[2])
467 sgl.v2f_t2f_c3b(bx + bw, by + bh, uv[(4 + r) & 7] , uv[(5 + r) & 7], c[0], c[1], c[2])
468 sgl.v2f_t2f_c3b(bx , by + bh, uv[(6 + r) & 7] , uv[(7 + r) & 7], c[0], c[1], c[2])
469 sgl.end()
470
471 // draw the zoom rectangle
472 sgl.disable_texture()
473
474 bw_old := bw
475 bh_old := bh
476 bw /= app.scale
477 bh /= app.scale
478 bx += (bw_old - bw) / 2 - (tr_x / 8) / app.scale
479 by += (bh_old - bh) / 2 - ((tr_y / 8) / app.scale) * ratio
480
481 c = [u8(255),255,0]! // yellow
482 sgl.begin_line_strip()
483 sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
484 sgl.v2f_c3b(bx + bw, by , c[0], c[1], c[2])
485 sgl.v2f_c3b(bx + bw, by + bh, c[0], c[1], c[2])
486 sgl.v2f_c3b(bx , by + bh, c[0], c[1], c[2])
487 sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
488 sgl.end()
489 }
490 */
491 sgl.disable_texture()
492
493 //
494 // Draw info text
495 //
496 x := 10
497 y := 10
498
499 app.gg.begin()
500
501 if app.state in [.scanning, .loading] {
502 if app.state == .scanning {
503 draw_text(mut app, text_scanning, x, y, 20)
504 } else {
505 draw_text(mut app, text_loading, x, y, 20)
506 }
507 } else if app.state == .show {
508 // print the info text if needed
509 if app.item_list.n_item > 0 && app.show_info_flag == true {
510 /*
511 // waiting for better autofree
512 num := app.item_list.lst[app.item_list.item_index].n_item
513 of_num := app.item_list.n_item
514 x_screen := int(w*2*app.scale*dw)
515 y_screen := int(h*2*app.scale*dw)
516 rotation_angle := 90 * rotation
517 scale_str := "${app.scale:.2}"
518 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}"
519 //text := "${num}/${of_num}"
520 draw_text(mut app, text, 10, 10, 20)
521 unsafe{
522 text.free()
523 }
524 */
525
526 // Using string builder to avoid memory leak
527 num := app.item_list.lst[app.item_list.item_index].n_item
528 of_num := app.item_list.n_item
529 x_screen := int(w * 2 * app.scale * dw)
530 y_screen := int(h * 2 * app.scale * dw)
531 rotation_angle := 90 * rotation
532 scale_str := '${app.scale:.2}'
533 app.bl.clear()
534 app.bl.write_string('${num}/${of_num}')
535 app.bl.write_string(' [${app.img_w}x${app.img_h}]=>[${x_screen}x${y_screen}]')
536 app.bl.write_string(' ${app.item_list.lst[app.item_list.item_index].name}')
537 app.bl.write_string(' scale: ${scale_str} rotation: ${rotation_angle}')
538 draw_text(mut app, app.bl.str(), 10, 10, 20)
539 } else {
540 if app.item_list.n_item <= 0 {
541 draw_text(mut app, text_drop_files, 10, 10, 20)
542 }
543 }
544 }
545
546 //
547 // Draw Help text
548 //
549 if app.show_help_flag == true {
550 mut txt_y := 30
551 for r in help_text_rows {
552 draw_text(mut app, r, 10, txt_y, 20)
553 txt_y += 20
554 }
555 }
556
557 app.gg.end()
558 app.frame_count++
559}
560
561// draw readable text
562fn draw_text(mut app App, in_txt string, in_x int, in_y int, fnt_sz f32) {
563 scale := app.gg.scale
564 font_size := int(fnt_sz * scale)
565
566 mut txt_conf_c0 := gx.TextCfg{
567 color: gx.white // gx.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
568 align: .left
569 size: font_size
570 }
571 mut txt_conf_c1 := gx.TextCfg{
572 color: gx.black // gx.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
573 align: .left
574 size: font_size
575 }
576
577 x := int(in_x * scale)
578 y := int(in_y * scale)
579 app.gg.draw_text(x + 2, y + 2, in_txt, txt_conf_c0)
580 app.gg.draw_text(x, y, in_txt, txt_conf_c1)
581}
582
583/******************************************************************************
584*
585* events management
586*
587******************************************************************************/
588fn clear_modifier_params(mut app App) {
589 app.scale = 1.0
590
591 app.sc_flag = false
592 app.sc_x = 0
593 app.sc_y = 0
594 app.last_sc_x = 0
595 app.last_sc_y = 0
596
597 app.tr_flag = false
598 app.tr_x = 0
599 app.tr_y = 0
600 app.last_tr_x = 0
601 app.last_tr_y = 0
602}
603
604fn my_event_manager(mut ev gg.Event, mut app App) {
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 = int(ev.mouse_x)
617 app.mouse_y = int(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 = int(touch_point.pos_x)
623 app.mouse_y = int(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: 0
797 // zip fields
798 zip: 0
799 item_list: 0
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}