ui_menu.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. let ui_menu_show: bool = false;
  2. let ui_menu_category: i32 = 0;
  3. let ui_menu_category_w: i32 = 0;
  4. let ui_menu_category_h: i32 = 0;
  5. let ui_menu_x: i32 = 0;
  6. let ui_menu_y: i32 = 0;
  7. let ui_menu_elements: i32 = 0;
  8. let ui_menu_keep_open: bool = false;
  9. let ui_menu_commands: (ui: zui_t)=>void = null;
  10. let ui_menu_show_first: bool = true;
  11. let ui_menu_hide_flag: bool = false;
  12. let _ui_menu_render_msg: string;
  13. function ui_menu_render() {
  14. let ui: zui_t = base_ui_menu;
  15. let menu_w: i32 = ui_menu_commands != null ? math_floor(base_default_element_w * zui_SCALE(base_ui_menu) * 2.3) : math_floor(zui_ELEMENT_W(ui) * 2.3);
  16. let _BUTTON_COL: i32 = ui.ops.theme.BUTTON_COL;
  17. ui.ops.theme.BUTTON_COL = ui.ops.theme.SEPARATOR_COL;
  18. let _ELEMENT_OFFSET: i32 = ui.ops.theme.ELEMENT_OFFSET;
  19. ui.ops.theme.ELEMENT_OFFSET = 0;
  20. let _ELEMENT_H: i32 = ui.ops.theme.ELEMENT_H;
  21. ui.ops.theme.ELEMENT_H = config_raw.touch_ui ? (28 + 2) : 28;
  22. zui_begin_region(ui, ui_menu_x, ui_menu_y, menu_w);
  23. if (ui_menu_commands != null) {
  24. g2_set_color(ui.ops.theme.ACCENT_SELECT_COL);
  25. zui_draw_rect(true, ui._x + -1, ui._y + -1, ui._w + 2, zui_ELEMENT_H(ui) * ui_menu_elements + 2);
  26. g2_set_color(ui.ops.theme.SEPARATOR_COL);
  27. zui_draw_rect(true, ui._x + 0, ui._y + 0, ui._w, zui_ELEMENT_H(ui) * ui_menu_elements);
  28. g2_set_color(0xffffffff);
  29. ui_menu_commands(ui);
  30. }
  31. else {
  32. ui_menu_start(ui);
  33. if (ui_menu_category == menu_category_t.FILE) {
  34. if (ui_menu_button(ui, tr("New .."), map_get(config_keymap, "file_new"))) {
  35. project_new_box();
  36. }
  37. if (ui_menu_button(ui, tr("Open..."), map_get(config_keymap, "file_open"))) {
  38. project_open();
  39. }
  40. if (ui_menu_button(ui, tr("Open Recent..."), map_get(config_keymap, "file_open_recent"))) {
  41. box_projects_show();
  42. }
  43. if (ui_menu_button(ui, tr("Save"), map_get(config_keymap, "file_save"))) {
  44. project_save();
  45. }
  46. if (ui_menu_button(ui, tr("Save As..."), map_get(config_keymap, "file_save_as"))) {
  47. project_save_as();
  48. }
  49. ui_menu_separator(ui);
  50. if (ui_menu_button(ui, tr("Import Texture..."), map_get(config_keymap, "file_import_assets"))) {
  51. project_import_asset(path_texture_formats.join(","), false);
  52. }
  53. if (ui_menu_button(ui, tr("Import Envmap..."))) {
  54. ui_files_show("hdr", false, false, function (path: string) {
  55. if (!ends_with(path, ".hdr")) {
  56. console_error(tr("Error: .hdr file expected"));
  57. return;
  58. }
  59. import_asset_run(path);
  60. });
  61. }
  62. ///if (is_paint || is_sculpt)
  63. if (ui_menu_button(ui, tr("Import Font..."))) {
  64. project_import_asset("ttf,ttc,otf");
  65. }
  66. if (ui_menu_button(ui, tr("Import Material..."))) {
  67. project_import_material();
  68. }
  69. if (ui_menu_button(ui, tr("Import Brush..."))) {
  70. project_import_brush();
  71. }
  72. ///end
  73. ///if (is_paint || is_lab)
  74. if (ui_menu_button(ui, tr("Import Swatches..."))) {
  75. project_import_swatches();
  76. }
  77. ///end
  78. if (ui_menu_button(ui, tr("Import Mesh..."))) {
  79. project_import_mesh();
  80. }
  81. if (ui_menu_button(ui, tr("Reimport Mesh"), map_get(config_keymap, "file_reimport_mesh"))) {
  82. project_reimport_mesh();
  83. }
  84. if (ui_menu_button(ui, tr("Reimport Textures"), map_get(config_keymap, "file_reimport_textures"))) {
  85. project_reimport_textures();
  86. }
  87. ui_menu_separator(ui);
  88. ///if (is_paint || is_lab)
  89. if (ui_menu_button(ui, tr("Export Textures..."), map_get(config_keymap, "file_export_textures_as"))) {
  90. ///if is_paint
  91. context_raw.layers_export = export_mode_t.VISIBLE;
  92. ///end
  93. box_export_show_textures();
  94. }
  95. if (ui_menu_button(ui, tr("Export Swatches..."))) {
  96. project_export_swatches();
  97. }
  98. ///end
  99. if (ui_menu_button(ui, tr("Export Mesh..."))) {
  100. context_raw.export_mesh_index = 0; // All
  101. box_export_show_mesh();
  102. }
  103. ///if is_paint
  104. if (ui_menu_button(ui, tr("Bake Material..."))) {
  105. box_export_show_bake_material();
  106. }
  107. ///end
  108. ui_menu_separator(ui);
  109. if (ui_menu_button(ui, tr("Exit"))) {
  110. sys_stop();
  111. }
  112. }
  113. else if (ui_menu_category == menu_category_t.EDIT) {
  114. let step_undo: string = "";
  115. let step_redo: string = "";
  116. if (history_undos > 0) {
  117. step_undo = history_steps[history_steps.length - 1 - history_redos].name;
  118. }
  119. if (history_redos > 0) {
  120. step_redo = history_steps[history_steps.length - history_redos].name;
  121. }
  122. ui.enabled = history_undos > 0;
  123. let vars_undo: map_t<string, string> = map_create();
  124. map_set(vars_undo, "step", step_undo);
  125. if (ui_menu_button(ui, tr("Undo {step}", vars_undo), map_get(config_keymap, "edit_undo"))) {
  126. history_undo();
  127. }
  128. ui.enabled = history_redos > 0;
  129. let vars_redo: map_t<string, string> = map_create();
  130. map_set(vars_redo, "step", step_redo);
  131. if (ui_menu_button(ui, tr("Redo {step}", vars_redo), map_get(config_keymap, "edit_redo"))) {
  132. history_redo();
  133. }
  134. ui.enabled = true;
  135. ui_menu_separator(ui);
  136. if (ui_menu_button(ui, tr("Preferences..."), map_get(config_keymap, "edit_prefs"))) {
  137. box_preferences_show();
  138. }
  139. }
  140. else if (ui_menu_category == menu_category_t.VIEWPORT) {
  141. if (ui_menu_button(ui, tr("Distract Free"), map_get(config_keymap, "view_distract_free"))) {
  142. ui_base_toggle_distract_free();
  143. ui_base_ui.is_hovered = false;
  144. }
  145. ///if !(krom_android || krom_ios)
  146. if (ui_menu_button(ui, tr("Toggle Fullscreen"), "alt+enter")) {
  147. base_toggle_fullscreen();
  148. }
  149. ///end
  150. ui.changed = false;
  151. ui_menu_fill(ui);
  152. let p: world_data_t = scene_world;
  153. let env_handle: zui_handle_t = zui_handle(__ID__);
  154. env_handle.value = p.strength;
  155. ui_menu_align(ui);
  156. p.strength = zui_slider(env_handle, tr("Environment"), 0.0, 8.0, true);
  157. if (env_handle.changed) {
  158. context_raw.ddirty = 2;
  159. }
  160. ui_menu_fill(ui);
  161. let enva_handle: zui_handle_t = zui_handle(__ID__);
  162. enva_handle.value = context_raw.envmap_angle / math_pi() * 180.0;
  163. if (enva_handle.value < 0) {
  164. enva_handle.value += (math_floor(-enva_handle.value / 360) + 1) * 360;
  165. }
  166. else if (enva_handle.value > 360) {
  167. enva_handle.value -= math_floor(enva_handle.value / 360) * 360;
  168. }
  169. ui_menu_align(ui);
  170. context_raw.envmap_angle = zui_slider(enva_handle, tr("Environment Angle"), 0.0, 360.0, true, 1) / 180.0 * math_pi();
  171. if (ui.is_hovered) {
  172. let vars: map_t<string, string> = map_create();
  173. map_set(vars, "shortcut", map_get(config_keymap, "rotate_envmap"));
  174. zui_tooltip(tr("{shortcut} and move mouse", vars));
  175. }
  176. if (enva_handle.changed) {
  177. context_raw.ddirty = 2;
  178. }
  179. if (scene_lights.length > 0) {
  180. let light: light_object_t = scene_lights[0];
  181. ui_menu_fill(ui);
  182. let lhandle: zui_handle_t = zui_handle(__ID__);
  183. let scale: f32 = 1333;
  184. lhandle.value = light.data.strength / scale;
  185. lhandle.value = math_floor(lhandle.value * 100) / 100;
  186. ui_menu_align(ui);
  187. light.data.strength = zui_slider(lhandle, tr("Light"), 0.0, 4.0, true) * scale;
  188. if (lhandle.changed) {
  189. context_raw.ddirty = 2;
  190. }
  191. ui_menu_fill(ui);
  192. light = scene_lights[0];
  193. let lahandle: zui_handle_t = zui_handle(__ID__);
  194. lahandle.value = context_raw.light_angle / math_pi() * 180;
  195. ui_menu_align(ui);
  196. let new_angle: f32 = zui_slider(lahandle, tr("Light Angle"), 0.0, 360.0, true, 1) / 180 * math_pi();
  197. if (ui.is_hovered) {
  198. let vars: map_t<string, string> = map_create();
  199. map_set(vars, "shortcut", map_get(config_keymap, "rotate_light"));
  200. zui_tooltip(tr("{shortcut} and move mouse", vars));
  201. }
  202. let ldiff: f32 = new_angle - context_raw.light_angle;
  203. if (math_abs(ldiff) > 0.005) {
  204. if (new_angle < 0) {
  205. new_angle += (math_floor(-new_angle / (2 * math_pi())) + 1) * 2 * math_pi();
  206. }
  207. else if (new_angle > 2 * math_pi()) {
  208. new_angle -= math_floor(new_angle / (2 * math_pi())) * 2 * math_pi();
  209. }
  210. context_raw.light_angle = new_angle;
  211. let m: mat4_t = mat4_rot_z(ldiff);
  212. mat4_mult_mat(light.base.transform.local, m);
  213. transform_decompose(light.base.transform);
  214. context_raw.ddirty = 2;
  215. }
  216. ui_menu_fill(ui);
  217. let sxhandle: zui_handle_t = zui_handle(__ID__);
  218. sxhandle.value = light.data.size;
  219. ui_menu_align(ui);
  220. light.data.size = zui_slider(sxhandle, tr("Light Size"), 0.0, 4.0, true);
  221. if (sxhandle.changed) {
  222. context_raw.ddirty = 2;
  223. }
  224. }
  225. ///if (is_paint || is_sculpt)
  226. ui_menu_fill(ui);
  227. let split_view_handle = zui_handle(__ID__);
  228. if (split_view_handle.init) {
  229. split_view_handle.selected = context_raw.split_view;
  230. }
  231. context_raw.split_view = zui_check(split_view_handle, " " + tr("Split View"));
  232. if (split_view_handle.changed) {
  233. base_resize();
  234. }
  235. ///end
  236. ///if is_lab
  237. ui_menu_fill(ui);
  238. let brush_scale_handle: zui_handle_t = zui_handle(__ID__);
  239. if (brush_scale_handle.init) {
  240. brush_scale_handle.value = context_raw.brush_scale;
  241. }
  242. ui_menu_align(ui);
  243. context_raw.brush_scale = zui_slider(brush_scale_handle, tr("UV Scale"), 0.01, 5.0, true);
  244. if (brush_scale_handle.changed) {
  245. make_material_parse_mesh_material();
  246. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  247. render_path_raytrace_uv_scale = context_raw.brush_scale;
  248. render_path_raytrace_ready = false;
  249. ///end
  250. }
  251. ///end
  252. ui_menu_fill(ui);
  253. let cull_handle: zui_handle_t = zui_handle(__ID__);
  254. if (cull_handle.init) {
  255. cull_handle.selected = context_raw.cull_backfaces;
  256. }
  257. context_raw.cull_backfaces = zui_check(cull_handle, " " + tr("Cull Backfaces"));
  258. if (cull_handle.changed) {
  259. make_material_parse_mesh_material();
  260. }
  261. ui_menu_fill(ui);
  262. let filter_handle: zui_handle_t = zui_handle(__ID__);
  263. if (filter_handle.init) {
  264. filter_handle.selected = context_raw.texture_filter;
  265. }
  266. context_raw.texture_filter = zui_check(filter_handle, " " + tr("Filter Textures"));
  267. if (filter_handle.changed) {
  268. make_material_parse_paint_material();
  269. make_material_parse_mesh_material();
  270. }
  271. ///if (is_paint || is_sculpt)
  272. ui_menu_fill(ui);
  273. context_raw.draw_wireframe = zui_check(context_raw.wireframe_handle, " " + tr("Wireframe"));
  274. if (context_raw.wireframe_handle.changed) {
  275. let current: image_t = _g2_current;
  276. g2_end();
  277. util_uv_cache_uv_map();
  278. g2_begin(current);
  279. make_material_parse_mesh_material();
  280. }
  281. ///end
  282. ///if is_paint
  283. ui_menu_fill(ui);
  284. context_raw.draw_texels = zui_check(context_raw.texels_handle, " " + tr("Texels"));
  285. if (context_raw.texels_handle.changed) {
  286. make_material_parse_mesh_material();
  287. }
  288. ///end
  289. ui_menu_fill(ui);
  290. let compass_handle: zui_handle_t = zui_handle(__ID__);
  291. if (compass_handle.init) {
  292. compass_handle.selected = context_raw.show_compass;
  293. }
  294. context_raw.show_compass = zui_check(compass_handle, " " + tr("Compass"));
  295. if (compass_handle.changed) {
  296. context_raw.ddirty = 2;
  297. }
  298. ui_menu_fill(ui);
  299. context_raw.show_envmap = zui_check(context_raw.show_envmap_handle, " " + tr("Envmap"));
  300. if (context_raw.show_envmap_handle.changed) {
  301. context_load_envmap();
  302. context_raw.ddirty = 2;
  303. }
  304. ui_menu_fill(ui);
  305. context_raw.show_envmap_blur = zui_check(context_raw.show_envmap_blur_handle, " " + tr("Blur Envmap"));
  306. if (context_raw.show_envmap_blur_handle.changed) {
  307. context_raw.ddirty = 2;
  308. }
  309. context_update_envmap();
  310. if (ui.changed) {
  311. ui_menu_keep_open = true;
  312. }
  313. }
  314. else if (ui_menu_category == menu_category_t.MODE) {
  315. let mode_handle: zui_handle_t = zui_handle(__ID__);
  316. mode_handle.position = context_raw.viewport_mode;
  317. let modes: string[] = [
  318. tr("Lit"),
  319. tr("Base Color"),
  320. ///if (is_paint || is_lab)
  321. tr("Normal"),
  322. tr("Occlusion"),
  323. tr("Roughness"),
  324. tr("Metallic"),
  325. tr("Opacity"),
  326. tr("Height"),
  327. ///end
  328. ///if (is_paint)
  329. tr("Emission"),
  330. tr("Subsurface"),
  331. ///end
  332. ///if (is_paint || is_sculpt)
  333. tr("TexCoord"),
  334. tr("Object Normal"),
  335. tr("Material ID"),
  336. tr("Object ID"),
  337. tr("Mask")
  338. ///end
  339. ];
  340. let shortcuts: string[] = ["l", "b", "n", "o", "r", "m", "a", "h", "e", "s", "t", "1", "2", "3", "4"];
  341. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  342. if (krom_raytrace_supported()) {
  343. array_push(modes, tr("Path Traced"));
  344. array_push(shortcuts, "p");
  345. }
  346. ///end
  347. for (let i: i32 = 0; i < modes.length; ++i) {
  348. ui_menu_fill(ui);
  349. let shortcut: string = config_raw.touch_ui ? "" : map_get(config_keymap, "viewport_mode") + ", " + shortcuts[i];
  350. zui_radio(mode_handle, i, modes[i], shortcut);
  351. }
  352. if (mode_handle.changed) {
  353. context_set_viewport_mode(mode_handle.position);
  354. // TODO: rotate mode is not supported for path tracing yet
  355. if (mode_handle.position == viewport_mode_t.PATH_TRACE && context_raw.camera_controls == camera_controls_t.ROTATE) {
  356. context_raw.camera_controls = camera_controls_t.ORBIT;
  357. viewport_reset();
  358. }
  359. }
  360. }
  361. else if (ui_menu_category == menu_category_t.CAMERA) {
  362. if (ui_menu_button(ui, tr("Reset"), map_get(config_keymap, "view_reset"))) {
  363. viewport_reset();
  364. viewport_scale_to_bounds();
  365. }
  366. ui_menu_separator(ui);
  367. if (ui_menu_button(ui, tr("Front"), map_get(config_keymap, "view_front"))) {
  368. viewport_set_view(0, -1, 0, math_pi() / 2, 0, 0);
  369. }
  370. if (ui_menu_button(ui, tr("Back"), map_get(config_keymap, "view_back"))) {
  371. viewport_set_view(0, 1, 0, math_pi() / 2, 0, math_pi());
  372. }
  373. if (ui_menu_button(ui, tr("Right"), map_get(config_keymap, "view_right"))) {
  374. viewport_set_view(1, 0, 0, math_pi() / 2, 0, math_pi() / 2);
  375. }
  376. if (ui_menu_button(ui, tr("Left"), map_get(config_keymap, "view_left"))) {
  377. viewport_set_view(-1, 0, 0, math_pi() / 2, 0, -math_pi() / 2);
  378. }
  379. if (ui_menu_button(ui, tr("Top"), map_get(config_keymap, "view_top"))) {
  380. viewport_set_view(0, 0, 1, 0, 0, 0);
  381. }
  382. if (ui_menu_button(ui, tr("Bottom"), map_get(config_keymap, "view_bottom"))) {
  383. viewport_set_view(0, 0, -1, math_pi(), 0, math_pi());
  384. }
  385. ui_menu_separator(ui);
  386. ui.changed = false;
  387. if (ui_menu_button(ui, tr("Orbit Left"), map_get(config_keymap, "view_orbit_left"))) {
  388. viewport_orbit(-math_pi() / 12, 0);
  389. }
  390. if (ui_menu_button(ui, tr("Orbit Right"), map_get(config_keymap, "view_orbit_right"))) {
  391. viewport_orbit(math_pi() / 12, 0);
  392. }
  393. if (ui_menu_button(ui, tr("Orbit Up"), map_get(config_keymap, "view_orbit_up"))) {
  394. viewport_orbit(0, -math_pi() / 12);
  395. }
  396. if (ui_menu_button(ui, tr("Orbit Down"), map_get(config_keymap, "view_orbit_down"))) {
  397. viewport_orbit(0, math_pi() / 12);
  398. }
  399. if (ui_menu_button(ui, tr("Orbit Opposite"), map_get(config_keymap, "view_orbit_opposite"))) {
  400. viewport_orbit_opposite();
  401. }
  402. if (ui_menu_button(ui, tr("Zoom In"), map_get(config_keymap, "view_zoom_in"))) {
  403. viewport_zoom(0.2);
  404. }
  405. if (ui_menu_button(ui, tr("Zoom Out"), map_get(config_keymap, "view_zoom_out"))) {
  406. viewport_zoom(-0.2);
  407. }
  408. // menuSeparator(ui);
  409. ui_menu_fill(ui);
  410. let cam: camera_object_t = scene_camera;
  411. context_raw.fov_handle = zui_handle(__ID__);
  412. if (context_raw.fov_handle.init) {
  413. context_raw.fov_handle.value = math_floor(cam.data.fov * 100) / 100;
  414. }
  415. ui_menu_align(ui);
  416. cam.data.fov = zui_slider(context_raw.fov_handle, tr("FoV"), 0.3, 1.4, true);
  417. if (context_raw.fov_handle.changed) {
  418. viewport_update_camera_type(context_raw.camera_type);
  419. }
  420. ui_menu_fill(ui);
  421. ui_menu_align(ui);
  422. let camera_controls_handle: zui_handle_t = zui_handle(__ID__);
  423. camera_controls_handle.position = context_raw.camera_controls;
  424. context_raw.camera_controls = zui_inline_radio(camera_controls_handle, [tr("Orbit"), tr("Rotate"), tr("Fly")], zui_align_t.LEFT);
  425. let vars: map_t<string, string> = map_create();
  426. map_set(vars, "rotate_shortcut", map_get(config_keymap, "action_rotate"));
  427. map_set(vars, "zoom_shortcut", map_get(config_keymap, "action_zoom"));
  428. map_set(vars, "pan_shortcut", map_get(config_keymap, "action_pan"));
  429. let orbit_and_rotate_tooltip: string = tr("Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan.", vars);
  430. let fly_tooltip: string = tr("Fly mode:\nHold the right mouse button and one of the following commands:\nmove mouse to rotate.\nw, up or scroll up to move forward.\ns, down or scroll down to move backward.\na or left to move left.\nd or right to move right.\ne to move up.\nq to move down.\nHold shift to move faster or alt to move slower.");
  431. if (ui.is_hovered) {
  432. zui_tooltip(orbit_and_rotate_tooltip + "\n\n" + fly_tooltip);
  433. }
  434. ui_menu_fill(ui);
  435. ui_menu_align(ui);
  436. context_raw.camera_type = zui_inline_radio(context_raw.cam_handle, [tr("Perspective"), tr("Orthographic")], zui_align_t.LEFT);
  437. if (ui.is_hovered) {
  438. zui_tooltip(tr("Camera Type") + " (" + map_get(config_keymap, "view_camera_type") + ")");
  439. }
  440. if (context_raw.cam_handle.changed) {
  441. viewport_update_camera_type(context_raw.camera_type);
  442. }
  443. if (ui.changed) {
  444. ui_menu_keep_open = true;
  445. }
  446. }
  447. else if (ui_menu_category == menu_category_t.HELP) {
  448. if (ui_menu_button(ui, tr("Manual"))) {
  449. file_load_url(manifest_url + "/manual");
  450. }
  451. if (ui_menu_button(ui, tr("How To"))) {
  452. file_load_url(manifest_url + "/howto");
  453. }
  454. if (ui_menu_button(ui, tr("What's New"))) {
  455. file_load_url(manifest_url + "/notes");
  456. }
  457. if (ui_menu_button(ui, tr("Issue Tracker"))) {
  458. file_load_url("https://github.com/armory3d/armortools/issues");
  459. }
  460. if (ui_menu_button(ui, tr("Report Bug"))) {
  461. ///if (krom_darwin || krom_ios) // Limited url length
  462. file_load_url("https://github.com/armory3d/armortools/issues/new?labels=bug&template=bug_report.md&body=*" + manifest_title + "%20" + manifest_version + "-" + config_get_sha() + ",%20" + sys_system_id());
  463. ///else
  464. file_load_url("https://github.com/armory3d/armortools/issues/new?labels=bug&template=bug_report.md&body=*" + manifest_title + "%20" + manifest_version + "-" + config_get_sha() + ",%20" + sys_system_id() + "*%0A%0A**Issue description:**%0A%0A**Steps to reproduce:**%0A%0A");
  465. ///end
  466. }
  467. if (ui_menu_button(ui, tr("Request Feature"))) {
  468. ///if (krom_darwin || krom_ios) // Limited url length
  469. file_load_url("https://github.com/armory3d/armortools/issues/new?labels=feature%20request&template=feature_request.md&body=*" + manifest_title + "%20" + manifest_version + "-" + config_get_sha() + ",%20" + sys_system_id());
  470. ///else
  471. file_load_url("https://github.com/armory3d/armortools/issues/new?labels=feature%20request&template=feature_request.md&body=*" + manifest_title + "%20" + manifest_version + "-" + config_get_sha() + ",%20" + sys_system_id() + "*%0A%0A**Feature description:**%0A%0A");
  472. ///end
  473. }
  474. ui_menu_separator(ui);
  475. if (ui_menu_button(ui, tr("Check for Updates..."))) {
  476. ///if krom_android
  477. file_load_url(manifest_url_android);
  478. ///elseif krom_ios
  479. file_load_url(manifest_url_ios);
  480. ///else
  481. // Retrieve latest version number
  482. file_download_bytes("https://server.armorpaint.org/" + to_lower_case(manifest_title) + ".html", function (url: string, buffer: buffer_t) {
  483. if (buffer != null) {
  484. // Compare versions
  485. let update: any = json_parse(sys_buffer_to_string(buffer));
  486. let update_version: i32 = math_floor(update.version);
  487. if (update_version > 0) {
  488. let date: string = config_get_date(); // 2019 -> 19
  489. date = substring(date, 2, date.length);
  490. let date_int: i32 = parse_int(string_replace_all(date, "-", ""));
  491. if (update_version > date_int) {
  492. let vars: map_t<string, string> = map_create();
  493. map_set(vars, "url", manifest_url);
  494. ui_box_show_message(tr("Update"), tr("Update is available!\nPlease visit {url}.", vars));
  495. }
  496. else {
  497. ui_box_show_message(tr("Update"), tr("You are up to date!"));
  498. }
  499. }
  500. }
  501. else {
  502. let vars: map_t<string, string> = map_create();
  503. map_set(vars, "url", manifest_url);
  504. ui_box_show_message(tr("Update"), tr("Unable to check for updates.\nPlease visit {url}.", vars));
  505. }
  506. });
  507. ///end
  508. }
  509. if (ui_menu_button(ui, tr("About..."))) {
  510. let msg: string = manifest_title + ".org - v" + manifest_version + " (" + config_get_date() + ") - " + config_get_sha() + "\n";
  511. msg += sys_system_id() + " - " + strings_graphics_api();
  512. ///if krom_windows
  513. let save: string = (path_is_protected() ? krom_save_path() : path_data()) + path_sep + "tmp.txt";
  514. krom_sys_command("wmic path win32_VideoController get name > \"" + save + "\"");
  515. let blob: buffer_t = krom_load_blob(save);
  516. let u8: u8_array_t = u8_array_create_from_buffer(blob);
  517. let gpu_raw: string = "";
  518. for (let i: i32 = 0; i < math_floor(u8.length / 2); ++i) {
  519. let c: string = string_from_char_code(u8[i * 2]);
  520. gpu_raw += c;
  521. }
  522. let gpus: string[] = string_split(gpu_raw, "\n");
  523. array_splice(gpus, 1, gpus.length - 2);
  524. let gpu: string = "";
  525. for (let i: i32 = 0; i < gpus.length; ++i) {
  526. let g: string = gpus[i];
  527. gpu += trim_end(g) + ", ";
  528. }
  529. gpu = substring(gpu, 0, gpu.length - 2);
  530. msg += "\n" + gpu;
  531. ///else
  532. // { lshw -C display }
  533. ///end
  534. _ui_menu_render_msg = msg;
  535. ui_box_show_custom(function (ui: zui_t) {
  536. let tab_vertical: bool = config_raw.touch_ui;
  537. if (zui_tab(zui_handle(__ID__), tr("About"), tab_vertical)) {
  538. let img: image_t = data_get_image("badge.k");
  539. zui_image(img);
  540. zui_end_element();
  541. let h: zui_handle_t = zui_handle(__ID__);
  542. if (h.init) {
  543. h.text = _ui_menu_render_msg;
  544. }
  545. zui_text_area(h, zui_align_t.LEFT, false);
  546. let row: f32[] = [1 / 3, 1 / 3, 1 / 3];
  547. zui_row(row);
  548. ///if (krom_windows || krom_linux || krom_darwin)
  549. if (zui_button(tr("Copy"))) {
  550. krom_copy_to_clipboard(_ui_menu_render_msg);
  551. }
  552. ///else
  553. zui_end_element();
  554. ///end
  555. if (zui_button(tr("Contributors"))) {
  556. file_load_url("https://github.com/armory3d/armortools/graphs/contributors");
  557. }
  558. if (zui_button(tr("OK"))) {
  559. ui_box_hide();
  560. }
  561. }
  562. }, 400, 320);
  563. }
  564. }
  565. }
  566. ui_menu_hide_flag = ui.combo_selected_handle_ptr == 0 && !ui_menu_keep_open && !ui_menu_show_first && (ui.changed || ui.input_released || ui.input_released_r || ui.is_escape_down);
  567. ui_menu_show_first = false;
  568. ui_menu_keep_open = false;
  569. ui.ops.theme.BUTTON_COL = _BUTTON_COL;
  570. ui.ops.theme.ELEMENT_OFFSET = _ELEMENT_OFFSET;
  571. ui.ops.theme.ELEMENT_H = _ELEMENT_H;
  572. zui_end_region();
  573. if (ui_menu_hide_flag) {
  574. ui_menu_hide();
  575. ui_menu_show_first = true;
  576. ui_menu_commands = null;
  577. }
  578. }
  579. function ui_menu_hide() {
  580. ui_menu_show = false;
  581. base_redraw_ui();
  582. }
  583. function ui_menu_draw(commands: (ui: zui_t)=>void = null, elements: i32, x: i32 = -1, y: i32 = -1) {
  584. zui_end_input();
  585. ui_menu_show = true;
  586. ui_menu_commands = commands;
  587. ui_menu_elements = elements;
  588. ui_menu_x = x > -1 ? x : math_floor(mouse_x + 1);
  589. ui_menu_y = y > -1 ? y : math_floor(mouse_y + 1);
  590. ui_menu_fit_to_screen();
  591. }
  592. function ui_menu_fit_to_screen() {
  593. // Prevent the menu going out of screen
  594. let menu_w: f32 = base_default_element_w * zui_SCALE(base_ui_menu) * 2.3;
  595. if (ui_menu_x + menu_w > sys_width()) {
  596. if (ui_menu_x - menu_w > 0) {
  597. ui_menu_x = math_floor(ui_menu_x - menu_w);
  598. }
  599. else {
  600. ui_menu_x = math_floor(sys_width() - menu_w);
  601. }
  602. }
  603. let menu_h: f32 = math_floor(ui_menu_elements * 30 * zui_SCALE(base_ui_menu)); // ui.ops.theme.ELEMENT_H
  604. if (ui_menu_y + menu_h > sys_height()) {
  605. if (ui_menu_y - menu_h > 0) {
  606. ui_menu_y = math_floor(ui_menu_y - menu_h);
  607. }
  608. else {
  609. ui_menu_y = sys_height() - menu_h;
  610. }
  611. ui_menu_x += 1; // Move out of mouse focus
  612. }
  613. }
  614. function ui_menu_fill(ui: zui_t) {
  615. g2_set_color(ui.ops.theme.ACCENT_SELECT_COL);
  616. g2_fill_rect(ui._x - 1, ui._y, ui._w + 2, zui_ELEMENT_H(ui) + 1 + 1);
  617. g2_set_color(ui.ops.theme.SEPARATOR_COL);
  618. g2_fill_rect(ui._x, ui._y, ui._w, zui_ELEMENT_H(ui) + 1);
  619. g2_set_color(0xffffffff);
  620. }
  621. function ui_menu_separator(ui: zui_t) {
  622. ui._y++;
  623. if (config_raw.touch_ui) {
  624. zui_fill(0, 0, ui._w / zui_SCALE(ui), 1, ui.ops.theme.ACCENT_SELECT_COL);
  625. }
  626. else {
  627. zui_fill(26, 0, ui._w / zui_SCALE(ui) - 26, 1, ui.ops.theme.ACCENT_SELECT_COL);
  628. }
  629. }
  630. function ui_menu_button(ui: zui_t, text: string, label: string = ""): bool {
  631. ui_menu_fill(ui);
  632. if (config_raw.touch_ui) {
  633. label = "";
  634. }
  635. // let icons: image_t = icon > -1 ? get("icons.k") : null;
  636. // let r: rect_t = tile25(icons, icon, 8);
  637. // return Zui.button(config_button_spacing + text, config_button_align, label, icons, r.x, r.y, r.w, r.h);
  638. return zui_button(config_button_spacing + text, config_button_align, label);
  639. }
  640. function ui_menu_align(ui: zui_t) {
  641. if (!config_raw.touch_ui) {
  642. let row: f32[] = [12 / 100, 88 / 100];
  643. zui_row(row);
  644. zui_end_element();
  645. }
  646. }
  647. function ui_menu_start(ui: zui_t) {
  648. // Draw top border
  649. g2_set_color(ui.ops.theme.ACCENT_SELECT_COL);
  650. if (config_raw.touch_ui) {
  651. g2_fill_rect(ui._x + ui._w / 2 + ui_menu_category_w / 2, ui._y - 1, ui._w / 2 - ui_menu_category_w / 2 + 1, 1);
  652. g2_fill_rect(ui._x - 1, ui._y - 1, ui._w / 2 - ui_menu_category_w / 2 + 1, 1);
  653. g2_fill_rect(ui._x + ui._w / 2 - ui_menu_category_w / 2, ui._y - ui_menu_category_h, ui_menu_category_w, 1);
  654. g2_fill_rect(ui._x + ui._w / 2 - ui_menu_category_w / 2, ui._y - ui_menu_category_h, 1, ui_menu_category_h);
  655. g2_fill_rect(ui._x + ui._w / 2 + ui_menu_category_w / 2, ui._y - ui_menu_category_h, 1, ui_menu_category_h);
  656. }
  657. else {
  658. g2_fill_rect(ui._x - 1 + ui_menu_category_w, ui._y - 1, ui._w + 2 - ui_menu_category_w, 1);
  659. g2_fill_rect(ui._x - 1, ui._y - ui_menu_category_h, ui_menu_category_w, 1);
  660. g2_fill_rect(ui._x - 1, ui._y - ui_menu_category_h, 1, ui_menu_category_h);
  661. g2_fill_rect(ui._x - 1 + ui_menu_category_w, ui._y - ui_menu_category_h, 1, ui_menu_category_h);
  662. }
  663. g2_set_color(0xffffffff);
  664. }